Stack arguments and a stack frame

Six registers carry six arguments, so a subroutine with seven has a problem. The seventh goes on the stack, and once anything is on the stack the subroutine needs a way to find it again while rsp moves around underneath.

That is what makes this program worth reading: it builds a complete stack frame, and you can watch the whole of it at once in the Stack tab of the memory panel.

Set a breakpoint on the mov rax, rbx line and open the Stack tab. Six qwords are laid out there, and every one of them was put there by an instruction you can point at:

holdsput there by
[rbp + 16]7, the last argumentpush 7
[rbp + 8]the return addresscall sum7
[rbp]the caller's rbppush rbp
[rbp - 8]the running totalsub rsp, 16
[rbp - 16]a second local, unusedsub rsp, 16
[rbp - 24]the saved rbxpush rbx

rbp sits in the middle of that, which is the point of it. rsp is down at the bottom and moves every time anything is pushed; rbp has not moved since the second instruction of the subroutine, so [rbp - 8] names the same slot from the first line of the body to the last.

The order of the teardown is worth noticing. pop rbx comes before leave, not after, because leave sets rsp back to rbp in one go and would sail straight past the saved rbx without restoring it. Anything pushed after the frame is set up has to be popped before the frame comes down.

add rsp, 8 after the call is the caller taking its own argument off again. The convention puts that job on the caller rather than the callee, and the reason is subroutines that take a variable number of arguments: the callee often cannot know how many were pushed, and this way it does not have to.

Delete the push rbx and the pop rbx and the answer is still 28. rbx in the caller is destroyed, though, and nothing in this program notices, which is exactly how that bug behaves in a large one.