Skip to content
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
194 changes: 168 additions & 26 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@ use rustc_hir::attrs::NativeLibKind;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_lint_defs::builtin::LINKER_INFO;
use rustc_macros::Diagnostic;
use rustc_metadata::EncodedMetadata;
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
use rustc_metadata::{
EncodedMetadata, NativeLibSearchFallback, find_native_static_library,
walk_native_lib_search_dirs,
};
use rustc_middle::bug;
use rustc_middle::error::DuplicateEiiImpls;
use rustc_middle::lint::emit_lint_base;
Expand Down Expand Up @@ -135,6 +132,173 @@ fn check_externally_implementable_item_linkage(sess: &Session, crate_info: &Crat
}
}

/// The fallback directories are passed to linker, but not used when rustc does the search,
/// because in the latter case the set of fallback directories cannot always be determined
/// consistently at the moment.
struct NativeLibSearchFallback<'a> {
self_contained_components: LinkSelfContainedComponents,
apple_sdk_root: Option<&'a Path>,
}

fn walk_native_lib_search_dirs<R>(
sess: &Session,
fallback: Option<NativeLibSearchFallback<'_>>,
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().cli_search_paths(PathKind::Native) {
f(&search_path.dir, false)?;
}
for search_path in sess.target_filesearch().cli_search_paths(PathKind::Framework) {
// Frameworks are looked up strictly in framework-specific paths.
if search_path.kind != PathKind::All {
f(&search_path.dir, true)?;
}
}

let Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root }) = fallback
else {
return ControlFlow::Continue(());
};

// 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)?;
}

let has_shared_llvm_apple_darwin =
sess.target.is_like_darwin && sess.target_tlib_path.dir.join("libLLVM.dylib").exists();

// 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.
// On Apple targets, shared LLVM is linked by name, so when `libLLVM.dylib` is
// present in the target libdir, add that directory to the linker search path.
// FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind
// and sanitizers to self-contained directory, and stop adding this search path.
// FIXME: On AIX this also has the side-effect of making the list of library search paths
// non-empty, which is needed or the linker may decide to record the LIBPATH env, if
// defined, as the search path instead of appending the default search paths.
if sess.target.cfg_abi == CfgAbi::Fortanix
|| sess.target.os == Os::Linux
|| sess.target.os == Os::Fuchsia
|| sess.target.is_like_aix
|| sess.target.is_like_darwin
&& (!sess.sanitizers().is_empty() || has_shared_llvm_apple_darwin)
|| sess.target.os == Os::Windows
&& sess.target.env == Env::Gnu
&& sess.target.cfg_abi == CfgAbi::Llvm
{
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.env == Env::MacAbi
{
f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
}

ControlFlow::Continue(())
}

pub(super) fn try_find_native_static_library(
sess: &Session,
name: &str,
verbatim: bool,
) -> Option<PathBuf> {
let default = sess.staticlib_components(verbatim);
let formats = if verbatim {
vec![default]
} else {
// On Windows, static libraries sometimes show up as libfoo.a and other
// times show up as foo.lib
let unix = ("lib", ".a");
if default == unix { vec![default] } else { vec![default, unix] }
};

walk_native_lib_search_dirs(sess, 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()
}

pub(super) fn try_find_native_dynamic_library(
sess: &Session,
name: &str,
verbatim: bool,
) -> Option<PathBuf> {
let default = sess.staticlib_components(verbatim);
let formats = if verbatim {
vec![default]
} else {
// While the official naming convention for MSVC import libraries
// is foo.lib, Meson follows the libfoo.dll.a convention to
// disambiguate .a for static libraries
let meson = ("lib", ".dll.a");
// and MinGW uses .a altogether
let mingw = ("lib", ".a");
vec![default, meson, mingw]
};

walk_native_lib_search_dirs(sess, 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()
}

pub(super) 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(diagnostics::MissingNativeLibrary::new(name, verbatim))
})
}

/// If `lib` is a static library that is bundled into the rlib as a packed archive, returns the
/// file name of that archive. Returns `None` for libraries that are instead unpacked into loose
/// object files, or not bundled at all.
fn find_bundled_library(
lib: &NativeLib,
sess: &Session,
crate_types: &[CrateType],
) -> Option<Symbol> {
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = lib.kind
&& crate_types.iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib))
&& (sess.opts.unstable_opts.packed_bundled_libs
|| lib.cfg.is_some()
|| whole_archive == Some(true))
{
return find_native_static_library(lib.name.as_str(), lib.verbatim, sess)
.file_name()
.and_then(|s| s.to_str())
.map(Symbol::intern);
}
None
}

/// Performs the linkage portion of the compilation phase. This will generate all
/// of the requested outputs for this compilation session.
pub fn link_binary(
Expand Down Expand Up @@ -374,28 +538,6 @@ pub fn each_linked_rlib(
Ok(())
}

/// If `lib` is a static library that is bundled into the rlib as a packed archive, returns the
/// file name of that archive. Returns `None` for libraries that are instead unpacked into loose
/// object files, or not bundled at all.
fn find_bundled_library(
lib: &NativeLib,
sess: &Session,
crate_types: &[CrateType],
) -> Option<Symbol> {
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = lib.kind
&& crate_types.iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib))
&& (sess.opts.unstable_opts.packed_bundled_libs
|| lib.cfg.is_some()
|| whole_archive == Some(true))
{
return find_native_static_library(lib.name.as_str(), lib.verbatim, sess)
.file_name()
.and_then(|s| s.to_str())
.map(Symbol::intern);
}
None
}

/// Create an 'rlib'.
///
/// An rlib in its current incarnation is essentially a renamed .a file (with "dummy" object files).
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ use std::{env, iter, mem, str};
use find_msvc_tools;
use rustc_hir::attrs::WindowsSubsystemKind;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::{
find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library,
};
use rustc_middle::bug;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::{
Expand All @@ -23,6 +20,9 @@ use tracing::{debug, warn};

use super::command::Command;
use super::symbol_export;
use crate::back::link::{
find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library,
};
use crate::back::symbol_export::allocator_shim_symbols;
use crate::base::needs_allocator_shim_for_linking;
use crate::{SymbolExport, diagnostics};
Expand Down
40 changes: 40 additions & 0 deletions compiler/rustc_codegen_ssa/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1340,3 +1340,43 @@ pub(crate) struct LtoProcMacro;
#[diag("cannot prefer dynamic linking when performing LTO")]
#[note("only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO")]
pub(crate) struct DynamicLinkingWithLTO;

#[derive(Diagnostic)]
#[diag("could not find native static library `{$libname}`, perhaps an -L flag is missing?")]
pub(crate) struct MissingNativeLibrary<'a> {
libname: &'a str,
#[subdiagnostic]
suggest_name: Option<SuggestLibraryName<'a>>,
}

impl<'a> MissingNativeLibrary<'a> {
pub(crate) fn new(libname: &'a str, verbatim: bool) -> Self {
// if it looks like the user has provided a complete filename rather just the bare lib name,
// then provide a note that they might want to try trimming the name
let suggested_name = if !verbatim {
if let Some(libname) = libname.strip_circumfix("lib", ".a") {
// this is a unix style filename so trim prefix & suffix
Some(libname)
} else if let Some(libname) = libname.strip_suffix(".lib") {
// this is a Windows style filename so just trim the suffix
Some(libname)
} else {
None
}
} else {
None
};

Self {
libname,
suggest_name: suggested_name
.map(|suggested_name| SuggestLibraryName { suggested_name }),
}
}
}

#[derive(Subdiagnostic)]
#[help("only provide the library name `{$suggested_name}`, not the full filename")]
pub(crate) struct SuggestLibraryName<'a> {
suggested_name: &'a str,
}
40 changes: 0 additions & 40 deletions compiler/rustc_metadata/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,46 +253,6 @@ pub(crate) struct FailedCopyToStdout {
)]
pub(crate) struct BinaryOutputToTty;

#[derive(Diagnostic)]
#[diag("could not find native static library `{$libname}`, perhaps an -L flag is missing?")]
pub(crate) struct MissingNativeLibrary<'a> {
libname: &'a str,
#[subdiagnostic]
suggest_name: Option<SuggestLibraryName<'a>>,
}

impl<'a> MissingNativeLibrary<'a> {
pub(crate) fn new(libname: &'a str, verbatim: bool) -> Self {
// if it looks like the user has provided a complete filename rather just the bare lib name,
// then provide a note that they might want to try trimming the name
let suggested_name = if !verbatim {
if let Some(libname) = libname.strip_circumfix("lib", ".a") {
// this is a unix style filename so trim prefix & suffix
Some(libname)
} else if let Some(libname) = libname.strip_suffix(".lib") {
// this is a Windows style filename so just trim the suffix
Some(libname)
} else {
None
}
} else {
None
};

Self {
libname,
suggest_name: suggested_name
.map(|suggested_name| SuggestLibraryName { suggested_name }),
}
}
}

#[derive(Subdiagnostic)]
#[help("only provide the library name `{$suggested_name}`, not the full filename")]
pub(crate) struct SuggestLibraryName<'a> {
suggested_name: &'a str,
}

#[derive(Diagnostic)]
#[diag("couldn't create a temp dir: {$err}")]
pub(crate) struct FailedCreateTempdir {
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,4 @@ pub mod locator;

pub use fs::{METADATA_FILENAME, emit_wrapper_file};
pub use host_dylib::{DylibError, load_symbol_from_dylib};
pub use native_libs::{
NativeLibSearchFallback, find_native_static_library, try_find_native_dynamic_library,
try_find_native_static_library, walk_native_lib_search_dirs,
};
pub use rmeta::{EncodedMetadata, METADATA_HEADER, ProcMacroKind, encode_metadata, rendered_const};
Loading
Loading