/* gcc -O0 -o simple_call simple_call.c */ #include #include int func( int input ) { int after = 4; after += input; //printf( "after @ %p, input @%p\n", &after, &input ); return after; } int main(int argc, char *argv[]) { int srt1 = 5; int srt2 = 6; srt2 = func( srt1 ); //printf( "srt2 = %d, srt1 @ %p, srt2 @ %p\n", srt2, &srt1, &srt2 ); return 0; } // Disassembly of section __TEXT,__text: // // 0000000100003f34 <_func>: // 100003f34: ff 43 00 d1 sub sp, sp, #16 (SP = SP - 0x16) (push space for this stack frame) // 100003f38: e0 0f 00 b9 str w0, [sp, #12] (*(SP+0x12) = W0) (put the input value on the stack) // 100003f3c: 88 00 80 52 mov w8, #4 (W8=4) (start process of putting 4 into local variable) // 100003f40: e8 0b 00 b9 str w8, [sp, #8] (*(SP+8) = W8) (finish process of putting 4 into local variable) (this means "after" is stored here) // 100003f44: e9 0f 40 b9 ldr w9, [sp, #12] (W9 = *(SP+0x12)) (take the value of the input var from the stack and put it into a register) // 100003f48: e8 0b 40 b9 ldr w8, [sp, #8] (W8 = *(SP+8)) (take the value of the local variable "after" and put it into a register) // 100003f4c: 08 01 09 0b add w8, w8, w9 (W8 = W8 + W9) (add the values and store them in register W8) // 100003f50: e8 0b 00 b9 str w8, [sp, #8] (*(SP+8) = W8) (Put the sum on the stack where "after" is stored) // 100003f54: e0 0b 40 b9 ldr w0, [sp, #8] (W0 = *(SP+8)) (Put the value of "after" into a register for returning // 100003f58: ff 43 00 91 add sp, sp, #16 (SP = SP+0x16) (Pop the stack frame) // 100003f5c: c0 03 5f d6 ret (PC = X30) (return control to the calling code) // // 0000000100003f60 <_main>: // 100003f60: ff c3 00 d1 sub sp, sp, #48 (SP = SP - 0x48) // 100003f64: fd 7b 02 a9 stp x29, x30, [sp, #32] (*(SP+0x32) = FP, LR (This stores 32 bits (4 bytes) from the two 16-bit registers into memory starting at SP+32) // 100003f68: fd 83 00 91 add x29, sp, #32 (FP = SP + 0x32) // 100003f6c: 08 00 80 52 mov w8, #0 (W8 = 0) // 100003f70: e8 07 00 b9 str w8, [sp, #4] (*(SP+0x4) = W8) (put a zero in ???) // 100003f74: bf c3 1f b8 stur wzr, [x29, #-4] (*(FP-0x4) = 0) // 100003f78: a0 83 1f b8 stur w0, [x29, #-8] (*(FP-0x8) = W0) (but w0 hasn't been given a value??) // 100003f7c: e1 0b 00 f9 str x1, [sp, #16] (*(SP+0x16) = X1) // 100003f80: a8 00 80 52 mov w8, #5 (W8 = 5) (start process of putting 5 in a variable) // 100003f84: e8 0f 00 b9 str w8, [sp, #12] (*(SP+0x12) = W8) (finish process of putting 5 in a variable, i.e. put it on stack) // 100003f88: 88 00 80 52 mov w8, #4 (W8 = 4) (start process of putting 5 in a variable) // 100003f8c: e8 0b 00 b9 str w8, [sp, #8] (*(SP+8) = W8) (finish process of putting 4 in a variable, i.e. put it on stack) // 100003f90: e0 0f 40 b9 ldr w0, [sp, #12] (W0 = *(SP+0x12)) (put the 5 into the register that acts as input to the function) // 100003f94: e8 ff ff 97 bl 0x100003f34 <_func> (X30 = 0x100003f98, branch to func, instruction at 0x100003f34) // 100003f98: e8 03 00 aa mov x8, x0 (X8=X0, which I think means grab the return value?) // 100003f9c: e0 07 40 b9 ldr w0, [sp, #4] (W0 = *(SP+0x4)) (take the 0 from the stack and put it in register W0 // 100003fa0: e8 0b 00 b9 str w8, [sp, #8] (*(SP+8) = W8) (store the return value on the stack) // 100003fa4: fd 7b 42 a9 ldp x29, x30, [sp, #32] (FP=*(SP+0x32), ReturnAddrReg=*(SP+0x32+0x4)) (reset frame pointer and return address) // 100003fa8: ff c3 00 91 add sp, sp, #48 (SP=SP+0x48) (pop this frame off the stack) // 100003fac: c0 03 5f d6 ret (I think it means PC = X30), return control to the "calling" code. // Commands // https://developer.arm.com/documentation/dui0068/b/ARM-Instruction-Reference (but id's registers in not quite right way, so I think I am looking at the wrong set of instructions, but this is good enough) // and https://diveintosystems.org/book/C9-ARM64/common.html // Is stur like str but use a different number of bytes? Otherwise, I don't understand how I am moving things that are // just 4 bytes from each other into the SP. // sub result_register, lh_register, rh (Subtract) // add result_register, lh_register, rh (Add) // str register_to_store_from, [register_on_which_memory_is_based] with flexible offset #-4 means "minus 4" and #4 means "plus 4" (Store Register) // stur register_to_store_from, memory location and offset to store to // mov result_register, flexible_value (could be register, could be value, etc I think) // ldr register_to_load_into, [register_on_which_memory_is_based] with flexible offset -#4 means "minus 4" and #4 means "plus 4" (Load Register) // ldp, loads pair registers, I think to contiguous bytes, e.g. [SP, #32] means SP+32 for first register and the next 4/8 bytes for following register (https://developer.arm.com/documentation/dui0801/g/A64-Data-Transfer-Instructions/LDP) // stp, like str, but put value in list of registers in corresponding mem location in list // bl label (copies next instruction into link register, then branches to label) // ret // Registers // https://developer.arm.com/documentation/dui0801/b/BABBGCAC // Thirty-one 64-bit general-purpose registers X0-X30, the bottom halves of which are accessible as W0-W30. // https://eclecticlight.co/2021/06/16/code-in-arm-assembly-registers-explained/ // w0 - lower 32 bits of register x0 // w8 - lower 32 bits of register x8 (so this is special and indicates return value) // wzr - hardwired to be zero (https://valsamaras.medium.com/arm-64-assembly-series-basic-definitions-and-registers-ec8cc1334e40) // https://eclecticlight.co/2021/06/16/code-in-arm-assembly-registers-explained/ // There are 31 general-purpose registers, each of which can be used for 64- or 32-bit values. When used for 64-bit, they’re named X0 to X30, and for 32-bit they’re W0 to W30. // x29 - frame pointer // x30 - link register (used for storing the return address, when the function calls aren't nested) // x0 through x7 - function arguments/results // x8 - result, if a pointer