linker: More systematic handling of CRT objects

This commit is contained in:
Vadim Petrochenkov 2020-04-29 20:47:07 +03:00
parent 23ffeea307
commit 49eb35c05e
14 changed files with 406 additions and 153 deletions

View File

@ -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") {

View File

@ -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;
@ -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));
}
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));
}
// 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,
}
}
/// 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 {
/// 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 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));
}
}
@ -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) {
@ -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);
@ -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 {
@ -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);
@ -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
@ -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);

View File

@ -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);
@ -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");
@ -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");
@ -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");
}
@ -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=[]"]);
}
@ -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) {
@ -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) {

View File

@ -0,0 +1,145 @@
//! Object files providing support for basic runtime facilities and added to the produced binaries
//! at the start and at the end of linking.
//!
//! Table of CRT objects for popular toolchains.
//! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc.
//! See https://dev.gentoo.org/~vapier/crt.txt for some more details.
//!
//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi |
//! |----------------------|------------------------|------------------------|------------------|-------------------|------|
//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 |
//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 |
//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - |
//!
//! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi |
//! |-----------------------|---------------|---------------|----------------|--------|------|
//! | dynamic-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - |
//! | dynamic-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - |
//! | static-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - |
//! | static-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - |
//! | dynamic-dylib | crtendS, crtn | crtendS, crtn | crtend_so | crtend | - |
//! | static-dylib (gcc) | crtend, crtn | crtendS, crtn | crtend_so | crtend | - |
//! | static-dylib (clang) | crtendS, crtn | N/A | crtend_so | crtend | - |
//!
//! Use cases for rustc linking the CRT objects explicitly:
//! - rustc needs to add its own Rust-specific objects (mingw is the example)
//! - gcc wrapper cannot be used for some reason and linker like ld or lld is used directly.
//! - gcc wrapper pulls wrong CRT objects (e.g. from glibc when we are targeting musl).
//!
//! In general it is preferable to rely on the target's native toolchain to pull the objects.
//! However, for some targets (musl, mingw) rustc historically provides a more self-contained
//! installation not requiring users to install the native target's toolchain.
//! In that case rustc distributes the objects as a part of the target's Rust toolchain
//! and falls back to linking with them manually.
//! Unlike native toolchains, rustc only currently adds the libc's objects during linking,
//! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710)
//! when linking in self-contained mode.
use crate::spec::LinkOutputKind;
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use std::str::FromStr;
pub type CrtObjects = BTreeMap<LinkOutputKind, Vec<String>>;
pub(super) fn new(obj_table: &[(LinkOutputKind, &[&str])]) -> CrtObjects {
obj_table.iter().map(|(z, k)| (*z, k.iter().map(|b| b.to_string()).collect())).collect()
}
pub(super) fn all(obj: &str) -> CrtObjects {
new(&[
(LinkOutputKind::DynamicNoPicExe, &[obj]),
(LinkOutputKind::DynamicPicExe, &[obj]),
(LinkOutputKind::StaticNoPicExe, &[obj]),
(LinkOutputKind::StaticPicExe, &[obj]),
(LinkOutputKind::DynamicDylib, &[obj]),
(LinkOutputKind::StaticDylib, &[obj]),
])
}
pub(super) fn pre_musl_fallback() -> CrtObjects {
new(&[
(LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o"]),
(LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o"]),
(LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o"]),
(LinkOutputKind::StaticPicExe, &["rcrt1.o", "crti.o"]),
(LinkOutputKind::DynamicDylib, &["crti.o"]),
(LinkOutputKind::StaticDylib, &["crti.o"]),
])
}
pub(super) fn post_musl_fallback() -> CrtObjects {
all("crtn.o")
}
pub(super) fn pre_mingw_fallback() -> CrtObjects {
new(&[
(LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]),
(LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]),
(LinkOutputKind::StaticNoPicExe, &["crt2.o", "rsbegin.o"]),
(LinkOutputKind::StaticPicExe, &["crt2.o", "rsbegin.o"]),
(LinkOutputKind::DynamicDylib, &["dllcrt2.o", "rsbegin.o"]),
(LinkOutputKind::StaticDylib, &["dllcrt2.o", "rsbegin.o"]),
])
}
pub(super) fn post_mingw_fallback() -> CrtObjects {
all("rsend.o")
}
pub(super) fn pre_mingw() -> CrtObjects {
all("rsbegin.o")
}
pub(super) fn post_mingw() -> CrtObjects {
all("rsend.o")
}
pub(super) fn pre_wasi_fallback() -> CrtObjects {
new(&[
(LinkOutputKind::DynamicNoPicExe, &["crt1.o"]),
(LinkOutputKind::DynamicPicExe, &["crt1.o"]),
(LinkOutputKind::StaticNoPicExe, &["crt1.o"]),
(LinkOutputKind::StaticPicExe, &["crt1.o"]),
])
}
pub(super) fn post_wasi_fallback() -> CrtObjects {
new(&[])
}
/// Which logic to use to determine whether to fall back to the "self-contained" mode or not.
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CrtObjectsFallback {
Musl,
Mingw,
Wasm,
}
impl FromStr for CrtObjectsFallback {
type Err = ();
fn from_str(s: &str) -> Result<CrtObjectsFallback, ()> {
Ok(match s {
"musl" => CrtObjectsFallback::Musl,
"mingw" => CrtObjectsFallback::Mingw,
"wasm" => CrtObjectsFallback::Wasm,
_ => return Err(()),
})
}
}
impl ToJson for CrtObjectsFallback {
fn to_json(&self) -> Json {
match *self {
CrtObjectsFallback::Musl => "musl",
CrtObjectsFallback::Mingw => "mingw",
CrtObjectsFallback::Wasm => "wasm",
}
.to_json()
}
}

View File

@ -1,4 +1,4 @@
use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};
use crate::spec::{crt_objects, LinkArgs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions};
pub fn opts() -> TargetOptions {
let mut pre_link_args = LinkArgs::new();
@ -23,7 +23,12 @@ pub fn opts() -> TargetOptions {
linker_is_gnu: true,
has_rpath: false,
pre_link_args,
pre_link_objects_exe: vec!["Scrt1.o".to_string()],
pre_link_objects: crt_objects::new(&[
(LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]),
(LinkOutputKind::DynamicPicExe, &["Scrt1.o"]),
(LinkOutputKind::StaticNoPicExe, &["Scrt1.o"]),
(LinkOutputKind::StaticPicExe, &["Scrt1.o"]),
]),
position_independent_executables: true,
has_elf_tls: true,
..Default::default()

View File

@ -1,29 +1,18 @@
use crate::spec::crt_objects::{self, CrtObjectsFallback};
use crate::spec::{LinkerFlavor, TargetOptions};
pub fn opts() -> TargetOptions {
let mut base = super::linux_base::opts();
// Make sure that the linker/gcc really don't pull in anything, including
// default objects, libs, etc.
base.pre_link_args_crt.insert(LinkerFlavor::Gcc, Vec::new());
base.pre_link_args_crt.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string());
// At least when this was tested, the linker would not add the
// `GNU_EH_FRAME` program header to executables generated, which is required
// when unwinding to locate the unwinding information. I'm not sure why this
// argument is *not* necessary for normal builds, but it can't hurt!
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,--eh-frame-hdr".to_string());
// When generating a statically linked executable there's generally some
// small setup needed which is listed in these files. These are provided by
// a musl toolchain and are linked by default by the `musl-gcc` script. Note
// that `gcc` also does this by default, it just uses some different files.
//
// Each target directory for musl has these object files included in it so
// they'll be included from there.
base.pre_link_objects_exe_crt.push("crt1.o".to_string());
base.pre_link_objects_exe_crt.push("crti.o".to_string());
base.post_link_objects_crt.push("crtn.o".to_string());
base.pre_link_objects_fallback = crt_objects::pre_musl_fallback();
base.post_link_objects_fallback = crt_objects::post_musl_fallback();
base.crt_objects_fallback = Some(CrtObjectsFallback::Musl);
// These targets statically link libc by default
base.crt_static_default = true;

View File

@ -35,6 +35,7 @@
//! to the list specified by the target, rather than replace.
use crate::spec::abi::{lookup as lookup_abi, Abi};
use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
@ -44,6 +45,8 @@ use std::{fmt, io};
use rustc_macros::HashStable_Generic;
pub mod abi;
pub mod crt_objects;
mod android_base;
mod apple_base;
mod apple_sdk_base;
@ -341,6 +344,54 @@ impl ToJson for TlsModel {
}
}
/// Everything is flattened to a single enum to make the json encoding/decoding less annoying.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum LinkOutputKind {
/// Dynamically linked non position-independent executable.
DynamicNoPicExe,
/// Dynamically linked position-independent executable.
DynamicPicExe,
/// Statically linked non position-independent executable.
StaticNoPicExe,
/// Statically linked position-independent executable.
StaticPicExe,
/// Regular dynamic library ("dynamically linked").
DynamicDylib,
/// Dynamic library with bundled libc ("statically linked").
StaticDylib,
}
impl LinkOutputKind {
fn as_str(&self) -> &'static str {
match self {
LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe",
LinkOutputKind::DynamicPicExe => "dynamic-pic-exe",
LinkOutputKind::StaticNoPicExe => "static-nopic-exe",
LinkOutputKind::StaticPicExe => "static-pic-exe",
LinkOutputKind::DynamicDylib => "dynamic-dylib",
LinkOutputKind::StaticDylib => "static-dylib",
}
}
pub(super) fn from_str(s: &str) -> Option<LinkOutputKind> {
Some(match s {
"dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe,
"dynamic-pic-exe" => LinkOutputKind::DynamicPicExe,
"static-nopic-exe" => LinkOutputKind::StaticNoPicExe,
"static-pic-exe" => LinkOutputKind::StaticPicExe,
"dynamic-dylib" => LinkOutputKind::DynamicDylib,
"static-dylib" => LinkOutputKind::StaticDylib,
_ => return None,
})
}
}
impl fmt::Display for LinkOutputKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
pub enum LoadTargetError {
BuiltinTargetNotFound(String),
Other(String),
@ -644,13 +695,19 @@ pub struct TargetOptions {
/// Linker arguments that are passed *before* any user-defined libraries.
pub pre_link_args: LinkArgs, // ... unconditionally
pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt
/// Objects to link before all others, always found within the
/// sysroot folder.
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable, unconditionally
pub pre_link_objects_exe_crt: Vec<String>, // ... when linking an executable with a bundled crt
pub pre_link_objects_dll: Vec<String>, // ... when linking a dylib
/// Objects to link before and after all other object code.
pub pre_link_objects: CrtObjects,
pub post_link_objects: CrtObjects,
/// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the
/// target's native gcc and fall back to the "self-contained" mode and pull them manually.
/// See `crt_objects.rs` for some more detailed documentation.
pub pre_link_objects_fallback: CrtObjects,
pub post_link_objects_fallback: CrtObjects,
/// Which logic to use to determine whether to fall back to the "self-contained" mode or not.
pub crt_objects_fallback: Option<CrtObjectsFallback>,
/// Linker arguments that are unconditionally passed after any
/// user-defined but before post_link_objects. Standard platform
/// user-defined but before post-link objects. Standard platform
/// libraries that should be always be linked to, usually go here.
pub late_link_args: LinkArgs,
/// Linker arguments used in addition to `late_link_args` if at least one
@ -659,10 +716,6 @@ pub struct TargetOptions {
/// Linker arguments used in addition to `late_link_args` if aall Rust
/// dependencies are statically linked.
pub late_link_args_static: LinkArgs,
/// Objects to link after all others, always found within the
/// sysroot folder.
pub post_link_objects: Vec<String>, // ... unconditionally
pub post_link_objects_crt: Vec<String>, // ... when linking with a bundled crt
/// Linker arguments that are unconditionally passed *after* any
/// user-defined libraries.
pub post_link_args: LinkArgs,
@ -932,11 +985,11 @@ impl Default for TargetOptions {
position_independent_executables: false,
needs_plt: false,
relro_level: RelroLevel::None,
pre_link_objects_exe: Vec::new(),
pre_link_objects_exe_crt: Vec::new(),
pre_link_objects_dll: Vec::new(),
post_link_objects: Vec::new(),
post_link_objects_crt: Vec::new(),
pre_link_objects: Default::default(),
post_link_objects: Default::default(),
pre_link_objects_fallback: Default::default(),
post_link_objects_fallback: Default::default(),
crt_objects_fallback: None,
late_link_args: LinkArgs::new(),
late_link_args_dynamic: LinkArgs::new(),
late_link_args_static: LinkArgs::new(),
@ -1191,6 +1244,45 @@ impl Target {
})
})).unwrap_or(Ok(()))
} );
($key_name:ident, crt_objects_fallback) => ( {
let name = (stringify!($key_name)).replace("_", "-");
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<CrtObjectsFallback>() {
Ok(fallback) => base.options.$key_name = Some(fallback),
_ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \
Use 'musl', 'mingw' or 'wasm'", s))),
}
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
($key_name:ident, link_objects) => ( {
let name = (stringify!($key_name)).replace("_", "-");
if let Some(val) = obj.find(&name[..]) {
let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
JSON object with fields per CRT object kind.", name))?;
let mut args = CrtObjects::new();
for (k, v) in obj {
let kind = LinkOutputKind::from_str(&k).ok_or_else(|| {
format!("{}: '{}' is not a valid value for CRT object kind. \
Use '(dynamic,static)-(nopic,pic)-exe' or \
'(dynamic,static)-dylib'", name, k)
})?;
let v = v.as_array().ok_or_else(||
format!("{}.{}: expected a JSON array", name, k)
)?.iter().enumerate()
.map(|(i,s)| {
let s = s.as_string().ok_or_else(||
format!("{}.{}[{}]: expected a JSON string", name, k, i))?;
Ok(s.to_owned())
})
.collect::<Result<Vec<_>, String>>()?;
args.insert(kind, v);
}
base.options.$key_name = args;
}
} );
($key_name:ident, link_args) => ( {
let name = (stringify!($key_name)).replace("_", "-");
if let Some(val) = obj.find(&name[..]) {
@ -1238,16 +1330,16 @@ impl Target {
key!(is_builtin, bool);
key!(linker, optional);
key!(lld_flavor, LldFlavor)?;
key!(pre_link_objects, link_objects);
key!(post_link_objects, link_objects);
key!(pre_link_objects_fallback, link_objects);
key!(post_link_objects_fallback, link_objects);
key!(crt_objects_fallback, crt_objects_fallback)?;
key!(pre_link_args, link_args);
key!(pre_link_args_crt, link_args);
key!(pre_link_objects_exe, list);
key!(pre_link_objects_exe_crt, list);
key!(pre_link_objects_dll, list);
key!(late_link_args, link_args);
key!(late_link_args_dynamic, link_args);
key!(late_link_args_static, link_args);
key!(post_link_objects, list);
key!(post_link_objects_crt, list);
key!(post_link_args, link_args);
key!(link_env, env);
key!(link_env_remove, list);
@ -1468,16 +1560,16 @@ impl ToJson for Target {
target_option_val!(is_builtin);
target_option_val!(linker);
target_option_val!(lld_flavor);
target_option_val!(pre_link_objects);
target_option_val!(post_link_objects);
target_option_val!(pre_link_objects_fallback);
target_option_val!(post_link_objects_fallback);
target_option_val!(crt_objects_fallback);
target_option_val!(link_args - pre_link_args);
target_option_val!(link_args - pre_link_args_crt);
target_option_val!(pre_link_objects_exe);
target_option_val!(pre_link_objects_exe_crt);
target_option_val!(pre_link_objects_dll);
target_option_val!(link_args - late_link_args);
target_option_val!(link_args - late_link_args_dynamic);
target_option_val!(link_args - late_link_args_static);
target_option_val!(post_link_objects);
target_option_val!(post_link_objects_crt);
target_option_val!(link_args - post_link_args);
target_option_val!(env - link_env);
target_option_val!(link_env_remove);

View File

@ -1,3 +1,4 @@
use super::crt_objects::CrtObjectsFallback;
use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
use std::collections::BTreeMap;
@ -123,6 +124,8 @@ pub fn options() -> TargetOptions {
pre_link_args,
crt_objects_fallback: Some(CrtObjectsFallback::Wasm),
// This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
// PIC code is implemented this has quite a drastric effect if it stays
// at the default, `pic`. In an effort to keep wasm binaries as minimal

View File

@ -21,10 +21,6 @@ pub fn target() -> Result<Target, String> {
// otherwise
clang_args.push("--target=wasm32-unknown-unknown".to_string());
// Disable attempting to link crt1.o since it typically isn't present and
// isn't needed currently.
clang_args.push("-nostdlib".to_string());
// For now this target just never has an entry symbol no matter the output
// type, so unconditionally pass this.
clang_args.push("-Wl,--no-entry".to_string());

View File

@ -73,7 +73,7 @@
//! you know what you're getting in to!
use super::wasm32_base;
use super::{LinkerFlavor, LldFlavor, Target};
use super::{crt_objects, LinkerFlavor, LldFlavor, Target};
pub fn target() -> Result<Target, String> {
let mut options = wasm32_base::options();
@ -84,9 +84,8 @@ pub fn target() -> Result<Target, String> {
.or_insert(Vec::new())
.push("--target=wasm32-wasi".to_string());
// When generating an executable be sure to put the startup object at the
// front so the main function is correctly hooked up.
options.pre_link_objects_exe_crt.push("crt1.o".to_string());
options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback();
options.post_link_objects_fallback = crt_objects::post_wasi_fallback();
// Right now this is a bit of a workaround but we're currently saying that
// the target by default has a static crt which we're taking as a signal

View File

@ -1,3 +1,4 @@
use crate::spec::crt_objects::{self, CrtObjectsFallback};
use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
pub fn opts() -> TargetOptions {
@ -10,8 +11,6 @@ pub fn opts() -> TargetOptions {
"-fno-use-linker-plugin".to_string(),
// Always enable DEP (NX bit) when it is available
"-Wl,--nxcompat".to_string(),
// Do not use the standard system startup files or libraries when linking
"-nostdlib".to_string(),
],
);
@ -80,18 +79,14 @@ pub fn opts() -> TargetOptions {
is_like_windows: true,
allows_weak_linkage: false,
pre_link_args,
pre_link_objects_exe: vec![
"crt2.o".to_string(), // mingw C runtime initialization for executables
"rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs
],
pre_link_objects_dll: vec![
"dllcrt2.o".to_string(), // mingw C runtime initialization for dlls
"rsbegin.o".to_string(),
],
pre_link_objects: crt_objects::pre_mingw(),
post_link_objects: crt_objects::post_mingw(),
pre_link_objects_fallback: crt_objects::pre_mingw_fallback(),
post_link_objects_fallback: crt_objects::post_mingw_fallback(),
crt_objects_fallback: Some(CrtObjectsFallback::Mingw),
late_link_args,
late_link_args_dynamic,
late_link_args_static,
post_link_objects: vec!["rsend.o".to_string()],
abi_return_struct_as_int: true,
emit_debug_gdb_scripts: false,
requires_uwtable: true,

View File

@ -3,20 +3,8 @@ use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
pub fn opts() -> TargetOptions {
let base = super::windows_gnu_base::opts();
// FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`.
let mut pre_link_args = LinkArgs::new();
pre_link_args.insert(
LinkerFlavor::Gcc,
vec![
// Tell GCC to avoid linker plugins, because we are not bundling
// them with Windows installer, and Rust does its own LTO anyways.
"-fno-use-linker-plugin".to_string(),
// Always enable DEP (NX bit) when it is available
"-Wl,--nxcompat".to_string(),
],
);
// FIXME: This should be updated for the exception machinery changes from #67502.
// FIXME: This should be updated for the exception machinery changes from #67502
// and inherit from `windows_gnu_base`, at least partially.
let mut late_link_args = LinkArgs::new();
let late_link_args_dynamic = LinkArgs::new();
let late_link_args_static = LinkArgs::new();
@ -40,11 +28,6 @@ pub fn opts() -> TargetOptions {
TargetOptions {
executables: false,
limit_rdylib_exports: false,
pre_link_args,
// FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`.
pre_link_objects_exe: vec!["rsbegin.o".to_string()],
// FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`.
pre_link_objects_dll: vec!["rsbegin.o".to_string()],
late_link_args,
late_link_args_dynamic,
late_link_args_static,

View File

@ -1,6 +1,6 @@
use std::iter;
use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions};
use super::{crt_objects, LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions};
pub fn target() -> Result<Target, String> {
const PRE_LINK_ARGS: &[&str] = &[
@ -68,7 +68,8 @@ pub fn target() -> Result<Target, String> {
PRE_LINK_ARGS.iter().cloned().map(String::from).collect(),
))
.collect(),
post_link_objects: vec!["libunwind.a".into()],
// FIXME: libunwind is certainly not a CRT object, use some other option instead.
post_link_objects: crt_objects::all("libunwind.a"),
override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(String::from).collect()),
relax_elf_relocations: true,
..Default::default()

View File

@ -2684,11 +2684,11 @@ impl<A: ToJson> ToJson for Vec<A> {
}
}
impl<A: ToJson> ToJson for BTreeMap<string::String, A> {
impl<T: ToString, A: ToJson> ToJson for BTreeMap<T, A> {
fn to_json(&self) -> Json {
let mut d = BTreeMap::new();
for (key, value) in self {
d.insert((*key).clone(), value.to_json());
d.insert(key.to_string(), value.to_json());
}
Json::Object(d)
}