General:
- http://ian.seyler.me/easy_x86-64/
- http://www.sandpile.org/x86/gpr.htm
- http://www.jacobmartin.org/code/asm.html
System calls:
- https://jameshfisher.com/2018/03/10/linux-assembly-hello-world.html
- https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux
- https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/
- https://syscalls.kernelgrok.com/
- http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
- https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl
Intel syntax:
opcode destination, source
The AT&T (or GAS, for Gnu Assembler) syntax has source(s) first, then destination last.
There are sixteen, general purpose 64-bit registers:
R0 / RAX - [A]ccumulator register
R1 / RCX - [C]ounter
R2 / RDX - [D]ata register
R3 / RBX - [B]ase register
R4 / RSP - [S]tack pointer
R5 / RBP - [B]ase (of stack) pointer
R6 / RSI - [S]ource [I]ndex (for string operations)
R7 / RDI - [D]estination [I]ndex (for string operations)
R8
R9
R10
R11
R12
R13
R14
R15
Other registers:
RIP - [I]nstruction [P]ointer
Sizes:
- Half-word/Byte: 8-bits
- Word: 16-bits (2 bytes)
- Double word: 32-bits (2 words = 4 bytes)
- Quad word: 64-bits (2 double words = 4 words = 8 bytes)
Pieces of registers:
R0D / RAXD
- 32-bits (double word) ofR0
.R0W / RAXW
- 16-bits (a word) ofR0
.R0B / RAXB
- 8-bits (a byte) ofR0
.
Some registers are broken down even further:
|<--- High byte Low byte ---> |
+-------------------------------------------------------------------+
| RAX |
+--------------------------------+----------------------------------+
| | EAX |
+--------------------------------+----------------+--------+--------+
| | | AX |
+--------------------------------+----------------+--------+--------+
| | | AH | AL |
+--------------------------------+----------------+--------+--------+
| 32 bits | 16 bits | 8 bits | 8 bits |
+--------------------------------+----------------+--------+--------+
Examples:
AL
- the lowest byte ofRAX
.AH
- the second to lowest byte ofRAX
.AX
- the lowest word ofRAX
.EAX
- the lowest double word ofRAX
.
Others: RBX
, RCX
, RDX
, ESP
, RBP
, RSI
, and RDI
can be broken down by L
, H
, and E
:
BL
- the lowest byte ofRBX
.CX
- the lowest word ofRCX
.EDX
- the lowest double word ofRDX
.SH
- the second to lowest byte ofRSP
.SP
- the lowest word ofRSP
.ESP
- the lowest double word ofRSP
.BP
- the lowest byte ofRBP
.EBP
- the lowest double word ofRBP
.SIL
- the lowest byte ofRSI
.DIL
- the lowest byte ofRDI
.ESI
- the lowest double word ofRSI
.EDI
- the lowest double word ofRDI
.
The instruction pointer (RIP
) can be broken down
into IP
(word), EIP
(double word).
The mov
instruction copies a value into a register.
mov rax, 15 ; Store 15 in RAX.
mov rcx, rax ; Store the value in RAX in RCX.
Addition:
mov rax, 11 ; Store 11 in RAX.
mov rcx, 500 ; Store 500 in RCX.
add rax, rcx ; Add the value in RCX to RAX. RAX now holds 511.
add rax, 10 ; Add 10 to RAX. RAX now holds 521.
Subtraction:
mov r15, 1337 ; Store 1355 in R15.
mov r12, 55 ; Store 55 in R12.
sub r15, r12 ; Subtract the value in R12 from R15. R15 now holds 1300.
sub r15, 301 ; Subtract 301 from R15. R15 now holds 999.
The (x86_64 ABI)[https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI] says:
- System call number is put in
RAX
. - Arguments are put in
RDI
,RSI
,RDX
,RCX
,R8
, andR9
(in that order). - The system call is invoked with the
syscall
instruction. - The return value is put in
RAX
.- Error? It returns
-errno
.
- Error? It returns
Find the system call number in the (system call table)[https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl]. For example, the number
for the write
call is 1
.
Find the man page for the system call to find the arguments.
For example, write
:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
With this, we can call write
:
- Put
1
intoRAX
to indicate that we want to callwrite
. - Put
1
intoRDI
to indicate thatfd
is stdout (i.e.,1
). - Put a string (e.g., from
.rodata
) intoRSI
(thebuf
argument). - Put the length of
buf
intoRDX
. - Invoke
syscall
.
For example, hello.s
:
global _start
section .text
_start:
mov rax, 1 ; Store 1 in RAX. 1 is the "write" sys call.
mov rdi, 1 ; Store 1 in RDI. 1 is the STDOUT fd.
mov rsi, msg ; Store `msg` in RSI. This is the value to write.
mov rdx, msglen ; Store msg length in RDX. This is how much to write.
syscall ; Invoke the call.
mov rax, 60 ; Store 60 in RAX. 60 is the "exit" sys call.
mov rdi, 0 ; Store 0 in RDI. 0 is the exit code.
syscall ; Invoke the call.
section .rodata
msg: db "Hello, world!", 10
msglen: equ $ - msg
A Makefile
:
prog = hello
all: clean build
clean:
rm -rf $(prog).o
rm -rf $(prog)
build:
nasm -f elf64 -o $(prog).o $(prog).s
ld -o $(prog) $(prog).o
Build and run:
make
./hello