Closed aidanhs closed 3 years ago
Compiling mini.rs statically (with glibc, as described at https://github.com/aidanhs/rustnotes) makes it not segfault, which is interesting. Doing --enable-debug in configure also makes it not segfault.
I starting to suspect the issue is in llvm (I don't think anything in the rust compiler itself generates asm?) and rust tickles the bug somehow.
Recompiling just the libstd crate without optimisations makes it work.
It appears that
get
on the thread local.register_dtor
, which calls the actual implementation of register_dtor
on linux.By inspection of the parameters passed to __cxa_thread_atexit_impl
in the linux implementation of register_dtor
with gdb, the t
parameter is an invalid pointer (checked by inspecting by /proc/$pid/maps
). In theory t
should be &self
in steps 2 and 3, though the functions look like they've all been inlined.
Managed to tear down std to get this test case:
#![no_std]
#![feature(thread_local)]
pub struct BB;
#[thread_local]
static mut KEY: Key = Key {
inner: BB,
dtor_running: false,
};
pub unsafe fn set() -> Option<&'static BB> {
if KEY.dtor_running {
return None
}
Some(&KEY.inner)
}
pub struct Key {
inner: BB,
dtor_running: bool,
}
$ rustc lib.rs --crate-type rlib -C opt-level=3 -C code-model=large
rustc: /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/include/llvm/CodeGen/MachineOperand.h:411: int64_t llvm::MachineOperand::getImm() const: Assertion `isImm() && "Wrong MachineOperand accessor"' failed.
(it seems that since I originally reported this issue, the nightlies now have llvm assertions enabled)
Abort seems to be coming from TwoAddressInstructionPass. Backtrace (from gdb) at the point of abort is:
```
#0 0x00007ffff7383428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff738502a in __GI_abort () at abort.c:89
#2 0x00007ffff737bbd7 in __assert_fail_base (fmt=
The rust code two comments up (https://github.com/rust-lang/rust/issues/37508#issuecomment-274222951) when compiled with rustc lib.rs --crate-type rlib --emit llvm-bc -C opt-level=1 -C code-model=large
will produce lib.bc
. Inspecting with llvm-dis:
; ModuleID = 'lib.bc'
source_filename = "lib.cgu-0.rs"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@_ZN3lib3KEY17hf88c4e1e7cc4a068E = internal thread_local global { {}, i1 } zeroinitializer, align 1
; Function Attrs: norecurse nounwind readonly uwtable
define i8* @_ZN3lib3set17hd243fd6d5c29a0c6E() unnamed_addr #0 {
entry-block:
%0 = load i8, i8* bitcast (i1* getelementptr inbounds ({ {}, i1 }, { {}, i1 }* @_ZN3lib3KEY17hf88c4e1e7cc4a068E, i64 0, i32 1) to i8*), align 1, !range !0
%1 = icmp eq i8 %0, 0
%. = select i1 %1, i8* bitcast ({ {}, i1 }* @_ZN3lib3KEY17hf88c4e1e7cc4a068E to i8*), i8* null
ret i8* %.
}
attributes #0 = { norecurse nounwind readonly uwtable }
!0 = !{i8 0, i8 2}
If you now try and compile this with llc -O1 -code-model=large -relocation-model=pic lib.bc
you will hit the assertion (assuming your llvm has assertions enabled):
llc: /home/aidanhs/Desktop/rust/rust-large/src/llvm/include/llvm/CodeGen/MachineOperand.h:411: int64_t llvm::MachineOperand::getImm() const: Assertion `isImm() && "Wrong MachineOperand accessor"' failed.
0 llc 0x00000000019c1060 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 72
1 llc 0x00000000019c13ff
2 llc 0x00000000019bf4a7 llvm::sys::RunSignalHandlers() + 153
3 llc 0x00000000019c08a7
4 libpthread.so.0 0x00007fd3c3b65390
5 libc.so.6 0x00007fd3c2d1b428 gsignal + 56
6 libc.so.6 0x00007fd3c2d1d02a abort + 362
7 libc.so.6 0x00007fd3c2d13bd7
8 libc.so.6 0x00007fd3c2d13c82
9 llc 0x0000000000b8276c
10 llc 0x0000000000d413c6 llvm::X86InstrInfo::convertToThreeAddress(llvm::ilist_iterator<llvm::MachineBasicBlock>&, llvm::MachineInstr&, llvm::LiveVariables*) const + 5096
11 llc 0x000000000138dd05
12 llc 0x0000000001390b48
13 llc 0x0000000001393def
14 llc 0x00000000011ce006 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) + 426
15 llc 0x0000000001556d7c llvm::FPPassManager::runOnFunction(llvm::Function&) + 330
16 llc 0x0000000001556f15 llvm::FPPassManager::runOnModule(llvm::Module&) + 133
17 llc 0x0000000001557290
18 llc 0x00000000015579a5 llvm::legacy::PassManagerImpl::run(llvm::Module&) + 271
19 llc 0x0000000001557bb1 llvm::legacy::PassManager::run(llvm::Module&) + 39
20 llc 0x0000000000b5948e
21 llc 0x0000000000b57c64 main + 363
22 libc.so.6 0x00007fd3c2d06830 __libc_start_main + 240
23 llc 0x0000000000b55f89 _start + 41
Stack dump:
0. Program arguments: ../../rust-large/llvm-build/dist/bin/llc -O1 -code-model=large -relocation-model=pic lib.bc
1. Running pass 'Function Pass Manager' on module 'lib.bc'.
2. Running pass 'Two-Address instruction pass' on function '@_ZN3lib3set17hd243fd6d5c29a0c6E'
Aborted (core dumped)
It works fine on the latest LLVM head. Turns out the fix was merged december 7th - https://llvm.org/bugs/show_bug.cgi?id=31271 is the bug, https://reviews.llvm.org/D27481 is the fix.
Looking at the llvm 4 release branch on the llvm-mirror github, this will be fixed when rust upgrades - https://github.com/rust-lang/rust/issues/37609.
I backported the referenced patch, built a debug rust with it (i.e. debug-rust-large-fixed) and then built a release mode rust also with the backported patch and debug-rust-large-fixed (which took forever) and successfully ended up with a release-rust-large-fixed which can build itself.
Presumably fixed by LLVM backport -- doesn't reproduce today. I'm marking as E-needstest since it seems like something we should track in tests.
@Mark-Simulacrum fwiw this is tricky to do a test for unless we want to do yet another rustc build in CI. I'm going to be rebasing our Rust fork 'shortly' and was planning to just close this issue once that's done and I've verified everything is working.
Oh... do we need to rebuild rustc for it? I was under the impression that just rustc -Ccode-model=large
was enough to test. That's all I did...
Urgh yes. Sorry, this issue has a lot of history for me and I'd forgotten parts of it.
Running the #![no_std]
testcase above is indeed a valid test of the issue :)
Triage: apparently https://github.com/rust-lang/rust/pull/42724 added a test, but it was removed before it was merged.
(allowing specifying code models was added to rustc in https://github.com/rust-lang/rust/pull/15698)
I want to build a large code model binary. But (correct me if I'm wrong) I believe this requires all code to be generated with a large code model (otherwise we could end up with rip relative addressing in the stdlib, which defeats the point).
So I tried the obvious (controlling it with
RUSTFLAGS
) and got the following build log:Brief explanation of the three parts:
In fact, this stage1 rustc segfaults when not doing much:
Reducing the command line used to build rustc:
GDB gives the backtrace as
I've dumped a bunch of info in the hope that someone might be able to lend a hand, but I can find little discussion of this feature (code models) so they may not be well-tested? I plan to investigate myself, but wanted a) to see if anyone has any pointers and b) make this a searchable issue.
As a datapoint, note that redox uses a .json target specification and builds its own stdlib manually with kernel model, which obviously works fine. Altering the configure+make command I used to use the kernel model rather than large also worked fine. So it looks like the issue is with memory model large.