我有一个简单的 ARM 汇编程序(如下),带有指向函数的链接分支,该函数将寄存器压入堆栈并分支回
lr
中的返回地址。
主要.S:
.global _start
.section .text
_start:
mov r0, #2 // garbage instructions to show that single step works properly with mov
mov r1, #2
mov r2, #2
bl subr
mov r7, #0x1
mov r0, #0
swi 0
subr:
mov r0, #1
push {r0}
bx lr
当我用GDB调试程序并使用
n
单步执行bl subr
指令时,它会继续到下一个断点而不是单步执行(下面的GIF 1)。当我进入 subr
然后单步跨过 bx lr
时,它会正常单步执行下一条指令(下面的 GIF 2)。我意识到问题是由于 push {r0}
中的 subr
指令造成的。如果将其删除,链接的分支指令将表现正常。
lr
中的地址是0x10064
,它匹配分支后的下一条指令的地址(第10行):
(gdb) i r lr
lr 0x10064 65636
(gdb) i line 10
Line 10 of "main.S" starts at address 0x10064 <_start+16> and ends at 0x10068 <_start+20>.
Step Over GDB 的 GIF(未按预期工作):
步入(正确):
我在 ARMv6l 架构上的 Raspbian Jessie 2017(QEMU 仿真,我可以使用的最稳定的操作系统)上使用 GDB 7.7.1。
问题:在一个真实的项目中(我目前正在开发一个项目),如何避免单步执行子例程,同时仍然以某种方式将返回值放在堆栈上(如果我做的事情完全错误,请告诉我)?为什么使用
push
指令会发生这种情况?
您正在使堆栈不平衡,因此 GDB 似乎假定该函数是递归的或仍然处于活动状态,因此在返回主函数时不会停止(因为堆栈不平衡)。 GDB正在等待堆栈返回正常(对于
_start
)和pc返回到“下一个”指令位置,两者一起,以便在下一个逻辑行处中断(我认为,至少).
不平衡堆栈不好,不仅会扰乱调试器,还会扰乱其他函数,例如,如果此类函数在调用不平衡堆栈的函数之前和之后使用了堆栈,例如保存它自己的返回地址在堆栈上以供稍后使用。
例如,尝试编写相同的代码,其中
_start
调用 main
,然后 main
调用 subr
。这是行不通的,因为 main
必须在调用 _start
之前推送自己的返回地址(即到 subr
),然后弹出它以便返回到 _start
,但它赢了由于堆栈已因 subr
不平衡而无法正确处理。