Skip to content

Commit

Permalink
WIP: Add weak native linkage modifier
Browse files Browse the repository at this point in the history
Passing the `-weak-l` / `-weak_framework`, and makes the dynamic linker `dyld` wait with resolving symbols until they're actually used.

This is somewhat niche, but I have at least three different reasons to do this:
-

If we ever want to support rust-lang#121293 in some shape or form, or just generally want `rustc` to have a more global view of the linking, we need to support the `-weak_framework` linker flag.

Supporting `-weak_framework` natively

- Never makes sense together with `as_needed`.

https://github.com/denoland/deno/blob/ab18dac09d9e5b00afee55ae0108f7c98bb2e2d3/.cargo/config.toml#L14-L24
  • Loading branch information
madsmtm committed Jan 23, 2025
1 parent a30f915 commit 9d41058
Show file tree
Hide file tree
Showing 27 changed files with 215 additions and 72 deletions.
35 changes: 25 additions & 10 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1357,7 +1357,7 @@ fn link_sanitizer_runtime(
let path = find_sanitizer_runtime(sess, &filename);
let rpath = path.to_str().expect("non-utf8 component in path");
linker.link_args(&["-rpath", rpath]);
linker.link_dylib_by_name(&filename, false, true);
linker.link_dylib_by_name(&filename, false, true, false);
} else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" {
// MSVC provides the `/INFERASANLIBS` argument to automatically find the
// compatible ASAN library.
Expand Down Expand Up @@ -1543,6 +1543,7 @@ fn print_native_static_libs(
.filter_map(|lib| {
let name = lib.name;
match lib.kind {
NativeLibKind::Dylib { weak: Some(true), .. } => Some(format!("-weak-l{name}")),
NativeLibKind::Static { bundle: Some(false), .. }
| NativeLibKind::Dylib { .. }
| NativeLibKind::Unspecified => {
Expand All @@ -1555,9 +1556,13 @@ fn print_native_static_libs(
Some(format!("-l{name}"))
}
}
NativeLibKind::Framework { .. } => {
// ld-only syntax, since there are no frameworks in MSVC
Some(format!("-framework {name}"))
// ld-only syntax, since there are no frameworks in MSVC.
NativeLibKind::Framework { weak, .. } => {
if weak.unwrap_or(false) {
Some(format!("-weak_framework {name}"))
} else {
Some(format!("-framework {name}"))
}
}
// These are included, no need to print them
NativeLibKind::Static { bundle: None | Some(true), .. }
Expand Down Expand Up @@ -2638,9 +2643,14 @@ fn add_native_libs_from_crate(
}
}
}
NativeLibKind::Dylib { as_needed } => {
NativeLibKind::Dylib { as_needed, weak } => {
if link_dynamic {
cmd.link_dylib_by_name(name, verbatim, as_needed.unwrap_or(true))
cmd.link_dylib_by_name(
name,
verbatim,
as_needed.unwrap_or(true),
weak.unwrap_or(false),
)
}
}
NativeLibKind::Unspecified => {
Expand All @@ -2651,12 +2661,17 @@ fn add_native_libs_from_crate(
cmd.link_staticlib_by_name(name, verbatim, false);
}
} else if link_dynamic {
cmd.link_dylib_by_name(name, verbatim, true);
cmd.link_dylib_by_name(name, verbatim, true, false);
}
}
NativeLibKind::Framework { as_needed } => {
NativeLibKind::Framework { as_needed, weak } => {
if link_dynamic {
cmd.link_framework_by_name(name, verbatim, as_needed.unwrap_or(true))
cmd.link_framework_by_name(
name,
verbatim,
as_needed.unwrap_or(true),
weak.unwrap_or(false),
)
}
}
NativeLibKind::RawDylib => {
Expand Down Expand Up @@ -2963,7 +2978,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) {
cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true);
cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true, false);
}

fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
Expand Down
53 changes: 36 additions & 17 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,19 @@ pub(crate) trait Linker {
crate_type: CrateType,
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, _weak: bool) {
bug!("dylib linked with unsupported linker")
}
fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) {
fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool, _weak: bool) {
bug!("dylib linked with unsupported linker")
}
fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
fn link_framework_by_name(
&mut self,
_name: &str,
_verbatim: bool,
_as_needed: bool,
_weak: bool,
) {
bug!("framework linked with unsupported linker")
}
fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool);
Expand Down Expand Up @@ -569,7 +575,7 @@ impl<'a> Linker for GccLinker<'a> {
}
}

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, weak: bool) {
if self.sess.target.os == "illumos" && name == "c" {
// libc will be added via late_link_args on illumos so that it will
// appear last in the library search order.
Expand All @@ -581,26 +587,39 @@ impl<'a> Linker for GccLinker<'a> {
self.hint_dynamic();
self.with_as_needed(as_needed, |this| {
let colon = if verbatim && this.is_gnu { ":" } else { "" };
this.link_or_cc_arg(format!("-l{colon}{name}"));
if weak {
this.link_or_cc_arg(format!("-weak-l{colon}{name}"));
} else {
this.link_or_cc_arg(format!("-l{colon}{name}"));
}
});
}

fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) {
fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool, weak: bool) {
self.hint_dynamic();
self.with_as_needed(as_needed, |this| {
this.link_or_cc_arg(path);
if weak {
this.link_or_cc_arg("-weak_library");
this.link_or_cc_arg(path);
} else {
this.link_or_cc_arg(path);
}
})
}

fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) {
fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool, weak: bool) {
self.hint_dynamic();
if !as_needed {
// FIXME(81490): ld64 as of macOS 11 supports the -needed_framework
// flag but we have no way to detect that here.
// self.link_or_cc_arg("-needed_framework").link_or_cc_arg(name);
self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
}
self.link_or_cc_args(&["-framework", name]);
if weak {
self.link_or_cc_args(&["-weak_framework", name]);
} else {
self.link_or_cc_args(&["-framework", name]);
}
}

fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
Expand Down Expand Up @@ -923,7 +942,7 @@ impl<'a> Linker for MsvcLinker<'a> {
}
}

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, _weak: bool) {
// On MSVC-like targets rustc supports import libraries using alternative naming
// scheme (`libfoo.a`) unsupported by linker, search for such libraries manually.
if let Some(path) = try_find_native_dynamic_library(self.sess, name, verbatim) {
Expand All @@ -933,7 +952,7 @@ impl<'a> Linker for MsvcLinker<'a> {
}
}

fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool, _weak: 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");
Expand Down Expand Up @@ -1171,12 +1190,12 @@ impl<'a> Linker for EmLinker<'a> {
) {
}

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, _weak: bool) {
// Emscripten always links statically
self.link_or_cc_args(&["-l", name]);
}

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

Expand Down Expand Up @@ -1338,11 +1357,11 @@ impl<'a> Linker for WasmLd<'a> {
}
}

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, _weak: bool) {
self.link_or_cc_args(&["-l", name]);
}

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

Expand Down Expand Up @@ -1643,12 +1662,12 @@ impl<'a> Linker for AixLinker<'a> {
}
}

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, _weak: bool) {
self.hint_dynamic();
self.link_or_cc_arg(format!("-l{name}"));
}

fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool, _weak: bool) {
self.hint_dynamic();
self.link_or_cc_arg(path);
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0455.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Some linking kinds are target-specific and not supported on all platforms.

Linking with `kind=framework` is only supported when targeting macOS,
as frameworks are specific to that operating system.
Linking with `kind=framework`, `kind=weak_framework` or `kind=weak_dylib` is
only supported when targeting macOS, as frameworks are specific to that
operating system.

Similarly, `kind=raw-dylib` is only supported when targeting Windows-like
platforms.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0458.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ Please specify a valid "kind" value, from one of the following:

* static
* dylib
* weak_dylib
* framework
* weak_framework
* raw-dylib
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ declare_features! (
(unstable, naked_functions, "1.9.0", Some(90957)),
/// Allows specifying the as-needed link modifier
(unstable, native_link_modifiers_as_needed, "1.53.0", Some(81490)),
/// Allows specifying the weak link modifier.
(unstable, native_link_modifiers_weak, "CURRENT_RUSTC_VERSION", None),
/// Allow negative trait implementations.
(unstable, negative_impls, "1.44.0", Some(68318)),
/// Allows the `!` pattern.
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("b"),
new_name: None,
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: None,
},
NativeLib {
Expand All @@ -404,7 +404,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("X"),
new_name: None,
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: None,
},
NativeLib {
Expand Down Expand Up @@ -448,7 +448,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("b"),
new_name: Some(String::from("X")),
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: None,
},
NativeLib {
Expand All @@ -470,7 +470,7 @@ fn test_native_libs_tracking_hash_different_values() {
NativeLib {
name: String::from("b"),
new_name: None,
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: Some(true),
},
NativeLib {
Expand Down Expand Up @@ -504,7 +504,7 @@ fn test_native_libs_tracking_hash_different_order() {
NativeLib {
name: String::from("b"),
new_name: None,
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: None,
},
NativeLib {
Expand All @@ -519,7 +519,7 @@ fn test_native_libs_tracking_hash_different_order() {
NativeLib {
name: String::from("b"),
new_name: None,
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: None,
},
NativeLib {
Expand Down Expand Up @@ -552,7 +552,7 @@ fn test_native_libs_tracking_hash_different_order() {
NativeLib {
name: String::from("b"),
new_name: None,
kind: NativeLibKind::Framework { as_needed: None },
kind: NativeLibKind::Framework { as_needed: None, weak: None },
verbatim: None,
},
];
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_metadata/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ metadata_install_missing_components =
maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview`
metadata_invalid_link_modifier =
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, weak
metadata_invalid_meta_files =
found invalid metadata files for crate `{$crate_name}`{$add_info}
Expand All @@ -134,6 +134,9 @@ metadata_lib_framework_apple =
metadata_lib_required =
crate `{$crate_name}` required to be available in {$kind} format, but was not found in this form
metadata_lib_weak_apple =
library modifier `weak` is only supported on Apple targets
metadata_link_arg_unstable =
link kind `link-arg` is unstable
Expand Down Expand Up @@ -165,6 +168,9 @@ metadata_link_requires_name =
`#[link]` attribute requires a `name = "string"` argument
.label = missing `name` argument
metadata_link_weak_apple =
link modifier `weak` is only supported on Apple targets
metadata_missing_native_library =
could not find native static library `{$libname}`, perhaps an -L flag is missing?
Expand Down Expand Up @@ -282,7 +288,7 @@ metadata_unknown_link_kind =
.label = unknown link kind
metadata_unknown_link_modifier =
unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed
unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed, weak
metadata_unsupported_abi =
ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
Expand All @@ -293,5 +299,8 @@ metadata_unsupported_abi_i686 =
metadata_wasm_import_form =
wasm import module must be of the form `wasm_import_module = "string"`
metadata_weak_compatibility =
linking modifier `weak` is only compatible with `dylib` and `framework` linking kinds
metadata_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind
18 changes: 18 additions & 0 deletions compiler/rustc_metadata/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ pub struct LinkFrameworkApple {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(metadata_link_weak_apple, code = E0455)]
pub struct LinkWeakApple {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(metadata_raw_dylib_only_windows, code = E0455)]
pub struct RawDylibOnlyWindows {
Expand Down Expand Up @@ -233,6 +240,13 @@ pub struct AsNeededCompatibility {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(metadata_weak_compatibility)]
pub struct WeakCompatibility {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(metadata_unknown_link_modifier)]
pub struct UnknownLinkModifier<'a> {
Expand Down Expand Up @@ -274,6 +288,10 @@ pub struct LinkOrdinalRawDylib {
#[diag(metadata_lib_framework_apple)]
pub struct LibFrameworkApple;

#[derive(Diagnostic)]
#[diag(metadata_lib_weak_apple)]
pub struct LibWeakApple;

#[derive(Diagnostic)]
#[diag(metadata_empty_renaming_target)]
pub struct EmptyRenamingTarget<'a> {
Expand Down
Loading

0 comments on commit 9d41058

Please sign in to comment.