Voglio scrivere una funzione generica che fa una chiamata di sistema. Qualcosa comeQual è il tipo di argomenti per le chiamate di sistema su Linux?
long my_syscall2(long number, long arg1, long arg2);
Voglio che sia il più portabile possibile. L'implementazione è ovviamente diversa per tutte le architetture. Anche la firma della funzione deve essere diversa? Posso usare long
o dovrei usare qualcos'altro?
Ecco le possibili soluzioni che ho trovato:
- Il kernel utilizza alcuni dark magic: (__SYSCALL_DEFINEx chiama __SC_LONG per ottenere il tipo, __SC_LONG contiene magia). Ho sentito da qualche parte che i tipi nello spazio utente non sono sempre gli stessi dello spazio del kernel, quindi non so se posso usarlo.
- musl-libc uses long for all architectures that it supports except x32: (definito in [arch] /syscall_arch.h).
- È possibile trovare la documentazione per tutte le architetture e i compilatori di processori che desidero supportare, cercare le dimensioni dei registri e le dimensioni dei tipi interi e selezionare qualsiasi tipo di intero con le stesse dimensioni dei registri.
Quindi credo che la domanda è: "C'è qualche regola che dice 'il tipo di argomenti chiamate di sistema sono sempre long
con alcune eccezioni come x32' o ho bisogno di cercare la documentazione per ogni architettura e compilatore?"
Modifica: So che alcune chiamate di sistema richiedono puntatori e altri tipi come parametri. Voglio scrivere funzioni generiche che possono chiamare qualsiasi chiamata di sistema, con tipi di parametri generici. Questi tipi di parametri generici dovrebbero essere abbastanza grandi da contenere uno qualsiasi dei tipi di parametri effettivi. So che è possibile perché esiste la funzione syscall().
Edit2: Ecco un'altra soluzione parziale a questo problema.
Le implementazioni di queste funzioni attualmente simile a questa:
static __inline long my_syscall2(long number, long arg1, long arg2)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(number), "D"(arg1), "S"(arg2)
: "rcx", "r11", "memory");
return ret;
}
La parte interessante è "=a"(ret)
, ciò significa che il valore di ritorno chiamata di sistema che viene memorizzato nel registro a
deve essere salvato nella variabile ret
. Invece di scrivere una funzione che crea una variabile locale, crea syscall, salva il suo valore di ritorno nella variabile e restituisce la variabile Potrei scrivere una macro che crea syscall e memorizza il risultato in una variabile fornita dal chiamante. Si sarebbe simile a questa:
#define my_syscall2(RET, NUMBER, ARG1, ARG2) \
__asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \
: "rcx", "r11", "memory");
e sarebbe utilizzato in questo modo:
long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);
In questo modo non ho bisogno di sapere registrati dimensione e tipo integer che è abbastanza grande per contenere un valore del registro.
Come gestireste le chiamate di sistema che non richiedono alcun parametro? – edmz
@black Definirei 6 funzioni my_syscall {0-5} – alkedr
In realtà è più complesso di quanto si possa rispondere qui succintamente (a meno che non si specifichi un'architettura), ma sentitevi liberi di utilizzare le mie implementazioni di dominio pubblico di syscall() e syscall0 () -syscall6() funziona su https://github.com/technosaurus/BQC ... il cast è troppo lungo per i parametri – technosaurus