This guide is part four of the series, X86–64 Assembly Language Program.
Sending Function Arguments and Returning a Result
In the previous post of this series, we saw how to define and call functions in x86–64 Assembly.
Now I wanted to know how to provide arguments to a function and return values. In other words, when this is done in a higher-level language, how is it translated at the Assembly code level?
The answer is pretty straightforward. In x86–64 Assembly, there is a convention defining which registers should be used for the first argument, second argument, and so forth.
+-----------------+----------+
| Argument number | Register |
+-----------------+----------+
| Arg 1 | $ rdi |
| Arg 2 | $ rsi |
| Arg 3 | $ rdx |
| Arg 4 | $ rcx |
| Arg 5 | $ r8 |
| Arg 6 | $ r9 |
| Arg 7 | stack |
+-----------------+----------+
All further arguments are pushed onto the stack.
Return values are expected to be stored in the register $rax.
Here is an example of code that utilizes the conventional registers to pass arguments and results in order to print out the command-line arguments
section .data
section .text
global _start
_start:
call .getNumberOfArgs ; expects return value in $rax
mov rdi, rax
call .printNumberOfArgs ; expects value to be in 1st argument, i.e. $rdi
call .exit
.getNumberOfArgs:
pop rbx ; this is the address of the calling fxn. Remove it from the stack for a moment
pop rax ; get argc from stack
push rbx ; return address of calling fxn to stack
ret
; expects value to be in 1st argument, i.e. $rdi
.printNumberOfArgs:
pop rbx ; this is the address of the calling fxn. Remove it from the stack for a moment
add rdi, 48 ; convert number of args to ascii (only works if < 10)
push rdi ; push the ascii converted argc to stack
mov rsi, rsp ; store value of rsp, i.e. pointer to ascii argc to param2 of sys_write fxn
mov rdx, 8 ; param3 of sys_write fxn is the number of bits to print
push rbx ; return the address of the calling fxn to top of stack.
call .print
; clean up the number that was pushed onto the stack. Retaining the return address currently on top of stack
pop rbx
pop rcx
push rbx
ret
.print: ; print expects the calling location to be at the top of the stack
mov rax, 1
mov rdi, 1
syscall
ret ; return to location pointed to at top of stack
.exit:
mov rax, 60
mov rdi, 0
syscall
There are some additional well-defined calling conventions that are helpful to follow. See here.
I hope you enjoyed this article. To learn more, find the next part of this series here: Conditionals, Jumping, and Looping.