Stabilize native library modifier syntax and the whole-archive modifier specifically by petrochenkov · Pull Request #93901 · rust-lang/rust (original) (raw)

Stabilization Report

This PR partially stabilizes RFC 2951: "Linking modifiers for native libraries" (tracking issue #83507).
The RFC was implemented about 9 months ago in #83507.

What is stabilized:

Generic modifier syntax

In #[link] attributes a new field is accepted - #[link(modifiers = "+foo,-bar")].
On command line the -l option is extended with modifiers, which can only be specified if the library kind is also specified - -l static:+foo,-bar=mylib.
The modifiers string is a non-empty comma-separated list of modifiers prepended with + or -, as specified in the RFC.

Multiple modifiers fields in a single #[link] attribute are not stabilized.
Multiple uses of the same modifier in a single attribute or option (e.g. +foo,-foo or +foo,+foo) are also not stabilized.
See the question (1) below for why I'm not stabilizing these.

whole-archive modifier

This is the only modifier that is being stabilized at this time.

Examples: #[link(name = "mylib", kind = "static", modifiers = "+whole-archive")], -l static:+whole-archive=mylib.

This modifier is only compatible with the static linking kind.
Using any other kind will result in a compiler error.

+whole-archive means that the static library is linked as a whole archive without throwing any object files away.

This modifier translates to --whole-archive for ld-like linkers, to /WHOLEARCHIVE for link.exe, and to -force_load for ld64.
The modifier does nothing for linkers that don't support it.

The default for this modifier is -whole-archive.
NOTE: The default may currently be different when building dylibs for some targets, but it is not guaranteed.
See the question (2) below for the details.

Test cases

Documentation

rust-lang/reference#1170

Questions requiring attention

(1) Modifier overriding

rustc has an obscure feature of overriding some data in #[link] attributes for the currently compiled crate using -l options on command line.
I dislike how it is currently implemented and would like to try changing the behavior to

That's why I left a FIXME in compiler/rustc_metadata/src/native_libs.rs and also kept any kind of #[link]/-l data overriding involving modifiers unstable, including the "+foo,-foo == -foo" stuff.

(2) Default value for the whole-archive modifier

The default for whole-archive is currently inconsistent.
It is usually false, but it's true when we are linking a +bundle static library directly (not through a rlib) into an executable or dylib.
These "rules" were never specified anywhere, and were changed sometimes, e.g. in 2020 whole-archive started being correctly supported on MSVC targets (#72785), but it actually caused one known regression (FaultyRAM/windres-rs#15) due to the compiler using link_whole_staticlib over-eagerly.

Ideally the default should be always false.
In this PR I switch it to false, because users now have a stable way to set whole-archive to true when necessary (which should be a rare case).

There's one problem however.
When we are building a dylib, it may reexport symbols from static libraries using extern {} blocks, see src\test\ui\abi\cross-crate\auxiliary\anon-extern-mod-cross-crate-1.rs for an example.
The problem is that the reexport by itself is not counted as a use (at least with ld-like ELF linkers), so that symbol is dropped when we are linking the dylib despite being actually necessary.
One blunt way to address this issue is to link the static library as whole-archive, then the exported symbols will also be included for sure.
This is what the current compiler behavior actually attempted to achieve, I think, it was just doing it not very correctly, with both false-positives and false-negatives.

I want to avoid breaking this dylib use case in this PR, because even if the user applied explicit +whole-archive to address such a regression, it would also be a blunt solution and not the best fix.
The best fix is to automatically mark exported symbols as used in rustc, because rustc have all the information necessary for that.
However, until that fix is implemented, rustc can use the blunt solution of using whole-archive by default when linking dylibs on some targets (the default_to_whole_archive changes in compiler\rustc_codegen_ssa\src\back\link.rs).
This solution is not used on all targets though, on Windows, for example, exported symbols are counted as used, so whole-archive is not used by default there (the exported_symbol_means_used_symbol changes in compiler\rustc_codegen_ssa\src\back\linker.rs).

(3) Consistency with --extern crate modifiers

Extern crates passed on command line can also be enhanced with modifiers on nightly - --extern foo,bar:my_crate=PATH (implemented in #67074).
There's no dedicated tracking issue for this command line syntax yet.

The currently supported modifiers are:

But it may make sense to add more of these in the future, e.g.

It would be nice to harmonize this unstable syntax with the native library modifier syntax, for example

The current syntax reference:

# Native lib modifiers
-l [KIND[:MODIFIERS]=]NAME[:RENAME]
# Rust crate modifiers
--extern [MODIFIERS:]NAME[=PATH]