Partilhar via


Tratamento de exceções x64

Uma visão geral das convenções de codificação e comportamento de manipulação de exceções estruturadas e C++ no x64. Para obter informações gerais sobre o tratamento de exceções, consulte Tratamento de exceções no Microsoft C++.

Descompactar dados para tratamento de exceções e suporte a depuradores

Várias estruturas de dados são necessárias para o tratamento de exceções e suporte à depuração.

struct RUNTIME_FUNCTION

A manipulação de exceções baseada em tabela requer uma entrada de tabela para todas as funções que alocam espaço de pilha ou chamam outra função (por exemplo, funções não folha). As entradas da tabela de funções têm o formato:

Tamanho Valor
ULONG Endereço de início da função
ULONG Endereço final da função
ULONG Desdobramento de endereço de informação

A estrutura RUNTIME_FUNCTION deve estar alinhada a DWORD na memória. Todos os endereços são relativos à imagem, ou seja, são deslocamentos de 32 bits do endereço inicial da imagem que contém a entrada da tabela de funções. Essas entradas são classificadas e colocadas na seção .pdata de uma imagem PE32+. Para funções geradas dinamicamente [compiladores JIT], o tempo de execução para suportar essas funções deve usar RtlInstallFunctionTableCallback ou RtlAddFunctionTable para fornecer essas informações ao sistema operacional. A falha em fazê-lo resultará em um tratamento de exceções pouco confiável e em dificuldades na depuração de processos.

struct UNWIND_INFO

A estrutura de dados de desmontagem é utilizada para registar os efeitos de uma função no ponteiro da pilha, assim como o local onde são guardados os registos não-voláteis na pilha.

Tamanho Valor
UBYTE: 3 Versão
UBYTE: 5 Bandeiras
UBYTE Tamanho do prólogo
UBYTE Contagem de códigos de desempacotamento
UBYTE: 4 Registo de frames
UBYTE: 4 Deslocamento do Frame Register (dimensionado)
USHORT * n Desenrolar matriz de códigos
variável Pode ser da forma (1) ou (2) abaixo

(1) Manipulador de exceções

Tamanho Valor
ULONG Endereço do manipulador de exceções
variável Dados de gestor específicos do idioma (opcional)

(2) Informações sobre o desenrolar encadeado

Tamanho Valor
ULONG Endereço de início da função
ULONG Endereço final da função
ULONG Desdobramento de endereço de informação

A estrutura UNWIND_INFO deve estar alinhada por DWORD na memória. Veja o que cada campo significa:

  • Versão

    Número da versão dos dados de unwind, atualmente 1.

  • Bandeiras

    Três bandeiras estão atualmente definidas:

    Bandeira Descrição
    UNW_FLAG_EHANDLER A função tem um manipulador de exceções que deve ser chamado ao procurar funções que precisam examinar exceções.
    UNW_FLAG_UHANDLER A função tem um manipulador de terminação que deve ser chamado ao desenrolar uma exceção.
    UNW_FLAG_CHAININFO Essa estrutura de informações de desenrolamento não é a principal para o procedimento. Em vez disso, as informações de desenrolamento encadeadas são o conteúdo de uma entrada anterior de RUNTIME_FUNCTION. Para obter informações, consulte Estruturas de dados de desenrolamento encadeadas. Se esse sinalizador estiver ativado, os sinalizadores UNW_FLAG_EHANDLER e UNW_FLAG_UHANDLER devem ser desativados. Além disso, os campos de registo de moldura e alocação de pilha fixa devem ter os mesmos valores que nas informações de desempacotamento principais.
  • Tamanho do prolog

    Comprimento do prólogo da função em bytes.

  • Contagem de códigos de desenrolar

    O número de slots na matriz de códigos de 'unwind'. Alguns códigos de desenrolamento, por exemplo, UWOP_SAVE_NONVOL, exigem mais de um slot na matriz.

  • Registo de frames

    Se for diferente de zero, a função usa um ponteiro de quadro (FP), e este campo é o número do registo não volátil utilizado como ponteiro de quadro, utilizando a mesma codificação para o campo de informações de operação dos nós UNWIND_CODE.

  • Deslocamento do registo de quadro (escalado)

    Se o campo de registro de quadro for diferente de zero, esse campo será o deslocamento dimensionado do RSP que é aplicado ao registro FP quando ele é estabelecido. O registo FP real é definido como RSP + 16 * este número, permitindo compensações de 0 a 240. Esse deslocamento permite posicionar o registo FP no centro da alocação da pilha local para estruturas de pilha dinâmicas, possibilitando uma melhor densidade de código através de instruções mais curtas. (Ou seja, mais instruções podem usar o formato de deslocamento assinado de 8 bits.)

  • Desenrolar matriz de códigos

    Uma matriz de itens que explica o efeito do prolog nos registros não voláteis e RSP. Veja a seção sobre UNWIND_CODE para os significados de itens individuais. Para fins de alinhamento, essa matriz sempre tem um número par de entradas, e a entrada final é potencialmente não usada. Nesse caso, a matriz é um a mais do que o indicado pelo campo de contagem de códigos de desenrolamento.

  • Endereço do manipulador de exceções

    Um ponteiro relativo à imagem para a exceção específica do idioma da função ou manipulador de terminação, se o sinalizador UNW_FLAG_CHAININFO estiver claro e um dos sinalizadores UNW_FLAG_EHANDLER ou UNW_FLAG_UHANDLER estiver definido.

  • Dados do manipulador específico do idioma

    Os dados do manipulador de exceções específicos do idioma da função. O formato desses dados não é especificado e completamente determinado pelo manipulador de exceção específico em uso.

  • Informações sobre o desenrolar encadeado

    Se o sinalizador UNW_FLAG_CHAININFO estiver definido, a estrutura UNWIND_INFO termina com três UWORDs. Estes UWORDs representam a informação RUNTIME_FUNCTION para a função do desenrolamento encadeado.

struct UNWIND_CODE

A matriz de código unwind é usada para registrar a sequência de operações no prolog que afetam os registos não voláteis e o RSP. Cada item de código tem este formato:

Tamanho Valor
UBYTE Deslocamento no Prolog
UBYTE: 4 Código de operação de desenrolamento
UBYTE: 4 Informações sobre a operação

A matriz é classificada por ordem decrescente de deslocamento no prólogo.

Deslocamento no Prolog

Deslocamento (do início do prólogo) do final da instrução que executa essa operação, mais 1 (ou seja, o deslocamento do início da próxima instrução).

Código de operação de desenrolamento

Nota: Certos códigos de operação exigem um deslocamento não assinado para um valor no quadro de pilha local. Este deslocamento começa no início, ou seja, no endereço mais baixo da alocação de pilha fixa. Se o campo Registro de quadros no UNWIND_INFO for zero, esse deslocamento será do RSP. Se o campo Registro de quadro for diferente de zero, esse deslocamento será de onde o RSP estava localizado quando o registro FP foi estabelecido. Ele é igual ao registo FP menos o deslocamento do registo FP (16 * o deslocamento do registo de moldura dimensionado no UNWIND_INFO). Se um registro FP for usado, qualquer código de desenrolar que tome um deslocamento só deve ser usado depois que o registro FP for estabelecido no prólogo.

Para todos os opcodes, exceto UWOP_SAVE_XMM128 e UWOP_SAVE_XMM128_FAR, o deslocamento é sempre um múltiplo de 8, porque todos os valores de pilha de interesse são armazenados em limites de 8 bytes (a pilha em si é sempre alinhada a 16 bytes). Para códigos de operação que aceitam um deslocamento curto (menos de 512K), o USHORT final nos nós para este código armazena o deslocamento dividido por 8. Para códigos de operação que utilizam um deslocamento longo (512K <= deslocamento < 4GB), os dois últimos nós USHORT para este código armazenam o deslocamento (no formato little-endian).

Para os opcodes UWOP_SAVE_XMM128 e UWOP_SAVE_XMM128_FAR, o deslocamento é sempre um múltiplo de 16, já que todas as operações XMM de 128 bits devem ocorrer em memória alinhada de 16 bytes. Portanto, um fator de escala de 16 é usado para UWOP_SAVE_XMM128, permitindo compensações de menos de 1M.

O código de operação de desempacotar é um destes valores:

  • UWOP_PUSH_NONVOL (0) 1 nó

    Empurre um registro inteiro não volátil, diminuindo o RSP em 8. A informação da operação é o número do registo. Devido às restrições nos epílogos, UWOP_PUSH_NONVOL os códigos de desempacotamento devem aparecer primeiro no prólogo e, correspondentemente, por último na matriz de códigos de desempacotamento. Esta ordenação relativa aplica-se a todos os outros códigos de desenrolamento, exceto UWOP_PUSH_MACHFRAME.

  • UWOP_ALLOC_LARGE (1) 2 ou 3 nós

    Aloque uma área grande na pilha. Existem duas formas. Se a informação da operação for igual a 0, então o tamanho da alocação dividida por 8 é registrado no slot seguinte, permitindo uma alocação de até 512K - 8. Se a informação da operação for igual a 1, então o tamanho não dimensionado da alocação é registrado nos próximos dois slots no formato little-endian, permitindo alocações de até 4GB - 8.

  • UWOP_ALLOC_SMALL (2) 1 nó

    Aloque uma área de tamanho pequeno na pilha. O tamanho da alocação é o campo de informações da operação * 8 + 8, permitindo alocações de 8 a 128 bytes.

    O código de desenrolar para uma alocação de pilha deve sempre usar a codificação mais curta possível:

    Tamanho da alocação Código de Desenrolamento
    8 a 128 bytes UWOP_ALLOC_SMALL
    136 a 512K-8 bytes UWOP_ALLOC_LARGE, informação da operação = 0
    512K para 4G-8 bytes UWOP_ALLOC_LARGE, informação da operação = 1
  • UWOP_SET_FPREG (3) 1 nó

    Estabeleça o registro de ponteiro do quadro definindo o registro para algum deslocamento do RSP atual. O desvio é igual ao campo de deslocamento (dimensionado) do Registo de Quadros na UNWIND_INFO * 16, permitindo deslocamentos que vão de 0 a 240. O uso de um deslocamento permite estabelecer um ponteiro de quadro que aponta para o meio da alocação de pilha fixa, ajudando a densidade do código ao permitir que mais acessos usem formas de instruções curtas. O campo de informações da operação é reservado e não deve ser usado.

  • UWOP_SAVE_NONVOL (4) 2 nós

    Guarde um registo inteiro não volátil na pilha usando um MOV em vez de um PUSH. Este código é usado principalmente para empacotamento ajustado, onde um registo não volátil é salvo na pilha numa posição que foi alocada anteriormente. A informação da operação é o número do registo. O deslocamento de pilha multiplicado por 8 é registrado na próxima posição de código de desenrolamento, conforme descrito na nota acima.

  • UWOP_SAVE_NONVOL_FAR (5) 3 nós

    Salve um registro inteiro não volátil na pilha com um deslocamento longo, usando um MOV em vez de um PUSH. Este código é usado principalmente para empacotamento ajustado, onde um registo não volátil é salvo na pilha numa posição que foi alocada anteriormente. A informação da operação é o número do registo. O deslocamento de pilha não dimensionado é registrado nos próximos dois slots de código de operação de desenrolamento, conforme descrito na nota acima.

  • UWOP_SAVE_XMM128 (8) 2 nós

    Guarde todos os 128 bits de um registo XMM não volátil na pilha. A informação da operação é o número do registo. O deslocamento de pilha ajustado por 16 é registado no slot seguinte.

  • UWOP_SAVE_XMM128_FAR (9) 3 nós

    Guarde todos os 128 bits de um registo XMM não volátil na pilha com um deslocamento longo. A informação da operação é o número do registo. O deslocamento de pilha não dimensionado é registrado nos próximos dois slots.

  • UWOP_PUSH_MACHFRAME (10) 1 nó

    Empurre uma armação de máquina. Esse código de desenrolar é usado para registrar o efeito de uma interrupção ou exceção de hardware. Existem duas formas. Se as informações da operação forem iguais a 0, um destes quadros foi colocado na pilha de dados:

    Localização Valor
    RSP+32 SS
    RSP+24 Antigo RSP
    RSP+16 FELAGS
    PÁR+8 Ciência da Computação
    DERP PIR

    Se as informações da operação forem iguais a 1, então um destes quadros foi empurrado:

    Localização Valor
    RSP+40 SS
    RSP+32 Antigo RSP
    RSP+24 FELAGS
    RSP+16 Ciência da Computação
    PÁR+8 PIR
    DERP Código de erro

    Esse código de desenrolar sempre aparece em um prólogo fictício, que nunca é realmente executado, mas em vez disso aparece antes do ponto de entrada real de uma rotina de interrupção, e existe apenas para fornecer um lugar para simular o push de um quadro de máquina. UWOP_PUSH_MACHFRAME registra essa simulação, que indica que a máquina fez conceitualmente esta operação:

    1. Retira o endereço de retorno RIP do topo da pilha para dentro de Temp

    2. Pressionar SS

    3. Empurrar RSP antigo

    4. Empurrar EFLAGS

    5. Empurre CS

    6. Pressionar Temp

    7. Código de erro do push (se as informações de operação forem iguais a 1)

    A operação simulada UWOP_PUSH_MACHFRAME diminui o RSP em 40 (op info é igual a 0) ou 48 (op info é igual a 1).

Informações sobre a operação

O significado dos bits de informação da operação depende do código da operação. Para codificar um registro de uso geral (inteiro), este mapeamento é usado:

Pouco Registar-se
0 RAX
1 RCX
2 RDX
3 RBX
4 DERP
5 RBP
6 LER
7 IDI
8 a 15 anos R8 a R15

Estruturas de informação de desenrolamento encadeadas

Se o sinalizador UNW_FLAG_CHAININFO estiver definido, uma estrutura de informações de desenrolar será secundária e o campo de endereço compartilhado do manipulador de exceções/informações encadeadas conterá as informações de desenrolar primárias. Este código de exemplo recupera as informações primárias de desenrolamento, supondo que unwindInfo seja a estrutura que tem o sinalizador UNW_FLAG_CHAININFO definido.

PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo->UnwindCode[( unwindInfo->CountOfCodes + 1 ) & ~1]);

Informação encadeada é útil em duas situações. Primeiro, ele pode ser usado para segmentos de código não contíguos. Ao usar informação encadeada, pode-se reduzir o tamanho da informação de desenrolamento necessária, porque não é necessário duplicar o array de códigos de desenrolamento da informação de desenrolamento primária.

Você também pode usar informações encadeadas para agrupar salvamentos de registro voláteis. O compilador pode atrasar o salvamento de alguns registos voláteis até que esteja fora do prólogo da função. Você pode registá-los com informações de desenrolamento primárias para a parte da função antes do código agrupado e, em seguida, configurando informações encadeadas com um tamanho de prólogo não zero, onde os códigos de desenrolamento nas informações encadeadas refletem armazenamentos dos registros não voláteis. Nesse caso, os códigos de desenrolar são todas as ocorrências de UWOP_SAVE_NONVOL. Não há suporte para um agrupamento que salva registros não voláteis usando um PUSH ou modifica o registro RSP usando uma alocação de pilha fixa adicional.

Um item UNWIND_INFO que tenha UNW_FLAG_CHAININFO definido pode conter uma entrada RUNTIME_FUNCTION em que o item UNWIND_INFO também tem UNW_FLAG_CHAININFO definido, às vezes referido como múltiplo shrink-wrapping. Eventualmente, os ponteiros de informação de desenmaraçamento encadeados chegam a um item UNWIND_INFO que tem UNW_FLAG_CHAININFO desmarcado. Este é o item principal UNWIND_INFO, que aponta para o ponto de entrada efetivo do procedimento.

Procedimento de desenrolar

A matriz de código de desenrolar é classificada em ordem decrescente. Quando ocorre uma exceção, o contexto completo é armazenado pelo sistema operacional em um registro de contexto. A lógica de despacho de exceção é então invocada, que executa repetidamente estas etapas para localizar um manipulador de exceções:

  1. Use o RIP atual armazenado no registro de contexto para procurar uma entrada de tabela RUNTIME_FUNCTION que descreva a função atual (ou parte da função, para entradas de UNWIND_INFO encadeadas).

  2. Se nenhuma entrada na tabela de função for encontrada, estará numa função de folha, e o RSP endereça diretamente o ponteiro de retorno. O ponteiro de retorno em [RSP] é armazenado no contexto atualizado, o RSP simulado é incrementado em 8 e a etapa 1 é repetida.

  3. Se uma entrada de tabela de função for encontrada, o RIP pode estar dentro de três regiões: a) em um epílogo, b) no prólogo, ou c) no código que pode ser coberto por um manipulador de exceções.

    • Caso a) Se o RIP estiver dentro de um epílogo, então o controle está deixando a função, não pode haver manipulador de exceção associado a essa exceção para esta função, e os efeitos do epílogo devem ser continuados para calcular o contexto da função chamador. Para determinar se o RIP está dentro de um epílogo, é examinado o fluxo de código a partir do RIP. Se esse fluxo de código puder ser correspondido à seção final de um epílogo legítimo, então ele estará em um epílogo, e a parte restante do epílogo será simulada, com o registro de contexto atualizado conforme cada instrução é processada. Após este processamento, o passo 1 é repetido.

    • Caso b) Se o RIP estiver dentro do prólogo, então o controle não entrou na função, não pode haver nenhum manipulador de exceção associado a essa exceção para esta função, e os efeitos do prólogo devem ser desfeitos para calcular o contexto da função chamadora. O RIP está dentro do prólogo se a distância entre o início da função e o RIP for menor ou igual ao tamanho do prólogo codificado nas informações de desenrolamento. Os efeitos do prolog são revertidos ao avançar através da matriz de códigos de desenrolar para a primeira entrada com um deslocamento menor ou igual ao deslocamento do RIP desde o início da função, e posteriormente desfazendo o efeito de todos os itens restantes na matriz de códigos de desenrolar. O passo 1 é então repetido.

    • Caso c) Se o RIP não estiver dentro de um prólogo ou epílogo, e a função tiver um manipulador de exceção (UNW_FLAG_EHANDLER está definido), então o manipulador específico da linguagem é chamado. O manipulador verifica seus dados e chama funções de filtro conforme apropriado. O manipulador específico do idioma pode retornar que a exceção foi manipulada ou que a pesquisa deve ser continuada. Ele também pode iniciar um desenrolar diretamente.

  4. Se o manipulador específico do idioma retornar um status manipulado, a execução será continuada usando o registro de contexto original.

  5. Se não houver um manipulador específico do idioma ou se o manipulador retornar um estado de "continuar pesquisa", o registo de contexto deverá ser restaurado ao estado inicial do chamador. Isso é feito processando todos os elementos da matriz de código de desenrolamento, desfazendo o efeito de cada um. O passo 1 é então repetido.

Quando informações encadeadas de unwind estão envolvidas, essas etapas básicas continuam a ser seguidas. A única diferença é que, ao percorrer a matriz de código de desenrolamento para desfazer os efeitos de um prólogo, uma vez alcançado o final da matriz, esta é então ligada às informações de desenrolamento do pai e toda a matriz de código de desenrolamento encontrada lá é percorrida. Esse encadeamento continua até chegar a uma informação de desenrolamento sem o sinalizador UNW_CHAINED_INFO e, então, conclui o processamento da sua matriz de códigos de desenrolamento.

O menor conjunto de dados de desenrolamento é de 8 bytes. Isso representaria uma função que alocou apenas 128 bytes de pilha ou menos e, possivelmente, salvou um registro não volátil. É também do tamanho de uma estrutura de informação de desenrolamento encadeada para um prólogo de comprimento zero sem códigos de desenrolamento.

Manipulador específico do idioma

O endereço relativo do manipulador específico do idioma está presente no UNWIND_INFO sempre que os sinalizadores UNW_FLAG_EHANDLER ou UNW_FLAG_UHANDLER estão definidos. Conforme descrito na seção anterior, o manipulador específico de idioma é chamado como parte da procura por um manipulador de exceção ou como parte do processo de desenrolar. Tem este protótipo:

typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN ULONG64 EstablisherFrame,
    IN OUT PCONTEXT ContextRecord,
    IN OUT PDISPATCHER_CONTEXT DispatcherContext
);

ExceptionRecord fornece um ponteiro para um registro de exceção, que tem a definição padrão do Win64.

EstablisherFrame é o endereço da base da alocação de pilha fixa para esta função.

ContextRecord aponta para o contexto de exceção no momento em que a exceção foi levantada (no caso do manipulador de exceção) ou para o contexto atual de desempacotamento (no caso do manipulador de terminação).

DispatcherContext aponta para o contexto do dispatcher para essa função. Tem a seguinte definição:

typedef struct _DISPATCHER_CONTEXT {
    ULONG64 ControlPc;
    ULONG64 ImageBase;
    PRUNTIME_FUNCTION FunctionEntry;
    ULONG64 EstablisherFrame;
    ULONG64 TargetIp;
    PCONTEXT ContextRecord;
    PEXCEPTION_ROUTINE LanguageHandler;
    PVOID HandlerData;
} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;

ControlPc é o valor de RIP dentro desta função. Este valor é um endereço de exceção ou o endereço onde o controlo saiu da função que o estabeleceu. O RIP é usado para determinar se o controle está dentro de alguma construção protegida dentro dessa função, por exemplo, um __try bloco para __try/__except ou __try/__finally.

ImageBase é a base de imagem (endereço de carregamento) do módulo que contém essa função, a ser adicionada aos deslocamentos de 32 bits usados na entrada da função e nas informações de desenrolamento, para gravar endereços relativos.

FunctionEntry fornece um ponteiro para a entrada da função RUNTIME_FUNCTION que contém a função e desenrola os endereços relativos da base de imagem de informações para esta função.

EstablisherFrame é o endereço da base da alocação de pilha fixa para esta função.

TargetIp Fornece um endereço de instrução opcional que especifica o endereço de continuação do desempacotamento. Esse endereço será ignorado se EstablisherFrame não for especificado.

ContextRecord aponta para o contexto da exceção, para ser usado pelo código de exceção do sistema para despacho/desenrolamento.

LanguageHandler aponta para a rotina do manipulador de linguagem específica que está a ser chamada.

HandlerData aponta para os dados específicos do manipulador de idioma para esta função.

Auxiliares de desenrolamento para MASM

Para escrever rotinas de montagem adequadas, há um conjunto de pseudooperações que podem ser usadas em paralelo com as instruções de montagem reais para criar os .pdata e .xdata apropriados. E há um conjunto de macros que fornecem o uso simplificado das pseudooperações para seus usos mais comuns.

Pseudooperações brutas

Pseudo-operação Descrição
QUADRO PROC [:ehandler] Faz com que o MASM gere uma entrada de tabela de funções em .pdata e informações de desenrolamento em .xdata para o comportamento de desenrolamento em tratamento de exceções estruturadas de uma função. Se ehandler estiver presente, esse proc será inserido no .xdata como o manipulador específico do idioma.

Quando o atributo FRAME é usado, deve ser seguido pela diretiva .ENDPROLOG. Se a função for uma função folha (conforme definido em Tipos de função), o atributo FRAME será desnecessário, assim como o restante dessas pseudooperações.
.PUSHREG Registo Gera uma entrada de código de desmontagem UWOP_PUSH_NONVOL para o número de registo especificado, utilizando o deslocamento atual no prólogo.

Use-o apenas com registros inteiros não voláteis. Para operações de push de registos voláteis, use .ALLOCSTACK 8 em vez disso.
.SETFRAME registo, deslocamento Preenche o campo de registo de frame e o deslocamento nas informações de desenrolamento usando o registo e o deslocamento especificados. O desvio deve ser um múltiplo de 16 e menor ou igual a 240. Esta diretiva também gera uma entrada de código de desenrolar UWOP_SET_FPREG para o registo especificado utilizando a compensação do prólogo atual.
.ALLOCSTACK tamanho Gera um UWOP_ALLOC_SMALL ou um UWOP_ALLOC_LARGE com o tamanho especificado para o deslocamento atual no prólogo.

O operando de tamanho deve ser um múltiplo de 8.
.SAVEREG registo, offset Gera um UWOP_SAVE_NONVOL ou uma entrada de código de desenrolar UWOP_SAVE_NONVOL_FAR para o registro especificado e o deslocamento usando o deslocamento do prólogo atual. O MASM escolhe a codificação mais eficiente.

O deslocamento deve ser positivo e um múltiplo de 8. O deslocamento é relativo à base do quadro do procedimento, que geralmente está no RSP, ou, se estiver usando um ponteiro de quadro, o ponteiro de quadro não dimensionado.
.SAVEXMM128 registo, deslocamento Gera uma entrada de código de desenrolar UWOP_SAVE_XMM128 ou UWOP_SAVE_XMM128_FAR para o registro XMM especificado e o deslocamento usando o deslocamento do prólogo atual. O MASM escolhe a codificação mais eficiente.

O deslocamento deve ser positivo e um múltiplo de 16. O deslocamento é relativo à base do quadro do procedimento, que geralmente está no RSP, ou, se estiver usando um ponteiro de quadro, o ponteiro de quadro não dimensionado.
. PUSHFRAME [código] Gera uma entrada de código de desenrolar UWOP_PUSH_MACHFRAME. Se o código opcional for especificado, a entrada do código de desmontagem receberá um modificador de 1. Caso contrário, o modificador é 0.
. ENDPROLOG Assinala o fim das declarações do prólogo. Deve ocorrer nos primeiros 255 bytes da função.

Aqui está um exemplo de prólogo de função com o uso adequado da maioria dos códigos de operação.

sample PROC FRAME
    db      048h; emit a REX prefix, to enable hot-patching
    push rbp
    .pushreg rbp
    sub rsp, 040h
    .allocstack 040h
    lea rbp, [rsp+020h]
    .setframe rbp, 020h
    movdqa [rbp], xmm7
    .savexmm128 xmm7, 020h ;the offset is from the base of the frame
                           ;not the scaled offset of the frame
    mov [rbp+018h], rsi
    .savereg rsi, 038h
    mov [rsp+010h], rdi
    .savereg rdi, 010h ; you can still use RSP as the base of the frame
                       ; or any other register you choose
    .endprolog

; you can modify the stack pointer outside of the prologue (similar to alloca)
; because we have a frame pointer.
; if we didn't have a frame pointer, this would be illegal
; if we didn't make this modification,
; there would be no need for a frame pointer

    sub rsp, 060h

; we can unwind from the next AV because of the frame pointer

    mov rax, 0
    mov rax, [rax] ; AV!

; restore the registers that weren't saved with a push
; this isn't part of the official epilog, as described in section 2.5

    movdqa xmm7, [rbp]
    mov rsi, [rbp+018h]
    mov rdi, [rbp-010h]

; Here's the official epilog

    lea rsp, [rbp+020h] ; deallocate both fixed and dynamic portions of the frame
    pop rbp
    ret
sample ENDP

Para obter mais informações sobre o exemplo de epilog, consulte Código Epilog em x64 prolog e epilog.

Macros MASM

Para simplificar o uso das pseudooperações Raw, há um conjunto de macros, definidas em ksamd64.inc, que podem ser usadas para criar prólogos e epílogos de procedimentos típicos.

Macro Descrição
alloc_stack(n) Aloca um quadro de pilha de n bytes (usando sub rsp, n) e emite as informações de desenrolamento apropriadas (.allocstack n)
save_reg reg, loc Salva um reg de registro não volátil na pilha no loc de deslocamento RSP e emite as informações de desenrolamento apropriadas. (.savereg reg, loc)
push_reg reg Empurra um reg de registro não volátil na pilha e emite as informações de desenrolamento apropriadas. (.pushreg reg)
rex_push_reg reg Salva um registo não volátil na pilha usando uma instrução push de 2 bytes e emite as informações apropriadas de desenrolamento (.pushreg reg). Use a macro se o push for a primeira instrução na função, para garantir que a função seja aplicável a patches dinâmicos.
save_xmm128 reg, loc Salva um registo XMM não volátil reg na pilha no deslocamento RSP loc e emite as informações de desenrolamento apropriadas (.savexmm128 reg, loc)
set_frame reg, deslocamento Define o registro de quadro reg como RSP + deslocamento (usando um mov, ou um lea), e emite as informações de desenrolar apropriadas (.set_frame reg, offset)
push_eflags Empurra os eflags com uma instrução pushfq e emite a informação de desenrolamento apropriada (.alloc_stack 8)

Aqui está um exemplo de prólogo de função com o uso adequado das macros:

sampleFrame struct
    Fill     dq ?; fill to 8 mod 16
    SavedRdi dq ?; Saved Register RDI
    SavedRsi dq ?; Saved Register RSI
sampleFrame ends

sample2 PROC FRAME
    alloc_stack(sizeof sampleFrame)
    save_reg rdi, sampleFrame.SavedRdi
    save_reg rsi, sampleFrame.SavedRsi
    .end_prolog

; function body

    mov rsi, sampleFrame.SavedRsi[rsp]
    mov rdi, sampleFrame.SavedRdi[rsp]

; Here's the official epilog

    add rsp, (sizeof sampleFrame)
    ret
sample2 ENDP

Desdobrar definições de dados em C

Aqui está uma descrição C dos dados de desmontagem:

typedef enum _UNWIND_OP_CODES {
    UWOP_PUSH_NONVOL = 0, /* info == register number */
    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
    UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;

typedef unsigned char UBYTE;

typedef union _UNWIND_CODE {
    struct {
        UBYTE CodeOffset;
        UBYTE UnwindOp : 4;
        UBYTE OpInfo   : 4;
    };
    USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;

#define UNW_FLAG_EHANDLER  0x01
#define UNW_FLAG_UHANDLER  0x02
#define UNW_FLAG_CHAININFO 0x04

typedef struct _UNWIND_INFO {
    UBYTE Version       : 3;
    UBYTE Flags         : 5;
    UBYTE SizeOfProlog;
    UBYTE CountOfCodes;
    UBYTE FrameRegister : 4;
    UBYTE FrameOffset   : 4;
    UNWIND_CODE UnwindCode[1];
/*  UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
*   union {
*       OPTIONAL ULONG ExceptionHandler;
*       OPTIONAL ULONG FunctionEntry;
*   };
*   OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, *PUNWIND_INFO;

typedef struct _RUNTIME_FUNCTION {
    ULONG BeginAddress;
    ULONG EndAddress;
    ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;

#define GetUnwindCodeEntry(info, index) \
    ((info)->UnwindCode[index])

#define GetLanguageSpecificDataPtr(info) \
    ((PVOID)&GetUnwindCodeEntry((info),((info)->CountOfCodes + 1) & ~1))

#define GetExceptionHandler(base, info) \
    ((PEXCEPTION_HANDLER)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))

#define GetChainedFunctionEntry(base, info) \
    ((PRUNTIME_FUNCTION)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))

#define GetExceptionDataPtr(info) \
    ((PVOID)((PULONG)GetLanguageSpecificData(info) + 1))

Ver também

Convenções de software x64