Repository navigation
document that extern statics may be bigger than the declared size #2334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
6491ee0
ec8f4ef
199eef1
41a5230
68f8440
1dc181c
1f9bdca
9c315ea
d1d36bd
97eb32c
45d4097
617bb2a
8d110a9
d3a2589
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,108 @@ Extern statics can be either immutable or mutable just like [statics] outside of | |
| r[items.extern.static.read-only] | ||
| An immutable static *must* be initialized before any Rust code is executed. It is not enough for the static to be initialized before Rust code reads from it. Once Rust code runs, mutating an immutable static (from inside or outside Rust) is UB, except if the mutation happens to bytes inside of an `UnsafeCell`. | ||
|
|
||
| r[items.extern.static.size] | ||
| An extern static resolves to an allocation that starts at the address of the static; the allocation must have at least the size and alignment of the type it is declared with (see [undefined.extern-static]). If the allocation is larger, accessing the extra bytes is allowed. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure whether it really has to start there. Having extra bytes to the left seems fine? (As long as the bytes are still only in one allocation, of course.) Cc @nikic does LLVM have any trouble with statics having dereferenceable bytes before their start address?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Global variables must point to the start of the allocation. |
||
|
|
||
| > [!NOTE] | ||
| > See [items.extern.attributes.link.kind-raw-dylib.size] for an exception to this rule. | ||
|
|
||
| r[items.extern.static.same-symbol] | ||
| An extern static resolves to the same allocation as any other static with the same symbol. | ||
|
|
||
| ```rust | ||
| #[unsafe(no_mangle)] | ||
| static mut COUNTER: u8 = 0; | ||
|
|
||
| unsafe extern "C" { | ||
| #[link_name = "COUNTER"] | ||
| static mut COUNTER_ALIAS: u8; | ||
| } | ||
|
|
||
| fn main() { | ||
| unsafe { | ||
| COUNTER += 1; | ||
| COUNTER_ALIAS += 2; | ||
| // The two statics share one allocation. | ||
| assert!(COUNTER == COUNTER_ALIAS && COUNTER == 3); | ||
| assert!(&raw const COUNTER == &raw const COUNTER_ALIAS); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| r[items.extern.static.allocations-disjoint] | ||
| The allocation that an extern static resolves to does not include the bytes of any static with a different symbol, even when the linker places the statics in the same section. | ||
|
|
||
| > [!NOTE] | ||
| > A pointer to one allocation can't be used, via offsetting, to access bytes in a different allocation. Since statics with different symbols have different allocations, you can't use a pointer derived from one static to access the bytes of another static that has a different symbol. | ||
| > | ||
| > In particular, an extern static that marks the start of a linker section cannot be used to access statics with different symbols placed in that section. | ||
|
|
||
| ```rust,standalone_crate | ||
| // Bytes placed in a section without a static may be read through the | ||
| // section's start symbol. | ||
|
|
||
| // Three bytes placed in a section named `entries`. | ||
| # #[cfg(target_os = "linux")] | ||
| core::arch::global_asm!( | ||
| ".pushsection entries,\"aR\"", // `R` is like `#[used]`. | ||
| ".byte 1, 2, 3", | ||
| ".popsection", | ||
| ); | ||
|
|
||
| # #[cfg(target_os = "linux")] | ||
| unsafe extern "C" { | ||
| // On ELF targets, the linker defines these symbols at the start | ||
| // and the end of the `entries` section. | ||
| static __start_entries: [u8; 0]; | ||
| static __stop_entries: [u8; 0]; | ||
| } | ||
|
|
||
| fn main() { | ||
| # #[cfg(target_os = "linux")] { | ||
| let start = &raw const __start_entries as *const u8; | ||
| let len = (&raw const __stop_entries).addr() - start.addr(); | ||
| // The bytes of the section belong to no static, so they are part | ||
| // of the allocation that `__start_entries` resolves to. | ||
| let entries = unsafe { core::slice::from_raw_parts(start, len) }; | ||
| assert_eq!(entries, &[1, 2, 3]); | ||
| # } | ||
| } | ||
| ``` | ||
|
|
||
| ```rust,standalone_crate | ||
| // A static placed in a section is an allocation of its own, which the | ||
| // section's start symbol cannot be used to read. | ||
|
|
||
| // A static placed in a section named `entries`. | ||
| # #[cfg(target_os = "linux")] | ||
| #[used] | ||
| #[unsafe(link_section = "entries")] | ||
| static ENTRY: [u8; 3] = [1, 2, 3]; | ||
|
|
||
| # #[cfg(target_os = "linux")] | ||
| unsafe extern "C" { | ||
| // On ELF targets, the linker defines these symbols at the start | ||
| // and the end of the `entries` section. | ||
| static __start_entries: [u8; 0]; | ||
| static __stop_entries: [u8; 0]; | ||
| } | ||
|
|
||
| fn main() { | ||
| # #[cfg(target_os = "linux")] { | ||
| let start = &raw const __start_entries as *const u8; | ||
| let stop = &raw const __stop_entries as *const u8; | ||
| // The section holds exactly the bytes of `ENTRY`. | ||
| assert_eq!(stop.addr() - start.addr(), 3); | ||
| assert_eq!(start.addr(), (&raw const ENTRY).addr()); | ||
| // But those bytes are the allocation of `ENTRY`, not of | ||
| // `__start_entries`, so reading them through `start` is undefined | ||
| // behavior. | ||
| //let x = unsafe { *start }; // UB. | ||
| # } | ||
| } | ||
| ``` | ||
|
|
||
| r[items.extern.abi] | ||
| ## ABI | ||
|
|
||
|
|
@@ -368,6 +470,9 @@ Specifying `kind = "dylib"` instructs the Rust compiler to link an import librar | |
| r[items.extern.attributes.link.kind-raw-dylib.platform-specific] | ||
| `raw-dylib` is only supported on Windows. Using it when targeting other platforms will result in a compiler error. | ||
|
|
||
| r[items.extern.attributes.link.kind-raw-dylib.size] | ||
| Unlike other extern statics, a `raw-dylib` static must resolve to an allocation whose size is exactly that of the type it is declared with (see [undefined.extern-static-raw-dylib]). | ||
|
|
||
| r[items.extern.attributes.link.import_name_type] | ||
| #### The `import_name_type` key | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see where you are coming from in terms of splitting this into two items. What I dislike about this is that it makes the two rules seem entirely unrelated in the structure of the page, they are just 2 items in a kind of long list.
View changes since the review
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed there is that tension. It's not the only place it comes up in the Reference. Adding identifiers to each rule tends to push things in this direction. I resisted it for some time, for the kind of reasons that you mention. But there is value in the regularity. (And, here in particular, there's value in each rule being stated with the same orientation.)
As a pattern, it helps to close some of the distance between the Reference and the FLS.