Search code examples
assemblyarmgnu

Segfault before _start runs when cross-assembling arm assembly


I'm attempting to cross-assemble my arm assembly since I don't have a screen for my target system (Raspberry Pi 4B). As a hello world, I have mcve.s:

.global main

.section .text, "ax"
main:
    movs r3, #10

Assembled using:

arm-none-eabi-gcc mcve.s --specs=nosys.specs -g -o mcve

When transferred to the pi using scp and run, I get a segfault. When I load it into gdb (+ gef), I get a segfault immediately:

Reading symbols from mcve...
gef➤  start
[+] Breaking at '{<text variable, no debug info>} 0x821c <main>'
[+] Breaking at '{<text variable, no debug info>} 0x816c <_start>'
[+] Breaking at entry-point: 0x816c
[!] Command 'entry-break' failed to execute properly, reason: During startup program terminated with signal SIGSEGV, Segmentation fault.

Looking around, I found a few ways of diagnosing this, but none have helped.

starti still crashes right away:

gef➤  starti
Starting program: /path/mcve 
During startup program terminated with signal SIGSEGV, Segmentation fault.

As does breaking on the entry point:

gef➤  info files
Symbols from "/path/mcve".
Local exec file:
    `/path/mcve', file type elf32-littlearm.
    Entry point: 0x816c
    0x00008000 - 0x00008018 is .init
    0x00008018 - 0x00008650 is .text
    0x00008650 - 0x00008668 is .fini
    0x00008668 - 0x000086b4 is .rodata
    0x000086b4 - 0x000086bc is .ARM.exidx
    0x000086bc - 0x000086c0 is .eh_frame
    0x000186c0 - 0x000186c8 is .init_array
    0x000186c8 - 0x000186cc is .fini_array
    0x000186d0 - 0x00018b04 is .data
    0x00018b04 - 0x00018b44 is .bss
gef➤  break *0x816c
Breakpoint 1 at 0x816c
gef➤  start
[+] Breaking at '{<text variable, no debug info>} 0x821c <main>'
[+] Breaking at '{<text variable, no debug info>} 0x816c <_start>'
[+] Breaking at entry-point: 0x816c
[!] Command 'entry-break' failed to execute properly, reason: During startup program terminated with signal SIGSEGV, Segmentation fault.

Although, I am capable of getting the source of _start:

gef➤  disass _start
Dump of assembler code for function _start:
   0x0000816c <+0>: ldr r3, [pc, #148]  ; 0x8208 <_start+156>
   0x00008170 <+4>: cmp r3, #0
   0x00008174 <+8>: ldreq   r3, [pc, #128]  ; 0x81fc <_start+144>
   0x00008178 <+12>:    mov sp, r3
   0x0000817c <+16>:    bl  0x80e4 <_stack_init>
   0x00008180 <+20>:    movs    r1, #0
   0x00008184 <+24>:    mov r11, r1
   0x00008188 <+28>:    mov r7, r1
   0x0000818c <+32>:    ldr r0, [pc, #120]  ; 0x820c <_start+160>
   0x00008190 <+36>:    ldr r2, [pc, #120]  ; 0x8210 <_start+164>
   0x00008194 <+40>:    subs    r2, r2, r0
   0x00008198 <+44>:    bl  0x82a8 <memset>
   0x0000819c <+48>:    ldr r3, [pc, #92]   ; 0x8200 <_start+148>
   0x000081a0 <+52>:    cmp r3, #0
   0x000081a4 <+56>:    beq 0x81b0 <_start+68>
   0x000081a8 <+60>:    mov lr, pc
   0x000081ac <+64>:    mov pc, r3
   0x000081b0 <+68>:    ldr r3, [pc, #76]   ; 0x8204 <_start+152>
   0x000081b4 <+72>:    cmp r3, #0
   0x000081b8 <+76>:    beq 0x81c4 <_start+88>
   0x000081bc <+80>:    mov lr, pc
   0x000081c0 <+84>:    mov pc, r3
   0x000081c4 <+88>:    movs    r0, #0
   0x000081c8 <+92>:    movs    r1, #0
   0x000081cc <+96>:    movs    r4, r0
   0x000081d0 <+100>:   movs    r5, r1
   0x000081d4 <+104>:   ldr r0, [pc, #56]   ; 0x8214 <_start+168>
   0x000081d8 <+108>:   cmp r0, #0
   0x000081dc <+112>:   beq 0x81e8 <_start+124>
   0x000081e0 <+116>:   ldr r0, [pc, #48]   ; 0x8218 <_start+172>
   0x000081e4 <+120>:   bl  0x84fc <atexit>
   0x000081e8 <+124>:   bl  0x8220 <__libc_init_array>
   0x000081ec <+128>:   movs    r0, r4
   0x000081f0 <+132>:   movs    r1, r5
   0x000081f4 <+136>:   bl  0x821c <main>
   0x000081f8 <+140>:   bl  0x8018 <exit>
   0x000081fc <+144>:   andeq   r0, r8, r0
   0x00008200 <+148>:   andeq   r0, r0, r0
   0x00008204 <+152>:   andeq   r0, r0, r0
   0x00008208 <+156>:   andeq   r0, r0, r0
   0x0000820c <+160>:   andeq   r8, r1, r4, lsl #22
   0x00008210 <+164>:   andeq   r8, r1, r4, asr #22
   0x00008214 <+168>:           ; <UNDEFINED> instruction: 0x000084fc
   0x00008218 <+172>:   andeq   r8, r0, r12, lsl r5

To try and take gcc out of the equation, I changed the entry point to _start in my assembly, and used ld and as directly:

arm-none-eabi-as -g mcve.s -o mcve.o
arm-none-eabi-ld mcve.o -o mcve

But I get identical behavior as before (with the exception of diass which shows my assembly).


Solution

  • I was using the wrong tool-chain 🤦‍♂️.

    I had manually downloaded and used gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2; although in retrospect, I'm not quite sure why.

    I purged it from my VM, and ran

    sudo apt install gcc-arm-linux-gnueabihf
    

    Then instead, I used the installed binaries:

    arm-linux-gnueabihf-as -g mcve.s -o mcve.o
    arm-linux-gnueabihf-ld mcve.o -o mcve
    

    And it works fine now.


    And now I just spent another afternoon fiddling with the wrong tool-chain. I nuked my existing Kali and chose to install the 64-bit version. The behavior this time was that it would assemble fine, but when run would say Illegal Instruction, and when loaded into gdb would cause it to hang indefinitely (but not cause any errors).

    The fix was to instead use:

    sudo apt install gcc-aarch64-linux-gnu