Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
ARM64EC é uma interface binária de aplicativo (ABI) que permite que binários ARM64 sejam executados nativamente e de forma interoperável com código x64. Especificamente, o ABI ARM64EC segue as convenções de software x64, tais como a convenção de chamada, uso de pilha e alinhamento de dados, tornando o código ARM64EC interoperável com o código x64. O sistema operacional emula a parte x64 do binário. (O EC no ARM64EC significa emulação compatível.)
Para obter mais informações sobre as ABIs x64 e ARM64, consulte Visão geral das convenções ABI x64 e Visão geral das convenções ABI ARM64.
ARM64EC não resolve as diferenças do modelo de memória entre arquiteturas baseadas em x64 e ARM. Para obter mais informações, consulte Problemas comuns de migração do Microsoft C++ ARM.
Definições
- ARM64 - O fluxo de código para processos ARM64 que contém código ARM64 tradicional.
- ARM64EC - O fluxo de código que utiliza um subconjunto do conjunto de registros ARM64 para fornecer interoperabilidade com código x64.
Mapeamento de registos
Os processos x64 podem ter threads executando ARM64EC código. Portanto, é sempre possível recuperar um contexto de registro x64, a ARM64EC usa um subconjunto dos registos principais ARM64 que correspondem 1:1 aos registros x64 emulados. É importante ressaltar que ARM64EC nunca usa registradores fora desse subconjunto, exceto para ler o endereço TEB (Thread Environment Block) de x18.
Os processos ARM64 nativos não devem regredir no desempenho quando algumas ou muitas funções são recompiladas como ARM64EC. Para manter o desempenho, a ABI segue estes princípios:
O subconjunto de registro ARM64EC inclui todos os registros que fazem parte da convenção de chamada de função ARM64.
A convenção de chamada ARM64EC mapeia diretamente para a convenção de chamada ARM64.
Rotinas auxiliares especiais, como __chkstk_arm64ec, usam convenções de chamadas personalizadas e registadores. Estes registos estão também incluídos no subconjunto ARM64EC de registos.
Mapeamento de registro para registros inteiros
| ARM64EC registo | Registo x64 | ARM64EC convenção de chamada | Convenção de chamada ARM64 | Convenção de chamada x64 |
|---|---|---|---|---|
x0 |
rcx |
volátil | volátil | volátil |
x1 |
rdx |
volátil | volátil | volátil |
x2 |
r8 |
volátil | volátil | volátil |
x3 |
r9 |
volátil | volátil | volátil |
x4 |
r10 |
volátil | volátil | volátil |
x5 |
r11 |
volátil | volátil | volátil |
x6 |
mm1 (baixo 64 bits de registro x87 R1 ) |
volátil | volátil | volátil |
x7 |
mm2 (baixo 64 bits de registro x87 R2 ) |
volátil | volátil | volátil |
x8 |
rax |
volátil | volátil | volátil |
x9 |
mm3 (baixo 64 bits de registro x87 R3 ) |
volátil | volátil | volátil |
x10 |
mm4 (baixo 64 bits de registro x87 R4 ) |
volátil | volátil | volátil |
x11 |
mm5 (baixo 64 bits de registro x87 R5 ) |
volátil | volátil | volátil |
x12 |
mm6 (baixo 64 bits de registro x87 R6 ) |
volátil | volátil | volátil |
x13 |
N/A | Não permitido | volátil | N/A |
x14 |
N/A | Não permitido | volátil | N/A |
x15 |
mm7 (baixo 64 bits de registro x87 R7 ) |
volátil | volátil | volátil |
x16 |
Parte alta dos 16 bits de cada um dos registadores x87 R0-R3 |
volátil(xip0) |
volátil(xip0) |
volátil |
x17 |
Parte alta dos 16 bits de cada um dos registadores x87 R4-R7 |
volátil(xip1) |
volátil(xip1) |
volátil |
x18 |
GS.base | fixo (TEB) | fixo (TEB) | fixo (TEB) |
x19 |
r12 |
não-volátil | não-volátil | não-volátil |
x20 |
r13 |
não-volátil | não-volátil | não-volátil |
x21 |
r14 |
não-volátil | não-volátil | não-volátil |
x22 |
r15 |
não-volátil | não-volátil | não-volátil |
x23 |
N/A | Não permitido | não-volátil | N/A |
x24 |
N/A | Não permitido | não-volátil | N/A |
x25 |
rsi |
não-volátil | não-volátil | não-volátil |
x26 |
rdi |
não-volátil | não-volátil | não-volátil |
x27 |
rbx |
não-volátil | não-volátil | não-volátil |
x28 |
N/A | Não permitido | Não permitido | N/A |
fp |
rbp |
não-volátil | não-volátil | não-volátil |
lr |
mm0 (baixo 64 bits de registro x87 R0 ) |
ambos | ambos | ambos |
sp |
rsp |
não-volátil | não-volátil | não-volátil |
pc |
rip |
ponteiro de instrução | ponteiro de instrução | ponteiro de instrução |
PSTATEsubconjunto: N/Z/C/V/SS1, 2 |
RFLAGS subconjunto: SF/ZF/CF/OF/TF |
volátil | volátil | volátil |
| N/A |
RFLAGS subconjunto: PF/AF |
N/A | N/A | volátil |
| N/A |
RFLAGS subconjunto: DF |
N/A | N/A | não-volátil |
1 Evite ler, escrever ou calcular mapeamentos diretamente entre PSTATE e RFLAGS. Estes bits podem ser utilizados no futuro e estão sujeitos a alterações.
2 A bandeira C de transporte ARM64EC é o inverso da bandeira CF de transporte x64 para operações de subtração. Não há manipulação especial, porque o sinalizador é volátil e, portanto, é descartado durante a transição entre as funções (ARM64EC e x64).
Mapeamento de registro para registros vetoriais
| ARM64EC registo | Registo x64 | ARM64EC convenção de chamada | Convenção de chamada ARM64 | Convenção de chamada x64 |
|---|---|---|---|---|
v0-v5 |
xmm0-xmm5 |
volátil | volátil | volátil |
v6-v7 |
xmm6-xmm7 |
volátil | volátil | não-volátil |
v8-v15 |
xmm8-xmm15 |
volátil 1 | volátil 1 | não-volátil |
v16-v31 |
xmm16-xmm31 |
Não permitido | volátil | não permitido (emulador x64 não suporta AVX-512) |
FPCR
2 |
MXCSR[15:6] |
não-volátil | não-volátil | não-volátil |
FPSR
2 |
MXCSR[5:0] |
volátil | volátil | volátil |
1 Estes registos ARM64 são especiais na medida em que os 64 bits inferiores não são voláteis, mas os 64 bits superiores são voláteis. Do ponto de vista de um chamador x64, eles são efetivamente voláteis porque o destinatário descartaria dados.
2 Evite ler, escrever ou calcular mapeamentos diretamente de FPCR e FPSR. Estes bits podem ser utilizados no futuro e estão sujeitos a alterações.
Empacotamento de Estruturas
ARM64EC segue as mesmas regras de empacotamento de estrutura usadas pelo x64 para garantir a interoperabilidade entre o código ARM64EC e o código x64. Para mais informações e exemplos sobre o empacotamento de estruturas x64, consulte Visão geral das convenções ABI x64.
Exceções de ponto flutuante
Para determinar se uma CPU ARM suporta exceções, escreva um valor que permita exceções ao registro FPCR e leia-o novamente. Se a CPU suportar exceções de ponto flutuante, os bits correspondentes às exceções suportadas permanecerão definidos, enquanto a CPU redefinirá os bits para exceções sem suporte.
No ARM64EC, o Windows captura exceções de ponto flutuante do processador e as desabilita no registro FPCR. Isso garante um comportamento consistente em diferentes variantes de processador.
Rotinas ABI auxiliares de emulação
O código ARM64EC e os thunks utilizam funções auxiliares de emulação para fazer a transição entre as funções x64 e ARM64EC.
A tabela a seguir descreve cada rotina especial de ABI e os registros que a ABI usa. As rotinas não modificam os registros preservados listados na coluna ABI. Não devem ser feitas suposições sobre registos não enumerados. No disco, os ponteiros das rotinas de ABI são nulos. No momento do carregamento, o carregador atualiza os ponteiros para apontar para as rotinas do emulador x64.
| Nome | Descrição | ABI |
|---|---|---|
__os_arm64x_dispatch_call_no_redirect |
Chamado por um thunk de saída para invocar um alvo x64, que pode ser uma função x64 ou uma sequência de avanço rápido x64. A rotina envia por push o endereço de retorno ARM64EC (no LR registro) seguido pelo endereço da instrução que sucede uma blr x16 instrução que invoca o emulador x64. Em seguida, executa a blr x16 instrução |
valor de retorno em x8 (rax) |
__os_arm64x_dispatch_ret |
Chamado por um thunk de entrada para retornar ao seu chamador x64. Ele remove o endereço de retorno x64 da pilha e invoca o emulador x64 para executar um salto para o endereço | N/A |
__os_arm64x_check_call |
Chamado por código ARM64EC com um ponteiro para um thunk de saída e para executar o endereço de destino ARM64EC indireto. O destino ARM64EC é considerado passível de correção e a execução sempre retorna ao chamador com os mesmos dados com os quais foi chamado ou com dados modificados | Argumentos:x9: O endereço de destinox10: O endereço de saída thunkx11: O endereço da sequência de avanço rápidoSaída: x9: Se a função de destino foi desviada, ela contém o endereço da sequência de avanço rápidox10: O endereço de saída thunkx11: Se a função foi redirecionada, ela contém o endereço de saída do thunk. Caso contrário, o endereço de destino saltou paraRegistos conservados: x0-x8, x15 ().chkstk e ainda q0-q7 |
__os_arm64x_check_icall |
Chamado pelo código ARM64EC, com um ponteiro para uma função de saída, para gerir um salto para um endereço de destino que seja x64 ou ARM64EC. Se o destino for x64 e o código x64 não tiver sido corrigido, a rotina definirá o registro de endereço de destino. Ele aponta para a versão ARM64EC da função, se existir. Caso contrário, ele define o registro para apontar para o thunk de saída que faz a transição para o destino x64. Em seguida, retorna ao código de chamada ARM64EC, que então salta para o endereço no registo. Essa rotina é uma versão não otimizada do __os_arm64x_check_call, onde o endereço de destino não é conhecido no momento da compilaçãoUsado num ponto de chamada de uma chamada indireta |
Argumentos:x9: O endereço de destinox10: O endereço de saída thunkx11: O endereço da sequência de avanço rápidoSaída: x9: Se a função de destino foi desviada, ela contém o endereço da sequência de avanço rápidox10: O endereço de saída thunkx11: Se a função foi redirecionada, ela contém o endereço de saída do thunk. Caso contrário, o endereço de destino saltou paraRegistos conservados: x0-x8, x15 (chkstk), e q0-q7 |
__os_arm64x_check_icall_cfg |
O mesmo que __os_arm64x_check_icall , mas também verifica se o endereço especificado é um destino de chamada indireta válido do Gráfico de Fluxo de Controle |
Argumentos:x10: O endereço do thunk de saídax11: O endereço da função de destinoSaída: x9: Se o destino for x64, o endereço da função. Caso contrário, indefinidox10: O endereço do thunk de saídax11: Se o destino for x64, ele contém o endereço do thunk de saída. Caso contrário, o endereço da funçãoRegistos conservados: x0-x8, x15 (chkstk), e q0-q7 |
__os_arm64x_get_x64_information |
Obtém a parte solicitada do contexto de registo x64 em tempo real | _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo) |
__os_arm64x_set_x64_information |
Estabelece a parte solicitada do contexto de registo x64 ativo | _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo) |
__os_arm64x_x64_jump |
Usado em mecanismos de ajuste sem assinatura e outros thunks que encaminham diretamente (jmp) uma chamada para outra função que pode ter qualquer assinatura, adiando a potencial aplicação da lógica correta para o alvo real. |
Argumentos:x9: alvo para saltarTodos os registos de parâmetros preservados (encaminhados) |
Tunks
Thunks são os mecanismos de baixo nível para suportar funções ARM64EC e x64 chamando umas às outras. Existem dois tipos: thunks de entrada para entrar em funções ARM64EC e thunks de saída para chamar funções x64.
Thunk de entrada e thunks de entrada intrínsecos: chamada de função de x64 para ARM64EC
Para suportar chamadores x64 quando uma função C/C++ é compilada como ARM64EC, o conjunto de ferramentas gera um único thunk de entrada que consiste em código de máquina ARM64EC. Os intrínsecos têm um "thunk" de entrada próprio. Todas as outras funções partilham uma entrada thunk com todas as funções que têm convenção de chamada, parâmetros e tipo de retorno compatíveis. O conteúdo do thunk depende da convenção de chamada da função C/C++.
Além de manipular parâmetros e o endereço de retorno, o thunk faz a ponte entre as diferenças de volatilidade entre os registos de vetores ARM64EC e x64 resultantes do mapeamento de registo vetorial ARM64EC:
| ARM64EC registo | Registo x64 | ARM64EC convenção de chamada | Convenção de chamada ARM64 | Convenção de chamada x64 |
|---|---|---|---|---|
v6-v15 |
xmm6-xmm15 |
volátil, mas salvo/restaurado na entrada thunk (x64 a ARM64EC) | volátil ou parcialmente volátil nos 64 bits superiores | não-volátil |
A entrada thunk executa as seguintes ações:
| Número do parâmetro | Uso da pilha |
|---|---|
| 0-4 | Armazena ARM64EC v6 e v7 no espaço de armazenamento reservado pelo chamadorComo o destinatário é ARM64EC, que não possui a noção de um espaço de armazenamento designado, os valores armazenados não são sobrescritos. Aloca 128 bytes extra na pilha e armazena ARM64EC de v8 a v15. |
| 5-8 |
x4 = 5º parâmetro da pilhax5 = 6º parâmetro da pilhax6 = 7º parâmetro da pilha de memóriax7 = 8º parâmetro da pilhaSe o parâmetro for SIMD, os registos v4-v7 serão usados. |
| 9+ | Aloca AlignUp(NumParams - 8 , 2) * 8 bytes na pilha. *Copia o 9.º parâmetro e os restantes para esta área |
* Alinhar o valor a um número par garante que a pilha permanece alinhada a 16 bytes
Se a função aceitar um parâmetro inteiro de 32 bits, o thunk terá permissão para enviar apenas 32 bits em vez dos 64 bits completos do registro pai.
Em seguida, o thunk usa uma instrução ARM64 bl para chamar a função ARM64EC. Depois que a função retorna, o thunk:
- Desfaz todas as alocações de pilha
- Chama o auxiliar do emulador para exibir o endereço de retorno x64 e retomar a
__os_arm64x_dispatch_retemulação x64.
Exit thunk: função de transição de ARM64EC para chamada de função x64
Para cada chamada que uma função ARM64EC C/C++ faz para o código x64 potencial, a cadeia de ferramentas MSVC gera um thunk de saída. O conteúdo do thunk depende dos parâmetros do destinatário x64 e se o destinatário está usando a convenção de chamada padrão ou __vectorcall. O compilador obtém essas informações de uma declaração de função para o destinatário.
Primeiro, o thunk envia por push o endereço de retorno que está no registro ARM64EC lr e um valor fictício de 8 bytes para garantir que a pilha esteja alinhada a 16 bytes. Em segundo lugar, o thunk lida com os parâmetros:
| Número do parâmetro | Uso da pilha |
|---|---|
| 0-4 | Aloca 32 bytes de espaço doméstico na pilha |
| 5-8 | Aloca AlignUp(NumParams - 4, 2) * 8 mais bytes mais acima na pilha. * Copia o 5º parâmetro e todos os subsequentes de ARM64EC x4-x7 para este espaço extra. |
| 9+ | Copia o nono parâmetro e os restantes para o espaço extra |
* Alinhar o valor a um número par garante que a pilha permanece alinhada a 16 bytes.
Em terceiro lugar, o thunk chama o auxiliar do emulador __os_arm64x_dispatch_call_no_redirect para invocar o emulador x64 e executar a função x64. A chamada deve ser uma blr x16 instrução (convenientemente, x16 é um registro volátil). Uma blr x16 instrução é necessária porque o emulador x64 analisa essa instrução como uma dica.
A função x64 geralmente tenta retornar ao auxiliar do emulador usando uma instrução x64 ret . Neste ponto, o emulador x64 deteta que está no código ARM64EC. Em seguida, ele lê a dica anterior de 4 bytes que coincide com a instrução ARM64 blr x16 . Como esta dica indica que o endereço de retorno está neste auxiliar, o emulador direciona-se diretamente para este endereço.
A função x64 tem permissão para retornar ao auxiliar do emulador usando qualquer instrução de ramificação, incluindo x64 jmp e call. O emulador também lida com esses cenários.
Quando o auxiliar retorna ao thunk, este:
- Desfaz qualquer alocação de memória
- Liberta o registo ARM64EC
lr - Executa uma instrução ARM64
ret lr.
ARM64EC decorar nomes de funções
Um nome de função ARM64EC tem uma decoração secundária aplicada após qualquer decoração específica do idioma. Para funções com ligação C (quer sejam compiladas como C ou usando extern "C"), um # é adicionado ao início do nome. Para funções decoradas em C++, uma $$h tag é inserida no nome.
foo => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ
__vectorcall
A cadeia de ferramentas ARM64EC atualmente não suporta __vectorcall. O compilador emite um erro quando deteta o uso de __vectorcall com ARM64EC.
Ver também
Entendendo ARM64EC ABI e código de montagem
Problemas comuns de migração do Microsoft C++ ARM
Nomes decorados