From 26181f01306c53ff78698c3584cc5bd779c5dd35 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 19 Mar 2025 19:53:43 +0300 Subject: [PATCH 1/3] linker: Move native library search from linker to rustc For static libraries only, for now. Linker's search by name is still used if rustc's search fails, because linker may search in additional platform-specific directories in addition to directories known to rustc. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 43 +++++++++++-------- src/doc/rustc/src/command-line-arguments.md | 6 +++ .../native-link-modifier-bundle/rmake.rs | 4 +- .../rmake.rs | 3 -- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 3f5e0c1bce9c1..4fb347b10e241 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -6,9 +6,7 @@ use std::{env, io, iter, mem, str}; use cc::windows_registry; 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_metadata::{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; @@ -614,15 +612,15 @@ impl<'a> Linker for GccLinker<'a> { } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { + if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { + return self.link_staticlib_by_path(&path, whole_archive); + } self.hint_static(); let colon = if verbatim && self.is_gnu { ":" } else { "" }; if !whole_archive { self.link_or_cc_arg(format!("-l{colon}{name}")); } else if self.sess.target.is_like_osx { - // -force_load is the macOS equivalent of --whole-archive, but it - // involves passing the full path to the library to link. - self.link_arg("-force_load"); - self.link_arg(find_native_static_library(name, verbatim, self.sess)); + self.link_args(&["-force_load", name]); } else { self.link_arg("--whole-archive") .link_or_cc_arg(format!("-l{colon}{name}")) @@ -953,15 +951,12 @@ impl<'a> Linker for MsvcLinker<'a> { } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { - // 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 { - let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; - let suffix = if verbatim { "" } else { ".lib" }; - self.link_arg(format!("{prefix}{name}{suffix}")); + return self.link_staticlib_by_path(&path, whole_archive); } + 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) { @@ -1190,7 +1185,10 @@ impl<'a> Linker for EmLinker<'a> { self.link_or_cc_arg(path); } - fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) { + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { + if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { + return self.link_staticlib_by_path(&path, whole_archive); + } self.link_or_cc_args(&["-l", name]); } @@ -1356,7 +1354,10 @@ impl<'a> Linker for WasmLd<'a> { self.link_or_cc_arg(path); } - fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { + if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { + return self.link_staticlib_by_path(&path, whole_archive); + } if !whole_archive { self.link_or_cc_args(&["-l", name]); } else { @@ -1490,7 +1491,10 @@ impl<'a> Linker for L4Bender<'a> { ) { } - fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { + if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { + return self.link_staticlib_by_path(&path, whole_archive); + } self.hint_static(); if !whole_archive { self.link_arg(format!("-PC{name}")); @@ -1664,12 +1668,15 @@ impl<'a> Linker for AixLinker<'a> { } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { + if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { + return self.link_staticlib_by_path(&path, whole_archive); + } self.hint_static(); if !whole_archive { self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") }); } else { let mut arg = OsString::from("-bkeepfile:"); - arg.push(find_native_static_library(name, verbatim, self.sess)); + arg.push(name); self.link_or_cc_arg(arg); } } diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index b704cee705b05..3e7184aeccc86 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -82,6 +82,12 @@ The name used in a `link` attribute may be overridden using the form `-l ATTR_NAME:LINK_NAME` where `ATTR_NAME` is the name in the `link` attribute, and `LINK_NAME` is the name of the actual library that will be linked. +The compiler may attempt to search for the library in native library search directories +(controlled by `-L`), and pass it to linker by full path if the search is successful. +Otherwise the library will be passed to linker by name, so it can perform its own search. +In some cases this enables use of alternative library naming schemes or `+verbatim` modifier +even if they are not natively supported by linker. + [link-attribute]: ../reference/items/external-blocks.html#the-link-attribute ### Linking modifiers: `whole-archive` diff --git a/tests/run-make/native-link-modifier-bundle/rmake.rs b/tests/run-make/native-link-modifier-bundle/rmake.rs index 058b66b15f12f..b246d8bcc3edd 100644 --- a/tests/run-make/native-link-modifier-bundle/rmake.rs +++ b/tests/run-make/native-link-modifier-bundle/rmake.rs @@ -68,7 +68,7 @@ fn main() { .crate_type("cdylib") .print("link-args") .run() - .assert_stdout_not_contains(r#"-l[" ]*native-staticlib"#); + .assert_stdout_not_contains("libnative-staticlib.a"); llvm_nm() .input(dynamic_lib_name("cdylib_bundled")) .run() @@ -81,7 +81,7 @@ fn main() { .crate_type("cdylib") .print("link-args") .run() - .assert_stdout_contains_regex(r#"-l[" ]*native-staticlib"#); + .assert_stdout_contains_regex(r"libnative-staticlib.a"); llvm_nm() .input(dynamic_lib_name("cdylib_non_bundled")) .run() diff --git a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs index 6868cb368ccc3..1cd4675d1842a 100644 --- a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs +++ b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs @@ -3,9 +3,6 @@ // This test is the same as native-link-modifier-rustc, but without rlibs. // See https://github.com/rust-lang/rust/issues/99425 -//@ ignore-apple -// Reason: linking fails due to the unusual ".ext" staticlib name. - use run_make_support::rustc; fn main() { From e850650f7373a02919a28046dc3d6b5a24316f87 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 20 Mar 2025 19:35:07 +0300 Subject: [PATCH 2/3] native libs: Explicitly limit foo.lib -> libfoo.a fallback to MSVC In practice this doesn't change anything for current built-in targets --- compiler/rustc_metadata/src/native_libs.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 1671b7e06b0af..cd3508374a0a4 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -95,14 +95,15 @@ pub fn try_find_native_static_library( name: &str, verbatim: bool, ) -> Option { + let target = (&*sess.target.staticlib_prefix, &*sess.target.staticlib_suffix); + let unix = ("lib", ".a"); let formats = if verbatim { - vec![("".into(), "".into())] + vec![("", "")] + } else if target != unix && sess.target.is_like_msvc { + // On Windows MSVC naming scheme `libfoo.a` is used as a fallback from default `foo.lib`. + vec![target, unix] } else { - let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone()); - // On Windows, static libraries sometimes show up as libfoo.a and other - // times show up as foo.lib - let unix = ("lib".into(), ".a".into()); - if os == unix { vec![os] } else { vec![os, unix] } + vec![target] }; walk_native_lib_search_dirs(sess, None, |dir, is_framework| { From 8ebabba12d7eee0953fb71e64ff8ad5e93590113 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 25 Mar 2025 13:43:04 +0300 Subject: [PATCH 3/3] Fix some more tests --- tests/run-make/rlib-format-packed-bundled-libs-3/rmake.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/rmake.rs b/tests/run-make/rlib-format-packed-bundled-libs-3/rmake.rs index 67e839bec703d..9026165671aa4 100644 --- a/tests/run-make/rlib-format-packed-bundled-libs-3/rmake.rs +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/rmake.rs @@ -16,6 +16,7 @@ use run_make_support::{ //@ only-linux // Reason: differences in the native lib compilation process causes differences // in the --print link-args output +// FIXME: The test actually passes on windows-gnu, enable it there. fn main() { build_native_static_lib("native_dep_1"); @@ -77,8 +78,10 @@ fn main() { .stdout_utf8(); let re = regex::Regex::new( -"--whole-archive.*native_dep_1.*--whole-archive.*lnative_dep_2.*no-whole-archive.*lnative_dep_4" - ).unwrap(); + "--whole-archive.*native_dep_1.*--whole-archive.*libnative_dep_2.a\ + .*no-whole-archive.*libnative_dep_4.a", + ) + .unwrap(); assert!(re.is_match(&out)); }