Segfault when calling library functions in no_std binary

In crate linking-mre:

main.rs:

#![no_std]
#![no_main]

use core::panic::PanicInfo;
use linking_mre::foo;

#[no_mangle]
extern "C" fn _start() -> ! {
    foo();
    loop {}
}

#[panic_handler]
fn panic_handler(_panic_info: &PanicInfo) -> ! {
    loop {}
}

lib.rs

#![no_std]
// This causes the code to fail even in release mode
//#[inline(never)]
pub fn foo() {}

When run with cargo run --target x86_64-unknown-none, the program segfaults. When the call to foo() is removed, the code loops as expected.
The code works in release mode, but not if #[inline(never)] is added to foo.

I suspect it's because the stack pointer is not setup correctly, because you are calling the function directly from the entry point (you use #[no_mangle], so I assume _start is entry point of the linker script). what bootloader are you using? maybe check out the documents of the bootloader about the environment and program states on entry point.

1 Like

Program runs correctly (ok, hangs as expected), when run via cargo run --release --target x86_64-unknown-none - however it is optimized-out as a straight-out simple infinite tight loop.

Here foo() is called indirectly (why? Because of separate module? Because PIC/PIE? Anybody wiser than me?) via dispatch table that seems not to be initialized when x86_64-unknown-none.

Disassembly of section .text:

0000000000001230 <_start>:

use core::panic::PanicInfo;
use barehand::foo;

#[no_mangle]
extern "C" fn _start() -> ! {
    1230:       50                      push   %rax
foo();
    1231:       48 8b 05 f0 10 00 00    mov    0x10f0(%rip),%rax        # 2328 <_DYNAMIC+0xe0>
    1238:       ff d0                   call   *%rax
loop {}
    123a:       eb fe                   jmp    123a <_start+0xa>
    123c:       cc                      int3
    123d:       cc                      int3
    123e:       cc                      int3
    123f:       cc                      int3

0000000000001240 <_ZN8barehand3foo17hf61938f27bb47417E>:
#![no_std]
// This causes the code to fail even in release mode
//#[inline(never)]
pub fn foo() {}
    1240:       c3                      ret

Address mov is pointing to is into .got (Global Offset Table) section, which is uninitialized.

Contents of section .got:
    2328 00000000 00000000                    ........

It should be initialized on load, but isn't (probably due to x86_64-unknown-none).

It turns out it failed because it was a position independent executable - creating a target.json without "position-independent-executables": true fixed the problem.

1 Like

So, one of my suspicions was right. PIC/PIE. :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.