rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.23k stars 12.57k forks source link

SIGSEGV in LLVM #65138

Open iankronquist opened 4 years ago

iankronquist commented 4 years ago

I was trying to compile some x86 early boot code and encountered a SIGSEGV in LLVM:

* thread #4, stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x00000001061cca13 librustc_codegen_llvm-llvm.dylib`(anonymous namespace)::MachineLICMBase::IsLoopInvariantInst(llvm::MachineInstr&) + 259
    frame #1: 0x00000001061c85c3 librustc_codegen_llvm-llvm.dylib`(anonymous namespace)::MachineLICMBase::runOnMachineFunction(llvm::MachineFunction&) + 3827
    frame #2: 0x00000001061bb9e9 librustc_codegen_llvm-llvm.dylib`llvm::MachineFunctionPass::runOnFunction(llvm::Function&) + 281
    frame #3: 0x0000000106a4f798 librustc_codegen_llvm-llvm.dylib`llvm::FPPassManager::runOnFunction(llvm::Function&) + 808
    frame #4: 0x0000000106a4faf3 librustc_codegen_llvm-llvm.dylib`llvm::FPPassManager::runOnModule(llvm::Module&) + 131
    frame #5: 0x0000000106a4ff39 librustc_codegen_llvm-llvm.dylib`llvm::legacy::PassManagerImpl::run(llvm::Module&) + 857
    frame #6: 0x0000000105187a82 librustc_codegen_llvm-llvm.dylib`LLVMRustWriteOutputFile + 562
    frame #7: 0x000000010514d2b6 librustc_codegen_llvm-llvm.dylib`rustc_codegen_llvm::back::write::write_output_file::hbc571aa7d02fc516 (.llvm.673626991590052633) + 86
    frame #8: 0x000000010509e4f9 librustc_codegen_llvm-llvm.dylib`rustc_codegen_llvm::back::write::codegen::_$u7b$$u7b$closure$u7d$$u7d$::hc9d89ef4b7f2ee38 (.llvm.113447327635721745) + 1225
    frame #9: 0x000000010509a983 librustc_codegen_llvm-llvm.dylib`rustc::util::common::time_ext::h4b9d4c0b3d1fd8b0 + 163
    frame #10: 0x000000010515084e librustc_codegen_llvm-llvm.dylib`rustc_codegen_llvm::back::write::codegen::h4e68325a1f78b6c4 + 3198
    frame #11: 0x0000000105049a7f librustc_codegen_llvm-llvm.dylib`rustc_codegen_ssa::back::write::execute_work_item::h259f7cf32e76685c + 495
    frame #12: 0x00000001050881f1 librustc_codegen_llvm-llvm.dylib`std::sys_common::backtrace::__rust_begin_short_backtrace::h78716bd57684f926 + 241
    frame #13: 0x000000010505379b librustc_codegen_llvm-llvm.dylib`std::panicking::try::do_call::hfb19d5a4eb5b64d5 (.llvm.18289163052277610129) + 43
    frame #14: 0x0000000102d3e45f libstd-3f46da93ba755ab0.dylib`__rust_maybe_catch_panic + 31
    frame #15: 0x0000000105053ab6 librustc_codegen_llvm-llvm.dylib`core::ops::function::FnOnce::call_once$u7b$$u7b$vtable.shim$u7d$$u7d$::hacde5084140c3890 + 134
    frame #16: 0x0000000102d1068e libstd-3f46da93ba755ab0.dylib`_$LT$alloc..boxed..Box$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$::call_once::h40bf035471a23543 + 62
    frame #17: 0x0000000102d3d26e libstd-3f46da93ba755ab0.dylib`std::sys::unix::thread::Thread::new::thread_start::h112df5042d5a4204 + 142
    frame #18: 0x00007fff7ca7d661 libsystem_pthread.dylib`_pthread_body + 340
    frame #19: 0x00007fff7ca7d50d libsystem_pthread.dylib`_pthread_start + 377
    frame #20: 0x00007fff7ca7cbf9 libsystem_pthread.dylib`thread_start + 13

It looks like the assertion here would fail, if assertions were enabled in the LLVM rust was built with: https://elixir.bootlin.com/llvm/latest/source/llvm/lib/CodeGen/MachineLICM.cpp#L1043

Here's my target specification:

{
  "arch": "x86",
  "cpu": "pentium4",
  "crt-static-default": true,
  "crt-static-respected": true,
  "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128",
  "dynamic-linking": true,
  "eliminate-frame-pointer": false,
  "env": "musl",
  "executables": true,
  "has-elf-tls": true,
  "has-rpath": true,
  "is-builtin": true,
  "linker-flavor": "gcc",
  "linker-is-gnu": true,
  "llvm-target": "i686-unknown-linux-musl",
  "max-atomic-width": 64,
  "os": "none",
  "position-independent-executables": true,
  "post-link-objects-crt": [
    "crtn.o"
  ],
  "pre-link-args": {
    "gcc": [
      "-Wl,--as-needed",
      "-Wl,-z,noexecstack",
      "-Wl,--eh-frame-hdr",
      "-m32",
      "-Wl,-melf_i386"
    ]
  },
  "pre-link-args-crt": {
    "gcc": [
      "-nostdlib"
    ]
  },
  "pre-link-objects-exe-crt": [
    "crt1.o",
    "crti.o"
  ],
  "relro-level": "full",
  "stack-probes": false,
  "target-c-int-width": "32",
  "target-endian": "little",
  "target-family": "none",
  "target-pointer-width": "32",
  "panic-strategy": "abort",
  "features": "-mmx,-sse,+soft-float",
  "vendor": "unknown"
}

Here is my minimal repro:

#![no_std]
#![feature(asm)]
#![feature(start)]
#![feature(const_fn)]
#![feature(lang_items)]
#![crate_type = "staticlib"]
use core::panic::PanicInfo;
use core::fmt::Write;
use core::fmt;

const VGA_START: *mut u16 = 0xb8000 as *mut u16;
const VGA_ENTRY_COUNT: usize = 0x1000 / core::mem::size_of::<u16>();
// Black on green.
const TEXT_COLOR: u16 = 0x200;

pub struct VgaConsole {
    cursor: usize,
}
pub const VGA_CONSOLE: VgaConsole = VgaConsole { cursor: 0 };

impl fmt::Write for VgaConsole {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        for c in s.chars() {
            // This has been heavily mangled to minimize the repro.
            let vga_region = unsafe { core::slice::from_raw_parts_mut(VGA_START, VGA_ENTRY_COUNT) };
            vga_region[self.cursor] = c as u16 | TEXT_COLOR;
            self.cursor += 1;
        }
        Ok(())
    }
}

pub fn inw(port: u16) -> u16 {
    let mut w: u16;
    unsafe {
        asm!("inw %dx, %ax" : "={ax}"(w) : "{dx}"(port));
    }
    w
}

pub const SECTOR_SIZE   :usize = 512;
pub struct Drive {
    command_port: u16,
    data_port: u16,
    is_primary : bool,
}

// ATAPI implementation minimized for the repro
impl Drive {
    pub fn identify(&self) {
        // read model
        let mut model: [u16; 256] = [0; 256];

        for i in 0..model.len() {
            model[i] = inw(self.data_port);
            let _ = write!(VGA_CONSOLE, "{:x}", model[i]);
        }
    }
}

fn hang() -> ! {
    loop {
        unsafe {
            asm!("cli; hlt;");
        }
    }
}

#[no_mangle]
pub extern "C" fn rmain() {
    let boot_disk = Drive { command_port: 0x3f0, data_port: 0x1f0, is_primary: true, };
    boot_disk.identify();
}

#[panic_handler]
#[no_mangle]
pub fn panic(info: &PanicInfo) -> ! {
    hang();
}
iankronquist commented 4 years ago

I suspect you will want this reported upstream. Let me know if you have advice how to do this.

nagisa commented 4 years ago

It is non-trivial to build libcore for the custom target to reproduce this, so some steps towards a way forward instead.

Please produce the LLVM bytecode file (rustc --emit=llvm-bc) and verify that running it through llc -O3 or opt -O3 also fails. Make sure your version of llc is at least similar to the LLVM version reported by rustc --version --verbose. If it does indeed fail with llc as well, please attach the bytecode here. This will allow me or somebody else to easily reproduce, investigate/minimize and report the bug upstream.

If you are not able to reproduce the assertion/crash with llc or opt, it would be great if you attempted to minimize your code sample further to #![no_core] or at least reduced the amount of core things used (esp. fmt:: and iterators via for).

iankronquist commented 4 years ago

Thanks or the steps forwards, I'll try to get to them later this week.