如何设置ARM堆栈帧以便GDB可以遍历它?

时间:2014-04-12 14:03:01

标签: assembly gdb arm bare-metal ellcc

我正在做一个小项目,在裸机上使用Linux标准C库(无操作系统)。我使用qemu-system-arm作为执行平台,使用GDB进行调试。我已经编写了一个小的系统调用处理程序来处理C库所做的SVC调用,但是由于我的未处理的syscall函数无法将堆栈遍历回调用者,即使SVC处理程序可以,也很困惑。 处理程序代码是:

SVC_Handler:
    srsfd   sp!, #Mode_SYS      // Save LR_svc and SPSR_svc on the sys stack.
    cpsid   i, #Mode_SYS        // Switch to sys mode.
    push    {r4-r12, lr}        // Save registers.

    // In a system call.
    // r7 is the call number.
__in_syscall:                   // The stack frame is valid here.
    cmp     r7, #512
    blhs    Unhandled_SVC       // Jump if too big for a syscall.
    adr     r8, SVC_Table       // Get the system call table.
    str     r7, SysCall         // Save call number for error reporting.
    ldr     r7, [r8, r7, lsl #2]// Get the stystem call entry.
    blx     r7                  // Dispatch. Return value is in r0/r1
goback:
    pop     {r4-r12, lr}        // Restore registers.
    rfeia   sp!                 // And return.

SysCall:
    .word   0

// Unhandled system calls.
Unhandled_SVC:
    stmfd   sp!, {r12, lr}
    push    {r2-r5}                 // Push extra arguments.
    mov     r3, r1
    mov     r2, r0
    ldr     r1, SysCall             // And the system call number.
    ldr     r0, stringPtr           // Get the format string.
    bl      printf
    add     sp, #16                 // clean up the stack.

    mov     r0, #-ENOSYS       
    ldmfd   sp!, {r12, pc}

如果我在__in_syscall设置断点,我可以看到堆栈帧就好了。如果我通过分支输入Unhandled_SVC或通过SVC_Table中的指针间接输入,GDB会显示堆栈帧,即使程序正确执行也会感到困惑。

我错过了什么?

这是我的ELLCC embedded compiler项目和完整source is here的一部分。

1 个答案:

答案 0 :(得分:1)

tl; dr - 可能你不能为你的系统调用用例做你想做的事。

但是,以下内容对于跟踪不涉及模式切换的ARM汇编程序非常有用。

有几个gnu汇编程序或 gas 伪操作,用于汇编程序中的堆栈跟踪。也就是说,您总是可以创建超出典型例程的汇编程序;例如,具有上下文切换的调度程序等

  1. .fnstart - 函数的开头(文本范围)
  2. .fnend - 功能结束(文字范围)
  3. .setfp - APCS堆栈框架的位置。
  4. .save - 堆栈中保存的寄存器列表。
  5. .pad - 堆叠上的其他保留空间
  6. .movsp - 增加堆栈。
  7. .cantunwind - 不要尝试解除此功能;当你超越正常时。
  8. 您当前的例程省略了pc注册表(针对SVC_Handler)例程,并且没有更新fp。这很好,但您需要告诉调试器不要查看可能存在于非保留寄存器中的参数。 特别有用的是 gas unwinding tutorial.

    您的用户sp和系统sp不同。所以当你追踪时,它只会在一个堆栈中。 GDB不知道跳模和/或堆栈。一种机制是将系统调用条目中的帧指针归零,以使其成为帧跟踪的终止。然后,您需要在Unhandled_SVC中设置一个真正的堆栈帧。如果要继续跟踪,则需要编写GDB宏来提取内核SVC调用信息并转换到例外堆栈。

    有关ARM堆栈帧的一些信息,请参阅:ARM Link and frame pointer