One of my side-projects this summer is to become minimally literate in assembly language. So I began by reading up a tutorial on the x86-64 and the Linux ABI.
And here for your reading pleasure is hello.s
. For
this example we need to know only of the following bits of
the ABI:
- The stack grows downwards (towards lower addresses) with
%rsp
pointing to the bottom of the stack. - The
call
instruction decrements%rsp
by 8, stores the address of the instruction after itself on the stack and then jumps to the address pointed to by its operand. - The
ret
instruction jumps to the address pointed to by%rsp
and increments%rsp
by 8 before resuming execution. - Integer or pointer parameters to functions are
passed in registers
%rdi
,%rsi
,%rdx
,%rcx
,%r8
and%r9
in that order and only if there are more than six such parameters is the stack used. An integer or pointer return value is returned in%rax
.
/*
A simple standalone hello world program.
Usage: hello [arg]
Prints "Hello $arg" if [arg] supplied,
else "Hello world".
*/
.text
/* We will link our program using gcc,
which will take care of initialization and trasfer
control to our 'main'. For this to work 'main'
has to be marked as a symbol visible to the linker.
*/
.global main
.type main, @function
main:
/* The ABI requires %rsp to be aligned to a 16-byte
boundary just before a call.
Since the %rsp is currently at a 8-byte
boundary because of the return address
from main, and because none of the calls we make pass
any arguments on the stack, the following adjustment
is enought for all the calls we make.
*/
subq $8, %rsp
/*Compare argc to 1*/
cmpl $1,%edi
jne .HAS_ARG
movq $.DEF_GREET, %rsi
jmp .PRINT
.HAS_ARG:
/*argv[1]*/
movq 8(%rsi), %rsi
.PRINT:
movq $.FMT_STR, %rdi
/*When calling variable argument
functions like 'printf' we must
place an upper bound on the number of
vector arguments in %al*/
%al,%al
xorb call printf
/*Ready to return. Reverse our adjustment
to %rsp
*/
addq $8,%rsp
movq $0, %rax
ret
.section .rodata
.FMT_STR:
.string "hello %s\n"
.DEF_GREET:
.string "world"
The program above calls printf
and so has to be linked to
the C library. I spent all afternoon feeding strange
incantations to the GNU linker ld
to create a working
executable. While I finally did get something which worked,
I did not feel confident that I was doing exactly right.
Finally I wimped out and called gcc
to do the linking,
after seeing that even the famous GHC
did this. Here’s the
Makefile
hello: hello.o
$@ $<
gcc -o
hello.o: hello.s
Next target, learning more about the instruction set, specially the SIMD instructions.