Molto è cambiato tra i386 e x86_64, incluse sia le istruzioni utilizzate per accedere al kernel sia i registri utilizzati per trasportare gli argomenti delle chiamate di sistema. Ecco il codice equivalente al tuo:
.section .data
.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rdi
syscall
Citando questa risposta a una domanda correlata:
I numeri delle chiamate di sistema si trovano nel codice sorgente di Linux in arch/x86/include/asm/unistd_64.h. Il numero di syscall viene passato nel registro rax. I parametri sono in rdi, rsi, rdx, r10, r8, r9. La chiamata viene richiamata con l'istruzione "syscall". La syscall sovrascrive il registro rcx. Il rendimento è in rax.
Stai riscontrando una differenza sorprendente tra i386 e x86_64:non usano lo stesso meccanismo di chiamata di sistema. Il codice corretto è:
movq $60, %rax
movq $2, %rdi ; not %rbx!
syscall
Interrompi 0x80
invoca sempre chiamate di sistema a 32 bit. Viene utilizzato per consentire l'esecuzione di applicazioni a 32 bit su sistemi a 64 bit.
Ai fini dell'apprendimento, dovresti probabilmente provare a seguire esattamente il tutorial, piuttosto che tradurre al volo a 64 bit:ci sono alcune altre significative differenze comportamentali in cui potresti imbatterti. Una volta che avrai familiarizzato con i386, allora puoi ritirare x86_64 separatamente.
per favore leggi questo Quali sono le convenzioni di chiamata per le chiamate di sistema UNIX e Linux su x86-64
e nota che usando int 0x80
per syscall su sistemi x64 è un vecchio livello di compatibilità. dovresti usare syscall
istruzioni sui sistemi x64.
puoi ancora utilizzare questo vecchio metodo, ma devi compilare i tuoi binari in modalità x86, consulta il manuale del tuo compilatore/assembler per i dettagli.
la risposta di sunsetwuff indica correttamente che il meccanismo per le chiamate di sistema è diverso per Linux x86 a 64 bit rispetto a Linux a 32 bit.
Tuttavia, questa risposta è incompleta e fuorviante per un paio di motivi:
- Il cambiamento è stato effettivamente introdotto prima che i sistemi a 64 bit diventassero popolari , motivato dall'osservazione che
int 0x80
era molto lento su Pentium 4. Linus Torvalds ha codificato una soluzione usandoSYSENTER
/SYSEXIT
istruzioni (che erano state introdotte da Intel intorno all'era Pentium Pro, ma che erano difettose e non davano alcun vantaggio pratico). Quindi i moderni sistemi Linux a 32 bit usano effettivamenteSYSENTER
, nonint 0x80
. - I kernel Linux x86 a 64 bit in realtà non usano
SYSENTER
eSYSEXIT
. In realtà usano il molto simileSYSCALL
/SYSRET
istruzioni.
Come sottolineato nei commenti, SYSENTER
in realtà non funziona su molti sistemi Linux a 64 bit —vale a dire AMD a 64 bit sistemi.
È una situazione certamente confusa. I dettagli cruenti sono qui, ma il risultato è questo:
Per un kernel a 32 bit, SYSENTER/SYSEXIT sono l'unica coppia compatibile [tra CPU AMD e Intel]
Solo per un kernel a 64 bit in modalità Long... SYSCALL/SYSRET è l'unica coppia compatibile [tra CPU AMD e Intel]
Sembra che su un Intel CPU in modalità a 64 bit, puoi cavartela usando SYSENTER
perché fa la stessa cosa di SYSCALL
, tuttavia questo non è il caso dei sistemi AMD.
Conclusione:usa sempre SYSCALL
su Linux su sistemi x86 a 64 bit . È ciò che effettivamente specifica l'ABI x86-64. (Vedi questa fantastica risposta wiki per ulteriori dettagli.)