Added in PR #39987.
RFC for stabilization: rust-lang/rfcs#2386 (merged)
This is an experimental feature and requires an RFC before it goes through the stabilization process.
Preserves static variables up to the object file level. This feature doesn't affect link time garbage collections of unused symbols / sections.
// foo.rs
#![crate_type = "lib"]
#![feature(used)]
#[used]
static FOO: u32 = 0;
$ rustc -C opt-level=3 --emit=obj foo.rs
$ nm -C foo.o
0000000000000000 r foo::FOO::h367266b77811307c
cc #41628
Is there anything besides the task of creating an RFC that blocks stabilization of this feature? This feature is a cornerstone in a library I wrote and use, and I'd love to be able to use the library on a stable toolchain.
I have opened an RFC to stabilize this: rust-lang/rfcs#2386
There is still an open bug where an used static is optimized away if the surrounding module is not used by any code: https://github.com/rust-lang/rust/issues/47384.
Needs to be done before stabilizing this: #[used] should only be allowed on static variables. Right now you can put it on functions (which has no effect) and the compiler won't complain -- that should be an error.
@phil-opp
There is still an open bug where an used static is optimized away if the surrounding module is not used by any code: #47384.
That's not a problem with #[used] AFAICT. The symbol is in the libfoo.rlib., which is the only thing #[used] guarantees. See below
#![feature(used)]
mod foo {
// private and non-reachable
#[no_mangle]
#[used]
static STATIC: [u32; 10] = [1; 10];
}
$ cargo build
$ nm -C target/debug/libfoo.rlib
foo-d351a64747b10042.18cd33xhzunvqw3h.rcgu.o:
0000000000000000 r STATIC
$ cargo build --release
$ nm -C target/release/libfoo.rlib
foo-f1e69902e2c50980.foo0-f8ffab837addfc521cde53270051d9b7.rs.rcgu.o:
0000000000000000 r STATIC
You are not getting the behavior you want for other reasons, mainly due to link-time visibility: you want STATIC to be an exported symbol (it should show up as R in the output of nm, not as r).
Also KEEP does not guarantee that a symbol will be in the output binary: the linker will stop inspecting inputs as soon as all undefined symbols are resolved so it may not get to libfoo.rlib, or the linker may look into libfoo.rlib but it may not look into the object file that contains STATIC -- these days .rlibs contain several object files due to parallel codegen.
To make your example work I would rewrite it like this:
// foo/src/lib.rs
#![feature(used)]
#![no_std] // not required but I'm using thumbv7m-none-eabi as the example target
pub mod foo {
// `STATIC` needs to be an *exported* symbol; they only way to do that right now is to make the
// item *both* public and reachable
#[doc(hidden)] // don't show this in the API docs
#[export_name = "STATIC"] // or you could use `no_mangle` and rename the item, same thing
#[used] // required or the compiler will drop this symbol when optimizations are enabled
pub static __HIDDEN: [u32; 10] = [1; 10];
}
// src/main.rs
#![feature(lang_items)]
#![no_std]
#![no_main]
extern crate foo;
#[lang = "panic_fmt"]
fn panic_fmt() {}
/* link.x */
EXTERN(STATIC); /* Forces the linker to look into libfoo.rlib */
/* In general this makes the linker continue looking into the inputs until it finds this symbol */
SECTIONS
{
/* put STATIC in the .static linker section */
.static : ALIGN(4)
{
KEEP(*(.rodata.STATIC));
}
}
$ cargo rustc --target thumbv7m-none-eabi -- -C linker=arm-none-eabi-ld -Z linker-flavor=ld -C link-arg=-Tlink.x
$ # note exported symbol: "R"
$ arm-none-eabi-nm -C target/thumbv7m-none-eabi/debug/deps/libfoo-739fb2098c2fa823.rlib
foo-739fb2098c2fa823.18cd33xhzunvqw3h.rcgu.o:
00000000 N
00000041 N
00000050 N
00000066 N
0000006a N
00000073 N
00000077 N
00000080 N
00000000 R STATIC
$ arm-none-eabi-objdump -Cd -j.static target/thumbv7m-none-eabi/debug/bar
target/thumbv7m-none-eabi/debug/bar: file format elf32-littlearm
Disassembly of section .static:
00000000 <STATIC>:
0: 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 ................
10: 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 ................
20: 01 00 00 00 01 00 00 00 ........
I think EXTERN was the main thing you were missing. Without it the linker doesn't even look into libfoo.rlib (then visibility doesn't even matter). Without EXTERN the linker will only look into libfoo.rlib if it's looking for other symbols -- this is why you got STATIC in the output binary when there were calls into functions defined in foo (the linker was looking for the function foo::hello and it happened to find STATIC). But it's not certain that will work because of parallel codegen: foo::hello may end in the first object file in libfoo.rlib then the linker won't bother to look into the second object file which could happen to contain STATIC.
Visibility is important too: if STATIC wasn't an exported symbol the linker would ignore it and you would end with an undefined reference to STATIC in your binary (U STATIC in the output of nm).
@japaric Thanks so much for the detailed explanation! The behavior makes sense to me now and I agree that this has nothing to do with the used attribute.
The stabilization RFC is now merged. Anyone writing up the stabilization PR?
#[used] item is misleading. It's used until linking, but not used at writing or compiling code.
should be #[to_be_used] or #[maybe_useful], or #[useful] for short?
useful also implies: compiler, don't dismiss this item, it's useful for later use, i.e. linking.
cc #51363
@rfcbot fcp merge
I propose that we stabilize this feature. The #[used] attribute, which can only be applied to static variables, forces the compiler to keep the variable in the output object file (.o, .rlib, etc.) even if the variable is not used, or referenced, by any other item in the crate.
See the latest RFC here.
Trying again w/ a lang tag...
@rfcbot fcp merge
Team member @cramertj has proposed to merge this. The next step is review by the rest of the tagged teams:
No concerns currently listed.
Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!
See this document for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the review above. :bell:
The final comment period, with a disposition to merge, as per the review above, is now complete.
Most helpful comment
I have opened an RFC to stabilize this: rust-lang/rfcs#2386