Ulteriori letture per uno qualsiasi degli argomenti qui:The Definitive Guide to Linux System Calls
Li ho verificati usando GNU Assembler (gas) su Linux.
Interfaccia del kernel
x86-32 aka i386 Linux System Call convention:
In x86-32 i parametri per la chiamata di sistema Linux vengono passati utilizzando i registri. %eax
per syscall_number. %ebx, %ecx, %edx, %esi, %edi, %ebp sono usati per passare 6 parametri alle chiamate di sistema.
Il valore restituito è in %eax
. Tutti gli altri registri (incluso EFLAGS) sono conservati in tutto il int $0x80
.
Ho preso il seguente frammento dal Linux Assembly Tutorial ma ne dubito. Se qualcuno può mostrare un esempio, sarebbe fantastico.
Se ci sono più di sei argomenti,
%ebx
deve contenere la posizione di memoria in cui è memorizzato l'elenco degli argomenti, ma non preoccuparti perché è improbabile che tu utilizzi una chiamata di sistema con più di sei argomenti.
Per un esempio e un po' più di lettura, fare riferimento a http://www.int80h.org/bsdasm/#alternate-calling-convention. Un altro esempio di Hello World per i386 Linux usando int 0x80
:Ciao, mondo in linguaggio assembly con chiamate di sistema Linux?
C'è un modo più veloce per effettuare chiamate di sistema a 32 bit:usando sysenter
. Il kernel mappa una pagina di memoria in ogni processo (il vDSO), con il lato spazio utente del sysenter
dance, che deve cooperare con il kernel per poter trovare l'indirizzo di ritorno. Arg per registrare la mappatura è la stessa di int $0x80
. Normalmente dovresti chiamare il vDSO invece di usare sysenter
direttamente. (Vedi La guida definitiva alle chiamate di sistema Linux per informazioni su collegamenti e chiamate nel vDSO e per ulteriori informazioni su sysenter
, e tutto ciò che riguarda le chiamate di sistema.)
x86-32 [Free|Open|Net|DragonFly]Convenzione delle chiamate di sistema BSD UNIX:
I parametri vengono passati allo stack. Inserisci i parametri (l'ultimo parametro inserito per primo) nello stack. Quindi invia altri 32 bit di dati fittizi (in realtà non sono dati fittizi. Fai riferimento al seguente collegamento per maggiori informazioni) e quindi fornisci un'istruzione di chiamata di sistema int $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
Convenzione delle chiamate di sistema x86-64 Linux:
(Nota:x86-64 Mac OS X è simile ma diverso da Linux. DA FARE:controlla cosa fa *BSD)
Fare riferimento alla sezione:"A.2 AMD64 Linux Kernel Conventions" di System V Application Binary Interface AMD64 Architecture Processor Supplement. Le ultime versioni di i386 e x86-64 System V psABI possono essere trovate collegate da questa pagina nel repository del manutentore ABI. (Vedi anche il wiki tag x86 per aggiornamenti collegamenti ABI aggiornati e molte altre cose interessanti su x86 asm.)
Ecco lo snippet di questa sezione:
- Le applicazioni a livello utente usano come registri interi per passare queste sequenze %rdi, %rsi, %rdx, %rcx,%r8 e %r9. L'interfaccia del kernel utilizza %rdi, %rsi, %rdx, %r10, %r8 e %r9.
- Una chiamata di sistema viene effettuata tramite
syscall
istruzioni . Questo danneggia %rcx e %r11 così come il valore di ritorno %rax, ma gli altri registri vengono preservati.- Il numero della syscall deve essere passato nel registro %rax.
- Le chiamate di sistema sono limitate a sei argomenti, nessun argomento viene passato direttamente nello stack.
- Ritornando dalla chiamata di sistema, il registro %rax contiene il risultato della chiamata di sistema. Un valore compreso tra -4095 e -1 indica un errore, è
-errno
.- Solo i valori della classe INTEGER o della classe MEMORY vengono passati al kernel.
Ricorda che questo è dall'appendice specifica per Linux all'ABI, e anche per Linux è informativo non normativo. (Ma in realtà è accurato.)
Questo int $0x80
a 32 bit ABI è utilizzabile nel codice a 64 bit (ma altamente sconsigliato). Cosa succede se si utilizza l'ABI Linux 0x80 int a 32 bit nel codice a 64 bit? Tronca ancora i suoi input a 32 bit, quindi non è adatto per i puntatori e azzera r8-r11.
Interfaccia utente:chiamata di funzione
Convenzione di chiamata della funzione x86-32:
In x86-32 i parametri venivano passati sullo stack. L'ultimo parametro è stato inserito prima nello stack finché tutti i parametri non sono stati completati e poi call
l'istruzione è stata eseguita. Viene utilizzato per chiamare le funzioni della libreria C (libc) su Linux dall'assembly.
Le versioni moderne dell'ABI i386 System V (utilizzato su Linux) richiedono l'allineamento a 16 byte di %esp
prima di un call
, come ha sempre richiesto l'ABI x86-64 System V. I chiamati possono presumere che e utilizzare SSE a 16 byte carica/memorizza quell'errore su non allineato. Ma storicamente, Linux richiedeva solo l'allineamento dello stack di 4 byte, quindi ci voleva del lavoro extra per riservare lo spazio allineato naturalmente anche per un double
di 8 byte o qualcosa del genere.
Alcuni altri moderni sistemi a 32 bit non richiedono ancora più di 4 byte di allineamento dello stack.
x86-64 System V user-space Function Convenzione di chiamata:
x86-64 System V passa argomenti nei registri, che è più efficiente della convenzione stack args di i386 System V. Evita la latenza e le istruzioni extra di memorizzare gli argomenti in memoria (cache) e quindi caricarli nuovamente nel chiamato. Funziona bene perché sono disponibili più registri ed è migliore per le moderne CPU ad alte prestazioni in cui la latenza e l'esecuzione fuori ordine contano. (L'ABI i386 è molto vecchio).
In questo nuovo meccanismo:prima i parametri sono divisi in classi. La classe di ciascun parametro determina il modo in cui viene passato alla funzione chiamata.
Per informazioni complete fare riferimento a:"3.2 Function Calling Sequence" del System V Application Binary Interface AMD64 Architecture Processor Supplement che recita, in parte:
Una volta classificati gli argomenti, i registri vengono assegnati (in ordine da sinistra a destra) per il passaggio come segue:
- Se la classe è MEMORY, passa l'argomento nello stack.
- Se la classe è INTEGER, viene utilizzato il successivo registro disponibile della sequenza %rdi, %rsi, %rdx, %rcx, %r8 e %r9
Quindi %rdi, %rsi, %rdx, %rcx, %r8 and %r9
sono i registri in ordine utilizzato per passare parametri interi/puntatori (ovvero classe INTEGER) a qualsiasi funzione libc dall'assembly. %rdi viene utilizzato per il primo parametro INTEGER. %rsi per il 2°, %rdx per il 3° e così via. Poi call
dovrebbero essere impartite istruzioni. Lo stack (%rsp
) deve essere allineato a 16B quando call
esegue.
Se sono presenti più di 6 parametri INTEGER, il settimo parametro INTEGER e successivi vengono passati nello stack. (Il chiamante si apre, come x86-32.)
I primi 8 argomenti in virgola mobile vengono passati in %xmm0-7, successivamente nello stack. Non ci sono registri vettoriali con conservazione delle chiamate. (Una funzione con una combinazione di argomenti FP e interi può avere più di 8 argomenti registro totali.)
Funzioni variadiche (come printf
) richiedono sempre %al
=il numero di argomenti del registro FP.
Esistono regole su quando impacchettare le strutture nei registri (rdx:rax
al ritorno) vs. in memoria. Consulta l'ABI per i dettagli e controlla l'output del compilatore per assicurarti che il tuo codice sia d'accordo con i compilatori su come qualcosa dovrebbe essere passato/restituito.
Tieni presente che la convenzione di chiamata delle funzioni di Windows x64 presenta molte differenze significative rispetto a x86-64 System V, come lo spazio ombra che deve essere riservato dal chiamante (invece di una zona rossa) e preservato dalla chiamata xmm6-xmm15. E regole molto diverse per quale arg va in quale registro.
Forse stai cercando l'ABI x86_64?
- www.x86-64.org/documentation/abi.pdf (404 al 24-11-2018)
- www.x86-64.org/documentation/abi.pdf (tramite Wayback Machine al 24-11-2018)
- Dove è documentata l'ABI x86-64 System V? - https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI è tenuto aggiornato (da HJ Lu, uno dei manutentori di ABI) con collegamenti ai PDF della versione corrente ufficiale.
Se non è esattamente quello che stai cercando, usa "x86_64 abi" nel tuo motore di ricerca preferito per trovare riferimenti alternativi.
Le convenzioni di chiamata definiscono il modo in cui i parametri vengono passati nei registri quando si chiama o si viene chiamati da un altro programma. E la migliore fonte di queste convenzioni è sotto forma di standard ABI definiti per ciascuno di questi hardware. Per facilità di compilazione, la stessa ABI è utilizzata anche dallo spazio utente e dal programma del kernel. Linux/Freebsd seguono lo stesso ABI per x86-64 e un altro set per 32 bit. Ma x86-64 ABI per Windows è diverso da Linux/FreeBSD. E generalmente ABI non differenzia la chiamata di sistema rispetto alle normali "chiamate di funzioni". Ad esempio, ecco un esempio particolare di convenzioni di chiamata x86_64 ed è lo stesso sia per lo spazio utente Linux che per il kernel:http://eli.thegreenplace.net/2011/ 09/06/stack-frame-layout-on-x86-64/ (notare la sequenza a,b,c,d,e,f dei parametri):
Le prestazioni sono una delle ragioni di queste ABI (ad es. passare i parametri tramite i registri invece di salvarli negli stack di memoria)
Per ARM ci sono vari ABI:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html
https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIBReference/iPhoneOSABIBReference.pdf
Convenzione ARM64:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
Per Linux su PowerPC:
http://refspecs.freestandards.org/elf/elfspec_ppc.pdf
http://www.0x04.net/doc/elf/psABI-ppc64.pdf
E per l'embedded c'è il PPC EABI:
http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf
Questo documento è una buona panoramica di tutte le diverse convenzioni:
http://www.agner.org/optimize/calling_conventions.pdf