Stack Assembly
When viewing the assembly code of a function, it is good to be able to recognize the function prologue & epilogue. Below is an assembly example of a simple "Hello World" application written in Objective-C and compiled for ARM64:
Prologue
0x100003f28 ffc300d1 sub sp, sp, 0x30
0x100003f2c fd7b02a9 stp x29, x30, [var_20h]
0x100003f30 fd830091 add x29, var_20h
Instruction Breakdown:
-
sub sp, sp, 0x30
- This instruction subtracts 0x30 (48 in decimal) from the stack pointer (sp). It effectively allocates 48 bytes of space on the stack for local variables or temporary storage within a function. The stack pointer is decremented because the ARM64 stack grows downwards (from high memory addresses to low).
-
stp x29, x30, [var_20h]
- The stp instruction stands for "store pair" and is used here to store two 64-bit registers (x29 and x30) onto the stack. x29 is the frame pointer (FP), and x30 is the link register (LR). The notation [var_20h] is a pseudo-instruction used by some disassemblers (like radare2 or Ghidra) to indicate an offset from the current stack pointer (sp). This means that x29 and x30 are stored 32 bytes below the top of the stack frame allocated by the first instruction. This operation saves the current frame pointer and return address on the stack, which is essential for maintaining the call stack's integrity.
-
add x29, var_20h
- This instruction sets up the new frame pointer (x29) for the current function call. It adds the offset (in this case, the same var_20h or -0x20 from the stack pointer) to the stack pointer, effectively pointing x29 to the start of the current frame on the stack. This setup is crucial for navigating the call stack and accessing local variables and parameters relative to the frame pointer.
Epilogue
0x100003f68 fd7b42a9 ldp x29, x30, \[var_20h\]
0x100003f6c ffc30091 add sp, sp, #0x30
0x100003f70 c0035fd6 ret
ldp`` x29, x30, [var_20h]
- The ldp instruction stands for "load pair" and is used here to
load the values of x29 (the frame pointer) and x30 (the link
register) from the stack. The notation [var_20h] indicates an
offset from the stack pointer where these values were previously
stored (in the function prologue). This instruction effectively
restores the frame pointer and the return address (contained in
x30) that were saved at the beginning of the function. Restoring
x29 ensures that the caller's frame pointer is correctly set up,
while
x30
is used for the return jump.
- The ldp instruction stands for "load pair" and is used here to
load the values of x29 (the frame pointer) and x30 (the link
register) from the stack. The notation [var_20h] indicates an
offset from the stack pointer where these values were previously
stored (in the function prologue). This instruction effectively
restores the frame pointer and the return address (contained in
x30) that were saved at the beginning of the function. Restoring
x29 ensures that the caller's frame pointer is correctly set up,
while
add sp, sp, #0x30
- After restoring the frame and link registers, the stack pointer (sp) is back to its original position before the function call. The stack pointer is incremented reclaiming the memory space.
ret
- The
ret
instruction causes a return to the address specified in the link register (x30). Sincex30
was restored to its original value (the return address) by the first instruction in this sequence, execution will continue from the point immediately after where this function was called. The ret instruction is crucial for ensuring that control flow returns correctly to the caller function.
- The
For reference, here is the source code, and full assembly code of the binary:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
Assembly code:
76: int main (int argc, char **argv, int64_t envp);
0x100003f28 ffc300d1 sub sp, sp, 0x30
0x100003f2c fd7b02a9 stp x29, x30, [var_20h]
0x100003f30 fd830091 add x29, var_20h
0x100003f34 08008052 movz w8, 0
0x100003f38 e80f00b9 str w8, [var_ch]
0x100003f3c bfc31fb8 stur wzr, [var_0h_2] ; argc
0x100003f40 a0831fb8 stur w0, [var_0h_3] ; argc
0x100003f44 e10b00f9 str x1, [var_0h_4] ; argv
0x100003f48 11000094 bl sym.imp.objc_autoreleasePoolPush
0x100003f4c e00300f9 str x0, [sp]
0x100003f50 000000b0 adrp x0, reloc.NSLog ; 0x100004000
0x100003f54 00600091 add x0, x0, 0x18 ; 0x100004018
0x100003f58 07000094 bl sym.imp.NSLog
0x100003f5c e00340f9 ldr x0, [sp] ; 0x4 ; 4
0x100003f60 08000094 bl sym.imp.objc_autoreleasePoolPop
0x100003f64 e00f40b9 ldr w0, [var_ch] ; 0x4 ; 4
0x100003f68 fd7b42a9 ldp x29, x30, [var_20h]
0x100003f6c ffc30091 add sp, sp, 0x30
0x100003f70 c0035fd6 ret