Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/behavior-considered-undefined.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ r[undefined.invalid]
r[undefined.asm]
* Incorrect use of inline assembly. For more details, refer to the [rules] to follow when writing code that uses inline assembly.

r[undefined.extern-static]
* Declaring an `extern static` when the allocation it resolves to is located at address 0 or is smaller, less aligned, or less mutable than declared.

r[undefined.extern-static-raw-dylib]
* Declaring an `extern static` imported with the [`raw-dylib` link kind] when the allocation it resolves to does not have the same size as the type it is declared with.
Comment on lines 64 to +68

@RalfJung RalfJung Oct 11, 2026 •

Copy link
Copy Markdown
Member Author

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

@traviscross traviscross Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

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.


r[undefined.runtime]
* Violating assumptions of the Rust runtime. Most assumptions of the Rust runtime are currently not explicitly documented.
* For assumptions specifically related to unwinding, see the [panic documentation][unwinding-ffi].
Expand Down Expand Up @@ -220,3 +226,4 @@ r[undefined.validity.undef]
[const-promoted]: destructors.md#constant-promotion
[lifetime-extended]: destructors.md#temporary-lifetime-extension
[zero-sized]: glossary.zst
[`raw-dylib` link kind]: items.extern.attributes.link.raw-dylib
105 changes: 105 additions & 0 deletions src/items/external-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

@RalfJung RalfJung Oct 12, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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?

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cc @nikic does LLVM have any trouble with statics having dereferenceable bytes before their start address?

Yes: https://github.com/llvm/llvm-project/blob/7159ce54c436f91a9437eced53800b8fcb4c34bc/llvm/lib/Analysis/AliasAnalysis.cpp#L927-L933

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

Expand Down Expand Up @@ -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

Expand Down
Loading