Asembler x86 / x86-64 — Ściągawka (NASM, Linux)

Podstawy i średniozaawansowane zagadnienia programowania w asemblerze: rejestry, tryby adresowania, instrukcje, pętle, procedury, wywołania systemowe oraz kompilacja. Składnia NASM (Intel), platforma Linux x86-64, chyba że zaznaczono inaczej.

1. Rejestry procesora

Rejestry ogólnego przeznaczenia

64-bit32-bit16-bit8-bitZastosowanie
RAXEAXAXALAkumulator, wartość zwracana
RBXEBXBXBLRejestr bazowy
RCXECXCXCLLicznik (pętle, przesunięcia)
RDXEDXDXDLDane, dzielenie/mnożenie
RSIESISISILWskaźnik źródłowy
RDIEDIDIDILWskaźnik docelowy
RBPEBPBPBPLPodstawa ramki stosu
RSPESPSPSPLWskaźnik szczytu stosu
R8–R15R8D–R15DR8W–R15WR8B–R15BDodatkowe rejestry (tylko x86-64)

Rejestr znaczników EFLAGS (najważniejsze bity)

  • ZF (Zero Flag) — wynik operacji równy zero
  • CF (Carry Flag) — przeniesienie/pożyczka poza rejestr
  • SF (Sign Flag) — wynik ujemny (najstarszy bit = 1)
  • OF (Overflow Flag) — przepełnienie ze znakiem
  • PF (Parity Flag) — parzysta liczba jedynek w młodszym bajcie

Rejestry to jedyne miejsce, gdzie CPU wykonuje obliczenia — pamięć RAM jest zawsze „wolniejszym” pośrednikiem.

2. Struktura programu i dyrektywy

Sekcje programu

  • section .data — dane zainicjalizowane (stałe, napisy)
  • section .bss — dane niezainicjalizowane (rezerwacja miejsca)
  • section .text — kod programu (instrukcje)
  • global _start — oznacza punkt wejścia programu dla linkera

Deklaracja danych (.data)

liczba  db 10        ; 1 bajt
slowo   dw 1000      ; 2 bajty (word)
podw    dd 100000    ; 4 bajty (dword)
poczw   dq 123456789 ; 8 bajtów (qword)
napis   db "Ala", 0  ; ciąg znaków zakończony 0
dlugosc equ $ - napis ; długość liczona przez asembler

Rezerwacja miejsca (.bss)

bufor   resb 64   ; 64 bajty
tablica resd 10   ; 10 elementów po 4 bajty

3. Tryby adresowania pamięci

  • Natychmiastowe: mov eax, 5 — stała liczbowa wprost w rozkazie
  • Rejestrowe: mov eax, ebx — wartość z jednego rejestru do drugiego
  • Bezpośrednie: mov eax, [zmienna] — odczyt spod nazwanego adresu
  • Pośrednie: mov eax, [ebx] — adres znajduje się w rejestrze
  • Z przemieszczeniem: mov eax, [ebx+4] — adres bazowy + stała
  • Bazowo-indeksowe: mov eax, [ebx+esi*4+8] — typowe przy iteracji po tablicach (esi = indeks, *4 = rozmiar elementu)

Nawiasy kwadratowe [ ] zawsze oznaczają „weź wartość spod tego adresu”, a nie sam adres.

4. Przesyłanie danych

InstrukcjaDziałanie
mov dst, srcKopiuje wartość src do dst
lea dst, [src]Ładuje obliczony adres (nie wartość!) do dst
push srcOdkłada wartość na stos, RSP -= 8
pop dstZdejmuje wartość ze stosu do dst, RSP += 8
xchg a, bZamienia miejscami wartości a i b

lea eax, [ebx+4] wpisze do EAX wartość „EBX+4”, natomiast mov eax, [ebx+4] wpisze zawartość pamięci pod tym adresem — częsta pomyłka początkujących.

5. Arytmetyka

InstrukcjaDziałanie
add dst, srcdst = dst + src
sub dst, srcdst = dst - src
inc dstdst = dst + 1
dec dstdst = dst - 1
neg dstdst = -dst
mul srcRDX:RAX = RAX * src (bez znaku)
imul srcRDX:RAX = RAX * src (ze znakiem)
div srcRAX = RDX:RAX / src, RDX = reszta (bez znaku)
idiv srcjak wyżej, ze znakiem

Przed div/idiv pamiętaj wyzerować lub rozszerzyć RDX (np. xor edx, edx), inaczej wynik dzielenia będzie błędny.

6. Operacje logiczne i bitowe

InstrukcjaDziałanie
and dst, srcIloczyn bitowy (maskowanie bitów)
or dst, srcSuma bitowa (ustawianie bitów)
xor dst, srcRóżnica symetryczna (też: xor eax,eax = zerowanie)
not dstNegacja bitowa (dopełnienie)
shl dst, nPrzesunięcie w lewo = mnożenie przez 2^n
shr dst, nPrzesunięcie w prawo logiczne (bez znaku)
sar dst, nPrzesunięcie w prawo arytmetyczne (ze znakiem)
rol/ror dst, nRotacja bitów w lewo/prawo

7. Porównania i skoki warunkowe

cmp a, b oblicza a-b tylko na flagach (nie zapisuje wyniku). test a, b robi to samo dla AND. Po nich stosuje się skok warunkowy:

SkokWarunek
jmpBezwarunkowy
je / jzRówne / zero (ZF=1)
jne / jnzRóżne / niezero
jg / jnleWiększe (ze znakiem)
jl / jngeMniejsze (ze znakiem)
jge / jleWiększe-równe / mniejsze-równe (ze znakiem)
ja / jbWiększe / mniejsze (bez znaku)
jae / jbeWiększe-równe / mniejsze-równe (bez znaku)

8. Pętle

loop etykieta automatycznie dekrementuje RCX i skacze, dopóki RCX ≠ 0.

    mov rcx, 5      ; licznik pętli
petla:
    ; ... treść pętli ...
    loop petla      ; RCX--, skok jeśli RCX != 0

Pętla typu while budowana ręcznie:

petla_while:
    cmp rax, 10
    jge koniec
    ; ... ciało pętli ...
    jmp petla_while
koniec:

9. Procedury i stos wywołań

InstrukcjaDziałanie
call etykietaOdkłada adres powrotu, skacze do procedury
retZdejmuje adres powrotu ze stosu i wraca

Typowy prolog/epilog procedury

moja_funkcja:
    push rbp          ; zachowaj starą ramkę
    mov rbp, rsp       ; ustaw nową ramkę stosu
    ; ... ciało funkcji, lokalne zmienne pod [rbp-N] ...
    mov rsp, rbp       ; zwolnij ramkę
    pop rbp
    ret

Konwencja wywołań System V AMD64 (Linux, 64-bit): pierwsze argumenty trafiają kolejno do RDI, RSI, RDX, RCX, R8, R9, a wynik zwracany jest w RAX.

10. Wywołania systemowe (Linux x86-64)

Numer funkcji w RAX, argumenty w RDI, RSI, RDX..., wywołanie instrukcją syscall.

RAXFunkcjaArgumenty
0readrdi=fd, rsi=bufor, rdx=liczba bajtów
1writerdi=fd, rsi=bufor, rdx=liczba bajtów
2openrdi=ścieżka, rsi=flagi, rdx=tryb
3closerdi=fd
60exitrdi=kod wyjścia

fd 0 = stdin, fd 1 = stdout, fd 2 = stderr.

11. Kompilacja, linkowanie, debugowanie

# Kompilacja pliku źródłowego do obiektu ELF64
nasm -f elf64 program.asm -o program.o

# Linkowanie do pliku wykonywalnego
ld program.o -o program

# Uruchomienie
./program

GDB — podstawowe komendy

  • break etykieta — punkt przerwania
  • run — uruchom program
  • stepi / nexti — krok po kroku (instrukcja)
  • info registers — podgląd rejestrów
  • x/10xb $rsp — podgląd pamięci na stosie

ndisasm plik.bin — deasembler surowego pliku binarnego, przydatny do analizy skompilowanego kodu.

Przykładowy program — "Hello, World!" (NASM, Linux x86-64)

section .data
    msg db "Hello, World!", 0xA
    len equ $ - msg

section .text
    global _start

_start:
    mov rax, 1        ; numer syscall: sys_write
    mov rdi, 1        ; deskryptor: stdout
    mov rsi, msg      ; adres napisu
    mov rdx, len      ; długość napisu
    syscall

    mov rax, 60       ; numer syscall: sys_exit
    xor rdi, rdi      ; kod wyjścia 0
    syscall

SŁOWNICZEK POJĘĆ