linker: Combine argument building into a single function

This commit is contained in:
Vadim Petrochenkov 2020-04-05 02:58:32 +03:00
parent ce25dabc66
commit 032462e06f
2 changed files with 118 additions and 115 deletions

View file

@ -154,7 +154,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> Command {
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");
// If our linker looks like a batch script on Windows then to execute this
@ -232,7 +232,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB
}
cmd.env("PATH", env::join_paths(new_path).unwrap());
(linker.to_path_buf(), cmd)
cmd
}
pub fn each_linked_rlib(
@ -487,95 +487,18 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
target_cpu: &str,
) {
info!("preparing {:?} to {:?}", crate_type, out_filename);
let (linker, flavor) = linker_and_flavor(sess);
let (linker_path, flavor) = linker_and_flavor(sess);
let mut cmd = linker_with_args::<B>(
&linker_path,
flavor,
sess,
crate_type,
tmpdir,
out_filename,
codegen_results,
target_cpu,
);
let any_dynamic_crate = crate_type == config::CrateType::Dylib
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
// The invocations of cc share some flags across platforms
let (pname, mut cmd) = get_linker(sess, &linker, flavor);
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
cmd.args(args);
}
if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) {
if sess.crt_static(Some(crate_type)) {
cmd.args(args);
}
}
cmd.args(&sess.opts.debugging_opts.pre_link_args);
if sess.target.target.options.is_like_fuchsia {
let prefix = match sess.opts.debugging_opts.sanitizer {
Some(Sanitizer::Address) => "asan/",
_ => "",
};
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
}
let pre_link_objects = if crate_type == config::CrateType::Executable {
&sess.target.target.options.pre_link_objects_exe
} else {
&sess.target.target.options.pre_link_objects_dll
};
for obj in pre_link_objects {
cmd.arg(get_file_path(sess, obj));
}
if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
cmd.arg(get_file_path(sess, obj));
}
}
if sess.target.target.options.is_like_emscripten {
cmd.arg("-s");
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
"DISABLE_EXCEPTION_CATCHING=1"
} else {
"DISABLE_EXCEPTION_CATCHING=0"
});
}
{
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
link_sanitizer_runtime(sess, crate_type, &mut *linker);
link_args::<B>(
&mut *linker,
flavor,
sess,
crate_type,
tmpdir,
out_filename,
codegen_results,
);
cmd = linker.finalize();
}
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
cmd.args(args);
}
if any_dynamic_crate {
if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) {
cmd.args(args);
}
} else {
if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) {
cmd.args(args);
}
}
for obj in &sess.target.target.options.post_link_objects {
cmd.arg(get_file_path(sess, obj));
}
if sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.post_link_objects_crt {
cmd.arg(get_file_path(sess, obj));
}
}
if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) {
cmd.args(args);
}
for &(ref k, ref v) in &sess.target.target.options.link_env {
cmd.env(k, v);
}
@ -597,7 +520,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
let mut i = 0;
loop {
i += 1;
prog = sess.time("run_linker", || exec_linker(sess, &mut cmd, out_filename, tmpdir));
prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir));
let output = match prog {
Ok(ref output) => output,
Err(_) => break,
@ -698,7 +621,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
output.extend_from_slice(&prog.stdout);
sess.struct_err(&format!(
"linking with `{}` failed: {}",
pname.display(),
linker_path.display(),
prog.status
))
.note(&format!("{:?}", &cmd))
@ -714,9 +637,12 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
let mut linker_error = {
if linker_not_found {
sess.struct_err(&format!("linker `{}` not found", pname.display()))
sess.struct_err(&format!("linker `{}` not found", linker_path.display()))
} else {
sess.struct_err(&format!("could not exec the linker `{}`", pname.display()))
sess.struct_err(&format!(
"could not exec the linker `{}`",
linker_path.display()
))
}
};
@ -1087,7 +1013,7 @@ pub fn get_file_path(sess: &Session, name: &str) -> PathBuf {
pub fn exec_linker(
sess: &Session,
cmd: &mut Command,
cmd: &Command,
out_filename: &Path,
tmpdir: &Path,
) -> io::Result<Output> {
@ -1233,15 +1159,66 @@ pub fn exec_linker(
}
}
fn link_args<'a, B: ArchiveBuilder<'a>>(
cmd: &mut dyn Linker,
fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
path: &Path,
flavor: LinkerFlavor,
sess: &'a Session,
crate_type: config::CrateType,
tmpdir: &Path,
out_filename: &Path,
codegen_results: &CodegenResults,
) {
target_cpu: &str,
) -> Command {
let base_cmd = get_linker(sess, path, flavor);
// FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction
// 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);
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
cmd.args(args);
}
if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) {
if sess.crt_static(Some(crate_type)) {
cmd.args(args);
}
}
cmd.args(&sess.opts.debugging_opts.pre_link_args);
if sess.target.target.options.is_like_fuchsia {
let prefix = match sess.opts.debugging_opts.sanitizer {
Some(Sanitizer::Address) => "asan/",
_ => "",
};
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
}
let pre_link_objects = if crate_type == config::CrateType::Executable {
&sess.target.target.options.pre_link_objects_exe
} else {
&sess.target.target.options.pre_link_objects_dll
};
for obj in pre_link_objects {
cmd.arg(get_file_path(sess, obj));
}
if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
cmd.arg(get_file_path(sess, obj));
}
}
if sess.target.target.options.is_like_emscripten {
cmd.arg("-s");
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
"DISABLE_EXCEPTION_CATCHING=1"
} else {
"DISABLE_EXCEPTION_CATCHING=0"
});
}
link_sanitizer_runtime(sess, crate_type, cmd);
// Linker plugins should be specified early in the list of arguments
cmd.linker_plugin_lto();
@ -1440,6 +1417,38 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
// Finally add all the linker arguments provided on the command line along
// with any #[link_args] attributes found inside the crate
cmd.args(user_link_args);
cmd.finalize();
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
cmd.args(args);
}
let any_dynamic_crate = crate_type == config::CrateType::Dylib
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
if any_dynamic_crate {
if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) {
cmd.args(args);
}
} else {
if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) {
cmd.args(args);
}
}
for obj in &sess.target.target.options.post_link_objects {
cmd.arg(get_file_path(sess, obj));
}
if sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.post_link_objects_crt {
cmd.arg(get_file_path(sess, obj));
}
}
if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) {
cmd.args(args);
}
cmd.take_cmd()
}
// # Native library linking

View file

@ -6,6 +6,7 @@ use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::mem;
use std::path::{Path, PathBuf};
use rustc_data_structures::fx::FxHashMap;
@ -117,8 +118,7 @@ pub trait Linker {
fn group_start(&mut self);
fn group_end(&mut self);
fn linker_plugin_lto(&mut self);
// Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
fn finalize(&mut self) -> Command;
fn finalize(&mut self);
}
impl dyn Linker + '_ {
@ -129,6 +129,10 @@ impl dyn Linker + '_ {
pub fn args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) {
self.cmd().args(args);
}
pub fn take_cmd(&mut self) -> Command {
mem::replace(self.cmd(), Command::new(""))
}
}
pub struct GccLinker<'a> {
@ -515,10 +519,8 @@ impl<'a> Linker for GccLinker<'a> {
self.linker_arg(&subsystem);
}
fn finalize(&mut self) -> Command {
fn finalize(&mut self) {
self.hint_dynamic(); // Reset to default before returning the composed command line.
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn group_start(&mut self) {
@ -768,9 +770,7 @@ impl<'a> Linker for MsvcLinker<'a> {
}
}
fn finalize(&mut self) -> Command {
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn finalize(&mut self) {}
// MSVC doesn't need group indicators
fn group_start(&mut self) {}
@ -937,9 +937,7 @@ impl<'a> Linker for EmLinker<'a> {
// noop
}
fn finalize(&mut self) -> Command {
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn finalize(&mut self) {}
// Appears not necessary on Emscripten
fn group_start(&mut self) {}
@ -1107,9 +1105,7 @@ impl<'a> Linker for WasmLd<'a> {
fn no_position_independent_executable(&mut self) {}
fn finalize(&mut self) -> Command {
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn finalize(&mut self) {}
// Not needed for now with LLD
fn group_start(&mut self) {}
@ -1209,14 +1205,12 @@ impl<'a> Linker for PtxLinker<'a> {
self.cmd.arg("-o").arg(path);
}
fn finalize(&mut self) -> Command {
fn finalize(&mut self) {
// Provide the linker with fallback to internal `target-cpu`.
self.cmd.arg("--fallback-arch").arg(match self.sess.opts.cg.target_cpu {
Some(ref s) => s,
None => &self.sess.target.target.options.cpu,
});
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn link_dylib(&mut self, _lib: Symbol) {