SINTASSI AT&T (GNU AS)

La sintassi AT&T (usata da GNU Assembler / GAS) differisce dalla Intel per ordine degli operandi, prefissi e suffissi.

REGOLE FONDAMENTALI
ElementoAT&TIntel (confronto)
Ordine operandisrc, dstdst, src
RegistriPrefisso %%raxNessun prefisso → rax
ImmediatiPrefisso $$42Nessun prefisso → 42
Suffisso dimensioneb(8) w(16) l(32) q(64)Keyword: BYTE PTR, ecc.
Indirizzamentodisp(base,index,scale)[base+index*scale+disp]
Commenti# commento oppure /* ... */; commento
SUFFISSI DI DIMENSIONE
movb  $0x41, %al        # 8 bit  (byte)
movw  $0x1234, %ax      # 16 bit (word)
movl  $0xDEAD, %eax     # 32 bit (long/doubleword)
movq  $0xCAFE, %rax     # 64 bit (quadword)
MODALITA' DI INDIRIZZAMENTO
movq  $42, %rax                 # Immediato
movq  %rbx, %rax                # Registro-registro
movq  (%rbx), %rax               # Indiretto: mem[RBX]
movq  8(%rbx), %rax              # Base + displacement: mem[RBX+8]
movq  (%rbx,%rcx,4), %rax       # Base + index*scale: mem[RBX+RCX*4]
movq  16(%rbx,%rcx,8), %rax    # Completo: mem[RBX+RCX*8+16]
movq  var(%rip), %rax           # RIP-relative (PIC, x86_64)
DIRETTIVE GAS ESSENZIALI
.section .text                  # Sezione codice
.section .data                  # Dati inizializzati
.section .bss                   # Dati non inizializzati
.section .rodata                # Dati read-only
.global  _start                 # Rendi simbolo visibile al linker
.extern  printf                 # Importa simbolo esterno

# Dichiarazione dati
.byte    0xFF                   # 1 byte
.word    0x1234                 # 2 byte (16 bit)
.long    0xDEADBEEF             # 4 byte (32 bit)
.quad    0xCAFEBABE             # 8 byte (64 bit)
.ascii   "hello"                # Stringa (no terminatore)
.asciz   "hello"                # Stringa con \0
.space   64                     # Riserva 64 byte (zero-filled)
.skip    64, 0xFF               # Riserva 64 byte riempiti con 0xFF
.string  "hello"                # Alias per .asciz (stringa con \0)
.align   16                     # Allinea a 16 byte
.p2align 4                      # Allinea a 2^4 = 16 byte
.equ     BUF_SIZE, 1024        # Costante simbolica
.set     LIMIT, 256            # Alias di .equ (riassegnabile con .set)
.comm    buffer, 4096, 16     # Alloca 4096 byte in BSS, allineati a 16
.lcomm   local_buf, 256       # Come .comm ma simbolo locale (non esportato)
.type    my_func, @function    # Tipo simbolo ELF (function/object)
.size    my_func, . - my_func  # Dimensione simbolo ELF
.include "macros.inc"           # Include file sorgente

# Macro
.macro PUSH_CALLEE_SAVED
    pushq  %rbx
    pushq  %r12
    pushq  %r13
.endm

# Macro con parametri
.macro SYSCALL_1 nr, arg1
    movq   $\nr, %rax
    movq   \arg1, %rdi
    syscall
.endm

# Ripetizione
.rept 8
    nop                        # Ripete 8 volte
.endr

# Assembly condizionale
.ifdef DEBUG
    # codice di debug
.endif
.if BUF_SIZE > 512
    # codice condizionale
.else
    # alternativa
.endif
CONFRONTO AT&T vs INTEL — SIDE BY SIDE

Le due sintassi rappresentano le stesse istruzioni macchina. GCC e GAS usano AT&T per default; NASM e la documentazione Intel usano la sintassi Intel. GAS accetta anche Intel con .intel_syntax noprefix.

OPERAZIONI BASE

AT&T (GAS)

movq   $42, %rax
movq   %rbx, %rax
movq   (%rbx), %rax
movq   8(%rbx), %rax
movq   (%rbx,%rcx,4), %rax
leaq   msg(%rip), %rdi
addq   $1, %rax
cmpq   $0, %rax
pushq  %rbp
call   func

Intel (NASM)

mov    rax, 42
mov    rax, rbx
mov    rax, [rbx]
mov    rax, [rbx+8]
mov    rax, [rbx+rcx*4]
lea    rdi, [rel msg]
add    rax, 1
cmp    rax, 0
push   rbp
call   func
ACCESSO ALLA MEMORIA

AT&T (GAS)

# Byte da memoria
movb   (%rsi), %al
# Long da indirizzo assoluto
movl   var, %eax
# Store word in memoria
movw   %ax, 4(%rdi)
# Indirizzamento completo
movl   -8(%rbp,%rsi,4), %eax

Intel (NASM)

; Byte da memoria
mov    al, BYTE [rsi]
; Long da indirizzo assoluto
mov    eax, DWORD [var]
; Store word in memoria
mov    WORD [rdi+4], ax
; Indirizzamento completo
mov    eax, DWORD [rbp+rsi*4-8]
HELLO WORLD COMPLETO

AT&T (GAS) — as + ld

.section .rodata
msg: .asciz "Hello!\n"
.equ LEN, 7

.section .text
.global _start
_start:
  movq  $1, %rax
  movq  $1, %rdi
  leaq  msg(%rip), %rsi
  movq  $LEN, %rdx
  syscall
  movq  $60, %rax
  xorq  %rdi, %rdi
  syscall

Intel (NASM) — nasm + ld

section .rodata
msg: db "Hello!", 0x0A
LEN equ 7

section .text
global _start
_start:
  mov   rax, 1
  mov   rdi, 1
  lea   rsi, [rel msg]
  mov   rdx, LEN
  syscall
  mov   rax, 60
  xor   rdi, rdi
  syscall
RIEPILOGO DIFFERENZE CHIAVE
AspettoAT&T (GAS)Intel (NASM)
Ordine operandisrc, dstdst, src
Prefisso registri%raxrax
Prefisso immediati$4242
Suffisso dimensioneSulla istruzione: movlSull'operando: DWORD PTR
Indirizzamentodisp(%base,%idx,scale)[base+idx*scale+disp]
Commenti# ... o /* ... */; ...
Stringhe.asciz "str"db "str", 0
Costanti.equ NAME, valNAME equ val
Sezioni.section .textsection .text
AssemblerGNU AS (gas)NASM, YASM, FASM
Usato daGCC, Clang, GDB, objdump -dDocumentazione Intel/AMD, NASM
Per usare la sintassi Intel in GAS: .intel_syntax noprefix all'inizio del file. Per tornare ad AT&T: .att_syntax prefix. In GCC inline asm: asm(".intel_syntax noprefix\n\t" ... ".att_syntax prefix"); Con objdump: objdump -d -M intel per disassemblare in sintassi Intel.
MAPPING DEI REGISTRI

In x86_64 ogni registro generale ha 4 sotto-registri accessibili. I registri R8–R15 sono nuovi in x86_64.

REGISTRI GENERAL PURPOSE
64-bit32-bit16-bit8-bit alto8-bit bassoUso tipico
%rax%eax%ax%ah%alAccumulatore, valore di ritorno
%rbx%ebx%bx%bh%blBase (callee-saved)
%rcx%ecx%cx%ch%clContatore, 4° arg
%rdx%edx%dx%dh%dlDati, 3° arg
%rsi%esi%si%silSource index, 2° arg
%rdi%edi%di%dilDest index, 1° arg
%rbp%ebp%bp%bplFrame pointer (callee-saved)
%rsp%esp%sp%splStack pointer
%r8%r8d%r8w%r8b5° arg
%r9%r9d%r9w%r9b6° arg
%r10%r10d%r10w%r10bTemporaneo (caller-saved)
%r11%r11d%r11w%r11bTemporaneo (caller-saved)
%r12%r12d%r12w%r12bCallee-saved
%r13%r13d%r13w%r13bCallee-saved
%r14%r14d%r14w%r14bCallee-saved
%r15%r15d%r15w%r15bCallee-saved
REGISTRI SPECIALI
RegistroDescrizione
%ripInstruction Pointer (prossima istruzione da eseguire)
%rflagsRegistro dei flag: CF, ZF, SF, OF, PF, AF, DF, IF, TF
%cs, %ds, %ss, %es, %fs, %gsRegistri di segmento (FS/GS usati per TLS in Linux)
FLAG PRINCIPALI (RFLAGS)
FlagBitSignificato
CF (Carry)0Riporto/prestito in operazioni unsigned
ZF (Zero)6Risultato = 0
SF (Sign)7Bit di segno del risultato
OF (Overflow)11Overflow in operazioni signed
PF (Parity)2Parità del byte basso
DF (Direction)10Direzione per istruzioni stringa (0=avanti, 1=indietro)
Scrivere un registro a 32 bit (es. movl $1, %eax) azzera automaticamente i 32 bit superiori del corrispondente registro a 64 bit. Scrivere a 8 o 16 bit NON azzera i bit superiori.
MEMORY LAYOUT (PROCESSO LINUX)

Layout tipico dello spazio di indirizzamento di un processo Linux in x86_64 (user space, 48-bit virtual).

0x7FFF...FFFF
STACKCresce verso il basso (↓). Variabili locali, indirizzi di ritorno, frame pointer.
↓ ↓ ↓
... spazio libero (stack cresce verso heap) ...
↑ ↑ ↑
HEAPCresce verso l'alto (↑). Allocazione dinamica (brk/mmap).
BSS.bss — Variabili globali/statiche non inizializzate (azzerato a runtime).
DATA.data / .rodata — Variabili globali inizializzate, stringhe costanti.
0x0040 0000
TEXT.text — Codice eseguibile (read-only, executable).
0x0000 0000
NULL page (unmapped, accesso = SIGSEGV)
Lo stack è allineato a 16 byte prima di ogni CALL (ABI System V). Il kernel randomizza gli indirizzi di stack/heap/mmap tramite ASLR.
DATA TYPES
TIPI INTERI & DIRETTIVE
Direttiva GASDimensioneSuffisso istruzioneEsempio
.byte1 byte (8 bit)b.byte 0xFF, 42, 'A'
.word2 byte (16 bit)w.word 0x1234
.long4 byte (32 bit)l.long 1000000
.quad8 byte (64 bit)q.quad 0xDEADCAFE
.float4 byte (IEEE 754)s (scalar single).float 3.14
.double8 byte (IEEE 754)d (scalar double).double 2.718281828
ARRAY
.section .data
array:
    .long  10, 20, 30, 40, 50       # Array di 5 interi a 32 bit
array_len:
    .equ   ARRAY_LEN, (array_len - array) / 4

.section .text
    # Accesso: array[i] = base + i * sizeof(element)
    leaq   array(%rip), %rdi     # RDI = &array[0]
    movl   $2, %esi               # indice i = 2
    movl   (%rdi,%rsi,4), %eax   # EAX = array[2] = 30
STRINGHE
.section .rodata
msg:    .asciz "Hello, World!\n"   # Stringa null-terminated
msg_len:
    .equ   MSG_LEN, msg_len - msg - 1

# Stringa con lunghezza calcolata a compile-time (no \0)
msg2:   .ascii "Ciao"
    .equ   MSG2_LEN, . - msg2    # '.' = posizione corrente
MOV & TRASFERIMENTO DATI
# ─── MOV base ───
movq   $0, %rax         # Carica immediato
movq   %rbx, %rax       # Copia registro
movq   (%rbx), %rax      # Load da memoria
movq   %rax, (%rbx)      # Store in memoria

# ─── Estensione ───
movzbq %al, %rax        # Zero-extend byte  → quad
movzwl %ax, %eax        # Zero-extend word  → long
movsbq %al, %rax        # Sign-extend byte  → quad
movswl %ax, %eax        # Sign-extend word  → long
movslq %eax, %rax       # Sign-extend long  → quad (cltq)
cltq                      # EAX sign-extend → RAX (alias di movslq %eax,%rax)
cqto                      # RAX sign-extend → RDX:RAX (per idivq)

# ─── LEA (Load Effective Address) ───
leaq   8(%rbx,%rcx,4), %rax  # RAX = RBX + RCX*4 + 8 (no memory access!)

# ─── Scambio ───
xchgq  %rax, %rbx       # Scambia RAX e RBX (atomico se con memoria)

# ─── Conditional move (evita branch) ───
cmpq   %rbx, %rax
cmovgq %rbx, %rax       # RAX = RBX se RAX > RBX (signed)
cmoveq %rcx, %rax       # RAX = RCX se ZF=1 (equal)
ARITMETICA
# ─── Somma / Sottrazione ───
addq   $10, %rax        # RAX += 10
addq   %rbx, %rax       # RAX += RBX
subq   %rbx, %rax       # RAX -= RBX
incq   %rax             # RAX++ (non modifica CF)
decq   %rax             # RAX-- (non modifica CF)
negq   %rax             # RAX = -RAX (complemento a 2)
adcq   $0, %rax         # RAX += 0 + CF (add with carry)
sbbq   %rbx, %rax       # RAX -= RBX + CF (subtract with borrow)

# ─── Moltiplicazione ───
imulq  %rbx, %rax       # RAX = RAX * RBX  (signed, 64-bit result)
imulq  $12, %rbx, %rax  # RAX = RBX * 12   (3 operandi)
mulq   %rbx             # RDX:RAX = RAX * RBX (unsigned, 128-bit result)

# ─── Divisione ───
cqto                      # Sign-extend RAX → RDX:RAX (preparazione per idivq)
idivq  %rbx             # RAX = RDX:RAX / RBX, RDX = RDX:RAX % RBX (signed)
xorq   %rdx, %rdx       # Zero-extend per divisione unsigned
divq   %rbx             # RAX = RDX:RAX / RBX, RDX = remainder (unsigned)

# ─── Confronto ───
cmpq   %rbx, %rax       # Calcola RAX - RBX, setta flag (risultato scartato)
testq  %rax, %rax       # AND logico, setta flag (test se zero)
LOGICA & OPERAZIONI SUI BIT
# ─── Logiche ───
andq   $0xFF, %rax      # Maschera: mantieni solo byte basso
orq    $0x20, %rax      # Setta bit (es: uppercase → lowercase)
xorq   %rax, %rax       # Azzera registro (modo efficiente)
notq   %rax             # Complemento a 1 (inverte tutti i bit)

# ─── Shift ───
shlq   $3, %rax         # Shift left 3 (= RAX * 8)
shrq   $1, %rax         # Shift right logico (unsigned / 2)
sarq   $1, %rax         # Shift right aritmetico (signed / 2, preserva segno)
shlq   %cl, %rax        # Shift di CL posizioni (solo CL per shift variabile!)

# ─── Rotazione ───
rolq   $4, %rax         # Ruota a sinistra di 4
rorq   $4, %rax         # Ruota a destra di 4

# ─── Bit test/set ───
btq    $5, %rax         # Testa bit 5 di RAX → CF
btsq   $5, %rax         # Test e Set bit 5
btrq   $5, %rax         # Test e Reset bit 5
bsfq   %rax, %rbx       # Bit Scan Forward: RBX = indice primo bit=1 (da LSB)
bsrq   %rax, %rbx       # Bit Scan Reverse: RBX = indice primo bit=1 (da MSB)
bswapq %rax             # Byte swap: inverte l'ordine dei byte (endian swap)
ISTRUZIONI STRINGA & PREFISSO REP

Operano su blocchi di memoria. Usano RSI (sorgente), RDI (destinazione), RCX (contatore). Il flag DF controlla la direzione (0=avanti, 1=indietro).

ISTRUZIONI BASE
IstruzioneOperazioneRegistri coinvolti
movsb/w/l/qCopia mem[RSI] → mem[RDI], avanza RSI e RDIRSI, RDI
stosb/w/l/qScrive AL/AX/EAX/RAX → mem[RDI], avanza RDIRAX, RDI
lodsb/w/l/qCarica mem[RSI] → AL/AX/EAX/RAX, avanza RSIRAX, RSI
cmpsb/w/l/qConfronta mem[RSI] vs mem[RDI], setta flagRSI, RDI
scasb/w/l/qConfronta AL/AX/EAX/RAX vs mem[RDI], setta flagRAX, RDI
PREFISSI REP
PrefissoCondizione loopUso tipico
repRipete RCX volte (decrementa RCX ogni iterazione)movsb, stosb, lodsb
repe / repzRipete finché RCX > 0 AND ZF=1cmpsb (confronto stringhe uguali)
repne / repnzRipete finché RCX > 0 AND ZF=0scasb (ricerca carattere)
ESEMPI PRATICI
# ─── memcpy: copia RCX byte da RSI a RDI ───
    cld                       # Direzione avanti (DF=0)
    movq   $256, %rcx       # Numero di byte
    leaq   src(%rip), %rsi
    leaq   dst(%rip), %rdi
    rep movsb                 # Copia byte per byte

# ─── Ottimizzato: copia a quadword (8 byte alla volta) ───
    movq   $32, %rcx        # 32 * 8 = 256 byte
    rep movsq                 # Copia 8 byte alla volta

# ─── memset: riempi RCX byte con AL ───
    cld
    movb   $0, %al          # Valore di riempimento
    movq   $1024, %rcx
    leaq   buffer(%rip), %rdi
    rep stosb                 # Azzeramento buffer

# ─── strlen: cerca \0 in stringa ───
    cld
    xorb   %al, %al         # Cerca byte 0
    movq   $-1, %rcx        # Contatore massimo
    leaq   str(%rip), %rdi
    repne scasb               # Scansiona finché trova \0
    notq   %rcx
    decq   %rcx             # RCX = lunghezza stringa

# ─── strcmp: confronta due stringhe ───
    cld
    movq   $100, %rcx       # Max byte da confrontare
    leaq   str1(%rip), %rsi
    leaq   str2(%rip), %rdi
    repe cmpsb                # Confronta finché uguali
    # ZF=1 se tutte uguali; se diversi, i byte differenti sono a -1(%rsi) e -1(%rdi)
DIREZIONE: CLD / STD
cld                           # Clear Direction Flag: RSI/RDI incrementano (avanti)
std                           # Set Direction Flag:   RSI/RDI decrementano (indietro)
La ABI System V richiede DF=0 all'ingresso e all'uscita di ogni funzione. Dopo std, ripristina sempre con cld prima di ret o call.
SALTI & CONTROLLO DI FLUSSO
SALTI CONDIZIONALI (DOPO CMP/TEST)
IstruzioneCondizioneFlagUso
je / jzEqual / ZeroZF=1a == b
jne / jnzNot equalZF=0a != b
jg / jnleGreater (signed)ZF=0 & SF=OFa > b
jge / jnlGreater or equalSF=OFa >= b
jl / jngeLess (signed)SF≠OFa < b
jle / jngLess or equalZF=1 | SF≠OFa <= b
ja / jnbeAbove (unsigned)CF=0 & ZF=0a > b (unsigned)
jae / jnbAbove or equalCF=0a >= b (unsigned)
jb / jnaeBelow (unsigned)CF=1a < b (unsigned)
jbe / jnaBelow or equalCF=1 | ZF=1a <= b (unsigned)
jsSign (negativo)SF=1risultato < 0
joOverflowOF=1overflow signed
jcCarryCF=1carry/borrow
SETcc — IMPOSTAZIONE CONDIZIONALE DI UN BYTE

Imposta un registro a 8 bit a 1 o 0 in base alla condizione (stesse condizioni dei salti).

cmpq   %rbx, %rax
sete   %al              # AL = 1 se RAX == RBX, 0 altrimenti
setne  %al              # AL = 1 se RAX != RBX
setg   %al              # AL = 1 se RAX >  RBX (signed)
setge  %al              # AL = 1 se RAX >= RBX (signed)
setl   %al              # AL = 1 se RAX <  RBX (signed)
setle  %al              # AL = 1 se RAX <= RBX (signed)
seta   %al              # AL = 1 se RAX >  RBX (unsigned)
setb   %al              # AL = 1 se RAX <  RBX (unsigned)

# Tipico pattern: converti flag in intero a 32 bit
cmpq   %rsi, %rdi
setg   %al              # AL = (RDI > RSI) ? 1 : 0
movzbl %al, %eax        # Zero-extend a 32 bit (azzera anche i 32 bit alti)
PATTERN: IF-ELSE
# if (RAX > RBX) { ... } else { ... }
    cmpq   %rbx, %rax
    jle    .Lelse
    # --- blocco if ---
    jmp    .Lendif
.Lelse:
    # --- blocco else ---
.Lendif:
PATTERN: LOOP (FOR / WHILE)
# for (int i = 0; i < 10; i++) { ... }
    xorl   %ecx, %ecx       # i = 0
.Lloop:
    cmpl   $10, %ecx
    jge    .Lend
    # --- corpo del loop ---
    incl   %ecx             # i++
    jmp    .Lloop
.Lend:

# Loop con LOOP (decrementa RCX, salta se RCX != 0)
    movq   $100, %rcx
.Lloop2:
    # --- corpo ---
    loop   .Lloop2          # RCX--, salta se RCX ≠ 0
L'istruzione loop è lenta sulle CPU moderne. Preferire dec/jnz o cmp/jne.
STACK & CHIAMATE A FUNZIONE
# ─── Operazioni base ───
pushq  %rax             # RSP -= 8; mem[RSP] = RAX
popq   %rax             # RAX = mem[RSP]; RSP += 8
pushq  $42              # Push immediato
pushfq                    # Push RFLAGS sullo stack
popfq                     # Pop RFLAGS dallo stack

# ─── Chiamata e ritorno ───
call   my_func          # Push return addr, jump to my_func
ret                       # Pop return addr, jump to it

# ─── Prologo / Epilogo funzione ───
my_func:
    pushq  %rbp           # Salva vecchio frame pointer
    movq   %rsp, %rbp     # Nuovo frame pointer
    subq   $32, %rsp      # Alloca 32 byte per variabili locali

    # --- corpo funzione ---
    # Variabili locali: -8(%rbp), -16(%rbp), ...
    # Argomenti (se passati via stack): 16(%rbp), 24(%rbp), ...

    movq   %rbp, %rsp     # Dealloca locali
    popq   %rbp           # Ripristina vecchio frame pointer
    ret

# ─── Forme compatte: enter / leave ───
enter  $32, $0           # Equivale a: push %rbp; mov %rsp,%rbp; sub $32,%rsp
leave                     # Equivale a: mov %rbp,%rsp; pop %rbp
    # NOTA: enter è lenta sulle CPU moderne, preferire push/mov/sub espliciti
    # leave è veloce e usata normalmente da GCC

# ─── Prologo/Epilogo senza frame pointer (con -fomit-frame-pointer) ───
fast_func:
    subq   $24, %rsp      # Alloca (mantieni allineamento a 16!)
    # --- corpo ---
    addq   $24, %rsp
    ret
Dopo call, RSP è disallineato di 8. Una funzione che chiama altre funzioni deve riallineare RSP a 16 byte prima della prossima call.
SYSCALL LINUX
x86_64: SYSCALL
RegistroUso
%raxNumero syscall
%rdiArgomento 1
%rsiArgomento 2
%rdxArgomento 3
%r10Argomento 4
%r8Argomento 5
%r9Argomento 6
%raxValore di ritorno (negativo = -errno)
%rcx, %r11Distrutti dalla syscall
x86 (32 bit): INT 0x80
RegistroUso
%eaxNumero syscall
%ebxArgomento 1
%ecxArgomento 2
%edxArgomento 3
%esiArgomento 4
%ediArgomento 5
%ebpArgomento 6
SYSCALL COMUNI
SyscallRAX (x86_64)EAX (x86)Firma
read03read(fd, buf, count)
write14write(fd, buf, count)
open25open(path, flags, mode)
close36close(fd)
mmap990mmap(addr, len, prot, flags, fd, off)
munmap1191munmap(addr, len)
brk1245brk(addr)
exit601exit(status)
fork572fork()
execve5911execve(path, argv, envp)
kill6237kill(pid, sig)
rt_sigaction13174rt_sigaction(sig, act, oldact, sigsetsize)
ESEMPIO COMPLETO: HELLO WORLD (x86_64)
.section .rodata
msg:    .asciz "Hello, World!\n"

.section .text
.global _start

_start:
    # write(1, msg, 14)
    movq   $1, %rax         # syscall: write
    movq   $1, %rdi         # fd: stdout
    leaq   msg(%rip), %rsi  # buf: &msg
    movq   $14, %rdx        # count: 14
    syscall

    # exit(0)
    movq   $60, %rax        # syscall: exit
    xorq   %rdi, %rdi       # status: 0
    syscall
ESEMPIO: HELLO WORLD (x86 / 32-bit)
.section .rodata
msg:    .asciz "Hello, World!\n"

.section .text
.global _start

_start:
    # write(1, msg, 14)
    movl   $4, %eax         # syscall: write
    movl   $1, %ebx         # fd: stdout
    leal   msg, %ecx        # buf: &msg
    movl   $14, %edx        # count
    int    $0x80

    # exit(0)
    movl   $1, %eax         # syscall: exit
    xorl   %ebx, %ebx       # status: 0
    int    $0x80
GESTIONE DEI SEGNALI

Per installare un signal handler in puro assembly si usa la syscall rt_sigaction (nr. 13 su x86_64).

STRUTTURA SIGACTION (x86_64 Linux)
# struct sigaction (semplificato, 152 byte su x86_64):
#   offset 0:   sa_handler / sa_sigaction   (8 byte, puntatore)
#   offset 8:   sa_flags                    (8 byte)
#   offset 16:  sa_restorer                 (8 byte, puntatore)
#   offset 24:  sa_mask                     (128 byte, sigset_t)

.equ SA_RESTORER, 0x04000000
.equ SIGINT,      2
.equ SIG_SIZE,    8               # sizeof(sigset_t) per il kernel = 8
ESEMPIO: HANDLER PER SIGINT
.section .data
caught_msg:  .asciz "Caught SIGINT!\n"
.equ CAUGHT_LEN, 15

.section .bss
    .align 8
sa:  .space 152                    # struct sigaction

.section .text
.global _start

# ─── Signal handler ───
sigint_handler:
    # write(1, caught_msg, CAUGHT_LEN)
    movq   $1, %rax
    movq   $1, %rdi
    leaq   caught_msg(%rip), %rsi
    movq   $CAUGHT_LEN, %rdx
    syscall
    ret

# ─── Restorer (richiesto dal kernel) ───
restorer:
    movq   $15, %rax        # rt_sigreturn
    syscall

_start:
    # Popola struct sigaction
    leaq   sa(%rip), %r12
    leaq   sigint_handler(%rip), %rax
    movq   %rax, (%r12)                     # sa_handler
    movq   $SA_RESTORER, 8(%r12)           # sa_flags
    leaq   restorer(%rip), %rax
    movq   %rax, 16(%r12)                  # sa_restorer

    # rt_sigaction(SIGINT, &sa, NULL, sizeof(sigset_t))
    movq   $13, %rax        # rt_sigaction
    movq   $SIGINT, %rdi
    movq   %r12, %rsi       # act
    xorq   %rdx, %rdx       # oldact = NULL
    movq   $SIG_SIZE, %r10
    syscall

    # Loop infinito (aspetta segnale)
.Lwait:
    pause                     # Hint CPU: spin-wait
    jmp    .Lwait
NO-STDLIB: COMPILAZIONE CON AS + LD

Workflow minimale senza libreria C. Entry point: _start. Nessuna libc, solo syscall dirette.

x86_64
# Assembla
$ as --64 -o program.o program.s

# Linka (statico, no libc)
$ ld -o program program.o

# Oppure in un singolo passo con gcc (no stdlib)
$ gcc -nostdlib -no-pie -o program program.s
x86 (32-bit)
# Assembla
$ as --32 -o program.o program.s

# Linka
$ ld -m elf_i386 -o program program.o

# Con gcc
$ gcc -m32 -nostdlib -no-pie -o program program.s
TEMPLATE NO-STDLIB
.section .text
.global _start

_start:
    # Il tuo codice qui...

    # IMPORTANTE: termina sempre con exit syscall!
    # Se ret da _start → SIGSEGV (non c'è return address).
    movq   $60, %rax
    xorq   %rdi, %rdi
    syscall
_start NON è una funzione: lo stack contiene argc, argv, envp — non un return address. Non usare ret!
ACCESSO A ARGC / ARGV DA _start
_start:
    # All'entry, lo stack contiene:
    #   (%rsp)       = argc
    #   8(%rsp)      = argv[0]  (path del programma)
    #   16(%rsp)     = argv[1]
    #   ...          = argv[n]
    #   NULL
    #   envp[0], envp[1], ..., NULL

    popq   %rdi             # RDI = argc
    movq   %rsp, %rsi       # RSI = &argv[0]
WITH-STDLIB: COMPILAZIONE CON GCC

Linkaggio con la libreria C standard. Entry point: main (la libc fornisce _start).

COMPILAZIONE
# Compila e linka con libc
$ gcc -o program program.s

# 32-bit
$ gcc -m32 -o program program.s

# Con debug symbols
$ gcc -g -o program program.s

# Statico (include libc nel binario)
$ gcc -static -o program program.s
ESEMPIO: PRINTF & SCANF
.section .rodata
fmt_out:  .asciz "Risultato: %d\n"
fmt_in:   .asciz "%d"

.section .bss
num:      .space 4

.section .text
.global main

main:
    pushq  %rbp
    movq   %rsp, %rbp

    # scanf("%d", &num)
    leaq   fmt_in(%rip), %rdi
    leaq   num(%rip), %rsi
    xorl   %eax, %eax       # AL = 0 (nessun arg XMM per variadic)
    call   scanf@PLT

    # printf("Risultato: %d\n", num * 2)
    movl   num(%rip), %esi
    addl   %esi, %esi       # ESI = num * 2
    leaq   fmt_out(%rip), %rdi
    xorl   %eax, %eax       # AL = 0
    call   printf@PLT

    # return 0
    xorl   %eax, %eax
    popq   %rbp
    ret
Per funzioni variadic (printf, scanf), AL deve contenere il numero di argomenti passati in registri XMM (0 se nessun float). Usare @PLT per il linkaggio dinamico (PIE).
ABI & CALLING CONVENTION (SYSTEM V AMD64)

La ABI System V AMD64 è lo standard su Linux, BSD, macOS per x86_64.

PASSAGGIO ARGOMENTI (INTERI / PUNTATORI)
ArgomentoRegistro
%rdi
%rsi
%rdx
%rcx
%r8
%r9
7°+Stack (push da destra a sinistra)
PASSAGGIO ARGOMENTI FLOAT
ArgomentoRegistro
1°–8° float/double%xmm0%xmm7
9°+Stack
VALORE DI RITORNO
TipoRegistro
Intero / puntatore%rax (e %rdx per valori a 128 bit)
Float / double%xmm0 (e %xmm1)
CALLER-SAVED vs CALLEE-SAVED

Caller-saved (il chiamante deve salvarli se ne ha bisogno dopo la call):

%rax  %rcx  %rdx  %rsi  %rdi
%r8   %r9   %r10  %r11

Callee-saved (la funzione chiamata deve ripristinarli):

%rbx  %rbp  %r12  %r13  %r14  %r15
ESEMPIO: CHIAMATA A FUNZIONE CON 7+ ARGOMENTI
# Chiama func(a, b, c, d, e, f, g, h) con g e h su stack
    pushq  $8               # h (8° arg, pushato per primo)
    pushq  $7               # g (7° arg)
    movq   $1, %rdi         # a
    movq   $2, %rsi         # b
    movq   $3, %rdx         # c
    movq   $4, %rcx         # d
    movq   $5, %r8          # e
    movq   $6, %r9          # f
    xorl   %eax, %eax       # AL = 0 (no XMM args)
    call   func
    addq   $16, %rsp       # Pulisci stack (2 * 8 byte)
RED ZONE

I 128 byte sotto %rsp sono la "red zone": le funzioni foglia (che non chiamano altre funzioni) possono usarli senza modificare RSP. Le funzioni che chiamano altre funzioni NON devono fare affidamento su questa zona.

SSE / VIRGOLA MOBILE (XMM)

Registri XMM0–XMM15 (128 bit ciascuno). Usati per float (32-bit), double (64-bit) e operazioni SIMD.

OPERAZIONI SCALARI
# ─── Single-precision (float, 32 bit) ───
movss    fval(%rip), %xmm0     # Load float
addss    %xmm1, %xmm0         # xmm0 += xmm1
subss    %xmm1, %xmm0         # xmm0 -= xmm1
mulss    %xmm1, %xmm0         # xmm0 *= xmm1
divss    %xmm1, %xmm0         # xmm0 /= xmm1
sqrtss   %xmm1, %xmm0         # xmm0 = sqrt(xmm1)

# ─── Double-precision (double, 64 bit) ───
movsd    dval(%rip), %xmm0     # Load double
addsd    %xmm1, %xmm0         # Addizione double
mulsd    %xmm1, %xmm0         # Moltiplicazione double
divsd    %xmm1, %xmm0         # Divisione double
ucomisd  %xmm1, %xmm0         # Confronto (setta EFLAGS)

# ─── Conversioni ───
cvtsi2sdq  %rax, %xmm0       # int64 → double
cvtsd2siq  %xmm0, %rax       # double → int64 (arrotonda secondo MXCSR)
cvttsd2siq %xmm0, %rax       # double → int64 (tronca verso zero)
cvttss2sil %xmm0, %eax       # float  → int32 (tronca verso zero)
cvtss2sd   %xmm0, %xmm0      # float → double
cvtsd2ss   %xmm0, %xmm0      # double → float
OPERAZIONI PACKED (SIMD)
# ─── 4 float in parallelo (packed single) ───
movaps   (%rdi), %xmm0         # Load 4 float allineati (16 byte)
addps    %xmm1, %xmm0         # xmm0[0..3] += xmm1[0..3]
mulps    %xmm1, %xmm0         # 4 moltiplicazioni parallele
movaps   %xmm0, (%rdi)         # Store risultato

# ─── 2 double in parallelo (packed double) ───
movapd   (%rdi), %xmm0         # Load 2 double allineati
addpd    %xmm1, %xmm0         # 2 addizioni parallele
movapd   %xmm0, (%rdi)         # Store

# ─── Non-allineato (più lento, ma senza vincoli) ───
movups   (%rdi), %xmm0         # Load non-allineato (single)
movupd   (%rdi), %xmm0         # Load non-allineato (double)
Le istruzioni movaps/movapd richiedono dati allineati a 16 byte. Accesso non allineato = SIGSEGV. Usare .align 16 prima dei dati.
ESEMPIO: SOMMA DI UN ARRAY DI DOUBLE
.section .data
    .align 16
arr:   .double 1.1, 2.2, 3.3, 4.4
.equ   ARR_LEN, 4

.section .text
.global main
main:
    pushq  %rbp
    movq   %rsp, %rbp
    xorpd  %xmm0, %xmm0     # sum = 0.0
    leaq   arr(%rip), %rdi
    movl   $ARR_LEN, %ecx
.Lsum:
    addsd  (%rdi), %xmm0     # sum += arr[i]
    addq   $8, %rdi          # next double
    decl   %ecx
    jnz    .Lsum
    # xmm0 contiene la somma (11.0)
    xorl   %eax, %eax
    popq   %rbp
    ret
INLINE ASSEMBLY IN C (GCC EXTENDED ASM)
SINTASSI
asm volatile (
    "istruzioni assembly"        /* template (AT&T syntax) */
    : "=constraint"(output)       /* output operands  */
    : "constraint"(input)         /* input operands   */
    : "clobber_list"              /* registri sporcati */
);
CONSTRAINT COMUNI
ConstraintSignificato
"r"Qualsiasi registro general purpose
"a"%rax / %eax / %ax / %al
"b"%rbx / %ebx
"c"%rcx / %ecx
"d"%rdx / %edx
"S"%rsi / %esi
"D"%rdi / %edi
"m"Operando in memoria
"i"Immediato intero
"x"Registro SSE (XMM)
"=r"Output: registro
"+r"Input+Output: stesso registro
"0"Matching constraint: usa lo stesso registro dell'operando 0
ESEMPI
/* Semplice: leggi il TSC (Time Stamp Counter) */
static inline unsigned long rdtsc(void) {
    unsigned hi, lo;
    asm volatile ("rdtsc" : "=a"(lo), "=d"(hi));
    return ((unsigned long)hi << 32) | lo;
}

/* Addizione con flag detection */
int add_check_overflow(int a, int b, int *overflow) {
    int result;
    asm (
        "addl %2, %0\n\t"
        "seto %b1"
        : "=r"(result), "=q"(*overflow)
        : "r"(b), "0"(a)
        : "cc"
    );
    return result;
}

/* Syscall wrapper (write) */
long my_write(int fd, const void *buf, unsigned long count) {
    long ret;
    asm volatile (
        "syscall"
        : "=a"(ret)
        : "a"((long)1), "D"(fd), "S"(buf), "d"(count)
        : "rcx", "r11", "memory"
    );
    return ret;
}

/* Atomic compare-and-swap */
static inline int cas(int *ptr, int old, int new) {
    int prev;
    asm volatile (
        "lock cmpxchgl %2, %1"
        : "=a"(prev), "+m"(*ptr)
        : "r"(new), "0"(old)
        : "memory", "cc"
    );
    return prev == old;
}
volatile impedisce al compilatore di eliminare o riordinare il blocco asm. Usalo sempre per I/O, syscall e operazioni con side-effect. Il clobber "memory" indica al compilatore che l'asm potrebbe leggere/scrivere memoria arbitraria. Il clobber "cc" indica che i flag sono modificati.
TECNICHE DI OTTIMIZZAZIONE
LEA PER ARITMETICA VELOCE
# LEA non accede alla memoria: calcola solo l'indirizzo
leaq  (%rax,%rax,2), %rax      # RAX = RAX * 3
leaq  (%rax,%rax,4), %rax      # RAX = RAX * 5
leaq  (,%rax,8), %rax          # RAX = RAX * 8
leaq  1(%rax,%rax,2), %rax    # RAX = RAX * 3 + 1
leaq  (%rdi,%rsi), %rax        # RAX = RDI + RSI (somma senza toccare flag)

# Moltiplicazione per costanti non-potenza-di-2:
# RAX * 7 = RAX * 8 - RAX
leaq  (,%rax,8), %rdx
subq  %rax, %rdx               # RDX = RAX * 7
AZZERARE UN REGISTRO
xorl  %eax, %eax      # Modo ottimale: 2 byte, azzera anche i 32 bit alti
                          # Rompe la dipendenza dal valore precedente di RAX
                          # Riconosciuto dalla CPU come idioma di azzeramento

# NON usare:
movq  $0, %rax         # 7 byte, più lento, NON rompe la dipendenza
LOOP UNROLLING
# ─── Loop base (1 iterazione per ciclo) ───
.Lloop:
    addl  (%rdi), %eax
    addq  $4, %rdi
    decl  %ecx
    jnz   .Lloop

# ─── Unrolled x4 (4 iterazioni per ciclo) ───
.Lloop4:
    addl    (%rdi), %eax
    addl  4(%rdi), %eax
    addl  8(%rdi), %eax
    addl 12(%rdi), %eax
    addq  $16, %rdi
    subl  $4, %ecx
    jnz   .Lloop4
    # (gestire il resto con un loop separato o duff's device)
ALLINEAMENTO DELLO STACK E DEI DATI
# Stack: allineare a 16 byte prima di CALL
andq  $-16, %rsp        # Forza allineamento RSP a 16

# Dati: allineare per SIMD
.align 16
vec_data: .float 1.0, 2.0, 3.0, 4.0

# Allineamento loop target (riduce penalita' di branch)
    .p2align 4           # Allinea a 2^4 = 16 byte
.Lhot_loop:
    # ...
BRANCH-FREE (CONDITIONAL MOVE)
# if (a > b) max = a; else max = b;
# Versione branch-free:
    cmpl  %esi, %edi       # confronta a, b
    movl  %esi, %eax       # eax = b (default)
    cmovgl %edi, %eax      # se a > b: eax = a
PREFETCH & HINT
prefetcht0  64(%rdi)     # Prefetch in L1 cache
prefetcht1  128(%rdi)    # Prefetch in L2 cache
prefetchnta 256(%rdi)    # Prefetch non-temporal (non inquina cache)
pause                      # Hint per spin-wait loop (riduce consumo CPU)
nop                        # No operation (padding)
lfence                     # Load fence (serializza load)
sfence                     # Store fence (serializza store)
mfence                     # Full memory fence
ISTRUZIONI ATOMICHE & LOCK PREFIX

Il prefisso lock rende atomica un'operazione read-modify-write su memoria. Essenziale per codice multithread. Senza lock, un'altra CPU può intervenire tra la lettura e la scrittura.

OPERAZIONI CON LOCK PREFIX
# ─── Incremento / Decremento atomico ───
lock incq  (%rdi)            # atomic: mem[RDI]++
lock decq  (%rdi)            # atomic: mem[RDI]--

# ─── Add / Sub atomico ───
lock addq  $1, (%rdi)       # atomic: mem[RDI] += 1
lock subq  $1, (%rdi)       # atomic: mem[RDI] -= 1

# ─── Operazioni logiche atomiche ───
lock orq   $0x01, (%rdi)   # atomic: mem[RDI] |= 0x01  (set bit)
lock andq  $~0x01, (%rdi)  # atomic: mem[RDI] &= ~0x01 (clear bit)
lock xorq  $0xFF, (%rdi)   # atomic: mem[RDI] ^= 0xFF  (toggle bits)
XADD — EXCHANGE AND ADD
# lock xaddq %rax, (%rdi)
# Atomicamente: temp = mem[RDI]; mem[RDI] += RAX; RAX = temp
# Utile per: fetch-and-add (contatori atomici)

movq       $1, %rax
lock xaddq %rax, (%rdi)     # RAX = vecchio valore, mem[RDI] += 1
CMPXCHG — COMPARE AND EXCHANGE (CAS)
# lock cmpxchgq %rcx, (%rdi)
# Atomicamente:
#   se mem[RDI] == RAX:  mem[RDI] = RCX, ZF=1  (successo)
#   altrimenti:          RAX = mem[RDI], ZF=0  (fallimento)

# ─── Tipico pattern CAS con retry ───
.Lretry:
    movq         (%rdi), %rax   # Leggi valore attuale (expected)
    leaq         1(%rax), %rcx # Calcola nuovo valore (desired)
    lock cmpxchgq %rcx, (%rdi) # Prova a scambiare
    jnz          .Lretry       # Ritenta se fallito

# ─── CMPXCHG16B: CAS su 128 bit (richiede RBX:RCX e RDX:RAX) ───
lock cmpxchg16b (%rdi)        # Confronta RDX:RAX con mem[RDI] (128 bit)
                                # Se uguale: mem[RDI] = RCX:RBX
                                # Altrimenti: RDX:RAX = mem[RDI]
XCHG — SCAMBIO (SEMPRE ATOMICO CON MEMORIA)
# xchg con operando in memoria ha lock implicito (non serve lock prefix)
xchgq  %rax, (%rdi)       # Atomicamente: swap RAX e mem[RDI]

# ─── Spinlock con xchg ───
.Lacquire:
    movq   $1, %rax
    xchgq  %rax, (%rdi)     # Prova ad acquisire (set lock=1)
    testq  %rax, %rax       # Era già 1 (locked)?
    jnz    .Lspin
    ret                       # Lock acquisito!
.Lspin:
    pause                     # Hint CPU: spin-wait
    cmpq   $0, (%rdi)       # Test-and-test-and-set (riduce traffico bus)
    jne    .Lspin
    jmp    .Lacquire

.Lrelease:
    movq   $0, (%rdi)       # Rilascio: store normale (sufficiente su x86)
    ret
BTS / BTR / BTC ATOMICI
lock btsq  $5, (%rdi)       # Atomic test-and-set bit 5 → CF = vecchio bit
lock btrq  $5, (%rdi)       # Atomic test-and-reset bit 5
lock btcq  $5, (%rdi)       # Atomic test-and-complement bit 5
Su x86/x86_64, gli store normali sono già release-ordered e i load sono acquire-ordered (modello TSO). lock aggiunge una full barrier. mfence è necessario solo in casi rari (es. pattern store-load). xchg con operando memoria è sempre atomico, anche senza lock.
PATTERN COMUNI IN ASSEMBLY
SWITCH / JUMP TABLE
# switch(n) con jump table (compilato da GCC per switch densi)
# Assume: n già in %edi, valori 0..3

.section .rodata
    .align 8
.Ljumptable:
    .quad  .Lcase0
    .quad  .Lcase1
    .quad  .Lcase2
    .quad  .Lcase3

.section .text
    cmpl   $3, %edi
    ja     .Ldefault             # n > 3 → default
    leaq   .Ljumptable(%rip), %rax
    movslq %edi, %rdi           # Estendi indice a 64 bit
    jmpq   *(%rax,%rdi,8)      # Salto indiretto: jumptable[n]

.Lcase0:
    # ... codice caso 0 ...
    jmp    .Lend_switch
.Lcase1:
    # ... codice caso 1 ...
    jmp    .Lend_switch
.Lcase2:
    # ... codice caso 2 ...
    jmp    .Lend_switch
.Lcase3:
    # ... codice caso 3 ...
    jmp    .Lend_switch
.Ldefault:
    # ... codice default ...
.Lend_switch:
ACCESSO A CAMPI DI STRUTTURA
# struct Point { long x; long y; long z; };
# Offset: x=0, y=8, z=16 (sizeof = 24)
.equ POINT_X, 0
.equ POINT_Y, 8
.equ POINT_Z, 16
.equ SIZEOF_POINT, 24

# Accesso: RDI = puntatore a struct Point
    movq   POINT_X(%rdi), %rax   # rax = p->x
    movq   POINT_Y(%rdi), %rbx   # rbx = p->y
    addq   %rbx, %rax             # rax = p->x + p->y
    movq   %rax, POINT_Z(%rdi)   # p->z = rax

# Iterare su un array di struct:
# for (int i = 0; i < n; i++) arr[i].z = arr[i].x + arr[i].y;
    xorl   %ecx, %ecx
.Lstruct_loop:
    cmpl   %esi, %ecx             # i < n ?
    jge    .Lstruct_end
    movq   POINT_X(%rdi), %rax
    addq   POINT_Y(%rdi), %rax
    movq   %rax, POINT_Z(%rdi)
    addq   $SIZEOF_POINT, %rdi   # Avanza al prossimo elemento
    incl   %ecx
    jmp    .Lstruct_loop
.Lstruct_end:
FUNCTION POINTER & CHIAMATA INDIRETTA
# Chiamata tramite puntatore a funzione
    movq   func_ptr(%rip), %rax  # Carica puntatore a funzione
    movq   $42, %rdi              # Primo argomento
    call   *%rax                   # Chiamata indiretta

# Vtable (array di function pointer, es. C++ virtual dispatch)
# RDI = puntatore a oggetto, primo campo = puntatore a vtable
    movq   (%rdi), %rax           # RAX = vtable ptr
    call   *16(%rax)              # Chiama il 3° metodo virtuale (offset 16)
DO-WHILE (TEST ALLA FINE)
# do { ... } while (--n);
# Forma più efficiente di un while: evita un salto iniziale
.Ldo:
    # --- corpo del loop ---
    decl   %ecx
    jnz    .Ldo                 # Ripete finché ECX != 0
MIN / MAX / ABS / SIGN SENZA BRANCH
# ─── min(a, b) senza branch ───
# EDI = a, ESI = b
    cmpl   %esi, %edi
    cmovgl %esi, %edi        # EDI = min(a, b)

# ─── abs(a) senza branch ───
# EDI = a
    movl   %edi, %eax
    sarl   $31, %eax         # EAX = 0 se a ≥ 0, -1 se a < 0
    xorl   %eax, %edi        # Complementa se negativo
    subl   %eax, %edi        # +1 se era negativo → EDI = abs(a)

# ─── sign(a): -1, 0, +1 senza branch ───
    movl   %edi, %eax
    sarl   $31, %eax         # EAX = -1 se negativo, 0 altrimenti
    testl  %edi, %edi
    movl   $1, %edx
    cmovgl %edx, %eax        # EAX = 1 se positivo

# ─── clamp(x, lo, hi) ───
# EDI = x, ESI = lo, EDX = hi
    cmpl   %esi, %edi
    cmovll %esi, %edi        # x = max(x, lo)
    cmpl   %edx, %edi
    cmovgl %edx, %edi        # x = min(x, hi)
BITWISE: POTENZA DI 2, POPCOUNT, ALLINEAMENTO
# ─── Test se x è potenza di 2 ───
# x & (x - 1) == 0 && x != 0
    leaq   -1(%rdi), %rax    # RAX = x - 1
    testq  %rdi, %rax        # x & (x - 1)
    setz   %al               # AL = 1 se potenza di 2 (o zero)

# ─── Allinea x al prossimo multiplo di align (potenza di 2) ───
# aligned = (x + align - 1) & ~(align - 1)
    leaq   15(%rdi), %rax    # RAX = x + 15 (align=16)
    andq   $-16, %rax        # RAX = aligned to 16

# ─── popcount (conta bit a 1) ─── (richiede POPCNT, SSE4.2)
popcntq %rdi, %rax         # RAX = numero di bit a 1 in RDI

# ─── lzcnt / tzcnt (leading/trailing zero count) ─── (ABM/BMI1)
lzcntq  %rdi, %rax         # RAX = zeri iniziali (da MSB)
tzcntq  %rdi, %rax         # RAX = zeri finali (da LSB)
DEBUGGING CON GDB

GDB è lo strumento essenziale per il debugging di programmi assembly su Linux. Compilare sempre con -g per i debug symbols.

AVVIO E COMANDI BASE
# Compila con debug symbols
$ as -g --64 -o prog.o prog.s && ld -o prog prog.o
$ gcc -g -no-pie -o prog prog.s

# Avvia GDB
$ gdb ./prog
$ gdb -tui ./prog                # Con interfaccia TUI (mostra sorgente)

# Comandi base
(gdb) run                        # Esegui il programma
(gdb) run arg1 arg2              # Esegui con argomenti
(gdb) quit                       # Esci
(gdb) start                      # Esegui e fermati a main/_start
BREAKPOINT
(gdb) break _start               # Breakpoint su label
(gdb) break *0x401000             # Breakpoint su indirizzo
(gdb) break *_start+20            # Offset da label
(gdb) info break                  # Lista breakpoint
(gdb) delete 1                   # Rimuovi breakpoint #1
(gdb) disable 2                  # Disabilita breakpoint #2

# Watchpoint: ferma quando un valore cambia
(gdb) watch *0x601000            # Watch su indirizzo di memoria
(gdb) watch $rax                  # Watch su registro
(gdb) rwatch *0x601000           # Read watchpoint (ferma alla lettura)
ESECUZIONE STEP-BY-STEP
(gdb) si                         # Step Instruction: esegui 1 istruzione (entra nelle call)
(gdb) ni                         # Next Instruction: esegui 1 istruzione (scavalca le call)
(gdb) si 5                       # Esegui 5 istruzioni
(gdb) continue                   # Continua fino al prossimo breakpoint
(gdb) finish                     # Esegui fino al ret della funzione corrente
(gdb) until *_start+40           # Esegui fino all'indirizzo specificato
ISPEZIONE REGISTRI
(gdb) info registers             # Mostra tutti i registri general purpose
(gdb) info all-registers         # Mostra TUTTI i registri (inclusi XMM, flag, segmento)
(gdb) print $rax                 # Stampa valore di RAX (decimale)
(gdb) print/x $rax               # Stampa in esadecimale
(gdb) print/t $rax               # Stampa in binario
(gdb) print/d $eax               # Stampa come signed decimal
(gdb) print/u $eax               # Stampa come unsigned decimal
(gdb) set $rax = 42              # Modifica un registro
(gdb) print $eflags              # Mostra flag: [ CF ZF SF OF ... ]
ISPEZIONE MEMORIA
# Formato: x/NFU indirizzo
#   N = numero di unità
#   F = formato: x(hex) d(dec) u(unsigned) t(binary) s(string) i(instruction) c(char)
#   U = unità: b(byte) h(halfword/2B) w(word/4B) g(giant/8B)

(gdb) x/10xb $rsp               # 10 byte in hex dallo stack
(gdb) x/4xg $rsp                # 4 quadword (64-bit) in hex dallo stack
(gdb) x/s 0x402000               # Stringa all'indirizzo
(gdb) x/10i $rip                # Disassembla 10 istruzioni da RIP
(gdb) x/10i _start               # Disassembla da label
(gdb) x/20xb &msg               # 20 byte dalla label msg

# Modifica memoria
(gdb) set *(long*)0x601000 = 42  # Scrivi valore in memoria
DISASSEMBLY & LAYOUT
(gdb) disassemble _start        # Disassembla funzione/label
(gdb) disassemble /r _start      # Con byte della macchina (raw hex)
(gdb) set disassembly-flavor att  # Sintassi AT&T (default)
(gdb) set disassembly-flavor intel # Sintassi Intel

# TUI (Text User Interface)
(gdb) layout asm                 # Mostra assembly nel pannello superiore
(gdb) layout regs                # Mostra registri + assembly
(gdb) layout split               # Mostra sorgente + assembly
(gdb) tui reg float              # Mostra registri float/XMM
(gdb) focus asm                  # Sposta il focus sul pannello asm
STACK & BACKTRACE
(gdb) backtrace                  # Mostra call stack (bt)
(gdb) info frame                  # Info sullo stack frame corrente
(gdb) info stack                  # Alias per backtrace
(gdb) x/8xg $rsp                 # Ispeziona le prime 8 quadword sullo stack
STRUMENTI COMPLEMENTARI
# objdump: disassembla un binario
$ objdump -d prog               # Disassembla sezioni eseguibili
$ objdump -d -M intel prog      # In sintassi Intel
$ objdump -d -M intel -S prog   # Intermezza con sorgente C (-g richiesto)

# readelf: ispeziona struttura ELF
$ readelf -h prog               # Header ELF
$ readelf -S prog               # Tabella delle sezioni
$ readelf -s prog               # Tabella dei simboli
$ readelf -l prog               # Program headers (segmenti)

# strace: traccia syscall in tempo reale
$ strace ./prog                  # Mostra ogni syscall
$ strace -e trace=write ./prog   # Filtra solo write

# nm: lista simboli
$ nm prog                        # Mostra simboli e indirizzi
Suggerimento: gdb -tui con layout regs offre la migliore esperienza di debugging per assembly. I registri si evidenziano in tempo reale quando cambiano valore. Usare si (step instruction) per avanzare istruzione per istruzione.