I have a simple ARM Assembly program (below) with a linked branch to a function, which pushes a register onto the stack and branches back to the return address in lr
.
main.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
When I debug the program with GDB and use n
to step over the bl subr
instruction, it continues to the next breakpoint instead of single stepping (GIF 1 below). When I step into subr
and then single step over bx lr
, it single steps to the next instruction normally (GIF 2 below). I realized the problem is because of the push {r0}
instruction in subr
. If it is removed, the linked branch instruction behaves as normal.
The address in lr
is 0x10064
, which matches the address of the next instruction after the branch (line 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 GIF of GDB (not working as expected):
Step Into (correct):
I am using GDB 7.7.1 on Raspbian Jessie 2017 (QEMU Emulation, stablest OS I could get working) on architecture ARMv6l.
The question: In a real project (I am currently working on one), how can I avoid having to single step through a subroutine while still placing the return value on the stack somehow (If I am doing things completely wrong, please tell me)? Why is this happening with the push
instruction?
You're unbalancing the stack, so it seems that GDB assumes that the function is recursive or otherwise still active, and so doesn't stop upon return to main (since the stack is unbalanced). GDB is waiting for the stack to return to normal (for _start
) and the pc to return to that "next" instruction location, both together, in order to break at the next logical line (methinks, at least).
Unbalancing the stack is not good and could very well upset not just the debugger, but other functions, say, if such function made any use of the stack before & then after making a call to a function that unbalances the stack, such as saving its own return address on the stack to use later.
For example, try writing that same code with _start
calling a main
, then that main
calling subr
. That just won't work b/c main
will have to push its own return address (i.e. to _start
) before calling subr
and then pop it after in order to return back to _start
, but it won't get it right since the stack has been unbalanced by subr
.