-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
rustc has wrong signature for C function with 16-byte aligned stack argument in x86_64 Linux #80127
Comments
That would be: rust/compiler/rustc_target/src/abi/call/mod.rs Lines 452 to 454 in fee693d
|
Assigning |
Fixes rust-lang#80127 This will require additional testing to see if the old FIXME still applies
@tmiasko Oops, I saw this sooner, but that's very much not it. That's only meant for optimizations, and LLVM makes the mistake of having multiple meanings on one attribute ( I wrote more here, but it looks like we're missing an entire dimension of ABI we weren't aware of (i.e. correct alignment of |
Can anybody give a description of what would have to be changed in order to fix this? This just bit us in the butt again in our production code base and we'd like to put some engineering time towards fixing it if it sounds doable. |
This I think:
|
What does it mean "see what Clang does in that case for alignment"? |
Looking at the abi handling code in clang for the respective calling conventions and copying the logic to set the alignment for arguments at LLVM ir level verbatim. |
@eddyb I don't understand why you think this. Could you elaborate on your reasoning here? The LLVM language reference states that
If that's the case, it doesn't seem all that weird to keep letting |
I believe @eddyb's suggestion is to at
on_stack field with something like Option<Align> (or maybe better a custom enum) and if it is Some use this alignment value instead of the value in pointee_align . pointee_align is only meant for optimizations I believe (allowing None unconditionally), is not meant to affect the ABI.
|
What if we just changed the implementation of PassMode::Indirect { ref attrs, extra_attrs: None, on_stack: true } => {
let i = apply(attrs);
let byval = llvm::CreateByValAttr(cx.llcx, arg.layout.llvm_type(cx));
- attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byval]);
+ let align = llvm::CreateAlignmentAttr(cx.llcx, arg.layout.align().abi);
+ attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byval, align]);
} We'd still need to ignore |
That is incorrect. The call conv alignment doesn't need to match the memory alignment.
The |
I think some wires got crossed in my brain as I was writing that up. I think what I really meant to say is that we don't strictly need to specify an Also, general conceptual question: in theory, shouldn't we be able to emit the same LLVM regardless of target architecture / calling convention? Is it just that LLVM IR doesn't truly live up to its "promise" of being target agnostic? Or that we don't trust the LLVM backend to spit out the exact code we want? Or is it nothing to do with LLVM at all and more to do with the fact that there are other backends that we'd like to support? |
I'm not sure if that always gives the correct alignment.
Nope, the frontend is required to handle half of the calling convention. Basically the part to lower from high level types to primitives. LLVM only handles the register assignment and stack layout. If you don't do the half you need to do as frontend, you will get LLVM to split up your structs into one argument for each field recursively, which is almost never the correct calling convention. Rustc made this mistake once for wasm32-unknown-unknown and can't change it because of wasm-bindgen depending on the current abi, so we are stuck with it not being abi compatible with C code compiled by clang. The wasm32-wasi and wasm32-unknown-emscripten targets do use the correct C abi fortunately. |
If not, then what would we use to set
Huh, interesting. |
That did probably be platform dependent. I unfortunatly can't find where clang does the abi computation. |
Visiting for P-high review it looks like PR #103830 is on track to fix this. |
rustc_target: Add alignment to indirectly-passed by-value types, correcting the alignment of `byval` on x86 in the process. Commit 88e4d2c from five years ago removed support for alignment on indirectly-passed arguments because of problems with the `i686-pc-windows-msvc` target. Unfortunately, the `memcpy` optimizations I recently added to LLVM 16 depend on this to forward `memcpy`s. This commit attempts to fix the problems with `byval` parameters on that target and now correctly adds the `align` attribute. The problem is summarized in [this comment] by `@eddyb.` Briefly, 32-bit x86 has special alignment rules for `byval` parameters: for the most part, their alignment is forced to 4. This is not well-documented anywhere but in the Clang source. I looked at the logic in Clang `TargetInfo.cpp` and tried to replicate it here. The relevant methods in that file are `X86_32ABIInfo::getIndirectResult()` and `X86_32ABIInfo::getTypeStackAlignInBytes()`. The `align` parameter attribute for `byval` parameters in LLVM must match the platform ABI, or miscompilations will occur. Note that this doesn't use the approach suggested by eddyb, because I felt it was overkill to store the alignment in `on_stack` when special handling is really only needed for 32-bit x86. As a side effect, this should fix rust-lang#80127, because it will make the `align` parameter attribute for `byval` parameters match the platform ABI on LLVM x86-64. [this comment]: rust-lang#80822 (comment)
Resurrect: rustc_target: Add alignment to indirectly-passed by-value types, correcting the alignment of byval on x86 in the process. Same as rust-lang#111551, which I [accidentally closed](rust-lang#111551 (comment)) :/ --- This resurrects PR rust-lang#103830, which has sat idle for a while. Beyond rust-lang#103830, this also: - fixes byval alignment for types containing vectors on Darwin (see `tests/codegen/align-byval-vector.rs`) - fixes byval alignment for overaligned types on x86 Windows (see `tests/codegen/align-byval.rs`) - fixes ABI for types with 128bit requested alignment on ARM64 Linux (see `tests/codegen/aarch64-struct-align-128.rs`) r? `@nikic` --- `@pcwalton's` original PR description is reproduced below: Commit 88e4d2c from five years ago removed support for alignment on indirectly-passed arguments because of problems with the `i686-pc-windows-msvc` target. Unfortunately, the `memcpy` optimizations I recently added to LLVM 16 depend on this to forward `memcpy`s. This commit attempts to fix the problems with `byval` parameters on that target and now correctly adds the `align` attribute. The problem is summarized in [this comment] by `@eddyb.` Briefly, 32-bit x86 has special alignment rules for `byval` parameters: for the most part, their alignment is forced to 4. This is not well-documented anywhere but in the Clang source. I looked at the logic in Clang `TargetInfo.cpp` and tried to replicate it here. The relevant methods in that file are `X86_32ABIInfo::getIndirectResult()` and `X86_32ABIInfo::getTypeStackAlignInBytes()`. The `align` parameter attribute for `byval` parameters in LLVM must match the platform ABI, or miscompilations will occur. Note that this doesn't use the approach suggested by eddyb, because I felt it was overkill to store the alignment in `on_stack` when special handling is really only needed for 32-bit x86. As a side effect, this should fix rust-lang#80127, because it will make the `align` parameter attribute for `byval` parameters match the platform ABI on LLVM x86-64. [this comment]: rust-lang#80822 (comment)
Resurrect: rustc_target: Add alignment to indirectly-passed by-value types, correcting the alignment of byval on x86 in the process. Same as rust-lang#111551, which I [accidentally closed](rust-lang#111551 (comment)) :/ --- This resurrects PR rust-lang#103830, which has sat idle for a while. Beyond rust-lang#103830, this also: - fixes byval alignment for types containing vectors on Darwin (see `tests/codegen/align-byval-vector.rs`) - fixes byval alignment for overaligned types on x86 Windows (see `tests/codegen/align-byval.rs`) - fixes ABI for types with 128bit requested alignment on ARM64 Linux (see `tests/codegen/aarch64-struct-align-128.rs`) r? `@nikic` --- `@pcwalton's` original PR description is reproduced below: Commit 88e4d2c from five years ago removed support for alignment on indirectly-passed arguments because of problems with the `i686-pc-windows-msvc` target. Unfortunately, the `memcpy` optimizations I recently added to LLVM 16 depend on this to forward `memcpy`s. This commit attempts to fix the problems with `byval` parameters on that target and now correctly adds the `align` attribute. The problem is summarized in [this comment] by `@eddyb.` Briefly, 32-bit x86 has special alignment rules for `byval` parameters: for the most part, their alignment is forced to 4. This is not well-documented anywhere but in the Clang source. I looked at the logic in Clang `TargetInfo.cpp` and tried to replicate it here. The relevant methods in that file are `X86_32ABIInfo::getIndirectResult()` and `X86_32ABIInfo::getTypeStackAlignInBytes()`. The `align` parameter attribute for `byval` parameters in LLVM must match the platform ABI, or miscompilations will occur. Note that this doesn't use the approach suggested by eddyb, because I felt it was overkill to store the alignment in `on_stack` when special handling is really only needed for 32-bit x86. As a side effect, this should fix rust-lang#80127, because it will make the `align` parameter attribute for `byval` parameters match the platform ABI on LLVM x86-64. [this comment]: rust-lang#80822 (comment)
We were writing some code which interacts with a C shared library, but when we were calling a function in the library from Rust, we found our program was crashing. Upon inspection in the debugger, it seems that some of the arguments were getting corrupted. The Rust function signature looked correct so it was surprising. I have managed to reduce the issue down to a minimal repro.
foo.h
foo.c
I compiled the C code into a shared library using clang
clang -shared foo.c -o libfoo.so -I. -g
here is the version of clang
I compiled the Rust code using cargo and
rustc 1.46.0 (04488afe3 2020-08-24)
, but told it to link againstlibfoo.so
cargo:rustc-link-lib=dylib=foo
I set an
rpath
on the binary so it can find the library and when I run it, it crashes with a stack overflowOpening it up in the debugger we can see the argument corruption
In this case the stack overflow happens in
printf
Comparing the local variables and arguments of the two frames
You can see things start going off the rails starting with argument
h
which doesn't have the right value, and every argument after that is wrong. Includingm
which has some garbage stack data, so its no surprise the stack overflowed whenprintf
was reading atm
If we take a look at the llvm-ir we can see the problem
The Rust declaration of the function looks like this
and the C definition of the function looks like this
In my limited understanding of this, the only difference is the
align 16
for thebyval
argument which Rust is missing. I can find this mentioned in the LLVM referenceSo, since the argument is being passed via the stack, it seems that the alignment of the stack storage can be specified. I wasn't sure at first if this mattered but going back and looking at the assembly produced I was able to determine that it does.
On the C side of the function call we can see that
h
has this addressif we subtract 8 from this address, we can see we then get the right value
The function we are calling is expecting the argument to be at
$rbp + 0x20
, but the Rust call-site is putting it at$rbp + 0x18
(which is not a 16-byte aligned address) So I assume this missing alignment is causing issues because the caller and callee don't agree where to look on the stack for the argumentThis issue has been assigned to @pcwalton via this comment.
The text was updated successfully, but these errors were encountered: