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, rbpto return the stack pointer to the current base pointer. It then per - A
pop rbpto pop the previous base pointer off the stack and load it intorbp.
The ret instruction is called which jumps to the calling function.