Skip to content

Relocatable section in no_std binary #110192

Open
@jfgoog

Description

@jfgoog

While experimenting with no_std, I came across a possible bug when using an external crate. I say "possible" because when I discussed with a couple of folks more knowledgeable than myself, they weren't sure if it's a rustc bug, a poorly chosen default, or a case of "yeah, linkers are like that"

The attached code, no_std_hello.zip, is a no_std program that depends on an external crate (ufmt, which provides formatted output without allocations). It requires an x86_64 Linux machine.

When run, it segfaults:

$ cargo run -q --target x86_64-unknown-none
Segmentation fault

This can be fixed with -C relocation-model=static:

$ RUSTFLAGS="-C relocation-model=static" cargo run -q --target x86_64-unknown-none
42

Looking at the version compiled without RUSTFLAGS (i.e. the one that segfaults), we see that it contains relocatable code, as indicated by the DYNAMIC ELF program header:

$ readelf -l -W target/x86_64-unknown-none/debug/no_std_hello

Elf file type is DYN (Position-Independent Executable file)
Entry point 0x1c6c
There are 7 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x000188 0x000188 R   0x8
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x00081a 0x00081a R   0x1000
  LOAD           0x000820 0x0000000000001820 0x0000000000001820 0x001d70 0x001d70 R E 0x1000
  LOAD           0x002590 0x0000000000004590 0x0000000000004590 0x0002e0 0x0002e0 RW  0x1000
  DYNAMIC        0x002710 0x0000000000004710 0x0000000000004710 0x0000e0 0x0000e0 RW  0x8
  GNU_RELRO      0x002590 0x0000000000004590 0x0000000000004590 0x0002e0 0x000a70 R   0x1
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0

 Section to Segment mapping:
  Segment Sections...
   00
   01     .dynsym .gnu.hash .hash .dynstr .rela.dyn .rodata
   02     .text
   03     .data.rel.ro .dynamic .got
   04     .dynamic
   05     .data.rel.ro .dynamic .got
   06

And, indeed, some examination with lldb shows that it segfaults when trying to call a function in a relocatable section. The relocatable section seems to exist because it's a (non-inlined) call to a function in the external crate (ufmt), which is compiled separately and then linked to produce the final binary:

(lldb) bt
* thread #1, name = 'no_std_hello', stop reason = signal SIGSEGV: address not mapped to object (fault address: 0x0)
  * frame #0: 0x0000000000000000
    frame #1: 0x00007ffff7ffbc59 no_std_hello`ufmt::impls::ixx::_$LT$impl$u20$ufmt..uDebug$u20$for$u20$i32$GT$::fmt::h8e97f3b1fc52378d(self=0x00007ffff7ffa5bc, f=0x00007fffffffd5b8) at ixx.rs:94:21
    frame #2: 0x00007ffff7ffbbe7 no_std_hello`no_std_hello::main::_$u7b$$u7b$closure$u7d$$u7d$::h6aa378adeec3265e [inlined] ufmt::impls::ixx::_$LT$impl$u20$ufmt..uDisplay$u20$for$u20$i32$GT$::fmt::hd4a7d095957703c9(self=0x00007ffff7ffa5bc, f=0x00007fffffffd5b8) at ixx.rs:115:9
    frame #3: 0x00007ffff7ffbbe2 no_std_hello`no_std_hello::main::_$u7b$$u7b$closure$u7d$$u7d$::h6aa378adeec3265e(f=0x00007fffffffd5b8) at main.rs:85:5
    frame #4: 0x00007ffff7ffbbac no_std_hello`_$LT$W$u20$as$u20$ufmt..UnstableDoAsFormatter$GT$::do_as_formatter::haa451b76a7c159d9(self=0x00007fffffffd668, f={closure_env#0} @ 0x00007fffffffd5d0) at lib.rs:417:9
    frame #5: 0x00007ffff7ffbf85 no_std_hello`main(_stack_top="\U00000001") at main.rs:85:5
    frame #6: 0x00007ffff7ffbc74 no_std_hello`_start + 8

The relevant section from objdump -d target/x86_64-unknown-none/debug/no_std_hello shows that we are indeed calling a relocatable function: (I also confirmed in lldb that the call to this function is what's causing the crash, not something else)

0000000000001c10 <_ZN4ufmt5impls3ixx46_$LT$impl$u20$ufmt..uDebug$u20$for$u20$i32$GT$3fmt17h8e97f3b1fc52378dE>:
    1c10:       48 83 ec 38             sub    $0x38,%rsp
    1c14:       48 89 74 24 08          mov    %rsi,0x8(%rsp)
    1c19:       48 89 7c 24 20          mov    %rdi,0x20(%rsp)
    1c1e:       48 89 74 24 28          mov    %rsi,0x28(%rsp)
    1c23:       8a 4c 24 37             mov    0x37(%rsp),%cl
    1c27:       48 8d 44 24 15          lea    0x15(%rsp),%rax
    1c2c:       0f b6 c9                movzbl %cl,%ecx
    1c2f:       48 ba 01 01 01 01 01    movabs $0x101010101010101,%rdx
    1c36:       01 01 01
    1c39:       48 0f af ca             imul   %rdx,%rcx
    1c3d:       48 89 08                mov    %rcx,(%rax)
    1c40:       89 48 07                mov    %ecx,0x7(%rax)
    1c43:       48 63 3f                movslq (%rdi),%rdi
    1c46:       48 8b 05 d3 2b 00 00    mov    0x2bd3(%rip),%rax        # 4820 <_DYNAMIC+0x110>
    1c4d:       48 8d 74 24 15          lea    0x15(%rsp),%rsi
    1c52:       ba 0b 00 00 00          mov    $0xb,%edx
    1c57:       ff d0                   call   *%rax
    1c59:       48 8b 7c 24 08          mov    0x8(%rsp),%rdi
    1c5e:       48 89 c6                mov    %rax,%rsi
    1c61:       e8 ca 03 00 00          call   2030 <_ZN4ufmt18Formatter$LT$W$GT$9write_str17h70e7e896fe1e8040E>
    1c66:       48 83 c4 38             add    $0x38,%rsp
    1c6a:       c3                      ret
    1c6b:       cc                      int3

My (rudimentary) understanding of ELF files is that when you have relocatable code (either because of a separate shared library or, as in this case, in the program itself), you would expect to see an INTERP header that invokes ld.so to fix up the relocatable addresses. In this case, however, with --target x86_64-unknown-none, it would be wrong (not to mention impossible) to construct such a header because we have no idea what the operating system is.

So...is this a bug? Or does no_std come with an expectation that you need to understand and customize how your program is linked? If it's not a bug, would a warning message be appropriate?

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-linkageArea: linking into static, shared libraries and binariesC-bugCategory: This is a bug.O-bare-metalTarget: Rust without an operating systemT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions