Pointers

Simple Pointers

int increment(int *x) {
    (*x)++;
    return 1;
}

int caller(int x) {
   increment(&x); 
   return x;
}

We compile this with -O0 which generates the following assembly:

increment:
    push    rbp
    mov    rbp, rsp
    mov    QWORD PTR [rbp-8], rdi
    mov    rax, QWORD PTR [rbp-8]
    mov    eax, DWORD PTR [rax]
    lea    edx, [rax+1]
    mov    rax, QWORD PTR [rbp-8]
    mov    DWORD PTR [rax], edx
    mov    eax, 1
    pop    rbp
    ret
    .size    increment, .-increment
    .globl    caller
    .type    caller, @function
caller:
    push    rbp
    mov    rbp, rsp
    sub    rsp, 8
    mov    DWORD PTR [rbp-4], edi
    lea    rax, [rbp-4]
    mov    rdi, rax
    call    increment
    mov    eax, DWORD PTR [rbp-4]
    leave
    ret

Following the function prologue, the room is made on the stack to place the x variable with sub rsp, 8. Because caller() is not a leaf function, it can't make use of the red zone.

The value of x is in the edi register, and it's moved on to the stack with mov DWORD PTR [rbp-4], edi. The lea rax, [rbp-4] line 'loads the effective address', so the address rbp - 4, which is the location of the x variable, is in the rax variable. This address is then copied to rdi, which is register where the first argument to increment() is stored. increment() is then called.

In the increment() function, the address of the variable to be incremented is placed on the stack with mov QWORD PTR [rbp-8], rdi. This address is then moved into rax. The mov eax, DWORD PTR [rax] moves the value that rax points to into the eax register. Note here that eax is the low 32 bits of the rax register.

The lea edx, [rax+1] takes the value in the rax register, adds 1, and loads it into the edx register. Refer to this StackOverflow discussion as to why lea is used instead of an add instruction.

The on the stack at rbp-8, which is the memory location of x, is loaded into rax. Then with the mov DWORD PTR [rax], edx line the incremented value in edx is moved into the memory location that rax contains.

Finally, the return value of 1 is loaded into eax, the previous base pointer is popped off the stack, and the function returns.

Back in caller(), the value of x which is on the stack at rbp-4 is moved into eax. The leave instruction performs two tasks:

  • A mov rsp, rbp to return the stack pointer to the current base pointer. It then per
  • A pop rbp to pop the previous base pointer off the stack and load it into rbp.

The ret instruction is called which jumps to the calling function.

results matching ""

    No results matching ""