Skip to content
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

Weird segfault when trying inline asm on macOS in Rust #63977

Closed
luojia65 opened this issue Aug 28, 2019 · 4 comments
Closed

Weird segfault when trying inline asm on macOS in Rust #63977

luojia65 opened this issue Aug 28, 2019 · 4 comments
Labels
A-inline-assembly Area: inline asm!(..) O-macos Operating system: macOS requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@luojia65
Copy link
Contributor

luojia65 commented Aug 28, 2019

Hello! I tried this piece of code:

#![feature(asm)]

fn main() {
    unsafe { asm!("
        xor rax, rax
    .process_loop:  
        nop
        add rax, 1
        cmp rax, 500
        jne .process_loop
    ":
    :
    :"rax"
    :"intel") };
}

On windows it compiles and runs normally. On my macOS it compiles with no error or warning output, but fired an output Segmentation fault: 11 when I try to execute the output binary file.
I tried IDA pro on both operating systems. If I compile this code in windows, the IDA gave me normal disassemble output I expected:
05292521505F9DA99853DC9222386FE8
But when I compile on macOS (for as default, a Mach-O binary file), it gave me this with weird nop lines and a red mark meaning there was something wrong:
C32586C63ED3FDE98899C2873BED237D
What can cause this error and is there what I can do to fix this problem? Thanks!

FYI, my mac is iMac (Retina 5K, 27-inch, 2017).
The uname -a output:

macdeiMac:~ mac$ uname -a
Darwin macdeiMac.local 18.7.0 Darwin Kernel Version 18.7.0: Thu Jun 20 18:42:21 PDT 2019; root:xnu-4903.270.47~4/RELEASE_X86_64 x86_64

The rustc -V output:

macdeiMac:~ mac$ rustc -V
rustc 1.38.0-nightly (0b680cfce 2019-07-09)

The sysctl machdep.cpu output:

Click to expand
macdeiMac:~ mac$ sysctl machdep.cpu
machdep.cpu.max_basic: 22
machdep.cpu.max_ext: 2147483656
machdep.cpu.vendor: GenuineIntel
machdep.cpu.brand_string: Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz
machdep.cpu.family: 6
machdep.cpu.model: 158
machdep.cpu.extmodel: 9
machdep.cpu.extfamily: 0
machdep.cpu.stepping: 9
machdep.cpu.feature_bits: 9221960262849657855
machdep.cpu.leaf7_feature_bits: 43806655 0
machdep.cpu.leaf7_feature_bits_edx: 2617254912
machdep.cpu.extfeature_bits: 1241984796928
machdep.cpu.signature: 591593
machdep.cpu.brand: 0
machdep.cpu.features: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM PBE SSE3 PCLMULQDQ DTES64 MON DSCPL VMX SMX EST TM2 SSSE3 FMA CX16 TPR PDCM SSE4.1 SSE4.2 x2APIC MOVBE POPCNT AES PCID XSAVE OSXSAVE SEGLIM64 TSCTMR AVX1.0 RDRAND F16C
machdep.cpu.leaf7_features: RDWRFSGS TSC_THREAD_OFFSET SGX BMI1 HLE AVX2 SMEP BMI2 ERMS INVPCID RTM FPU_CSDS MPX RDSEED ADX SMAP CLFSOPT IPT MDCLEAR TSXFA IBRS STIBP L1DF SSBD
machdep.cpu.extfeatures: SYSCALL XD 1GBPAGE EM64T LAHF LZCNT PREFETCHW RDTSCP TSCI
machdep.cpu.logical_per_package: 16
machdep.cpu.cores_per_package: 8
machdep.cpu.microcode_version: 180
machdep.cpu.processor_flag: 1
machdep.cpu.mwait.linesize_min: 64
machdep.cpu.mwait.linesize_max: 64
machdep.cpu.mwait.extensions: 3
machdep.cpu.mwait.sub_Cstates: 1319200
machdep.cpu.thermal.sensor: 1
machdep.cpu.thermal.dynamic_acceleration: 1
machdep.cpu.thermal.invariant_APIC_timer: 1
machdep.cpu.thermal.thresholds: 2
machdep.cpu.thermal.ACNT_MCNT: 1
machdep.cpu.thermal.core_power_limits: 1
machdep.cpu.thermal.fine_grain_clock_mod: 1
machdep.cpu.thermal.package_thermal_intr: 1
machdep.cpu.thermal.hardware_feedback: 0
machdep.cpu.thermal.energy_policy: 1
machdep.cpu.xsave.extended_state: 31 832 1088 0
machdep.cpu.xsave.extended_state1: 15 832 256 0
machdep.cpu.arch_perf.version: 4
machdep.cpu.arch_perf.number: 8
machdep.cpu.arch_perf.width: 48
machdep.cpu.arch_perf.events_number: 7
machdep.cpu.arch_perf.events: 0
machdep.cpu.arch_perf.fixed_number: 3
machdep.cpu.arch_perf.fixed_width: 48
machdep.cpu.cache.linesize: 64
machdep.cpu.cache.L2_associativity: 4
machdep.cpu.cache.size: 256
machdep.cpu.tlb.inst.large: 8
machdep.cpu.tlb.data.small: 64
machdep.cpu.tlb.data.small_level1: 128
machdep.cpu.address_bits.physical: 39
machdep.cpu.address_bits.virtual: 48
machdep.cpu.core_count: 4
machdep.cpu.thread_count: 4
machdep.cpu.tsc_ccc.numerator: 284
machdep.cpu.tsc_ccc.denominator: 2
@estebank estebank added A-inline-assembly Area: inline asm!(..) O-macos Operating system: macOS T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 28, 2019
@Centril Centril added the requires-nightly This issue requires a nightly compiler in some way. label Oct 25, 2019
@Amanieu
Copy link
Member

Amanieu commented Nov 1, 2019

Could you try removing the . from the label name? Maybe the assembler is treating it as a directive?

@Amanieu
Copy link
Member

Amanieu commented Nov 1, 2019

Also, you should mark the asm! as volatile, otherwise it might get optimized away by LLVM since it has no outputs. Although the lack of volatile shouldn't be causing this particular error...

@stevecheckoway
Copy link

Solution

I think you want to use ${:private} rather than a dot. E.g., this works

#![feature(asm)]

fn main() {
    let result: u64;
    unsafe { asm!("
        xor rax, rax
    ${:private}process_loop:
        nop
        add rax, 1
        cmp rax, 500
        jne ${:private}process_loop
    ": "={rax}"(result)
    :
    :
    :"intel") };
    println!("Result: {}", result);
}

Alternative solution

If you switch to AT&T syntax, you can use 0: and 0b like this.

#![feature(asm)]

fn main() {
    let result: u64;
    unsafe { asm!("
        xorl %eax, %eax
    0:
        nop
        addq $$1, %rax
        cmpq $$500, %rax
        jne 0b
    ": "={rax}"(result)
    :
    :
    :) };
    println!("Result: {}", result);
}

Explanation

What seems to be happening is the following. If you ask rustc to --emit asm, you can see your inline assembly. Here's the start of the main function (I've stripped out the .cfi_ directives to make it easier to read).

	.p2align	4, 0x90
__ZN1d4main17h3e6e8052bbc66057E:
	pushq	%rbp
	movq	%rsp, %rbp
	subq	$112, %rsp
	movq	__ZN4core3fmt3num3imp52_$LT$impl$u20$core..fmt..Display$u20$for$u20$u64$GT$3fmt17h37acbe90c85d9165E@GOTPCREL(%rip), %rsi
	## InlineAsm Start


	xorq	%rax, %rax
.process_loop:
	nop
	addq	$1, %rax
	cmpq	$500, %rax
	jne	.process_loop


	## InlineAsm End

If you compile it and look at the disassembly you see all of those nops that you saw from IDA.

d::main::h3e6e8052bbc66057:
100000d80:      pushq   %rbp
100000d81:      movq    %rsp, %rbp
100000d84:      subq    $112, %rsp
100000d88:      leaq    117905(%rip), %rsi
100000d8f:      xorq    %rax, %rax
100000d92:      nop
100000d93:      nop
100000d94:      nop
100000d95:      nop
100000d96:      nop
100000d97:      nop
100000d98:      nop
100000d99:      nop
100000d9a:      nop
100000d9b:      nop
100000d9c:      nop
100000d9d:      nop
100000d9e:      nop
100000d9f:      nop

_main:
100000da0:      pushq   %rbp
100000da1:      movq    %rsp, %rbp
100000da4:      subq    $16, %rsp

You can see that the initial portion looks the same, up through the first instruction of the inline assembly, but then there's just the nop padding to put the _main symbol at 16-byte alignment.

The culprit here is actually the very last line of the assembly file.

.subsections_via_symbols

If you check out Apple's documentation, you can see what's happening here. .process_loop is a global symbol, .subsections_via_symbols causes the linker to break the __TEXT,__text section into subsections at the global symbols. Since there are no references to .process_loop outside of that subsection, the subsection is deleted.

Finally, at run time, _main runs which sets up the rest of whatever Rust needs and your main function gets called but most of that code is missing and it falls through back into _main which tries set up the Rust stuff again which crashes. You can see this in the backtrace.

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x00000001000070b2 d`std::rt::lang_start_internal::h573771bdd5f2def7 [inlined] core::mem::zeroed::hc0abb49ba4fea050 at mod.rs:499:4 [opt]
    frame #1: 0x00000001000070af d`std::rt::lang_start_internal::h573771bdd5f2def7 [inlined] std::sys::unix::stack_overflow::imp::make_handler::hbf628ed912a2c133 at stack_overflow.rs:155 [opt]
    frame #2: 0x00000001000070af d`std::rt::lang_start_internal::h573771bdd5f2def7 [inlined] std::sys::unix::stack_overflow::imp::init::he5642e500424f708 at stack_overflow.rs:119 [opt]
    frame #3: 0x000000010000707b d`std::rt::lang_start_internal::h573771bdd5f2def7 at rt.rs:38 [opt]
    frame #4: 0x0000000100000d55 d`std::rt::lang_start::hba2a92a3b9483f06 + 53
    frame #5: 0x0000000100000dd2 d`main + 34
    frame #6: 0x0000000100001e10 d`std::thread::local::fast::destroy_value::h0b652cde96d37f2a at mod.rs:184
    frame #7: 0x0000000100000d7a d`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hda773a68d12ca370 + 10
    frame #8: 0x00000001000068f8 d`std::panicking::try::do_call::h92a7a402bb9bf6ba [inlined] std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::ha06528cd672f08b7 at rt.rs:52:12 [opt]
    frame #9: 0x00000001000068ec d`std::panicking::try::do_call::h92a7a402bb9bf6ba at panicking.rs:292 [opt]
    frame #10: 0x0000000100007eef d`__rust_maybe_catch_panic at lib.rs:78:7 [opt]
    frame #11: 0x00000001000071ae d`std::rt::lang_start_internal::h573771bdd5f2def7 [inlined] std::panicking::try::h1c0d44dceba304e7 at panicking.rs:270:12 [opt]
    frame #12: 0x000000010000717b d`std::rt::lang_start_internal::h573771bdd5f2def7 [inlined] std::panic::catch_unwind::h20a0db65f17b0ccd at panic.rs:394 [opt]
    frame #13: 0x000000010000717b d`std::rt::lang_start_internal::h573771bdd5f2def7 at rt.rs:51 [opt]
    frame #14: 0x0000000100000d55 d`std::rt::lang_start::hba2a92a3b9483f06 + 53
    frame #15: 0x0000000100000dd2 d`main + 34
    frame #16: 0x00007fff62aea3d5 libdyld.dylib`start + 1

This should probably be a warning or error in rustc.

@Amanieu
Copy link
Member

Amanieu commented May 23, 2020

You should only use local labels in inline assembly. This is stated in the documentation for the new asm! macro.

@Amanieu Amanieu closed this as completed May 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inline-assembly Area: inline asm!(..) O-macos Operating system: macOS requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants