418 lines
16 KiB
Rust
418 lines
16 KiB
Rust
use std::env;
|
|
use std::fs::File;
|
|
use std::io;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use tempfile::Builder as TempFileBuilder;
|
|
|
|
use rustc::session::config::{self, CrateType, DebugInfo, RUST_CGU_EXT};
|
|
use rustc::session::search_paths::PathKind;
|
|
use rustc::session::Session;
|
|
use rustc_codegen_ssa::back::command::Command;
|
|
use rustc_codegen_ssa::back::linker::*;
|
|
use rustc_codegen_ssa::back::link::*;
|
|
use rustc_fs_util::fix_windows_verbatim_for_gcc;
|
|
use rustc_target::spec::{LinkerFlavor, PanicStrategy, RelroLevel};
|
|
|
|
use crate::prelude::*;
|
|
|
|
use crate::link_copied::*;
|
|
|
|
pub(crate) fn link_rlib(sess: &Session, res: &CodegenResults, output_name: PathBuf) {
|
|
let file = File::create(&output_name).unwrap();
|
|
let mut builder = ar::Builder::new(file);
|
|
|
|
// Add main object file
|
|
for module in &res.modules {
|
|
if let Some(ref object_path) = module.object {
|
|
let object = File::open(object_path).expect("Someone deleted our object file");
|
|
let object_len = object.metadata().unwrap().len();
|
|
builder.append(
|
|
&ar::Header::new((module.name.to_string() + RUST_CGU_EXT).into_bytes(), object_len),
|
|
object,
|
|
)
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
// Non object files need to be added after object files, because ranlib will
|
|
// try to read the native architecture from the first file, even if it isn't
|
|
// an object file
|
|
builder
|
|
.append(
|
|
&ar::Header::new(
|
|
crate::metadata::METADATA_FILENAME.as_bytes().to_vec(),
|
|
res.metadata.raw_data.len() as u64,
|
|
),
|
|
::std::io::Cursor::new(res.metadata.raw_data.clone()),
|
|
)
|
|
.unwrap();
|
|
|
|
// Finalize archive
|
|
std::mem::drop(builder);
|
|
|
|
// Run ranlib to be able to link the archive
|
|
let status = std::process::Command::new("ranlib")
|
|
.arg(output_name)
|
|
.status()
|
|
.expect("Couldn't run ranlib");
|
|
if !status.success() {
|
|
sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
|
|
}
|
|
}
|
|
|
|
pub(crate) fn link_bin(sess: &Session, codegen_results: &CodegenResults, out_filename: &Path) {
|
|
let tmpdir = match TempFileBuilder::new().prefix("rustc").tempdir() {
|
|
Ok(tmpdir) => tmpdir,
|
|
Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)),
|
|
};
|
|
|
|
let (linker, flavor) = linker_and_flavor(sess);
|
|
let (pname, mut cmd) = get_linker(sess, &linker, flavor);
|
|
|
|
let root = sess.target_filesearch(PathKind::Native).get_lib_path();
|
|
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() {
|
|
cmd.args(args);
|
|
}
|
|
}
|
|
if let Some(ref args) = sess.opts.debugging_opts.pre_link_args {
|
|
cmd.args(args);
|
|
}
|
|
cmd.args(&sess.opts.debugging_opts.pre_link_arg);
|
|
|
|
for obj in &sess.target.target.options.pre_link_objects_exe {
|
|
cmd.arg(root.join(obj));
|
|
}
|
|
|
|
if sess.crt_static() {
|
|
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
|
|
cmd.arg(root.join(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 target_cpu = "x86_64-apple-darwin"; //::llvm_util::target_cpu(sess);
|
|
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
|
|
link_args(&mut *linker, flavor, sess, CrateType::Executable, tmpdir.path(),
|
|
out_filename, codegen_results);
|
|
cmd = linker.finalize();
|
|
}
|
|
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
|
|
cmd.args(args);
|
|
}
|
|
for obj in &sess.target.target.options.post_link_objects {
|
|
cmd.arg(root.join(obj));
|
|
}
|
|
if sess.crt_static() {
|
|
for obj in &sess.target.target.options.post_link_objects_crt {
|
|
cmd.arg(root.join(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);
|
|
}
|
|
|
|
if sess.opts.debugging_opts.print_link_args {
|
|
println!("{:?}", &cmd);
|
|
}
|
|
|
|
// May have not found libraries in the right formats.
|
|
sess.abort_if_errors();
|
|
|
|
// Invoke the system linker
|
|
//
|
|
// Note that there's a terribly awful hack that really shouldn't be present
|
|
// in any compiler. Here an environment variable is supported to
|
|
// automatically retry the linker invocation if the linker looks like it
|
|
// segfaulted.
|
|
//
|
|
// Gee that seems odd, normally segfaults are things we want to know about!
|
|
// Unfortunately though in rust-lang/rust#38878 we're experiencing the
|
|
// linker segfaulting on Travis quite a bit which is causing quite a bit of
|
|
// pain to land PRs when they spuriously fail due to a segfault.
|
|
//
|
|
// The issue #38878 has some more debugging information on it as well, but
|
|
// this unfortunately looks like it's just a race condition in macOS's linker
|
|
// with some thread pool working in the background. It seems that no one
|
|
// currently knows a fix for this so in the meantime we're left with this...
|
|
let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok();
|
|
let mut prog;
|
|
let mut i = 0;
|
|
loop {
|
|
i += 1;
|
|
prog = exec_linker(sess, &mut cmd, out_filename, tmpdir.path());
|
|
let output = match prog {
|
|
Ok(ref output) => output,
|
|
Err(_) => break,
|
|
};
|
|
if output.status.success() {
|
|
break
|
|
}
|
|
let mut out = output.stderr.clone();
|
|
out.extend(&output.stdout);
|
|
let out = String::from_utf8_lossy(&out);
|
|
|
|
// Check to see if the link failed with "unrecognized command line option:
|
|
// '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so,
|
|
// reperform the link step without the -no-pie option. This is safe because
|
|
// if the linker doesn't support -no-pie then it should not default to
|
|
// linking executables as pie. Different versions of gcc seem to use
|
|
// different quotes in the error message so don't check for them.
|
|
if sess.target.target.options.linker_is_gnu &&
|
|
flavor != LinkerFlavor::Ld &&
|
|
(out.contains("unrecognized command line option") ||
|
|
out.contains("unknown argument")) &&
|
|
out.contains("-no-pie") &&
|
|
cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") {
|
|
for arg in cmd.take_args() {
|
|
if arg.to_string_lossy() != "-no-pie" {
|
|
cmd.arg(arg);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if !retry_on_segfault || i > 3 {
|
|
break
|
|
}
|
|
let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11";
|
|
let msg_bus = "clang: error: unable to execute command: Bus error: 10";
|
|
if !(out.contains(msg_segv) || out.contains(msg_bus)) {
|
|
break
|
|
}
|
|
}
|
|
|
|
match prog {
|
|
Ok(prog) => {
|
|
if !prog.status.success() {
|
|
let mut output = prog.stderr.clone();
|
|
output.extend_from_slice(&prog.stdout);
|
|
sess.struct_err(&format!("linking with `{}` failed: {}",
|
|
pname.display(),
|
|
prog.status))
|
|
.note(&format!("{:?}", &cmd))
|
|
.note(&String::from_utf8_lossy(&output))
|
|
.emit();
|
|
sess.abort_if_errors();
|
|
}
|
|
},
|
|
Err(e) => {
|
|
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
|
|
|
|
let mut linker_error = {
|
|
if linker_not_found {
|
|
sess.struct_err(&format!("linker `{}` not found", pname.display()))
|
|
} else {
|
|
sess.struct_err(&format!("could not exec the linker `{}`", pname.display()))
|
|
}
|
|
};
|
|
|
|
linker_error.note(&e.to_string());
|
|
|
|
if !linker_not_found {
|
|
linker_error.note(&format!("{:?}", &cmd));
|
|
}
|
|
|
|
linker_error.emit();
|
|
|
|
if sess.target.target.options.is_like_msvc && linker_not_found {
|
|
sess.note_without_error("the msvc targets depend on the msvc linker \
|
|
but `link.exe` was not found");
|
|
sess.note_without_error("please ensure that VS 2013, VS 2015 or VS 2017 \
|
|
was installed with the Visual C++ option");
|
|
}
|
|
sess.abort_if_errors();
|
|
}
|
|
}
|
|
|
|
|
|
// On macOS, debuggers need this utility to get run to do some munging of
|
|
// the symbols. Note, though, that if the object files are being preserved
|
|
// for their debug information there's no need for us to run dsymutil.
|
|
if sess.target.target.options.is_like_osx &&
|
|
sess.opts.debuginfo != DebugInfo::None
|
|
{
|
|
match Command::new("dsymutil").arg(out_filename).output() {
|
|
Ok(..) => {}
|
|
Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn link_args(cmd: &mut dyn Linker,
|
|
flavor: LinkerFlavor,
|
|
sess: &Session,
|
|
crate_type: config::CrateType,
|
|
tmpdir: &Path,
|
|
out_filename: &Path,
|
|
codegen_results: &CodegenResults) {
|
|
|
|
// Linker plugins should be specified early in the list of arguments
|
|
cmd.cross_lang_lto();
|
|
|
|
// The default library location, we need this to find the runtime.
|
|
// The location of crates will be determined as needed.
|
|
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
|
|
|
|
// target descriptor
|
|
let t = &sess.target.target;
|
|
|
|
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
|
|
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
|
|
cmd.add_object(obj);
|
|
}
|
|
cmd.output_filename(out_filename);
|
|
|
|
// If we're building a dynamic library then some platforms need to make sure
|
|
// that all symbols are exported correctly from the dynamic library.
|
|
if crate_type != config::CrateType::Executable ||
|
|
sess.target.target.options.is_like_emscripten {
|
|
cmd.export_symbols(tmpdir, crate_type);
|
|
}
|
|
|
|
let obj = codegen_results.allocator_module
|
|
.as_ref()
|
|
.and_then(|m| m.object.as_ref());
|
|
if let Some(obj) = obj {
|
|
cmd.add_object(obj);
|
|
}
|
|
|
|
// Try to strip as much out of the generated object by removing unused
|
|
// sections if possible. See more comments in linker.rs
|
|
if !sess.opts.cg.link_dead_code {
|
|
let keep_metadata = crate_type == config::CrateType::Dylib;
|
|
cmd.gc_sections(keep_metadata);
|
|
}
|
|
|
|
let used_link_args = &codegen_results.crate_info.link_args;
|
|
|
|
if crate_type == config::CrateType::Executable {
|
|
let mut position_independent_executable = false;
|
|
|
|
if t.options.position_independent_executables {
|
|
let empty_vec = Vec::new();
|
|
let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec);
|
|
let more_args = &sess.opts.cg.link_arg;
|
|
let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter());
|
|
|
|
if !sess.crt_static() && !args.any(|x| *x == "-static") {
|
|
position_independent_executable = true;
|
|
}
|
|
}
|
|
|
|
if position_independent_executable {
|
|
cmd.position_independent_executable();
|
|
} else {
|
|
// 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
let relro_level = match sess.opts.debugging_opts.relro_level {
|
|
Some(level) => level,
|
|
None => t.options.relro_level,
|
|
};
|
|
match relro_level {
|
|
RelroLevel::Full => {
|
|
cmd.full_relro();
|
|
},
|
|
RelroLevel::Partial => {
|
|
cmd.partial_relro();
|
|
},
|
|
RelroLevel::Off => {
|
|
cmd.no_relro();
|
|
},
|
|
RelroLevel::None => {
|
|
},
|
|
}
|
|
|
|
// Pass optimization flags down to the linker.
|
|
cmd.optimize();
|
|
|
|
// Pass debuginfo flags down to the linker.
|
|
cmd.debuginfo();
|
|
|
|
// We want to, by default, prevent the compiler from accidentally leaking in
|
|
// any system libraries, so we may explicitly ask linkers to not link to any
|
|
// libraries by default. Note that this does not happen for windows because
|
|
// windows pulls in some large number of libraries and I couldn't quite
|
|
// figure out which subset we wanted.
|
|
//
|
|
// This is all naturally configurable via the standard methods as well.
|
|
if !sess.opts.cg.default_linker_libraries.unwrap_or(false) &&
|
|
t.options.no_default_libraries
|
|
{
|
|
cmd.no_default_libraries();
|
|
}
|
|
|
|
// Take careful note of the ordering of the arguments we pass to the linker
|
|
// here. Linkers will assume that things on the left depend on things to the
|
|
// right. Things on the right cannot depend on things on the left. This is
|
|
// all formally implemented in terms of resolving symbols (libs on the right
|
|
// resolve unknown symbols of libs on the left, but not vice versa).
|
|
//
|
|
// For this reason, we have organized the arguments we pass to the linker as
|
|
// such:
|
|
//
|
|
// 1. The local object that LLVM just generated
|
|
// 2. Local native libraries
|
|
// 3. Upstream rust libraries
|
|
// 4. Upstream native libraries
|
|
//
|
|
// The rationale behind this ordering is that those items lower down in the
|
|
// list can't depend on items higher up in the list. For example nothing can
|
|
// depend on what we just generated (e.g. that'd be a circular dependency).
|
|
// Upstream rust libraries are not allowed to depend on our local native
|
|
// libraries as that would violate the structure of the DAG, in that
|
|
// scenario they are required to link to them as well in a shared fashion.
|
|
//
|
|
// Note that upstream rust libraries may contain native dependencies as
|
|
// well, but they also can't depend on what we just started to add to the
|
|
// link line. And finally upstream native libraries can't depend on anything
|
|
// in this DAG so far because they're only dylibs and dylibs can only depend
|
|
// on other dylibs (e.g. other native deps).
|
|
add_local_native_libraries(cmd, sess, codegen_results);
|
|
add_upstream_rust_crates(cmd, sess, codegen_results, crate_type, tmpdir);
|
|
add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
|
|
|
|
// Tell the linker what we're doing.
|
|
if crate_type != config::CrateType::Executable {
|
|
cmd.build_dylib(out_filename);
|
|
}
|
|
if crate_type == config::CrateType::Executable && sess.crt_static() {
|
|
cmd.build_static_executable();
|
|
}
|
|
|
|
if sess.opts.debugging_opts.pgo_gen.is_some() {
|
|
cmd.pgo_gen();
|
|
}
|
|
|
|
// Finally add all the linker arguments provided on the command line along
|
|
// with any #[link_args] attributes found inside the crate
|
|
if let Some(ref args) = sess.opts.cg.link_args {
|
|
cmd.args(args);
|
|
}
|
|
cmd.args(&sess.opts.cg.link_arg);
|
|
cmd.args(&used_link_args);
|
|
}
|