AMD System V ABI Reference
An ABI reference for quick lookup. If you crash or just want to step through disassembly, check the contents of the registers to see where parameters are being passed.
| Arg | Register | Notes |
|---|---|---|
| 1 | RDI |
Usually self or address to hold return value |
| 2 | RSI |
Usually _cmd but may contain self |
| 3 | RDX |
Contains _cmd if RSI contains self |
| 4 | RCX |
|
| 5 | R8 |
|
| 6 | R9 |
|
| Floating point | XMM0-XMM7 |
Only if floating-point used |
| Return value | RAX |
Value or address to hold value |
| Return value | RDX |
Only if return value is 9-16 bytes |
void abc(int64_t x, int64_t y, int64_t z) {
// RDI=x, RSI=y, RDX=z
}
@implementation ClassABC
- (void)abcWithX:(int64_t)x y:(int64_t)y z:(int64_t)z
{
// RDI=self, RSI=_cmd, RDX=x, RCX=y, R8=z
}
@end
If there are more arguments or arguments that cannot be passed on registers they are pushed on the stack from right to left.
// RDI=t, RSI=u, RDX=v, RCX=w, R8=x, R9=y (z pushed to stack)
void abc(int64_t t, int64_t u, int64_t v,
int64_t w, int64_t x, int64_t y,
int64_t z) { }
If the return value is too large to fit in RAX then RDX is also used.
If it is too large to fit in two registers then the caller allocates space and stores the address in RDI. This same address is returned in RAX. This effectively shifts all the arguments over and is why the objc_msgsend_stret functions exist.
For variadic functions RAX is used as a secret argument to pass the number of XMM vector registers used.
Note: This is an abbreviated explanation. Read the ABI document for the full details.
Swift
Swift does not commit to using the platform default calling convention except when calling foreign functions. You can read about the Swift Calling Conventions.
Assembly Notes
| Symbol | Notes |
|---|---|
| [instruction] [source], [dest] | Unix syntax is source, dest. Intel syntax is a spiteful and hateful thing. We shall never speak of it again. |
| $ | Constant Value |
| % | Register Name |
| () | Load from memory (pointer dereference) |
| b suffix | 8-bit byte form of instruction |
| w suffix | 16-bit word form of instruction |
| l suffix | 32-bit long form of instruction |
| q suffix | 64-bit quadword form of instruction |
The full form of an address can include displacement(base register, offset register, multiplier).
Example
You're crashing with EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). You can see disassembly something like this:
pushq %rbp
movq %rsp, %rbp
pushq %rbx
subq $0xa8, %rsp
movq %rdi, %r9
leaq 0xdbb4(%rip), %r8 ; "XPC API Misuse: %s"
leaq -0xb0(%rip), %rbx
movl $0xa0, %esi
movl $0x0, %edx
movl $0xa0, %ecx
xorl %eax, %eax
movq %rbx, %rdi
callq 0x10aabbccd ; symbol stub for __snprintf_chk
movq %rbx, 0x1b0cd(%rip)
leaq 0xdb99(%rip), %rax ; "API Misuse"
movq %rax, 0x1b0c7(%rip)
ud2 ; <-- Stopped here
The ud2 instruction is exactly what the mnemonic seems like: undefined. It's a deliberate crash. The first few lines are standard function prolog stuff. Then we see the address of a string is loaded along with some other parameter setup. Then we see it is calling __snprintf_chk which we can guess is going to print a string into a string buffer, most likely the crash message. Given what we know about the ABI, RAX would have held the printed string but alas the movq instruction has blasted it.
However if we unwind the execution mentally and use what we know about the ABI we can still find it. The first parameter is probably the buffer to write to. Right before the call RBX was copied to RDI which lines up.
leais load effective address with theqquad-word suffix. It usesRIPas a base, indexes0xB0bytes backwards. Then instead of reading from that address likeMOVwould it stores the address itself.
Modern processors have address calculation units soleainstructions can often happen in parallel to normal integer instructions. Take any instruction that would dereference a pointer then substituteleato get the pointer instead.
We see that RBX was found by subtracting 0xB0 from RIP then using that as a pointer to load from. Nothing has overwritten RBX since then so let's see what it holds:
(lldb) x -c 70 $rbx
0x7ffeea3d7f70: 58 50 43 20 41 50 49 20 4d 69 73 75 73 65 3a 20 XPC API Misuse:
0x7ffeea3d7f80: 41 63 74 69 76 61 74 69 6f 6e 20 6f 66 20 61 20 Activation of a
0x7ffeea3d7f90: 63 6f 6e 6e 65 63 74 69 6f 6e 20 77 69 74 68 6f connection witho
0x7ffeea3d7fa0: 75 74 20 61 6e 20 65 76 65 6e 74 20 68 61 6e 64 ut an event hand
0x7ffeea3d7fb0: 6c 65 72 2e 00 00 ler...
There it is!
This blog represents my own personal opinion and is not endorsed by my employer.