Stack arguments and a stack frame

Stack arguments and a stack frame

The first six integer arguments travel in registers. For a seventh, the caller uses the stack. This example adds seven numbers and uses rbp to keep the stack argument and a local variable easy to find while rsp changes.

default rel
global _start

section .text
; sum7(a, b, c, d, e, f, g) -> the total
sum7:
    push rbp                ; save the caller's frame pointer
    mov rbp, rsp            ; fixed base for this frame
    sub rsp, 16             ; reserve two local qwords
    push rbx                ; save the caller's rbx before using it

    mov rbx, rdi            ; total of the first six arguments
    add rbx, rsi
    add rbx, rdx
    add rbx, rcx
    add rbx, r8
    add rbx, r9
    mov [rbp - 8], rbx      ; write that total to the first local
    add rbx, [rbp + 16]     ; add the seventh argument
    mov rax, rbx            ; return the answer

    pop rbx                 ; restore the caller's rbx
    leave                   ; mov rsp, rbp, then pop rbp
    ret

_start:
    mov rdi, 1
    mov rsi, 2
    mov rdx, 3
    mov rcx, 4
    mov r8, 5
    mov r9, 6
    sub rsp, 8              ; padding: _start began with rsp aligned to 16
    push 7                  ; seventh argument; rsp is aligned again
    call sum7
    add rsp, 16             ; remove the argument and padding
    mov r12, rax            ; keep the answer, 28, visible in the register view

    mov rax, 60
    xor rdi, rdi
    syscall

The playground starts _start with rsp divisible by 16. sub rsp, 8 and push 7 each move it by eight bytes, so it is divisible by 16 immediately before call, as the System V convention requires. The padding is below the argument as the caller builds the stack, so it ends up above the argument in the frame.

Set a breakpoint at mov rax, rbx, then inspect rbp, rsp, and nearby memory in the Stack tab. Use the offsets in this table to find the slots; the view may not give reserved slots names. At this point rsp = rbp - 24 and rbx holds 28.

LocationMeaningHow it got there
[rbp + 24]alignment padding; value unusedsub rsp, 8 reserved it
[rbp + 16]7, the seventh argumentpush 7 wrote it
[rbp + 8]return addresscall sum7 wrote it
[rbp]caller's rbppush rbp wrote it
[rbp - 8]21, total of the first sixsub rsp, 16 reserved it; mov wrote it
[rbp - 16]second local; value unusedsub rsp, 16 reserved it
[rbp - 24]saved caller's rbxpush rbx wrote it

Reserving a slot only moves rsp; it does not put a useful value there. Neither the padding nor the second local is read, so do not rely on whatever bytes the debugger displays for them. The first local becomes 21 only when mov [rbp - 8], rbx runs.

rbp stays fixed while rsp changes. That makes [rbp + 16] the seventh argument and [rbp - 8] the first local throughout this call. To finish, pop rbx must run before leave: leave sets rsp back to rbp, past the saved rbx. Then ret reads the return address. The caller's add rsp, 16 releases both the argument and its padding. Step past mov r12, rax to see 28 in r12; the exit setup then reuses rax.

Your turn

Complete a call to sum7(3, 4, 5, 6, 7, 8, 9). The first six arguments are set up for you. In the caller, reserve padding, push the seventh argument, and remove both after the call. In sum7, build a frame, reserve 16 bytes, save rbx, add the first six arguments in rbx, store that subtotal at [rbp - 8], then add the seventh from [rbp + 16]. Return the total in rax and restore both rbx and the frame.

Before running, predict the subtotal and final answer. Press Test to check that r12 has the answer, r13 has the caller's original rbx, and r14 and r15 are zero. The caller records rsp % 16 in r14 immediately before call, so zero checks the call's alignment. It subtracts the final rsp from the original one in r15, so zero also checks cleanup.

default rel
global _start

section .text
sum7:
    ; Build a frame, reserve two qwords, and save rbx.

    ; Add the first six arguments in rbx and store the subtotal locally.
    ; Add the seventh argument and put the answer in rax.

    ; Restore rbx and the frame before returning.
    ret

_start:
    mov rbx, 1234           ; recognizable value the caller wants preserved
    mov r15, rsp            ; remember the original stack pointer
    mov rdi, 3
    mov rsi, 4
    mov rdx, 5
    mov rcx, 6
    mov r8, 7
    mov r9, 8
    ; Add padding and push the seventh argument, 9.

    mov r14, rsp
    and r14, 15             ; record rsp % 16 immediately before call
    call sum7
    ; Remove the seventh argument and padding.
    sub r15, rsp            ; zero only if the caller restored rsp

    mov r12, rax            ; answer
    mov r13, rbx            ; caller's rbx after the call
    mov rax, 60
    xor rdi, rdi
    syscall
Show solution
default rel
global _start

section .text
sum7:
    push rbp
    mov rbp, rsp
    sub rsp, 16
    push rbx

    mov rbx, rdi
    add rbx, rsi
    add rbx, rdx
    add rbx, rcx
    add rbx, r8
    add rbx, r9
    mov [rbp - 8], rbx      ; 33
    add rbx, [rbp + 16]     ; 33 + 9 = 42
    mov rax, rbx

    pop rbx
    leave
    ret

_start:
    mov rbx, 1234
    mov r15, rsp
    mov rdi, 3
    mov rsi, 4
    mov rdx, 5
    mov rcx, 6
    mov r8, 7
    mov r9, 8
    sub rsp, 8
    push 9
    mov r14, rsp
    and r14, 15
    call sum7
    add rsp, 16
    sub r15, rsp

    mov r12, rax
    mov r13, rbx
    mov rax, 60
    xor rdi, rdi
    syscall