Skip to content

Commit 379c255

Browse files
committed
linker: Factor out more parts of linker_with_args and add some docs
1 parent fd6fa68 commit 379c255

File tree

1 file changed

+183
-121
lines changed
  • src/librustc_codegen_ssa/back

1 file changed

+183
-121
lines changed

src/librustc_codegen_ssa/back/link.rs

Lines changed: 183 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,87 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &'a Session, flavor: LinkerFla
12551255
}
12561256
}
12571257

1258+
/// Add object files containing code from the current crate.
1259+
fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
1260+
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
1261+
cmd.add_object(obj);
1262+
}
1263+
}
1264+
1265+
/// Add object files for allocator code linked once for the whole crate tree.
1266+
fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
1267+
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
1268+
if let Some(obj) = obj {
1269+
cmd.add_object(obj);
1270+
}
1271+
}
1272+
1273+
/// Add object files containing metadata for the current crate.
1274+
fn add_local_crate_metadata_objects(
1275+
cmd: &mut dyn Linker,
1276+
crate_type: config::CrateType,
1277+
codegen_results: &CodegenResults,
1278+
) {
1279+
// When linking a dynamic library, we put the metadata into a section of the
1280+
// executable. This metadata is in a separate object file from the main
1281+
// object file, so we link that in here.
1282+
if crate_type == config::CrateType::Dylib || crate_type == config::CrateType::ProcMacro {
1283+
let obj = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref());
1284+
if let Some(obj) = obj {
1285+
cmd.add_object(obj);
1286+
}
1287+
}
1288+
}
1289+
1290+
/// Link native libraries corresponding to the current crate and all libraries corresponding to
1291+
/// all its dependency crates.
1292+
/// FIXME: Consider combining this with the functions above adding object files for the local crate.
1293+
fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<'a>>(
1294+
cmd: &mut dyn Linker,
1295+
sess: &'a Session,
1296+
crate_type: config::CrateType,
1297+
codegen_results: &CodegenResults,
1298+
tmpdir: &Path,
1299+
) {
1300+
// Take careful note of the ordering of the arguments we pass to the linker
1301+
// here. Linkers will assume that things on the left depend on things to the
1302+
// right. Things on the right cannot depend on things on the left. This is
1303+
// all formally implemented in terms of resolving symbols (libs on the right
1304+
// resolve unknown symbols of libs on the left, but not vice versa).
1305+
//
1306+
// For this reason, we have organized the arguments we pass to the linker as
1307+
// such:
1308+
//
1309+
// 1. The local object that LLVM just generated
1310+
// 2. Local native libraries
1311+
// 3. Upstream rust libraries
1312+
// 4. Upstream native libraries
1313+
//
1314+
// The rationale behind this ordering is that those items lower down in the
1315+
// list can't depend on items higher up in the list. For example nothing can
1316+
// depend on what we just generated (e.g., that'd be a circular dependency).
1317+
// Upstream rust libraries are not allowed to depend on our local native
1318+
// libraries as that would violate the structure of the DAG, in that
1319+
// scenario they are required to link to them as well in a shared fashion.
1320+
//
1321+
// Note that upstream rust libraries may contain native dependencies as
1322+
// well, but they also can't depend on what we just started to add to the
1323+
// link line. And finally upstream native libraries can't depend on anything
1324+
// in this DAG so far because they're only dylibs and dylibs can only depend
1325+
// on other dylibs (e.g., other native deps).
1326+
//
1327+
// If -Zlink-native-libraries=false is set, then the assumption is that an
1328+
// external build system already has the native dependencies defined, and it
1329+
// will provide them to the linker itself.
1330+
if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) {
1331+
add_local_native_libraries(cmd, sess, codegen_results);
1332+
}
1333+
add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir);
1334+
if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) {
1335+
add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
1336+
}
1337+
}
1338+
12581339
/// Add sysroot and other globally set directories to the directory search list.
12591340
fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &'a Session) {
12601341
// The default library location, we need this to find the runtime.
@@ -1271,6 +1352,95 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &'a Session) {
12711352
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
12721353
}
12731354

1355+
/// Add options requesting executables to be position-independent or not position-independent.
1356+
fn add_position_independent_executable_args(
1357+
cmd: &mut dyn Linker,
1358+
sess: &'a Session,
1359+
flavor: LinkerFlavor,
1360+
crate_type: config::CrateType,
1361+
codegen_results: &CodegenResults,
1362+
) {
1363+
if crate_type != config::CrateType::Executable {
1364+
return;
1365+
}
1366+
1367+
let mut position_independent_executable = false;
1368+
if sess.target.target.options.position_independent_executables {
1369+
let attr_link_args = &*codegen_results.crate_info.link_args;
1370+
let mut user_defined_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args);
1371+
if is_pic(sess)
1372+
&& !sess.crt_static(Some(crate_type))
1373+
&& !user_defined_link_args.any(|x| x == "-static")
1374+
{
1375+
position_independent_executable = true;
1376+
}
1377+
}
1378+
1379+
if position_independent_executable {
1380+
cmd.position_independent_executable();
1381+
} else {
1382+
// recent versions of gcc can be configured to generate position
1383+
// independent executables by default. We have to pass -no-pie to
1384+
// explicitly turn that off. Not applicable to ld.
1385+
if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld {
1386+
cmd.no_position_independent_executable();
1387+
}
1388+
}
1389+
}
1390+
1391+
/// Add options making relocation sections in the produced ELF files read-only
1392+
/// and suppressing lazy binding.
1393+
fn add_relro_args(cmd: &mut dyn Linker, sess: &'a Session) {
1394+
let relro_level = match sess.opts.debugging_opts.relro_level {
1395+
Some(level) => level,
1396+
None => sess.target.target.options.relro_level,
1397+
};
1398+
match relro_level {
1399+
RelroLevel::Full => {
1400+
cmd.full_relro();
1401+
}
1402+
RelroLevel::Partial => {
1403+
cmd.partial_relro();
1404+
}
1405+
RelroLevel::Off => {
1406+
cmd.no_relro();
1407+
}
1408+
RelroLevel::None => {}
1409+
}
1410+
}
1411+
1412+
/// Add library search paths used at runtime by dynamic linkers.
1413+
fn add_rpath_args(
1414+
cmd: &mut dyn Linker,
1415+
sess: &'a Session,
1416+
codegen_results: &CodegenResults,
1417+
out_filename: &Path,
1418+
) {
1419+
// FIXME (#2397): At some point we want to rpath our guesses as to
1420+
// where extern libraries might live, based on the
1421+
// addl_lib_search_paths
1422+
if sess.opts.cg.rpath {
1423+
let target_triple = sess.opts.target_triple.triple();
1424+
let mut get_install_prefix_lib_path = || {
1425+
let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
1426+
let tlib = filesearch::relative_target_lib_path(&sess.sysroot, target_triple);
1427+
let mut path = PathBuf::from(install_prefix);
1428+
path.push(&tlib);
1429+
1430+
path
1431+
};
1432+
let mut rpath_config = RPathConfig {
1433+
used_crates: &codegen_results.crate_info.used_crates_dynamic,
1434+
out_filename: out_filename.to_path_buf(),
1435+
has_rpath: sess.target.target.options.has_rpath,
1436+
is_like_osx: sess.target.target.options.is_like_osx,
1437+
linker_is_gnu: sess.target.target.options.linker_is_gnu,
1438+
get_install_prefix_lib_path: &mut get_install_prefix_lib_path,
1439+
};
1440+
cmd.args(&rpath::get_rpath_flags(&mut rpath_config));
1441+
}
1442+
}
1443+
12741444
/// Produce the linker command line containing linker path and arguments.
12751445
/// `NO-OPT-OUT` marks the arguments that cannot be removed from the command line
12761446
/// by the user without creating a custom target specification.
@@ -1332,9 +1502,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
13321502
add_library_search_dirs(cmd, sess);
13331503

13341504
// OBJECT-FILES-YES
1335-
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
1336-
cmd.add_object(obj);
1337-
}
1505+
add_local_crate_regular_objects(cmd, codegen_results);
13381506

13391507
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
13401508
cmd.output_filename(out_filename);
@@ -1353,21 +1521,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
13531521
cmd.export_symbols(tmpdir, crate_type);
13541522

13551523
// OBJECT-FILES-YES
1356-
// When linking a dynamic library, we put the metadata into a section of the
1357-
// executable. This metadata is in a separate object file from the main
1358-
// object file, so we link that in here.
1359-
if crate_type == config::CrateType::Dylib || crate_type == config::CrateType::ProcMacro {
1360-
let obj = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref());
1361-
if let Some(obj) = obj {
1362-
cmd.add_object(obj);
1363-
}
1364-
}
1524+
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
13651525

13661526
// OBJECT-FILES-YES
1367-
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
1368-
if let Some(obj) = obj {
1369-
cmd.add_object(obj);
1370-
}
1527+
add_local_crate_allocator_objects(cmd, codegen_results);
13711528

13721529
// OBJECT-FILES-NO, AUDIT-ORDER
13731530
// FIXME: Order dependent, applies to the following objects. Where should it be placed?
@@ -1379,53 +1536,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
13791536
}
13801537

13811538
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
1382-
if crate_type == config::CrateType::Executable {
1383-
let mut position_independent_executable = false;
1384-
1385-
if sess.target.target.options.position_independent_executables {
1386-
if is_pic(sess)
1387-
&& !sess.crt_static(Some(crate_type))
1388-
&& !sess
1389-
.opts
1390-
.cg
1391-
.link_args
1392-
.iter()
1393-
.chain(&*codegen_results.crate_info.link_args)
1394-
.any(|x| x == "-static")
1395-
{
1396-
position_independent_executable = true;
1397-
}
1398-
}
1399-
1400-
if position_independent_executable {
1401-
cmd.position_independent_executable();
1402-
} else {
1403-
// recent versions of gcc can be configured to generate position
1404-
// independent executables by default. We have to pass -no-pie to
1405-
// explicitly turn that off. Not applicable to ld.
1406-
if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld {
1407-
cmd.no_position_independent_executable();
1408-
}
1409-
}
1410-
}
1539+
add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results);
14111540

14121541
// OBJECT-FILES-NO, AUDIT-ORDER
1413-
let relro_level = match sess.opts.debugging_opts.relro_level {
1414-
Some(level) => level,
1415-
None => sess.target.target.options.relro_level,
1416-
};
1417-
match relro_level {
1418-
RelroLevel::Full => {
1419-
cmd.full_relro();
1420-
}
1421-
RelroLevel::Partial => {
1422-
cmd.partial_relro();
1423-
}
1424-
RelroLevel::Off => {
1425-
cmd.no_relro();
1426-
}
1427-
RelroLevel::None => {}
1428-
}
1542+
add_relro_args(cmd, sess);
14291543

14301544
// OBJECT-FILES-NO, AUDIT-ORDER
14311545
// Pass optimization flags down to the linker.
@@ -1450,43 +1564,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
14501564
}
14511565

14521566
// OBJECT-FILES-YES
1453-
// Take careful note of the ordering of the arguments we pass to the linker
1454-
// here. Linkers will assume that things on the left depend on things to the
1455-
// right. Things on the right cannot depend on things on the left. This is
1456-
// all formally implemented in terms of resolving symbols (libs on the right
1457-
// resolve unknown symbols of libs on the left, but not vice versa).
1458-
//
1459-
// For this reason, we have organized the arguments we pass to the linker as
1460-
// such:
1461-
//
1462-
// 1. The local object that LLVM just generated
1463-
// 2. Local native libraries
1464-
// 3. Upstream rust libraries
1465-
// 4. Upstream native libraries
1466-
//
1467-
// The rationale behind this ordering is that those items lower down in the
1468-
// list can't depend on items higher up in the list. For example nothing can
1469-
// depend on what we just generated (e.g., that'd be a circular dependency).
1470-
// Upstream rust libraries are not allowed to depend on our local native
1471-
// libraries as that would violate the structure of the DAG, in that
1472-
// scenario they are required to link to them as well in a shared fashion.
1473-
//
1474-
// Note that upstream rust libraries may contain native dependencies as
1475-
// well, but they also can't depend on what we just started to add to the
1476-
// link line. And finally upstream native libraries can't depend on anything
1477-
// in this DAG so far because they're only dylibs and dylibs can only depend
1478-
// on other dylibs (e.g., other native deps).
1479-
//
1480-
// If -Zlink-native-libraries=false is set, then the assumption is that an
1481-
// external build system already has the native dependencies defined, and it
1482-
// will provide them to the linker itself.
1483-
if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) {
1484-
add_local_native_libraries(cmd, sess, codegen_results);
1485-
}
1486-
add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir);
1487-
if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) {
1488-
add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
1489-
}
1567+
link_local_crate_native_libs_and_dependent_crate_libs::<B>(
1568+
cmd,
1569+
sess,
1570+
crate_type,
1571+
codegen_results,
1572+
tmpdir,
1573+
);
14901574

14911575
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
14921576
// Tell the linker what we're doing.
@@ -1508,29 +1592,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
15081592
}
15091593

15101594
// OBJECT-FILES-NO, AUDIT-ORDER
1511-
// FIXME (#2397): At some point we want to rpath our guesses as to
1512-
// where extern libraries might live, based on the
1513-
// addl_lib_search_paths
1514-
if sess.opts.cg.rpath {
1515-
let target_triple = sess.opts.target_triple.triple();
1516-
let mut get_install_prefix_lib_path = || {
1517-
let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
1518-
let tlib = filesearch::relative_target_lib_path(&sess.sysroot, target_triple);
1519-
let mut path = PathBuf::from(install_prefix);
1520-
path.push(&tlib);
1521-
1522-
path
1523-
};
1524-
let mut rpath_config = RPathConfig {
1525-
used_crates: &codegen_results.crate_info.used_crates_dynamic,
1526-
out_filename: out_filename.to_path_buf(),
1527-
has_rpath: sess.target.target.options.has_rpath,
1528-
is_like_osx: sess.target.target.options.is_like_osx,
1529-
linker_is_gnu: sess.target.target.options.linker_is_gnu,
1530-
get_install_prefix_lib_path: &mut get_install_prefix_lib_path,
1531-
};
1532-
cmd.args(&rpath::get_rpath_flags(&mut rpath_config));
1533-
}
1595+
add_rpath_args(cmd, sess, codegen_results, out_filename);
15341596

15351597
// OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
15361598
add_user_defined_link_args(cmd, sess, codegen_results);

0 commit comments

Comments
 (0)