4

我正在做一个小项目,在裸机(无操作系统)上为 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 嵌入式编译器项目的一部分,完整的源代码在这里

4

1 回答 1

1

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

但是,以下内容对于跟踪不涉及模式切换的 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 链接和帧指针

于 2015-11-03T21:10:50.567 回答