Skip to content

Commit 885024d

Browse files
committed
linker: Link dylib crates by path
1 parent c3774be commit 885024d

File tree

4 files changed

+113
-85
lines changed

4 files changed

+113
-85
lines changed

Diff for: compiler/rustc_codegen_ssa/src/back/link.rs

+12-30
Original file line numberDiff line numberDiff line change
@@ -2813,6 +2813,15 @@ fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf {
28132813
}
28142814
}
28152815

2816+
fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf {
2817+
if let Some(dir) = path.parent() {
2818+
let file_name = path.file_name().expect("library path has no file name component");
2819+
rehome_sysroot_lib_dir(sess, dir).join(file_name)
2820+
} else {
2821+
fix_windows_verbatim_for_gcc(path)
2822+
}
2823+
}
2824+
28162825
// Adds the static "rlib" versions of all crates to the command line.
28172826
// There's a bit of magic which happens here specifically related to LTO,
28182827
// namely that we remove upstream object files.
@@ -2843,15 +2852,8 @@ fn add_static_crate(
28432852
let src = &codegen_results.crate_info.used_crate_source[&cnum];
28442853
let cratepath = &src.rlib.as_ref().unwrap().0;
28452854

2846-
let mut link_upstream = |path: &Path| {
2847-
let rlib_path = if let Some(dir) = path.parent() {
2848-
let file_name = path.file_name().expect("rlib path has no file name path component");
2849-
rehome_sysroot_lib_dir(sess, dir).join(file_name)
2850-
} else {
2851-
fix_windows_verbatim_for_gcc(path)
2852-
};
2853-
cmd.link_staticlib_by_path(&rlib_path, false);
2854-
};
2855+
let mut link_upstream =
2856+
|path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false);
28552857

28562858
if !are_upstream_rust_objects_already_included(sess)
28572859
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum)
@@ -2915,27 +2917,7 @@ fn add_static_crate(
29152917

29162918
// Same thing as above, but for dynamic crates instead of static crates.
29172919
fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
2918-
// Just need to tell the linker about where the library lives and
2919-
// what its name is
2920-
let parent = cratepath.parent();
2921-
// When producing a dll, the MSVC linker may not actually emit a
2922-
// `foo.lib` file if the dll doesn't actually export any symbols, so we
2923-
// check to see if the file is there and just omit linking to it if it's
2924-
// not present.
2925-
if sess.target.is_like_msvc && !cratepath.with_extension("dll.lib").exists() {
2926-
return;
2927-
}
2928-
if let Some(dir) = parent {
2929-
cmd.include_path(&rehome_sysroot_lib_dir(sess, dir));
2930-
}
2931-
// "<dir>/name.dll -> name.dll" on windows-msvc
2932-
// "<dir>/name.dll -> name" on windows-gnu
2933-
// "<dir>/libname.<ext> -> name" elsewhere
2934-
let stem = if sess.target.is_like_msvc { cratepath.file_name() } else { cratepath.file_stem() };
2935-
let stem = stem.unwrap().to_str().unwrap();
2936-
// Convert library file-stem into a cc -l argument.
2937-
let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 };
2938-
cmd.link_dylib_by_name(&stem[prefix..], false, true);
2920+
cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true);
29392921
}
29402922

29412923
fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {

Diff for: compiler/rustc_codegen_ssa/src/back/linker.rs

+81-55
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,12 @@ pub fn get_linker<'a>(
169169
pub trait Linker {
170170
fn cmd(&mut self) -> &mut Command;
171171
fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path);
172-
fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool);
172+
fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
173+
bug!("dylib linked with unsupported linker")
174+
}
175+
fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) {
176+
bug!("dylib linked with unsupported linker")
177+
}
173178
fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
174179
bug!("framework linked with unsupported linker")
175180
}
@@ -320,28 +325,53 @@ impl<'a> GccLinker<'a> {
320325
}
321326
} else {
322327
self.cmd.arg("-shared");
323-
if self.sess.target.is_like_windows {
324-
// The output filename already contains `dll_suffix` so
325-
// the resulting import library will have a name in the
326-
// form of libfoo.dll.a
327-
let implib_name =
328-
out_filename.file_name().and_then(|file| file.to_str()).map(|file| {
329-
format!(
330-
"{}{}{}",
331-
self.sess.target.staticlib_prefix,
332-
file,
333-
self.sess.target.staticlib_suffix
334-
)
335-
});
336-
if let Some(implib_name) = implib_name {
337-
let implib = out_filename.parent().map(|dir| dir.join(&implib_name));
338-
if let Some(implib) = implib {
339-
self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap()));
340-
}
328+
if let Some(name) = out_filename.file_name() {
329+
if self.sess.target.is_like_windows {
330+
// The output filename already contains `dll_suffix` so
331+
// the resulting import library will have a name in the
332+
// form of libfoo.dll.a
333+
let mut implib_name = OsString::from(&*self.sess.target.staticlib_prefix);
334+
implib_name.push(name);
335+
implib_name.push(&*self.sess.target.staticlib_suffix);
336+
let mut out_implib = OsString::from("--out-implib=");
337+
out_implib.push(out_filename.with_file_name(implib_name));
338+
self.linker_arg(out_implib);
339+
} else {
340+
// When dylibs are linked by a full path this value will get into `DT_NEEDED`
341+
// instead of the full path, so the library can be later found in some other
342+
// location than that specific path.
343+
let mut soname = OsString::from("-soname=");
344+
soname.push(name);
345+
self.linker_arg(soname);
341346
}
342347
}
343348
}
344349
}
350+
351+
fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) {
352+
if !as_needed {
353+
if self.sess.target.is_like_osx {
354+
// FIXME(81490): ld64 doesn't support these flags but macOS 11
355+
// has -needed-l{} / -needed_library {}
356+
// but we have no way to detect that here.
357+
self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
358+
} else if self.is_gnu && !self.sess.target.is_like_windows {
359+
self.linker_arg("--no-as-needed");
360+
} else {
361+
self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier);
362+
}
363+
}
364+
365+
f(self);
366+
367+
if !as_needed {
368+
if self.sess.target.is_like_osx {
369+
// See above FIXME comment
370+
} else if self.is_gnu && !self.sess.target.is_like_windows {
371+
self.linker_arg("--as-needed");
372+
}
373+
}
374+
}
345375
}
346376

347377
impl<'a> Linker for GccLinker<'a> {
@@ -443,27 +473,17 @@ impl<'a> Linker for GccLinker<'a> {
443473
// to the linker.
444474
return;
445475
}
446-
if !as_needed {
447-
if self.sess.target.is_like_osx {
448-
// FIXME(81490): ld64 doesn't support these flags but macOS 11
449-
// has -needed-l{} / -needed_library {}
450-
// but we have no way to detect that here.
451-
self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
452-
} else if self.is_gnu && !self.sess.target.is_like_windows {
453-
self.linker_arg("--no-as-needed");
454-
} else {
455-
self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier);
456-
}
457-
}
458476
self.hint_dynamic();
459-
self.cmd.arg(format!("-l{}{name}", if verbatim && self.is_gnu { ":" } else { "" },));
460-
if !as_needed {
461-
if self.sess.target.is_like_osx {
462-
// See above FIXME comment
463-
} else if self.is_gnu && !self.sess.target.is_like_windows {
464-
self.linker_arg("--as-needed");
465-
}
466-
}
477+
self.with_as_needed(as_needed, |this| {
478+
this.cmd.arg(format!("-l{}{name}", if verbatim && this.is_gnu { ":" } else { "" }));
479+
});
480+
}
481+
482+
fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) {
483+
self.hint_dynamic();
484+
self.with_as_needed(as_needed, |this| {
485+
this.cmd.arg(path);
486+
})
467487
}
468488

469489
fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) {
@@ -813,6 +833,15 @@ impl<'a> Linker for MsvcLinker<'a> {
813833
self.cmd.arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));
814834
}
815835

836+
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
837+
// When producing a dll, MSVC linker may not emit an implib file if the dll doesn't export
838+
// any symbols, so we skip linking if the implib file is not present.
839+
let implib_path = path.with_extension("dll.lib");
840+
if implib_path.exists() {
841+
self.cmd().arg(implib_path);
842+
}
843+
}
844+
816845
fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
817846
let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" };
818847
let suffix = if verbatim { "" } else { ".lib" };
@@ -1039,6 +1068,10 @@ impl<'a> Linker for EmLinker<'a> {
10391068
self.cmd.arg("-l").arg(name);
10401069
}
10411070

1071+
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
1072+
self.cmd().arg(path);
1073+
}
1074+
10421075
fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {
10431076
self.cmd.arg("-l").arg(name);
10441077
}
@@ -1212,6 +1245,10 @@ impl<'a> Linker for WasmLd<'a> {
12121245
self.cmd.arg("-l").arg(name);
12131246
}
12141247

1248+
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
1249+
self.cmd().arg(path);
1250+
}
1251+
12151252
fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
12161253
if !whole_archive {
12171254
self.cmd.arg("-l").arg(name);
@@ -1355,10 +1392,6 @@ impl<'a> Linker for L4Bender<'a> {
13551392

13561393
fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
13571394

1358-
fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
1359-
bug!("dylibs are not supported on L4Re");
1360-
}
1361-
13621395
fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
13631396
self.hint_static();
13641397
if !whole_archive {
@@ -1537,6 +1570,11 @@ impl<'a> Linker for AixLinker<'a> {
15371570
self.cmd.arg(format!("-l{name}"));
15381571
}
15391572

1573+
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
1574+
self.hint_dynamic();
1575+
self.cmd().arg(path);
1576+
}
1577+
15401578
fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
15411579
self.hint_static();
15421580
if !whole_archive {
@@ -1738,10 +1776,6 @@ impl<'a> Linker for PtxLinker<'a> {
17381776

17391777
fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
17401778

1741-
fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
1742-
panic!("external dylibs not supported")
1743-
}
1744-
17451779
fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
17461780
panic!("staticlibs not supported")
17471781
}
@@ -1820,10 +1854,6 @@ impl<'a> Linker for LlbcLinker<'a> {
18201854

18211855
fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
18221856

1823-
fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
1824-
panic!("external dylibs not supported")
1825-
}
1826-
18271857
fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
18281858
panic!("staticlibs not supported")
18291859
}
@@ -1911,10 +1941,6 @@ impl<'a> Linker for BpfLinker<'a> {
19111941

19121942
fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
19131943

1914-
fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
1915-
panic!("external dylibs not supported")
1916-
}
1917-
19181944
fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
19191945
panic!("staticlibs not supported")
19201946
}

Diff for: tests/run-make/dylib-soname/foo.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn something() {}

Diff for: tests/run-make/dylib-soname/rmake.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Checks that produced dylibs have a relative SONAME set, so they don't put "unmovable" full paths
2+
// into DT_NEEDED when used by a full path.
3+
4+
//@ only-linux
5+
//@ ignore-cross-compile
6+
7+
use run_make_support::regex::Regex;
8+
use run_make_support::{cmd, run_in_tmpdir, rustc};
9+
10+
fn main() {
11+
run_in_tmpdir(|| {
12+
rustc().crate_name("foo").crate_type("dylib").input("foo.rs").run();
13+
cmd("readelf")
14+
.arg("-d")
15+
.arg("libfoo.so")
16+
.run()
17+
.assert_stdout_contains("Library soname: [libfoo.so]");
18+
});
19+
}

0 commit comments

Comments
 (0)