/ / Chiamare una funzione C da Assembly - cambiare convenzione di chiamata - c, assembly, linux-kernel, nasm

Chiamare una funzione C da Assembly - cambiare convenzione di chiamata - c, assembly, linux-kernel, nasm

Ho un'applicazione di assemblaggio per Linux x64dove passo argomenti alle funzioni tramite i registri, quindi sto usando una certa convenzione di chiamata, in questo caso la chiamata veloce. Ora voglio chiamare una funzione C dall'applicazione di assemblaggio che, diciamo, prevede 10 argomenti. Devo passare a cdecl per questo e passare gli argomenti tramite stack, indipendentemente dal fatto in qualsiasi altra parte della mia applicazione, li sto passando attraverso i registri? È consentito combinare convenzioni di chiamata in una sola applicazione?

risposte:

2 per risposta № 1

Suppongo che entro fastcallintendi la convenzione di chiamata amd64 utilizzata dall'ABI SysV (ovvero ciò che utilizza Linux) in cui vengono passati i primi argomenti rdi, rsi, e rdx.

L'ABI è leggermente complicato, la seguente è una semplificazione. Potresti voler leggere la specifica per dettagli.

In generale, i primi pochi argomenti interi (più a sinistra) o puntatore vengono inseriti nei registri rdi, rsi, rdx, rcx, r8, e r9. Vengono passati argomenti in virgola mobile xmm0 a xmm7. Se lo spazio del registro è esaurito, gli argomenti aggiuntivi vengono passati attraverso lo stack da destra a sinistra. Ad esempio, per chiamare una funzione con 10 argomenti interi:

foo(a, b, c, d, e, f, g, h, i, k);

avresti bisogno di un codice come questo:

mov $a,%edi
mov $b,%esi
mov $c,%edx
mov $d,%ecx
mov $e,%r8d
mov $f,%r9d
push $k
push $i
push $h
push $g
call foo
add $32,%rsp

Per il tuo esempio concreto, di getnameinfo:

int getnameinfo(
const struct sockaddr *sa,
socklen_t salen,
char *host,
size_t hostlen,
char *serv,
size_t servlen,
int flags);

Passeresti sa in rdi, salen in rsi, host in rdx, hostlen in rcx, serv in r8, servlen in r9 e flags in pila.


2 per risposta № 2

Sì, naturalmente. La convenzione di chiamata viene applicata in base alla funzione. Questa è un'applicazione perfettamente valida:

int __stdcall func1()
{
return(1);
}

int __fastcall func2()
{
return(2);
}

int __cdecl main(void)
{
func1();
func2();

return(0);
}

-1 per risposta № 3

Puoi, ma non è necessario.

__attribute__((fastcall)) chiede solo che i primi due parametri vengano passati nei registri: tutto il resto verrà comunque automaticamente passato nello stack, proprio come con cdecl. Questo viene fatto al fine di non limitare il numero di parametri che possono essere dati a una funzione scegliendo una determinata convenzione di chiamata.

Nel tuo esempio con 10 parametri per una funzione chiamata con fastcall convenzione di chiamata, i primi due parametri verranno passati nei registri, gli altri 8 automaticamente nello stack, proprio come con la convenzione di chiamata standard.

Come hai scelto di usare fastcall per tutte le altre funzioni, non vedo un motivo per cui si desidera modificarlo per una funzione specifica.