Skip to content

Commit

Permalink
linker: More systematic handling of CRT objects
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed May 14, 2020
1 parent 23ffeea commit 49eb35c
Show file tree
Hide file tree
Showing 14 changed files with 405 additions and 152 deletions.
13 changes: 7 additions & 6 deletions src/bootstrap/compile.rs
Expand Up @@ -125,15 +125,16 @@ fn copy_third_party_objects(
target_deps.push(target);
};

// Copies the crt(1,i,n).o startup objects
// Copies the CRT objects.
//
// Since musl supports fully static linking, we can cross link for it even
// with a glibc-targeting toolchain, given we have the appropriate startup
// files. As those shipped with glibc won't work, copy the ones provided by
// musl so we have them on linux-gnu hosts.
// rustc historically provides a more self-contained installation for musl targets
// not requiring the presence of a native musl toolchain. For example, it can fall back
// to using gcc from a glibc-targeting toolchain for linking.
// To do that we have to distribute musl startup objects as a part of Rust toolchain
// and link with them manually in the self-contained mode.
if target.contains("musl") {
let srcdir = builder.musl_root(target).unwrap().join("lib");
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] {
copy_and_stamp(&srcdir, obj);
}
} else if target.ends_with("-wasi") {
Expand Down
147 changes: 86 additions & 61 deletions src/librustc_codegen_ssa/back/link.rs
Expand Up @@ -11,7 +11,9 @@ use rustc_session::search_paths::PathKind;
/// need out of the shared crate context before we get rid of it.
use rustc_session::{filesearch, Session};
use rustc_span::symbol::Symbol;
use rustc_target::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel};
use rustc_target::spec::crt_objects::CrtObjectsFallback;
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor};
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel};

use super::archive::ArchiveBuilder;
use super::command::Command;
Expand Down Expand Up @@ -1130,33 +1132,70 @@ fn exec_linker(
}
}

/// Add begin object files defined by the target spec.
fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: CrateType) {
let pre_link_objects = if crate_type == CrateType::Executable {
&sess.target.target.options.pre_link_objects_exe
} else {
&sess.target.target.options.pre_link_objects_dll
fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) {
(CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe,
(CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe,
(CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe,
(CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe,
(_, true, _) => LinkOutputKind::StaticDylib,
(_, false, _) => LinkOutputKind::DynamicDylib,
};
for obj in pre_link_objects {
cmd.add_object(&get_object_file_path(sess, obj));

// Adjust the output kind to target capabilities.
let pic_exe_supported = sess.target.target.options.position_independent_executables;
let static_pic_exe_supported = false; // FIXME: Add this option to target specs.
let static_dylib_supported = sess.target.target.options.crt_static_allows_dylibs;
match kind {
LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe,
LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe,
LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib,
_ => kind,
}
}

if crate_type == CrateType::Executable && sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
cmd.add_object(&get_object_file_path(sess, obj));
}
/// Whether we link to our own CRT objects instead of relying on gcc to pull them.
/// We only provide such support for a very limited number of targets.
fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
match sess.target.target.options.crt_objects_fallback {
// FIXME: Find a better heuristic for "native musl toolchain is available",
// based on host and linker path, for example.
// (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)),
// FIXME: Find some heuristic for "native mingw toolchain is available",
// likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429).
Some(CrtObjectsFallback::Mingw) => sess.target.target.target_vendor != "uwp",
// FIXME: Figure out cases in which WASM needs to link with a native toolchain.
Some(CrtObjectsFallback::Wasm) => true,
None => false,
}
}

/// Add end object files defined by the target spec.
fn add_post_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: CrateType) {
for obj in &sess.target.target.options.post_link_objects {
/// Add pre-link object files defined by the target spec.
fn add_pre_link_objects(
cmd: &mut dyn Linker,
sess: &Session,
link_output_kind: LinkOutputKind,
fallback: bool,
) {
let opts = &sess.target.target.options;
let objects = if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects };
for obj in objects.get(&link_output_kind).iter().copied().flatten() {
cmd.add_object(&get_object_file_path(sess, obj));
}
if sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.post_link_objects_crt {
cmd.add_object(&get_object_file_path(sess, obj));
}
}

/// Add post-link object files defined by the target spec.
fn add_post_link_objects(
cmd: &mut dyn Linker,
sess: &Session,
link_output_kind: LinkOutputKind,
fallback: bool,
) {
let opts = &sess.target.target.options;
let objects = if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects };
for obj in objects.get(&link_output_kind).iter().copied().flatten() {
cmd.add_object(&get_object_file_path(sess, obj));
}
}

Expand Down Expand Up @@ -1320,38 +1359,6 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) {
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
}

/// Add options requesting executables to be position-independent or not position-independent.
fn add_position_independent_executable_args(
cmd: &mut dyn Linker,
sess: &Session,
flavor: LinkerFlavor,
crate_type: CrateType,
codegen_results: &CodegenResults,
) {
if crate_type != CrateType::Executable {
return;
}

if sess.target.target.options.position_independent_executables {
let attr_link_args = &*codegen_results.crate_info.link_args;
let mut user_defined_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args);
if sess.relocation_model() == RelocModel::Pic
&& !sess.crt_static(Some(crate_type))
&& !user_defined_link_args.any(|x| x == "-static")
{
cmd.position_independent_executable();
return;
}
}

// Recent versions of gcc can be configured to generate position
// independent executables by default. We have to pass -no-pie to
// explicitly turn that off. Not applicable to ld.
if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld {
cmd.no_position_independent_executable();
}
}

/// Add options making relocation sections in the produced ELF files read-only
/// and suppressing lazy binding.
fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) {
Expand Down Expand Up @@ -1417,6 +1424,8 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
// to the linker args construction.
assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp");
let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu);
let link_output_kind = link_output_kind(sess, crate_type);
let crt_objects_fallback = crt_objects_fallback(sess, crate_type);

// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_pre_link_args(cmd, sess, flavor, crate_type);
Expand All @@ -1430,8 +1439,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
}

// NO-OPT-OUT, OBJECT-FILES-NO
if crt_objects_fallback {
cmd.no_crt_objects();
}

// NO-OPT-OUT, OBJECT-FILES-YES
add_pre_link_objects(cmd, sess, crate_type);
add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);

// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
if sess.target.target.options.is_like_emscripten {
Expand Down Expand Up @@ -1490,7 +1504,16 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
}

// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results);
// FIXME: Support `StaticPicExe` correctly.
match link_output_kind {
LinkOutputKind::DynamicPicExe | LinkOutputKind::StaticPicExe => {
cmd.position_independent_executable()
}
LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => {
cmd.no_position_independent_executable()
}
_ => {}
}

// OBJECT-FILES-NO, AUDIT-ORDER
add_relro_args(cmd, sess);
Expand Down Expand Up @@ -1520,12 +1543,14 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
);

// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// Tell the linker what we're doing.
if crate_type != CrateType::Executable {
cmd.build_dylib(out_filename);
}
if crate_type == CrateType::Executable && sess.crt_static(Some(crate_type)) {
cmd.build_static_executable();
// FIXME: Merge with the previous `link_output_kind` match,
// and support `StaticPicExe` and `StaticDylib` correctly.
match link_output_kind {
LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {
cmd.build_static_executable()
}
LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => cmd.build_dylib(out_filename),
_ => {}
}

// OBJECT-FILES-NO, AUDIT-ORDER
Expand All @@ -1551,7 +1576,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
add_late_link_args(cmd, sess, flavor, crate_type, codegen_results);

// NO-OPT-OUT, OBJECT-FILES-YES
add_post_link_objects(cmd, sess, crate_type);
add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);

// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_post_link_args(cmd, sess, flavor);
Expand Down
21 changes: 20 additions & 1 deletion src/librustc_codegen_ssa/back/linker.rs
Expand Up @@ -123,6 +123,7 @@ pub trait Linker {
fn pgo_gen(&mut self);
fn control_flow_guard(&mut self);
fn debuginfo(&mut self, strip: Strip);
fn no_crt_objects(&mut self);
fn no_default_libraries(&mut self);
fn build_dylib(&mut self, out_filename: &Path);
fn build_static_executable(&mut self);
Expand Down Expand Up @@ -266,7 +267,9 @@ impl<'a> Linker for GccLinker<'a> {
self.cmd.arg("-pie");
}
fn no_position_independent_executable(&mut self) {
self.cmd.arg("-no-pie");
if !self.is_ld {
self.cmd.arg("-no-pie");
}
}
fn full_relro(&mut self) {
self.linker_arg("-zrelro");
Expand Down Expand Up @@ -404,6 +407,12 @@ impl<'a> Linker for GccLinker<'a> {
}
}

fn no_crt_objects(&mut self) {
if !self.is_ld {
self.cmd.arg("-nostartfiles");
}
}

fn no_default_libraries(&mut self) {
if !self.is_ld {
self.cmd.arg("-nodefaultlibs");
Expand Down Expand Up @@ -644,6 +653,10 @@ impl<'a> Linker for MsvcLinker<'a> {
// noop
}

fn no_crt_objects(&mut self) {
// noop
}

fn no_default_libraries(&mut self) {
self.cmd.arg("/NODEFAULTLIB");
}
Expand Down Expand Up @@ -907,6 +920,8 @@ impl<'a> Linker for EmLinker<'a> {
});
}

fn no_crt_objects(&mut self) {}

fn no_default_libraries(&mut self) {
self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]);
}
Expand Down Expand Up @@ -1106,6 +1121,8 @@ impl<'a> Linker for WasmLd<'a> {
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
}

fn no_crt_objects(&mut self) {}

fn no_default_libraries(&mut self) {}

fn build_dylib(&mut self, _out_filename: &Path) {
Expand Down Expand Up @@ -1271,6 +1288,8 @@ impl<'a> Linker for PtxLinker<'a> {

fn pgo_gen(&mut self) {}

fn no_crt_objects(&mut self) {}

fn no_default_libraries(&mut self) {}

fn control_flow_guard(&mut self) {
Expand Down

0 comments on commit 49eb35c

Please sign in to comment.