Search code examples
assemblyarmgdbcortex-mraspberry-pi-pico

arm-none-eabi-gdb continues instead of stepping over in no-sdk baremetal assembly


In main function after nexti instruction on bl, gdb debugger continues instead of stopping. The issue does not occur in functions called by main (e.g. I can step over functions called in init_pin_output). What am I missing in setup?

start.s:

@ ...
@ vector_table
@ ...

.thumb_func
.global reset
.align 4
reset:
    ldr r2, PPB_BASE
    ldr r1, VTOR_OFFSET
    add r1, r1, r2
    ldr r0, =vector_table
    str r0, [r1]

    ldr r1, SRAM_STRIPED_END
    mov sp, r1

platform_entry:
    ldr r1, =main
    blx r1
    mov r0, r0
    bkpt #0     @ should not return

main.s:

.thumb_func
.global main
.align 4
main:
    push {lr}
    @ ...
    .init_led:
        movs r0, #25
        bl   init_pin_output
    .init_lcd:
        bl init_screen
    @ ...
    @ delay loop
    @ ...
    pop {pc}

.thumb_func
.global init_pin_output
.align 4
init_pin_output:
    push {lr}

    ldr  r1, =out_pin
    str  r0, [r1]

    ldr  r0, out_pin
    movs r1, #5                     @ 5 - SIO
    bl   GPIO_function_select

    ldr  r0, out_pin
    bl   output_enable_pin

    pop  {pc}

.align 4
out_pin:            .word 0
  • Tried calling main from wrapper function with the same result.

  • On backtrace gdb outputs: „Backtrace stopped: previous frame identical to this frame (corrupt stack?)”.

  • After inspecting disassembly of sdk generated .elf files I did not encounter frame pointer modification (as it is not effective in thumb mode).


Solution

  • Found the problem in my code. It turns out that naming sections (for readability reasons) breaks the GDB's temporary breakpoint, as it expects execution to return to the very same section (as stated in this answer). My code in main.s looked more like:

    thumb_func
    .global main
    .align 4
    main:
        push {lr}
        .clk:
            bl   setup_internal_clk
    
        .init_gpio:
            bl   init_gpio
    
        .init_ps2:
            movs r0, #13
            bl   init_keyboard
    
        @ ...
    

    The problem was that when returning from function, the code did not return to the same section - it jumped directly to the next one. The solution is either to avoid making function calls at the end of a section or to add nop instructions after the function call.