Leitura adicional para qualquer um dos tópicos aqui: O Guia Definitivo para Chamadas do Sistema Linux
Eu os verifiquei usando o GNU Assembler (gas) no Linux.
Interface do Kernel
x86-32 aka convenção de chamada do sistema i386 Linux:
No x86-32, os parâmetros para a chamada do sistema Linux são passados usando registradores. %eax
para syscall_number. % ebx,% ecx,% edx,% esi,% edi,% ebp são usados para passar 6 parâmetros para chamadas do sistema.
O valor de retorno está em %eax
. Todos os outros registros (incluindo EFLAGS) são preservados em todo o int $0x80
.
Tirei o seguinte trecho do Tutorial de Montagem do Linux, mas tenho dúvidas sobre isso. Se alguém puder mostrar um exemplo, seria ótimo.
Se houver mais de seis argumentos,
%ebx
deve conter o local da memória em que a lista de argumentos está armazenada - mas não se preocupe, pois é improvável que você use um syscall com mais de seis argumentos.
Para um exemplo e um pouco mais de leitura, consulte http://www.int80h.org/bsdasm/#alternate-calling-convention . Outro exemplo de um Hello World para i386 Linux usando int 0x80
: Olá, mundo em linguagem assembly com chamadas de sistema Linux?
Existe uma maneira mais rápida de fazer chamadas ao sistema de 32 bits: using sysenter
. O kernel mapeia uma página de memória em todos os processos (o vDSO), com o lado do espaço do usuário da sysenter
dança, que precisa cooperar com o kernel para que ele possa encontrar o endereço de retorno. Arg para registrar o mapeamento é o mesmo que para int $0x80
. Você normalmente deve chamar o vDSO em vez de usar sysenter
diretamente. (Consulte o Guia definitivo para chamadas do sistema Linux para obter informações sobre como vincular e ligar para o vDSO, para obter mais informações sobre sysenter
e tudo mais a ver com chamadas do sistema.)
x86-32 [Grátis | Aberto | Net | DragonFly] Convenção de chamada do sistema BSD UNIX:
Os parâmetros são passados na pilha. Empurre os parâmetros (último parâmetro pressionado primeiro) para a pilha. Em seguida, empurre um número adicional de dados fictícios de 32 bits (não são dados fictícios. Consulte o link a seguir para obter mais informações) e depois forneça uma instrução de chamada do sistemaint $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
Convenção de Chamada de Sistema Linux x86-64:
x86-64 O Mac OS X é semelhante, mas diferente . TODO: verifique o que o * BSD faz.
Consulte a seção: " Convenções do A.2 AMD64 Linux Kernel" do Suplemento do processador de arquitetura da interface binária do aplicativo System V da AMD64 . As versões mais recentes dos psABIs do System V i386 e x86-64 podem ser encontradas no link desta página no do mantenedor da ABI . (Veja também ox86 tag wiki para links ABI atualizados e muitas outras coisas boas sobre o x86 asm.)
Aqui está o trecho desta seção:
- Aplicativos em nível de usuário usam como registros inteiros para passar a sequência% rdi,% rsi,% rdx,% rcx,% r8 e% r9. A interface do kernel usa% rdi,% rsi,% rdx,% r10,% r8 e% r9.
- Uma chamada do sistema é feita através do
syscall
instrução . Isso derruba% rcx e% r11 , bem como o valor de retorno% rax, mas outros registros são preservados.
- O número do syscall deve ser passado no registro% rax.
- As chamadas de sistema são limitadas a seis argumentos, nenhum argumento é passado diretamente na pilha.
- Retornando do syscall, o registro% rax contém o resultado da chamada do sistema. Um valor no intervalo entre -4095 e -1 indica um erro, é
-errno
.
- Somente valores da classe INTEGER ou MEMORY são passados para o kernel.
Lembre-se de que isso é do apêndice específico do Linux para a ABI, e mesmo para o Linux é informativo, não normativo. (Mas é de fato preciso.)
Essa int $0x80
ABI de 32 bits é utilizável no código de 64 bits (mas altamente não recomendado). O que acontece se você usar a ABI int 0x80 Linux de 32 bits no código de 64 bits? Ele ainda trunca suas entradas para 32 bits, portanto não é adequado para ponteiros e zera r8-r11.
Interface do usuário: chamada de função
Convenção de Chamada de Função x86-32:
No x86-32, os parâmetros foram passados na pilha. O último parâmetro foi pressionado primeiro na pilha até que todos os parâmetros estejam concluídos e, em seguida, a call
instrução foi executada. Isso é usado para chamar funções de biblioteca C (libc) no Linux a partir do assembly.
As versões modernas do i386 System V ABI (usado no Linux) requerem alinhamento de 16 bytes %esp
antes de a call
, como o x86-64 System V ABI sempre exigiu. Os Callees podem assumir isso e usar cargas / lojas SSE de 16 bytes que causam falhas em desalinhados. Porém, historicamente, o Linux exigia apenas alinhamento de pilha de 4 bytes, por isso foi necessário um trabalho extra para reservar espaço alinhado naturalmente, mesmo para 8 bytes double
ou algo assim.
Alguns outros sistemas modernos de 32 bits ainda não exigem alinhamento de pilha de mais de 4 bytes.
Convenção de Chamada de Função do Espaço do Usuário do System V x86-64:
O x86-64 System V passa args nos registradores, o que é mais eficiente que a convenção de pilha de args do i386 System V. Isso evita a latência e as instruções extras de armazenar argumentos na memória (cache) e, em seguida, carregá-los novamente no chamado. Isso funciona bem porque há mais registros disponíveis e é melhor para CPUs modernas de alto desempenho em que a latência e a execução fora de ordem são importantes. (O AB38 i386 é muito antigo).
Neste novo mecanismo: Primeiro, os parâmetros são divididos em classes. A classe de cada parâmetro determina a maneira pela qual ele é passado para a função chamada.
Para obter informações completas, consulte: "3.2 Sequência de chamada de função" do suplemento ao processador de arquitetura AMD64 da interface do sistema V de aplicativos que lê, em parte:
Depois que os argumentos são classificados, os registros são atribuídos (na ordem da esquerda para a direita) para passar da seguinte maneira:
- Se a classe for MEMORY, passe o argumento na pilha.
- Se a classe for INTEGER, o próximo registro disponível da sequência% rdi,% rsi,% rdx,% rcx,% r8 e% r9 será usado
O mesmo %rdi, %rsi, %rdx, %rcx, %r8 and %r9
acontece com os registradores na ordem usada para passar parâmetros inteiros / ponteiros (por exemplo, classe INTEGER) para qualquer função libc do assembly. % rdi é usado para o primeiro parâmetro INTEGER. % rsi para o 2º,% rdx para o 3º e assim por diante. Então a call
instrução deve ser dada. A pilha ( %rsp
) deve estar alinhada com 16B ao call
executar.
Se houver mais de 6 parâmetros INTEGER, o sétimo parâmetro INTEGER e mais tarde serão passados para a pilha. (O chamador aparece, o mesmo que x86-32.)
Os primeiros 8 argumentos de ponto flutuante são passados em% xmm0-7, posteriormente na pilha. Não há registros vetoriais preservados por chamada. (Uma função com uma combinação de argumentos FP e número inteiro pode ter mais de 8 argumentos no total do registro.)
Funções variáveis ( comoprintf
) sempre precisam %al
= o número de argumentos do registro FP.
Existem regras para quando empacotar estruturas em registradores ( rdx:rax
no retorno) vs. na memória. Consulte a ABI para obter detalhes e verifique a saída do compilador para garantir que seu código concorda com os compiladores sobre como algo deve ser passado / retornado.
Observe que a convenção de chamada de função do Windows x64 possui várias diferenças significativas em relação ao x86-64 System V, como espaço de sombra que deve ser reservado pelo chamador (em vez de uma zona vermelha) e xmm6-xmm15 preservado na chamada. E regras muito diferentes para as quais arg entra e em que registro.