如何设置ARM堆栈框架,以便GDB可以遍历它



我正在做一个小项目,为裸机(无操作系统)上的ARM使用Linux标准C库。我使用qemu system arm作为执行平台,使用GDB进行调试。我已经编写了一个小的系统调用处理程序来处理C库进行的SVC调用,但我感到困惑的是,即使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处设置了一个断点,我可以很好地看到堆栈帧。如果我通过分支或通过SVC_Table中的指针间接输入Unhandled_SVC,即使程序执行正确,GDB也会混淆堆栈帧的显示。

我错过了什么?

这是我的ELLCC嵌入式编译器项目的一部分,完整的源代码在这里。

tl;dr-对于系统调用的用例,您可能无法随心所欲。

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


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

  1. .fnstart-函数的开始(文本范围)
  2. .fnend-函数结束(文本范围)
  3. .setfp—APCS堆栈帧的位置
  4. .save—堆栈上已保存寄存器的列表
  5. .pad-堆栈上的其他保留空间
  6. .movsp—递增堆栈
  7. .cantunwind-不要试图解除此功能;当你超越正常的时候

当前例程省略了pc寄存器(用于SVC_Handler)例程,并且不更新fp。这很好,但您需要告诉调试器不要查看可能在未保留寄存器中的参数。特别有用的是气体展开教程

您的用户sp和系统sp不同。因此,当您进行跟踪时,它将只在一个堆栈中。GDB将不知道如何跳转模式和/或堆栈。一种机制是将系统调用项中的帧指针归零,从而终止帧跟踪。然后,您需要在Unhandled_SVC中设置一个真正的堆栈帧。如果要继续跟踪,则需要编写一个GDB宏来提取内核SVC调用信息并转换到除外的堆栈。

有关ARM堆栈帧的一些信息,请参阅:ARM链接和帧指针。

相关内容

最新更新