Skip to content

Commit

Permalink
linker: Link dylib crates by path
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Jun 10, 2024
1 parent d402830 commit e776337
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 83 deletions.
42 changes: 12 additions & 30 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -2846,15 +2855,8 @@ fn add_static_crate(
let src = &codegen_results.crate_info.used_crate_source[&cnum];
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)
};
cmd.link_staticlib_by_path(&rlib_path, false);
};
let mut link_upstream =
|path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false);

if !are_upstream_rust_objects_already_included(sess)
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum)
Expand Down Expand Up @@ -2918,27 +2920,7 @@ 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
// not present.
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));
}
// "<dir>/name.dll -> name.dll" on windows-msvc
// "<dir>/name.dll -> name" on windows-gnu
// "<dir>/libname.<ext> -> 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);
cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true);
}

fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
Expand Down
134 changes: 81 additions & 53 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,12 @@ pub fn get_linker<'a>(
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_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
bug!("dylib linked with unsupported linker")
}
fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) {
bug!("dylib linked with unsupported linker")
}
fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
bug!("framework linked with unsupported linker")
}
Expand Down Expand Up @@ -320,28 +325,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> {
Expand Down Expand Up @@ -443,27 +475,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) {
Expand Down Expand Up @@ -813,6 +835,15 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));
}

fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
// When producing a dll, MSVC linker may not emit an implib file if the dll doesn't export
// any symbols, so we skip linking if the implib file is not present.
let implib_path = path.with_extension("dll.lib");
if implib_path.exists() {
self.cmd().arg(implib_path);
}
}

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" };
Expand Down Expand Up @@ -1039,6 +1070,10 @@ impl<'a> Linker for EmLinker<'a> {
self.cmd.arg("-l").arg(name);
}

fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
self.cmd().arg(path);
}

fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {
self.cmd.arg("-l").arg(name);
}
Expand Down Expand Up @@ -1212,6 +1247,10 @@ impl<'a> Linker for WasmLd<'a> {
self.cmd.arg("-l").arg(name);
}

fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
self.cmd().arg(path);
}

fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
if !whole_archive {
self.cmd.arg("-l").arg(name);
Expand Down Expand Up @@ -1355,10 +1394,6 @@ impl<'a> Linker for L4Bender<'a> {

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) {
bug!("dylibs are not supported on L4Re");
}

fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
self.hint_static();
if !whole_archive {
Expand Down Expand Up @@ -1537,6 +1572,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 {
Expand Down Expand Up @@ -1738,10 +1778,6 @@ impl<'a> Linker for PtxLinker<'a> {

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) {
panic!("external dylibs not supported")
}

fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
panic!("staticlibs not supported")
}
Expand Down Expand Up @@ -1820,10 +1856,6 @@ impl<'a> Linker for LlbcLinker<'a> {

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) {
panic!("external dylibs not supported")
}

fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
panic!("staticlibs not supported")
}
Expand Down Expand Up @@ -1911,10 +1943,6 @@ impl<'a> Linker for BpfLinker<'a> {

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) {
panic!("external dylibs not supported")
}

fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
panic!("staticlibs not supported")
}
Expand Down

0 comments on commit e776337

Please sign in to comment.