Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

linker: More systematic handling of CRT objects #71769

Merged
merged 1 commit into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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,
Copy link

@tchebb tchebb Jul 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missing something, or is this logic inverted from what it should be? If crt_static() returns true, aren't we only allowed to create a DynamicDylib when crt_static_allows_dylibs is also true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case is here for consistency, but we should never hit it right now because if invalid_output_for_target is true then we skip the crate type and never get to linking.
But if we didn't skip it (like we don't skip PIC executables when +crt-static is enabled but not supported), then we would fall back from StaticDylib to 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")
Copy link
Contributor Author

@petrochenkov petrochenkov May 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only case of rustc interpreting user-provided raw linker arguments is removed here.
First, -static wins over -pie so we could still pass -pie if the user passed -static with the same effect.
Second, it was a hack, this static guessing wasn't done consistently in other places, equivalent cases like -Wl,-static weren't recognized, and we shouldn't be interpreting raw linker args in general.

{
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 {
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
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
Original file line number Diff line number Diff line change
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
Loading