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 native library modifier syntax both on command line and in
#[link]
attributes. - One of the modifiers -
whole-archive
.
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
src/test/ui/native-library-link-flags/*
src/test/ui/feature-gates/feature-gate-native_link_modifiers*
src/test/run-make/native-link-modifier-whole-archive
Documentation
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
- avoid reordering the libraries, and
- avoid overriding anything unless explicitly requested with the
:RENAME
part of the-l
option.
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:
priv
, for Tracking issue for RFC 1977: public & private dependencies #44663noprelude
, for-Z build-std
in cargo
But it may make sense to add more of these in the future, e.g.
prefer-dynamic
, for [WIP] parameterize -C prefer-dynamic #88101- or maybe even
whole-archive
for rlibs too
It would be nice to harmonize this unstable syntax with the native library modifier syntax, for example
- use
+
and-
for boolean values ---extern +foo,-bar:my_crate=PATH
, - and maybe move the modifiers to the left of
my_crate
?--extern my_crate:+foo,-bar=PATH
The current syntax reference:
# Native lib modifiers
-l [KIND[:MODIFIERS]=]NAME[:RENAME]
# Rust crate modifiers
--extern [MODIFIERS:]NAME[=PATH]