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: Synchronize native library search in rustc and linker #129366

Merged
merged 3 commits into from
Aug 31, 2024
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
61 changes: 15 additions & 46 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::BTreeSet;
use std::ffi::OsString;
use std::fs::{read, File, OpenOptions};
use std::io::{BufWriter, Write};
use std::ops::Deref;
use std::ops::{ControlFlow, Deref};
use std::path::{Path, PathBuf};
use std::process::{ExitStatus, Output, Stdio};
use std::{env, fmt, fs, io, mem, str};
Expand All @@ -18,8 +18,8 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs};
use rustc_middle::bug;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Linkage;
Expand Down Expand Up @@ -2110,50 +2110,19 @@ fn add_library_search_dirs(
return;
}

// Library search paths explicitly supplied by user (`-L` on the command line).
for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() {
cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir));
}
for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() {
// Contrary to the `-L` docs only framework-specific paths are considered here.
if search_path.kind != PathKind::All {
cmd.framework_path(&search_path.dir);
}
}

// The toolchain ships some native library components and self-contained linking was enabled.
// Add the self-contained library directory to search paths.
if self_contained_components.intersects(
LinkSelfContainedComponents::LIBC
| LinkSelfContainedComponents::UNWIND
| LinkSelfContainedComponents::MINGW,
) {
let lib_path = sess.target_tlib_path.dir.join("self-contained");
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
}

// Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot
// library directory instead of the self-contained directories.
// Sanitizer libraries have the same issue and are also linked by name on Apple targets.
// The targets here should be in sync with `copy_third_party_objects` in bootstrap.
// FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind
// and sanitizers to self-contained directory, and stop adding this search path.
if sess.target.vendor == "fortanix"
|| sess.target.os == "linux"
|| sess.target.os == "fuchsia"
|| sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty()
{
cmd.include_path(&fix_windows_verbatim_for_gcc(&sess.target_tlib_path.dir));
}

// Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks
// we must have the support library stubs in the library search path (#121430).
if let Some(sdk_root) = apple_sdk_root
&& sess.target.llvm_target.contains("macabi")
{
cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib"));
cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"));
}
walk_native_lib_search_dirs(
sess,
self_contained_components,
apple_sdk_root,
|dir, is_framework| {
if is_framework {
cmd.framework_path(dir);
} else {
cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
}
ControlFlow::<()>::Continue(())
},
);
jieyouxu marked this conversation as resolved.
Show resolved Hide resolved
}

/// Add options making relocation sections in the produced ELF files read-only
Expand Down
14 changes: 10 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{env, iter, mem, str};

use cc::windows_registry;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
use rustc_metadata::{find_native_static_library, try_find_native_static_library};
use rustc_middle::bug;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols;
Expand Down Expand Up @@ -891,9 +891,15 @@ impl<'a> Linker for MsvcLinker<'a> {
}

fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" };
let suffix = if verbatim { "" } else { ".lib" };
self.link_arg(format!("{prefix}{name}{suffix}"));
// On MSVC-like targets rustc supports static libraries using alternative naming
// scheme (`libfoo.a`) unsupported by linker, search for such libraries manually.
if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) {
self.link_staticlib_by_path(&path, whole_archive);
} else {
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" };
let suffix = if verbatim { "" } else { ".lib" };
self.link_arg(format!("{prefix}{name}{suffix}"));
}
}

fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(control_flow_enum)]
#![feature(coroutines)]
#![feature(decl_macro)]
#![feature(error_iter)]
Expand Down Expand Up @@ -34,7 +35,9 @@ pub mod locator;

pub use creader::{load_symbol_from_dylib, DylibError};
pub use fs::{emit_wrapper_file, METADATA_FILENAME};
pub use native_libs::find_native_static_library;
pub use native_libs::{
find_native_static_library, try_find_native_static_library, walk_native_lib_search_dirs,
};
pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER};

rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
92 changes: 82 additions & 10 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::path::PathBuf;
use std::ops::ControlFlow;
use std::path::{Path, PathBuf};

use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
use rustc_attr as attr;
Expand All @@ -16,10 +17,68 @@ use rustc_session::Session;
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use rustc_target::spec::LinkSelfContainedComponents;

use crate::{errors, fluent_generated};

pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
pub fn walk_native_lib_search_dirs<R>(
sess: &Session,
self_contained_components: LinkSelfContainedComponents,
apple_sdk_root: Option<&Path>,
mut f: impl FnMut(&Path, bool /*is_framework*/) -> ControlFlow<R>,
) -> ControlFlow<R> {
// Library search paths explicitly supplied by user (`-L` on the command line).
for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() {
f(&search_path.dir, false)?;
}
for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() {
// Frameworks are looked up strictly in framework-specific paths.
if search_path.kind != PathKind::All {
f(&search_path.dir, true)?;
}
}

// The toolchain ships some native library components and self-contained linking was enabled.
// Add the self-contained library directory to search paths.
if self_contained_components.intersects(
LinkSelfContainedComponents::LIBC
| LinkSelfContainedComponents::UNWIND
| LinkSelfContainedComponents::MINGW,
) {
f(&sess.target_tlib_path.dir.join("self-contained"), false)?;
}

// Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot
// library directory instead of the self-contained directories.
// Sanitizer libraries have the same issue and are also linked by name on Apple targets.
// The targets here should be in sync with `copy_third_party_objects` in bootstrap.
// FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind
// and sanitizers to self-contained directory, and stop adding this search path.
if sess.target.vendor == "fortanix"
|| sess.target.os == "linux"
|| sess.target.os == "fuchsia"
|| sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty()
{
f(&sess.target_tlib_path.dir, false)?;
}

// Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks
// we must have the support library stubs in the library search path (#121430).
if let Some(sdk_root) = apple_sdk_root
&& sess.target.llvm_target.contains("macabi")
{
f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
}

ControlFlow::Continue(())
}

pub fn try_find_native_static_library(
sess: &Session,
name: &str,
verbatim: bool,
) -> Option<PathBuf> {
let formats = if verbatim {
vec![("".into(), "".into())]
} else {
Expand All @@ -30,16 +89,29 @@ pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) ->
if os == unix { vec![os] } else { vec![os, unix] }
};

for path in sess.target_filesearch(PathKind::Native).search_paths() {
for (prefix, suffix) in &formats {
let test = path.dir.join(format!("{prefix}{name}{suffix}"));
if test.exists() {
return test;
// FIXME: Account for self-contained linking settings and Apple SDK.
walk_native_lib_search_dirs(
sess,
LinkSelfContainedComponents::empty(),
None,
|dir, is_framework| {
if !is_framework {
for (prefix, suffix) in &formats {
let test = dir.join(format!("{prefix}{name}{suffix}"));
if test.exists() {
return ControlFlow::Break(test);
}
}
}
}
}
ControlFlow::Continue(())
},
)
.break_value()
}

sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim));
pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
try_find_native_static_library(sess, name, verbatim)
.unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)))
}

fn find_bundled_library(
Expand Down
2 changes: 1 addition & 1 deletion src/doc/rustc/src/command-line-arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ KIND=PATH` where `KIND` may be one of:
directory.
- `native` — Only search for native libraries in this directory.
- `framework` — Only search for macOS frameworks in this directory.
- `all` — Search for all library kinds in this directory. This is the default
- `all` — Search for all library kinds in this directory, except frameworks. This is the default
if `KIND` is not specified.

<a id="option-l-link-lib"></a>
Expand Down
2 changes: 2 additions & 0 deletions tests/run-make/native-lib-alt-naming/native.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[no_mangle]
pub extern "C" fn native_lib_alt_naming() {}
15 changes: 15 additions & 0 deletions tests/run-make/native-lib-alt-naming/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// On MSVC the alternative naming format for static libraries (`libfoo.a`) is accepted in addition
// to the default format (`foo.lib`).

//REMOVE@ only-msvc

use run_make_support::rustc;

fn main() {
// Prepare the native library.
rustc().input("native.rs").crate_type("staticlib").output("libnative.a").run();

// Try to link to it from both a rlib and a bin.
rustc().input("rust.rs").crate_type("rlib").arg("-lstatic=native").run();
rustc().input("rust.rs").crate_type("bin").arg("-lstatic=native").run();
}
1 change: 1 addition & 0 deletions tests/run-make/native-lib-alt-naming/rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub fn main() {}
Loading