From d22d97965374403b15883aae0484d51d2166d2f5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 6 Jun 2024 19:57:36 +0300 Subject: [PATCH] linker: Link dylib crates by path --- compiler/rustc_codegen_ssa/src/back/link.rs | 33 +++---- compiler/rustc_codegen_ssa/src/back/linker.rs | 97 ++++++++++++------- src/ci/github-actions/jobs.yml | 5 + 3 files changed, 79 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index bdb808b1d4fff..9ce46fc86863d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2816,6 +2816,15 @@ fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf { } } +fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf { + if let Some(dir) = path.parent() { + let file_name = path.file_name().expect("library path has no file name component"); + rehome_sysroot_lib_dir(sess, dir).join(file_name) + } else { + fix_windows_verbatim_for_gcc(path) + } +} + // Adds the static "rlib" versions of all crates to the command line. // There's a bit of magic which happens here specifically related to LTO, // namely that we remove upstream object files. @@ -2847,12 +2856,7 @@ fn add_static_crate( let cratepath = &src.rlib.as_ref().unwrap().0; let mut link_upstream = |path: &Path| { - let rlib_path = if let Some(dir) = path.parent() { - let file_name = path.file_name().expect("rlib path has no file name path component"); - rehome_sysroot_lib_dir(sess, dir).join(file_name) - } else { - fix_windows_verbatim_for_gcc(path) - }; + let rlib_path = rehome_lib_path(sess, path); cmd.link_staticlib_by_path(&rlib_path, false); }; @@ -2918,9 +2922,6 @@ fn add_static_crate( // Same thing as above, but for dynamic crates instead of static crates. fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { - // Just need to tell the linker about where the library lives and - // what its name is - let parent = cratepath.parent(); // When producing a dll, the MSVC linker may not actually emit a // `foo.lib` file if the dll doesn't actually export any symbols, so we // check to see if the file is there and just omit linking to it if it's @@ -2928,17 +2929,9 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { if sess.target.is_like_msvc && !cratepath.with_extension("dll.lib").exists() { return; } - if let Some(dir) = parent { - cmd.include_path(&rehome_sysroot_lib_dir(sess, dir)); - } - // "/name.dll -> name.dll" on windows-msvc - // "/name.dll -> name" on windows-gnu - // "/libname. -> name" elsewhere - let stem = if sess.target.is_like_msvc { cratepath.file_name() } else { cratepath.file_stem() }; - let stem = stem.unwrap().to_str().unwrap(); - // Convert library file-stem into a cc -l argument. - let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 }; - cmd.link_dylib_by_name(&stem[prefix..], false, true); + + let dylib_path = rehome_lib_path(sess, cratepath); + cmd.link_dylib_by_path(&dylib_path, true); } fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index a82478900b17f..62e12113bc315 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -170,6 +170,9 @@ pub trait Linker { fn cmd(&mut self) -> &mut Command; fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path); fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool); + fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { + self.cmd().arg(path); + } fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { bug!("framework linked with unsupported linker") } @@ -320,28 +323,55 @@ impl<'a> GccLinker<'a> { } } else { self.cmd.arg("-shared"); - if self.sess.target.is_like_windows { - // The output filename already contains `dll_suffix` so - // the resulting import library will have a name in the - // form of libfoo.dll.a - let implib_name = - out_filename.file_name().and_then(|file| file.to_str()).map(|file| { - format!( - "{}{}{}", - self.sess.target.staticlib_prefix, - file, - self.sess.target.staticlib_suffix - ) - }); - if let Some(implib_name) = implib_name { - let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); - if let Some(implib) = implib { - self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap())); + if let Some(name) = out_filename.file_name() { + if self.sess.target.is_like_windows { + // The output filename already contains `dll_suffix` so + // the resulting import library will have a name in the + // form of libfoo.dll.a + let mut implib_name = OsString::from(&*self.sess.target.staticlib_prefix); + implib_name.push(name); + implib_name.push(&*self.sess.target.staticlib_suffix); + if let Some(implib) = out_filename.parent().map(|dir| dir.join(&implib_name)) { + let mut out_implib = OsString::from("--out-implib="); + out_implib.push(implib); + self.linker_arg(out_implib); } + } else { + // When dylibs are linked by a full path this value will get into `DT_NEEDED` + // instead of the full path, so the library can be later found in some other + // location than that specific path. + let mut soname = OsString::from("-soname="); + soname.push(name); + self.linker_arg(soname); } } } } + + fn open_as_needed(&mut self, as_needed: bool) { + if !as_needed { + if self.sess.target.is_like_osx { + // FIXME(81490): ld64 doesn't support these flags but macOS 11 + // has -needed-l{} / -needed_library {} + // but we have no way to detect that here. + self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); + } else if self.is_gnu && !self.sess.target.is_like_windows { + self.linker_arg("--no-as-needed"); + } else { + self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier); + } + } + } + + fn close_as_needed(&mut self, as_needed: bool) { + if !as_needed { + if self.sess.target.is_like_osx { + // See above FIXME comment + } else if self.is_gnu && !self.sess.target.is_like_windows { + self.linker_arg("--as-needed"); + } + } + } } impl<'a> Linker for GccLinker<'a> { @@ -443,27 +473,17 @@ impl<'a> Linker for GccLinker<'a> { // to the linker. return; } - if !as_needed { - if self.sess.target.is_like_osx { - // FIXME(81490): ld64 doesn't support these flags but macOS 11 - // has -needed-l{} / -needed_library {} - // but we have no way to detect that here. - self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); - } else if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--no-as-needed"); - } else { - self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier); - } - } self.hint_dynamic(); + self.open_as_needed(as_needed); self.cmd.arg(format!("-l{}{name}", if verbatim && self.is_gnu { ":" } else { "" },)); - if !as_needed { - if self.sess.target.is_like_osx { - // See above FIXME comment - } else if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--as-needed"); - } - } + self.close_as_needed(as_needed); + } + + fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) { + self.hint_dynamic(); + self.open_as_needed(as_needed); + self.cmd.arg(path); + self.close_as_needed(as_needed); } fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) { @@ -1537,6 +1557,11 @@ impl<'a> Linker for AixLinker<'a> { self.cmd.arg(format!("-l{name}")); } + fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { + self.hint_dynamic(); + self.cmd().arg(path); + } + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { self.hint_static(); if !whole_archive { diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 8c84e721f5de5..497b181e30039 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -91,6 +91,11 @@ pr: <<: *job-linux-16c - image: x86_64-gnu-tools <<: *job-linux-16c + - image: x86_64-msvc + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler + SCRIPT: make ci-msvc + <<: *job-windows-8c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating