I am trying to understand how assembly works with arguments and return values.
So far, I have learnt that %eax is is the return value and to load a single argument, I need to load the effective address of %rip + offset into %rid by using leaq var(%rip), %rdi .
To learn more about arguments, I created a c program that takes in 10 (11 arguments including the formatting string) to try and find out the order of registers. I then converted the C code into assembly using gcc on my Mac.
Here is the C code I used:
#include <stdio.h>
int main(){
printf("%s %s %s %s %s %s %s %s %s %s", "1 ", "2", "3", "4", "5", "6", "7", "8", "9", "10");
return 0;
}
And hear is the assembly output:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 13
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
.cfi_offset %rbx, -40
.cfi_offset %r14, -32
.cfi_offset %r15, -24
subq $8, %rsp
leaq L_.str.10(%rip), %r10
leaq L_.str.9(%rip), %r11
leaq L_.str.8(%rip), %r14
leaq L_.str.7(%rip), %r15
leaq L_.str.6(%rip), %rbx
leaq L_.str(%rip), %rdi
leaq L_.str.1(%rip), %rsi
leaq L_.str.2(%rip), %rdx
leaq L_.str.3(%rip), %rcx
leaq L_.str.4(%rip), %r8
leaq L_.str.5(%rip), %r9
movl $0, %eax
pushq %r10
pushq %r11
pushq %r14
pushq %r15
pushq %rbx
callq _printf
addq $48, %rsp
xorl %eax, %eax
addq $8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "%s %s %s %s %s %s %s %s %s %s"
L_.str.1: ## @.str.1
.asciz "1 "
L_.str.2: ## @.str.2
.asciz "2"
L_.str.3: ## @.str.3
.asciz "3"
L_.str.4: ## @.str.4
.asciz "4"
L_.str.5: ## @.str.5
.asciz "5"
L_.str.6: ## @.str.6
.asciz "6"
L_.str.7: ## @.str.7
.asciz "7"
L_.str.8: ## @.str.8
.asciz "8"
L_.str.9: ## @.str.9
.asciz "9"
L_.str.10: ## @.str.10
.asciz "10"
.subsections_via_symbols
After that, I then cleared the code up which removes some macOS only settings? The code still works.
.text
.globl _main ## -- Begin function main
_main: ## @main
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
subq $8, %rsp
leaq L_.str.10(%rip), %r10
leaq L_.str.9(%rip), %r11
leaq L_.str.8(%rip), %r14
leaq L_.str.7(%rip), %r15
leaq L_.str.6(%rip), %rbx
leaq L_.str(%rip), %rdi
leaq L_.str.1(%rip), %rsi
leaq L_.str.2(%rip), %rdx
leaq L_.str.3(%rip), %rcx
leaq L_.str.4(%rip), %r8
leaq L_.str.5(%rip), %r9
movl $0, %eax
pushq %r10
pushq %r11
pushq %r14
pushq %r15
pushq %rbx
callq _printf
addq $48, %rsp
xorl %eax, %eax
addq $8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
.data
L_.str: ## @.str
.asciz "%s %s %s %s %s %s %s %s %s %s"
L_.str.1: ## @.str.1
.asciz "1 "
L_.str.2: ## @.str.2
.asciz "2"
L_.str.3: ## @.str.3
.asciz "3"
L_.str.4: ## @.str.4
.asciz "4"
L_.str.5: ## @.str.5
.asciz "5"
L_.str.6: ## @.str.6
.asciz "6"
L_.str.7: ## @.str.7
.asciz "7"
L_.str.8: ## @.str.8
.asciz "8"
L_.str.9: ## @.str.9
.asciz "9"
L_.str.10: ## @.str.10
.asciz "10"
I understand that at the beginning of the code, that the base pointer is pushed onto the stack which is then copied into the stack pointer for later use.
The leaq is then loading each string into each register that will be used as an argument to printf.
What I want to know is why are registers r10 r11 r14 and r15 before the first argument is loaded into memory and that registers rsi rdx rcx r8 and 'r9' loaded into memory after the first argument? Also why are r14 and r15 used instead of r12 and r13?
Also why is 8 added and subtracted from the stack pointer in this case and does it matter which order the registers are pushed and popped?
I hope all the subquestions are related to this question, if not let me know. Also car me up on any knowledge I may be getting wrong. This is what I have learnt by converting c to assembly.