In this example, we'll see how to pass parameters by reference.
This time, instead of using the RAX register to return the result of the p_pow function, we pass a third parameter by reference that will hold the result.
This is the calling C code:
#include "stdlib.h" #include "stdio.h" #include "assert.h" // Assembly function declaration extern void p_pow(int, int, int *); int main(void) { int result; p_pow(2, 2, &result); assert(4 == result); p_pow(3, 2, &result); assert(9 == result); p_pow(5, 2, &result); assert(25 == result); p_pow(6, 2, &result); assert(36 == result); p_pow(3, 3, &result); assert(27 == result); p_pow(3, 0, &result); assert(1 == result); p_pow(1, 5, &result); assert(1 == result); p_pow(-2, 2, &result); assert(4 == result); p_pow(-2, 3, &result); assert(-8 == result); printf("All tests passed!\n"); return EXIT_SUCCESS; }Look at the prototype of the p_pow function.
extern void p_pow(int, int, int *);We want to pass result by reference. Since in C everything is passed by default, to pass a parameter by reference what we actually do is passing by value the memory address where result is stored. That's why we've used a pointer.
This is the assembly code:
section .data section .text ; Make the function name global so it can be seen from the C code global p_pow p_pow: push rbp mov rbp, rsp ; Stack initial state is stored push rdx ; Store initial value of RDX ; (memory address where the result will be stored) ; because RDX can be modified by MUL operation ; The base (b) is being passed in RDI register ; and the exponent (e) is being passed in RCX register mov eax, 1 ; Register RAX will hold the result temporarily cmp esi, 0 ; if (e == 0) -> b^0 = 1, and we're done jle pow_end mul_loop: mul edi ; eax*ebx = edx:eax (when operating ; with ints, edx is not used). dec esi ; esi = esi - 1 jg mul_loop ; If esi > 0, it continues iterating pow_end: pop rdx ; Restore initial value of RDX ; (memory address where the result will be stored) mov [RDX], dword eax ; Copy final result in memory mov rsp, rbp ; Stack initial state is restored pop rbp retNow we compile, link and execute the program and we get:
$ yasm -f elf64 -g dwarf2 pow.asm $ gcc -g -o pow pow.o pow_c.c $ ./pow All tests passed!Note that in the assembly code, the RDX register is holding the memory address where result is stored. That's how the assembly program is able to change its value. As I explained in a previous post, in the calling convention of the System V AMD64 ABI the registers RDI, RSI, RDX, RCX, R8 and R9 are used for integer and pointer arguments.
To see it better, we'll use gdb.
This is the content of the registers in the first call to p_pow right after executing mov [RDX], dword eax:
(gdb) info register rax 0x4 4 rbx 0x0 0 rcx 0x0 0 rdx 0x7fffffffe0fc 140737488347388 rsi 0x0 0 rdi 0x2 2 rbp 0x7fffffffe0e0 0x7fffffffe0e0 rsp 0x7fffffffe0e0 0x7fffffffe0e0 r8 0x7ffff7dd7300 140737351873280 r9 0x7ffff7deb5f0 140737351955952 r10 0x7fffffffdf50 140737488346960 r11 0x7ffff7a76c90 140737348332688 r12 0x400460 4195424 r13 0x7fffffffe1e0 140737488347616 r14 0x0 0 r15 0x0 0 rip 0x400568 0x400568 <pow_end+3> eflags 0x246 [ PF ZF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0The RDX register contains the memory address of result and the content of that memory address is 4:
(gdb) x/d $rdx 0x7fffffffe0fc: 4
We've seen how by passing its memory address by value, we were able to change the content of a variable from inside an assembly function, as though we were passing the variable by reference.
No comments:
Post a Comment