Os links a seguir explicam as convenções de chamada do sistema x86-32 para UNIX (sabor BSD) e Linux:
Mas quais são as convenções de chamada do sistema x86-64 no UNIX e Linux?
Os links a seguir explicam as convenções de chamada do sistema x86-32 para UNIX (sabor BSD) e Linux:
Mas quais são as convenções de chamada do sistema x86-64 no UNIX e Linux?
sysret
funciona, além de o rax ser substituído pelo valor de retorno. Todos os outros registradores são preservados no amd64.Respostas:
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 oint $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.
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 dasysenter
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 paraint $0x80
. Você normalmente deve chamar o vDSO em vez de usarsysenter
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 sobresysenter
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 sistema
int $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:
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 acall
, 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 bytesdouble
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:
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 acall
instrução deve ser dada. A pilha (%rsp
) deve estar alinhada com 16B aocall
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 ( como
printf
) 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.
fonte
int 0x80
ABI do Linux em código de 64 bits, é exatamente isso que acontece: stackoverflow.com/questions/46087730/… . Ele zera r8-r11 e funciona exatamente como quando executado em um processo de 32 bits. Nessa sessão de perguntas e respostas, tenho um exemplo mostrando como funciona ou falha ao truncar um ponteiro. E eu também procurei na fonte do kernel para mostrar por que ela se comporta dessa maneira.Talvez você esteja procurando a ABI x86_64?
Se não é exatamente isso que você procura, use 'x86_64 abi' em seu mecanismo de pesquisa preferido para encontrar referências alternativas.
fonte
As convenções de chamada definem como os parâmetros são passados nos registradores ao chamar ou ser chamado por outro programa. E a melhor fonte dessas convenções está na forma de padrões ABI definidos para cada um desses hardwares. Para facilitar a compilação, a mesma ABI também é usada pelo espaço do usuário e pelo programa do kernel. O Linux / Freebsd segue a mesma ABI para x86-64 e outro conjunto para 32 bits. Mas o x86-64 ABI para Windows é diferente do Linux / FreeBSD. E, geralmente, a ABI não diferencia a chamada do sistema versus as "chamadas de funções" normais. Ou seja, aqui está um exemplo específico de convenções de chamada x86_64 e é o mesmo para o espaço de usuário e o kernel do Linux: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64 / (observe a sequência a, b, c, d, e, f dos parâmetros):
O desempenho é uma das razões para essas ABI (por exemplo, passar parâmetros por meio de registradores em vez de salvar em pilhas de memória)
Para ARM, existem várias 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/iPhoneOSABIReference/iPhoneOSABIReference.pdf
Convenção ARM64:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
Para Linux no PowerPC:
http://refspecs.freestandards.org/elf/elfspec_ppc.pdf
http://www.0x04.net/doc/elf/psABI-ppc64.pdf
E para incorporado existe o PPC EABI:
http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf
Este documento é uma boa visão geral de todas as diferentes convenções:
http://www.agner.org/optimize/calling_conventions.pdf
fonte
Comentários de origem do kernel 5.0 do Linux
Eu sabia que as especificidades do x86 estão abaixo
arch/x86
e que o syscall está abaixoarch/x86/entry
. Portanto, um rápidogit grep rdi
nesse diretório me leva a arch / x86 / entry / entry_64.S :e para 32 bits em arch / x86 / entry / entry_32.S :
implementação de chamada do sistema glibc 2.29 Linux x86_64
Agora vamos trapacear observando as principais implementações da libc e ver o que elas estão fazendo.
O que poderia ser melhor do que pesquisar na glibc que estou usando agora enquanto escrevo esta resposta? :-)
O glibc 2.29 define syscalls x86_64 em
sysdeps/unix/sysv/linux/x86_64/sysdep.h
e que contém algum código interessante, por exemplo:e:
que eu sinto que são bastante auto-explicativas. Observe como isso parece ter sido projetado para corresponder exatamente à convenção de chamada das funções ABI AMD64 regulares do Sistema V: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions
Lembrete rápido dos clobbers:
cc
significa registros de sinalizadores. Mas Peter Cordes comenta que isso é desnecessário aqui.memory
significa que um ponteiro pode ser passado na montagem e usado para acessar a memóriaPara obter um exemplo executável mínimo explícito do zero, consulte esta resposta: Como chamar uma chamada de sistema via sysenter no assembly embutido?
Faça alguns syscalls na montagem manualmente
Não é muito científico, mas divertido:
x86_64.S
GitHub upstream .
aarch64
Eu mostrei um exemplo mínimo de userland executável em: /reverseengineering/16917/arm64-syscalls-table/18834#18834 TODO o código do kernel grep aqui, deve ser fácil.
fonte
"cc"
clobber é desnecessário: os syscalls do Linux salvam / restauram RFLAGS (as instruçõessyscall
/sysret
fazem isso usando R11, e o kernel não modifica o R11 / RFLAGS salvo, exceto por meio deptrace
chamadas do sistema depurador.) Não que isso importe, porque um"cc"
clobber é implícito para x86 / x86-64 no GNU C Extended asm, portanto você não pode obter nada deixando de fora.