Lecture Notes: 03 More ASM
·3 mins
Another example:
// add1.c
long
add1(long x)
{
return x + 1;
}
int
main(int _ac, char* _av[])
// initial _ marks args as not used
{
long x = add1(5);
printf("%ld\n", x);
return 0;
}
# C => asm
$ gcc -S -o add1.s add1.c
# take a look at hello.s
- Two functions: add1, main
- each starts at label, ends at “ret”
- In main, the value 5 is moved to “%rdi”
- That must be where the function’s first argument goes.
- No, that’s “%edi”
- I said “%rdi”, wait a second…
- Then add1 is called
- In add1, the value from %rdi goes to some places.
- Eventually, “addq $1, …” happens to it.
- Back in main, %rax is moved to %rsi, and printf is called.
This almost makes sense, but it’s a bit of a mess. Let’s figure it out.
AMD64 Assembly Review #
Registers: rax, rcx, rdx, rbx, rdi, rsi, rbp, rsp, r9, …, r15
Size variants: rax, eax, ax, ah/al
Calling convention:
- arguments go in, in order: rdi, rsi, rdx, rcx, r8, r9, (stack)
- return value comes out in rax
- (second return in rdx)
- To call a varargs, function, you must first zero %al
Rewrite the assembly manually #
.global main
# add2.s
.text
# long add2(long x)
# - the argument comes in in %rdi
# - we return the result by putting it in %rax
add2:
enter $0, $0
# long y = x;
mov %rdi, %rax
# y = y + 2;
add $2, %rax
# return y;
leave
ret
main:
enter $0, $0
# long x = 5;
mov $5, %rdi
# y = add1(x)
call add2
# result in %rax
# printf("%ld\n", y)
# - first arg goes in %rdi
# - second arg goes in %rsi
# - for a variable arg function, we need to zero %al
# - %al is the bottom 8 bits of %ax/%eax/%rax
mov $long_fmt, %rdi
mov %rax, %rsi
mov $0, %al
call printf
leave
ret
.data
long_fmt: .string "%ld\n"
To compile this simple hand-written assembly, we use:
$ gcc -no-pie -o add2 add2.s
Another Assembly Example #
- Scan through the AMD64 instruction list on course site.
- Example: cond_br
$ gcc -no-pie -o prog prog.s
.global main
.text
main:
enter $0, $0
# print prompt
mov $prompt, %rdi
call puts
mov $long_fmt, %rdi
mov $num, %rsi
mov $0, %al
call scanf
# copy value at address
# with dollar sign, copy literal address
mov num, %rax
# if (%rax <= 10)
cmp $10, %rax
jle smaller_than_ten
bigger_than_ten:
mov $bigger, %rdi
jmp main_done
smaller_than_ten:
mov $smaller, %rdi
main_done:
call puts
leave
ret
.data
num: .string "12345678" # 8 bytes, to fit a long
prompt: .string "enter a number"
long_fmt: .string "%ld"
eol: .string "\n"
bigger: .string "bigger than ten"
smaller: .string "smaller than ten"