/ / Chamando uma função C do Assembly - alternando convenção de chamada - c, assembly, linux-kernel, nasm

Chamando uma função C do Assembly - alternando convenção de chamada - c, assembly, linux-kernel, nasm

Eu tenho um aplicativo de montagem para Linux x64onde eu passo argumentos para as funções via registradores, assim eu estou usando uma certa convenção de chamada certa, neste caso chamada rápida. Agora eu quero chamar uma função C da aplicação de montagem que, digamos, espera 10 argumentos. Eu tenho que mudar para cdecl para isso e passar os argumentos via pilha, independentemente do fato em todos os lugares na minha aplicação eu estou passando-os através de registros? É permitido misturar convenções de chamada em uma aplicação?

Respostas:

2 para resposta № 1

Eu suponho que por jejum, você quer dizer a convenção de chamada amd64 usada pelo SysV ABI (ou seja, o que o Linux usa) onde os primeiros poucos argumentos são passados ​​em rdi, rsie rdx.

O ABI é um pouco complicado, o que se segue é uma simplificação. Você pode querer ler a especificação para detalhes.

Em geral, os primeiros poucos inteiros (à esquerda) ou argumentos de ponteiro são colocados nos registradores rdi, rsi, rdx, rcx, r8e r9. Argumentos de ponto flutuante são passados ​​em xmm0 para xmm7. Se o espaço de registro estiver esgotado, argumentos adicionais serão passados ​​pela pilha da direita para a esquerda. Por exemplo, para chamar uma função com 10 argumentos inteiros:

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

você precisaria de código como este:

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

Para o seu exemplo concreto, de getnameinfo:

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

Você passaria sa dentro rdi, salen dentro rsi, host dentro rdx, hostlen dentro rcx, serv dentro r8, servlen dentro r9 e flags na pilha.


2 para resposta № 2

Sim, claro. A convenção de chamadas é aplicada por função. Esta é uma aplicação perfeitamente válida:

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

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

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

return(0);
}

-1 para resposta № 3

Você pode, mas você não precisa.

__attribute__((fastcall)) só pede que os dois primeiros parâmetros sejam passados ​​nos registradores - todo o resto será automaticamente transmitido na pilha, assim como cdecl. Isso é feito para não limitar o número de parâmetros que podem ser dados a uma função escolhendo uma determinada convenção de chamada.

No seu exemplo com 10 parâmetros para uma função que é chamada com o fastcall convenção de chamada, os dois primeiros parâmetros serão passados ​​em registradores, os 8 restantes serão automaticamente colocados na pilha, assim como na convenção de chamada padrão.

Como você escolheu usar fastcall para todas as outras funções, não vejo uma razão pela qual você queira alterar isso para uma função específica.