-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Deduplication of functions containing inline assembly contradicts Reference's documentation about code-patching. #116857
Comments
@zachs18 can you test specifically with |
This language could be interpreted as the compiler cannot make assumptions about the assembly because they might get patched, without actually promising that all assembly blocks can be patched in useful ways. It might some annotation to opt out of |
@workingjubilee Yes, |
That's certainly already what happens in some cases? #![no_std]
use core::arch::asm;
pub fn foo<A>() {
unsafe { asm!("nop"); }
}
fn main() {
let _ = foo::<u16>();
let _ = foo::<i32>();
let _ = foo::<f64>();
} The compiler does not promise there will be exactly three nops in the program. |
I'm not sure there's a way to locate instructions in a non-naked function in ways that are guaranteed to work by Rust. There can be arbitrary code around the asm, so an exported However this bug also occurs with |
@rustbot label -needs-triage +T-compiler +A-codegen |
...Technically, a SIGILL is always guaranteed to find the instructions, no? |
cc @rust-lang/opsem is this a soundness hole? |
I just came across a very similar issue here. However, while it could be argued that two actually identical functions like in the original example might get deduplicated, my issue here is that apparently code is getting deduplicated just based on the text-contents of the asm block, even though repeating the block in two different functions has different meaning. use std::arch::asm;
use std::hint::black_box;
fn foo_() -> *mut u64 {
let p: *mut u64;
unsafe {
asm! {
"lea {p},[rip + 1f]",
".pushsection .bss",
".align 8",
"1:",
".quad 0",
".popsection",
p = out(reg) p,
}
};
p
}
fn foo() -> *mut u64 {
let f: fn() -> _ = black_box(foo_); // prevent inlining
f()
}
fn bar_() -> *mut u64 {
let p: *mut u64;
unsafe {
asm! {
"lea {p},[rip + 1f]",
".pushsection .bss",
".align 8",
"1:",
".quad 0",
".popsection",
p = out(reg) p,
}
};
p
}
fn bar() -> *mut u64 {
let f: fn() -> _ = black_box(bar_); // prevent inlining
f()
}
fn main() {
println!("{:x?} {:x?}", foo(), bar());
} The two addresses printed should be different, but they are identical due to the incorrect code-deduplication. I'm not enitirely sure if this should be considered a separate issue instead, since the case for this being incorrect is a lot stronger than the original issue. Furthermore, I would also expect any inlining of one of the functions to return the same address, which it currently doesn't, likely for the same reason. The only way the asm instructions should currently be allowed to be repeated verbatim and thus generate a different static variable for the same function call is if it is used inside generic code which is monomorphisized in different code-generation units (The reason for that is the the same reason we currently can't have any generic static variables, which is annoying, but a different issue). |
@Amanieu what is the expected behavior here? |
The current behavior is correct: the compiler is allowed to deduplicate identical inline assembly. Perhaps this could be clarified in the documentation. Essentially, if you intend to do any kind of hot patching then you need some way to uniquely identify the assembly that you intend to patch. This is usually done by recording the address of the assembly code in a separate section like this:
This will work correctly no matter how many times the asm is duplicated or deduplicated since you'll just end up with more or fewer entries in the |
Should this be mentioned or at least hinted at in the documentation? I can see how the current docs (quoted in the OP) could lead to misunderstandings here. |
(I'm not sure if this should be considered a codegen bug (i.e. the Reference is correct) or a documentation bug (i.e. the codegen is correct). This issue assumes it is a codegen bug. If this is determined to be a documentation bug, then I can instead report this on the rust-lang/reference repo).
I tried this code:
View the resulting assembly using either godbolt.org or
cargo-show-asm
.I expected to see this happen:
foo
andbar
are not deduplicated, due to the Rust Reference's documentation thatInstead, this happened: When compiled with optimizations (
-Copt-level=2
or above),foo
andbar
are deduplicated (godbolt link)Notes:
extern "ABI"
and#[no_mangle]
do not inherently prevent deduplication.#[link_section = "..."]
s work to prevent deduplication (if the functions are given the same#[link_section = "..."]
then they are still deduplicated).Meta
Happens on nightly (2023-10-16), stable (1.73.0), and 1.59.0 (when inline asm was stabilized) (and probably all versions between).
rustc --version --verbose
:(no backtrace)
@rustbot label +A-inline-assembly
The text was updated successfully, but these errors were encountered: