-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #39987 - japaric:used, r=arielb1
#[used] attribute (For an explanation of what this feature does, read the commit message) I'd like to propose landing this as an experimental feature (experimental as in: no clear stabilization path -- like `asm!`, `#[linkage]`) as it's low maintenance (I think) and relevant to the "Usage in resource-constrained environments" exploration area. The main use case I see is running code before `main`. This could be used, for instance, to cheaply initialize an allocator before `main` where the alternative is to use `lazy_static` to initialize the allocator on its first use which it's more expensive (atomics) and doesn't work on ARM Cortex-M0 microcontrollers (no `AtomicUsize` on that platform) Here's a `std` example of that: ``` rust unsafe extern "C" fn before_main_1() { println!("Hello"); } unsafe extern "C" fn before_main_2() { println!("World"); } #[link_section = ".init_arary"] #[used] static INIT_ARRAY: [unsafe extern "C" fn(); 2] = [before_main_1, before_main_2]; fn main() { println!("Goodbye"); } ``` ``` $ rustc -C lto -C opt-level=3 before_main.rs $ ./before_main Hello World Goodbye ``` In general, this pattern could be used to let *dependencies* run code before `main` (which sounds like it could go very wrong in some cases). There are probably other use cases; I hope that the people I have cc-ed can comment on those. Note that I'm personally unsure if the above pattern is something we want to promote / allow and that's why I'm proposing this feature as experimental. If this leads to more footguns than benefits then we can just axe the feature. cc @nikomatsakis ^ I know you have some thoughts on having a process for experimental features though I'm fine with writing an RFC before landing this. - `dead_code` lint will have to be updated to special case `#[used]` symbols. - Should we extend `#[used]` to work on non-generic functions? cc rust-lang/rfcs#1002 cc rust-lang/rfcs#1459 cc @dpc @JinShil
- Loading branch information
Showing
9 changed files
with
238 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# `used` | ||
|
||
The tracking issue for this feature | ||
is: [40289](https://github.com/rust-lang/rust/issues/40289). | ||
|
||
------------------------ | ||
|
||
The `#[used]` attribute can be applied to `static` variables to prevent the Rust | ||
compiler from optimizing them away even if they appear to be unused by the crate | ||
(appear to be "dead code"). | ||
|
||
``` rust | ||
#![feature(used)] | ||
|
||
#[used] | ||
static FOO: i32 = 1; | ||
|
||
static BAR: i32 = 2; | ||
|
||
fn main() {} | ||
``` | ||
|
||
If you compile this program into an object file, you'll see that `FOO` makes it | ||
to the object file but `BAR` doesn't. Neither static variable is used by the | ||
program. | ||
|
||
``` text | ||
$ rustc -C opt-level=3 --emit=obj used.rs | ||
$ nm -C used.o | ||
0000000000000000 T main | ||
U std::rt::lang_start | ||
0000000000000000 r used::FOO | ||
0000000000000000 t used::main | ||
``` | ||
|
||
Note that the *linker* knows nothing about the `#[used]` attribute and will | ||
remove `#[used]` symbols if they are not referenced by other parts of the | ||
program: | ||
|
||
``` text | ||
$ rustc -C opt-level=3 used.rs | ||
$ nm -C used | grep FOO | ||
``` | ||
|
||
"This doesn't sound too useful then!" you may think but keep reading. | ||
|
||
To preserve the symbols all the way to the final binary, you'll need the | ||
cooperation of the linker. Here's one example: | ||
|
||
The ELF standard defines two special sections, `.init_array` and | ||
`.pre_init_array`, that may contain function pointers which will be executed | ||
*before* the `main` function is invoked. The linker will preserve symbols placed | ||
in these sections (at least when linking programs that target the `*-*-linux-*` | ||
targets). | ||
|
||
``` rust,ignore | ||
#![feature(used)] | ||
extern "C" fn before_main() { | ||
println!("Hello, world!"); | ||
} | ||
#[link_section = ".init_array"] | ||
#[used] | ||
static INIT_ARRAY: [extern "C" fn(); 1] = [before_main]; | ||
fn main() {} | ||
``` | ||
|
||
So, `#[used]` and `#[link_section]` can be combined to obtain "life before | ||
main". | ||
|
||
``` text | ||
$ rustc -C opt-level=3 before-main.rs | ||
$ ./before-main | ||
Hello, world! | ||
``` | ||
|
||
Another example: ARM Cortex-M microcontrollers need their reset handler, a | ||
pointer to the function that will executed right after the microcontroller is | ||
turned on, to be placed near the start of their FLASH memory to boot properly. | ||
|
||
This condition can be met using `#[used]` and `#[link_section]` plus a linker | ||
script. | ||
|
||
``` rust,ignore | ||
#![feature(lang_items)] | ||
#![feature(used)] | ||
#![no_main] | ||
#![no_std] | ||
extern "C" fn reset_handler() -> ! { | ||
loop {} | ||
} | ||
#[link_section = ".reset_handler"] | ||
#[used] | ||
static RESET_HANDLER: extern "C" fn() -> ! = reset_handler; | ||
#[lang = "panic_fmt"] | ||
fn panic_fmt() {} | ||
``` | ||
|
||
``` text | ||
MEMORY | ||
{ | ||
FLASH : ORIGIN = 0x08000000, LENGTH = 128K | ||
RAM : ORIGIN = 0x20000000, LENGTH = 20K | ||
} | ||
SECTIONS | ||
{ | ||
.text ORIGIN(FLASH) : | ||
{ | ||
/* Vector table */ | ||
LONG(ORIGIN(RAM) + LENGTH(RAM)); /* initial SP value */ | ||
KEEP(*(.reset_handler)); | ||
/* Omitted: The rest of the vector table */ | ||
*(.text.*); | ||
} > FLASH | ||
/DISCARD/ : | ||
{ | ||
/* Unused unwinding stuff */ | ||
*(.ARM.exidx.*) | ||
} | ||
} | ||
``` | ||
|
||
``` text | ||
$ xargo rustc --target thumbv7m-none-eabi --release -- \ | ||
-C link-arg=-Tlink.x -C link-arg=-nostartfiles | ||
$ arm-none-eabi-objdump -Cd target/thumbv7m-none-eabi/release/app | ||
./target/thumbv7m-none-eabi/release/app: file format elf32-littlearm | ||
Disassembly of section .text: | ||
08000000 <app::RESET_HANDLER-0x4>: | ||
8000000: 20005000 .word 0x20005000 | ||
08000004 <app::RESET_HANDLER>: | ||
8000004: 08000009 .... | ||
08000008 <app::reset_handler>: | ||
8000008: e7fe b.n 8000008 <app::reset_handler> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
#[used] | ||
fn foo() {} | ||
//~^^ ERROR the `#[used]` attribute is an experimental feature | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
-include ../tools.mk | ||
|
||
ifdef IS_WINDOWS | ||
# Do nothing on MSVC. | ||
all: | ||
exit 0 | ||
else | ||
all: | ||
$(RUSTC) -C opt-level=3 --emit=obj used.rs | ||
nm $(TMPDIR)/used.o | grep FOO | ||
endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
#![crate_type = "lib"] | ||
#![feature(used)] | ||
|
||
#[used] | ||
static FOO: u32 = 0; | ||
|
||
static BAR: u32 = 0; |