【发布时间】:2014-07-12 07:13:02
【问题描述】:
我有一个从 C 函数调用的 ARM 汇编函数。
在某些时候,我会做这样的事情:
.syntax unified
.arm
.text
.globl myfunc
.extern printf
myfunc:
stmdb sp!, {r4-r11} // save stack from C call
...做事...
// (NOT SHOWN): Load values into r1 and r2 to be printed by format string above
ldr r0, =message // Load format string above
push {lr} // me attempting to preserve my stack
bl printf // actual call to printf
pop {lr} // me attempting to recover my stack
ldmia sp!, {r4-r11} // recover stack from C call
mov r0, r2 // Move return value into r0
mov pc, lr // Return to C
.section data
message:
.asciz "Output: %d, %d\n"
.end
这有时会运行,有时会崩溃,运行几次然后崩溃等等。它实际上在准裸机上下文中运行,所以我无法运行调试器。我 99% 确定这是一个堆栈——还是对齐? -- 事情,根据这个Printf Change values in registers, ARM Assembly 和这个Call C function from Assembly -- the application freezes at "call printf" and I have no idea why。
任何人都可以提供一些关于如何运行上述代码块的具体想法,或者在这里提供最佳实践的一般想法吗?理想情况下,我希望能够在我的程序集文件中多次调用相同的输出函数,以便随时调试。
提前致谢!
【问题讨论】:
-
堆栈需要 8 字节对齐。只是推动lr,打破了这一点。在 lr 旁边推送一些东西以使推送 8 字节对齐(您也可以在函数的序言中这样做)。为什么不在函数调用之前和之后打印堆栈值以查看它崩溃的位置。由于您正在使用裸机,您还可以处理不同的 CPU 模式并打印寄存器值,例如当 CPU 进入 ABORT 时的崩溃处理程序。不久前我为 printf 做了类似的事情:github.com/auselen/arm-printf
-
我建议你不要使用
printf来调试汇编器。编写您自己的轮询 UART 例程。还有一些简单的例程可以将 int 转换为十六进制字符。printf需要大量机器才能正常工作。轮询的 UART 例程甚至可能不需要堆栈。看看here for some implementation。如果你走 auselen 的路线,我建议你获得一个 snprintf 实现并将缓冲区传递给轮询的 UART。 -
如果你坚持现有的,我建议你把
lr保存和恢复和寄存器保存一起。查看ARM link and frame pointer 了解一些汇编程序。你的回报就是ldmia sp!, {r4-r11,pc}。您可能应该更新fp,因为printf可能想要使用它并且可能出于某种原因踩到您的堆栈。即,C 代码使用[fp]访问本地“C”变量和其他机器。
标签: debugging assembly stack arm printf