Repository navigation
Specifying linkage on externs silently removes indirection #31508
Description
Activity
This seems like very strange behaviour to me. I'm not sure if "some pointers can't be null" is a sensible reason for this behaviour at all.
- addedA-linkageArea: linking into static, shared libraries and binariesArea: linking into static, shared libraries and binariesA-codegenArea: Code generationArea: Code generation
on Feb 9, 2016 The supported added in #12556 (the indirection here) was only really intended for weak symbols. Weak symbols are currently required to be pointers as otherwise this is not memory safe:
extern { #[linkage = "extern_weak"] static SYMBOL: extern fn(); }
because
fntypes aren't nullable. I believe it was intended that thislinkagerestriction was detected at compile-time in some typechecking pass, although I'm not sure if that was ever implemented.I guess this bug in particular is about the non-weak pointer case. However, I think the weak pointer case also warrants more discussion, for which I've opened a topic on internals.r-l.o.
Here's the code in question btw: https://github.com/rust-lang/rust/blob/3e9589c/src/librustc_trans/trans/foreign.rs#L139-L145 . It does not distinguish at all between different types of linkage.
- addedT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
on Sep 20, 2018 - addedrequires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.This issue requires a nightly compiler in some way. When possible, use a F-* label instead.
on Oct 15, 2023 Since #163405 the only remaining
#[linkage]values are some form of weak linkage aside fromavailable_externallywhich isn't valid in extern blocks at all. We probably want to replace#[linkage]with a better way of doing weak linkage.
When compiling the following code:
using
running
./externswill output something like the following:Wat.
Taking a look at the IR:
So, Rust removes a layer of indirection defining
static explicitvar: [u8;10]and adding a new variablestatic _rust_extern_with_linkage_explicitvar: *const [u8;10]=&explicitvar. All mentions ofexplicitvarin Rust source code get replaced with_rust_extern_with_linkage_explicitvar. This results in the C version and this new Rust version not having the same type! To get “correct” behavior in the example above, you would need to definestatic explicitvar: *const *const [u8;10]instead.This weird assymmetry between the types associated with symbols in Rust and in C is a source of great confusion and can easily lead to bugs. In the example above, we just read 2 bytes past some pointer by interpreting it as a 10-byte array.
This weird behavior was introduced in #12556 (see also #11978), the rationale being weak linkage and the fact that some pointers can't be null in the Rust typesystem. While true, I don't think that's sufficient rationale to add this layer of indirection. I think the layer of indirection should be removed completely. For weak linkage, a restriction can be added to allow only zeroable types.