use object::elf;
use object::write::Object;
use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind};
+use regex::Regex;
use tempfile::Builder as TempFileBuilder;
use std::ffi::OsString;
// Invoke the system linker
info!("{:?}", &cmd);
let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok();
+ let unknown_arg_regex =
+ Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap();
let mut prog;
let mut i = 0;
loop {
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.
+ // Check to see if the link failed with an error message that indicates it
+ // doesn't recognize the -no-pie option. If so, reperform the link step
+ // without it. 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.linker_is_gnu
&& flavor != LinkerFlavor::Ld
- && (out.contains("unrecognized command line option")
- || out.contains("unknown argument"))
+ && unknown_arg_regex.is_match(&out)
&& out.contains("-no-pie")
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie")
{
// Fallback from '-static-pie' to '-static' in that case.
if sess.target.linker_is_gnu
&& flavor != LinkerFlavor::Ld
- && (out.contains("unrecognized command line option")
- || out.contains("unknown argument"))
+ && unknown_arg_regex.is_match(&out)
&& (out.contains("-static-pie") || out.contains("--no-dynamic-linker"))
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie")
{
add_local_native_libraries(cmd, sess, codegen_results);
}
- // Rust libraries.
+ // Upstream rust libraries and their nobundle static libraries
add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir);
- // Native libraries linked with `#[link]` attributes at and `-l` command line options.
+ // Upstream dymamic native libraries linked with `#[link]` attributes at and `-l`
+ // command line options.
// If -Zlink-native-libraries=false is set, then the assumption is that an
// external build system already has the native dependencies defined, and it
// will provide them to the linker itself.
if sess.opts.debugging_opts.link_native_libraries {
- add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
+ add_upstream_native_libraries(cmd, sess, codegen_results);
}
// Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
}
}
-/// # Rust Crate linking
+/// # Linking Rust crates and their nobundle static libraries
///
/// Rust crates are not considered at all when creating an rlib output. All dependencies will be
/// linked when producing the final output (instead of the intermediate rlib version).
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
Linkage::Static => {
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+
+ // Link static native libs with "-bundle" modifier only if the crate they originate from
+ // is being linked statically to the current crate. If it's linked dynamically
+ // or is an rlib already included via some other dylib crate, the symbols from
+ // native libs will have already been included in that dylib.
+ //
+ // If -Zlink-native-libraries=false is set, then the assumption is that an
+ // external build system already has the native dependencies defined, and it
+ // will provide them to the linker itself.
+ if sess.opts.debugging_opts.link_native_libraries {
+ // Skip if this library is the same as the last.
+ let mut last = None;
+ for lib in &codegen_results.crate_info.native_libraries[&cnum] {
+ if lib.name.is_some()
+ && relevant_lib(sess, lib)
+ && matches!(lib.kind, NativeLibKind::Static { bundle: Some(false), .. })
+ && last != lib.name
+ {
+ cmd.link_staticlib(lib.name.unwrap(), lib.verbatim.unwrap_or(false));
+ last = lib.name;
+ }
+ }
+ }
}
Linkage::Dynamic => add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0),
}
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
- crate_type: CrateType,
) {
- // Be sure to use a topological sorting of crates because there may be
- // interdependencies between native libraries. When passing -nodefaultlibs,
- // for example, almost all native libraries depend on libc, so we have to
- // make sure that's all the way at the right (liblibc is near the base of
- // the dependency chain).
- //
- // This passes RequireStatic, but the actual requirement doesn't matter,
- // we're just getting an ordering of crate numbers, we're not worried about
- // the paths.
- let (_, data) = codegen_results
- .crate_info
- .dependency_formats
- .iter()
- .find(|(ty, _)| *ty == crate_type)
- .expect("failed to find crate type in dependency format list");
-
- let crates = &codegen_results.crate_info.used_crates;
let mut last = (NativeLibKind::Unspecified, None);
- for &cnum in crates {
+ for &cnum in &codegen_results.crate_info.used_crates {
for lib in codegen_results.crate_info.native_libraries[&cnum].iter() {
let name = match lib.name {
Some(l) => l,
NativeLibKind::Framework { as_needed } => {
cmd.link_framework(name, as_needed.unwrap_or(true))
}
- NativeLibKind::Static { bundle: Some(false), .. } => {
- // Link "static-nobundle" native libs only if the crate they originate from
- // is being linked statically to the current crate. If it's linked dynamically
- // or is an rlib already included via some other dylib crate, the symbols from
- // native libs will have already been included in that dylib.
- if data[cnum.as_usize() - 1] == Linkage::Static {
- cmd.link_staticlib(name, verbatim)
- }
- }
- // ignore statically included native libraries here as we've
- // already included them when we included the rust library
- // previously
- NativeLibKind::Static { bundle: None | Some(true), .. } => {}
+ // ignore static native libraries here as we've
+ // already included them in add_local_native_libraries and
+ // add_upstream_rust_crates
+ NativeLibKind::Static { .. } => {}
NativeLibKind::RawDylib => {}
}
}
if let LinkerFlavor::Gcc = flavor {
match ld_impl {
LdImpl::Lld => {
- let tools_path =
- sess.host_filesearch(PathKind::All).get_tools_search_paths(false);
- let lld_path = tools_path
- .into_iter()
- .map(|p| p.join("gcc-ld"))
- .find(|p| {
- p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" }).exists()
- })
- .unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found"));
- cmd.cmd().arg({
- let mut arg = OsString::from("-B");
- arg.push(lld_path);
- arg
- });
+ if sess.target.lld_flavor == LldFlavor::Ld64 {
+ let tools_path =
+ sess.host_filesearch(PathKind::All).get_tools_search_paths(false);
+ let ld64_exe = tools_path
+ .into_iter()
+ .map(|p| p.join("gcc-ld"))
+ .map(|p| {
+ p.join(if sess.host.is_like_windows { "ld64.exe" } else { "ld64" })
+ })
+ .find(|p| p.exists())
+ .unwrap_or_else(|| sess.fatal("rust-lld (as ld64) not found"));
+ cmd.cmd().arg({
+ let mut arg = OsString::from("-fuse-ld=");
+ arg.push(ld64_exe);
+ arg
+ });
+ } else {
+ let tools_path =
+ sess.host_filesearch(PathKind::All).get_tools_search_paths(false);
+ let lld_path = tools_path
+ .into_iter()
+ .map(|p| p.join("gcc-ld"))
+ .find(|p| {
+ p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" })
+ .exists()
+ })
+ .unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found"));
+ cmd.cmd().arg({
+ let mut arg = OsString::from("-B");
+ arg.push(lld_path);
+ arg
+ });
+ }
}
}
} else {