Tratamento de exceções x64

Uma visão geral do tratamento de exceções estruturado e das convenções e comportamentos de código no tratamento de exceções do C++ no x64. Para obter informações gerais sobre o tratamento de exceções, consulte Tratamento de exceções no Microsoft C++.

Desativar dados para tratamento de exceções e suporte ao depurador

Para recuperar registros não voláteis quando uma exceção é tratada, as funções não-folha são anotadas com dados estáticos. Esses dados, comumente referidos como "Informações de Desempilhamento da Função", descrevem como desempilhar corretamente a função a partir de uma instrução arbitrária. Esses dados são armazenados como pdata ou dados de procedimento, que, por sua vez, se referem a xdata, os dados de tratamento de exceções.

As informações de desenrolamento de função são compostas por várias estruturas de dados, descritas em seguida.

Para obter informações de desenrolamento que dão suporte ao INTEL APX (Extensões avançadas de desempenho), consulte a especificação de versão prévia do Unwind V3.

struct RUNTIME_FUNCTION

O tratamento de exceções baseado em tabela requer uma entrada na tabela para cada função que aloca espaço de pilha ou chama outra função, como as funções não-folha, por exemplo. 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 Endereço das informações de desenrolamento

A RUNTIME_FUNCTION estrutura deve ser DWORD alinhada 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 ordenadas e colocadas na seção .pdata de uma imagem PE32+. Para funções geradas dinamicamente [compiladores JIT], o runtime para dar suporte a essas funções deve usar RtlInstallFunctionTableCallback ou RtlAddFunctionTable fornecer essas informações ao sistema operacional. Não fazer isso resulta em um tratamento de exceções não confiável e na depuração de processos.

struct UNWIND_INFO

A estrutura de informações de desenrolamento de dados registra os efeitos que uma função tem no ponteiro de pilha e onde os registros não ativos são salvos na pilha:

Tamanho Valor
UBYTE: 3 Versão
UBYTE: 5 Sinalizadores
UBYTE Tamanho do prólogo
UBYTE Contagem de códigos de desenrolamento
UBYTE: 4 Registro de Frames
UBYTE: 4 Deslocamento do Registrador de Frame (escalonado)
USHORT * n Matriz de códigos de desempacotamento
variável Pode ser na forma (1) ou (2) abaixo

(1) Manipulador de Exceção

Tamanho Valor
ULONG Endereço do manipulador de exceção
variável Dados específicos do manipulador de linguagem (opcional)

(2) Informações de desenrolamento encadeadas

Tamanho Valor
ULONG Endereço de início da função
ULONG Endereço final da função
ULONG Endereço das informações de desenrolamento

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

  • Versão

    Número de versão dos dados de desenrolamento; atualmente 1.

  • Sinalizadores

    Três sinalizadores estão definidos atualmente:

    Sinalizador Descrição
    UNW_FLAG_EHANDLER A função tem um manipulador de exceção que o sistema operacional chama para examinar o estado da exceção e potencialmente tratá-la. Recursos de linguagem, como a cláusula C __try , registram esse manipulador.
    UNW_FLAG_UHANDLER A função tem um manipulador de terminação que o sistema de operações chama ao descontrair a pilha. Esse manipulador pode liberar recursos alocados pela função em código seguro de exceção. Recursos da linguagem, como destrutores locais de objetos C++ e cláusulas C __finally, registram esse manipulador de terminação.
    UNW_FLAG_CHAININFO Essa estrutura de informações de desenrolamento não é a principal para o procedimento. Em vez disso, a entrada de informações de desenrolamento encadeada é o conteúdo de uma entrada anterior RUNTIME_FUNCTION . Para obter informação, consulte Estruturas de informação de desenrolamento encadeadas. Se este sinalizador estiver ativado, então os sinalizadores UNW_FLAG_EHANDLER e UNW_FLAG_UHANDLER devem ser desativados. Além disso, o registrador de moldura e os campos de alocação de pilha fixa devem ter os mesmos valores que nas informações primárias de desenrolamento.
  • Tamanho do prólogo

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

  • Contagem de códigos de desmontagem

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

  • Registro de quadros

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

  • Deslocamento do registro de quadros (escalonado)

    Esse campo é um deslocamento escalonado entre o valor do registrador RSP e o valor do registrador Ponteiro de Quadro (FP) selecionado. O registro FP selecionado é definido como RSP + 16 * esse valor, o que significa que você pode usar deslocamentos de 0 a 240. Esse deslocamento aponta o registro FP para o meio da alocação de pilha local para quadros de pilha dinâmicos, para que você obtenha melhor densidade de código por meio de instruções mais curtas. (Ou seja, mais instruções podem usar o formato de deslocamento de 8 bits com sinal).

  • Matriz de códigos de desenrolamento

    Uma lista de itens que explica o efeito do prólogo nos registradores não voláteis e RSP. Consulte a seção sobre o código das operações de unwind para ver o significado de cada item. Para manter o alinhamento de dados correto, esse vetor sempre contém um número par de entradas, e a entrada final pode não ser utilizada. Nesse caso, a matriz fica mais longa do que o indicado pela contagem de campos de códigos de desenrolamento.

  • Endereço do manipulador de exceção

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

  • Dados do manipulador específico da linguagem

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

  • Informações de Desempacotamento Encadeado

    Se o sinalizador UNW_FLAG_CHAININFO estiver definido, a UNWIND_INFO estrutura terminará com três UWORDs. Esses UWORDs representam as informações RUNTIME_FUNCTION da função de desenrolamento encadeado.

struct UNWIND_CODE

Use a matriz de códigos de desenrolamento para registrar a sequência de operações no prólogo que afetam os registradores não voláteis e RSP. Cada item de código tem esse formato:

Tamanho Valor
UBYTE Deslocamento no prólogo
UBYTE: 4 Código de operação de Unwind
UBYTE: 4 Informações da operação

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

Deslocamento no prólogo

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 Unwind

Determinados códigos de operação exigem um deslocamento sem sinal para um valor no quadro da pilha local. Esse deslocamento é do início, ou seja, o endereço mais baixo da alocação de pilha fixa. Se o campo Registrador de quadro em UNWIND_INFO for zero, esse deslocamento será a partir de RSP. Se o campo Registro de Quadros não for zero, esse deslocamento será de onde RSP foi localizado quando o registro FP foi estabelecido. É igual ao registro FP menos o deslocamento do registro FP (16 * o deslocamento do registro de quadro escalonado em UNWIND_INFO). Se um registro FP for usado, qualquer código de desenrolamento que envolva um deslocamento só deverá 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 interesse da pilha são armazenados em limites de 8 bytes (a pilha em si é sempre alinhada a 16 bytes). Para códigos de operação que exigem um deslocamento curto (inferior a 512 K), o elemento final USHORT nos nós desse código de operação contém o deslocamento dividido por 8. Para códigos de operação que usam um deslocamento longo (512K <= deslocamento < 4 GB), os dois nós USHORT finais desse código armazenam o deslocamento (em formato little-endian).

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

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

  • UWOP_PUSH_NONVOL (0) 1 nó

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

  • UWOP_ALLOC_LARGE (1) 2 ou 3 nós

    Alocar uma área grande na pilha. Existem duas formas. Se as informações de operação forem iguais a 0, o tamanho da alocação dividida por 8 será registrado no slot seguinte, permitindo uma alocação de até 512 K - 8. Se as informações da operação forem iguais a 1, o tamanho não escalonado da alocação será registrado nos dois slots seguintes no formato little-endian, permitindo alocações de até 4 GB - 8.

  • UWOP_ALLOC_SMALL (2) 1 nó

    Alocar uma pequena área 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 desenrolamento de uma alocação de pilha deve sempre usar a codificação mais curta possível:

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

    Defina o registrador de ponteiro de quadro configurando-o com um deslocamento em relação ao RSP atual. O deslocamento é igual ao campo de deslocamento do registrador de quadro (escalado) em UNWIND_INFO * 16, permitindo deslocamentos 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 de código, permitindo que mais acessos usem formatos de instrução curtos. O campo de informações de operação é reservado e não deve ser usado.

  • UWOP_SAVE_NONVOL (4) 2 nós

    Salvar um registro inteiro não volátil na pilha usando um MOV em vez de um PUSH. Esse código é usado primariamente para encapsulamento reduzido, em que um registro não volátil é salvo na pilha em uma posição que foi alocada anteriormente. A informação da operação é o número do registro. O deslocamento de pilha escalonado por 8 é registrado no próximo slot do código de operação de desenrolamento, conforme descrito na nota acima.

  • UWOP_SAVE_NONVOL_FAR (5) 3 nós

    Salvar um registro inteiro não volátil na pilha com um deslocamento longo usando um MOV em vez de um PUSH. Esse código é usado primariamente para encapsulamento reduzido, em que um registro não volátil é salvo na pilha em uma posição que foi alocada anteriormente. A informação da operação é o número do registro. O deslocamento de pilha não escalonado é registrado nos próximos dois slots do código de operação de desenrolamento, conforme descrito na nota acima.

  • UWOP_SAVE_XMM128 (8) 2 nós

    Salvar todos os 128 bits de um registro não volátil XMM na pilha. A informação da operação é o número do registro. O deslocamento de pilha multiplicado por 16 é registrado no próximo slot.

  • UWOP_SAVE_XMM128_FAR (9) 3 nós

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

  • UWOP_PUSH_MACHFRAME (10) 1 nó

    Efetuar push de um quadro do computador. Esse código de desenrolamento registra o efeito de uma interrupção ou exceção de hardware. Ele tem dois formulários. Um valor de 0 indica que o hardware empilhou um quadro como este na pilha:

    Localidade Valor
    RSP+32 SS
    RSP+24 Velho RSP
    RSP+16 EFLAGS
    RSP+8 CS
    RSP RIP

    Um valor de 1 indica que o hardware empilhou um quadro como este na pilha:

    Localidade Valor
    RSP+40 SS
    RSP+32 Velho RSP
    RSP+24 EFLAGS
    RSP+16 CS
    RSP+8 RIP
    RSP Código do erro

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

    1. Remova RIP o endereço de retorno do topo da pilha e coloque-o em Temp

    2. Pressione SS

    3. Efetuar push antigo RSP

    4. Pressionar EFLAGS

    5. Pressione CS

    6. Push Temp

    7. Código de Erro de Push (se op info for igual a 1)

    A operação simulada UWOP_PUSH_MACHFRAME decrementa RSP em 40 (se as informações da operação forem iguais a 0) ou em 48 (se as informações da operação forem iguais a 1).

Informações da operação

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

bit Registrar-se
0 RAX
1 RCX
2 RDX
3 RBX
4 RSP
5 RBP
6 RSI
7 RDI
8 a 15 R8 a R15

Estruturas de informação de desenrolamento encadeadas

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

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

Informações encadeadas são úteis em duas situações. Primeiro, elas podem ser usadas para segmentos de código não contíguos. Usando informações encadeadas, você pode reduzir o tamanho das informações de desenrolamento necessárias, pois não precisa duplicar a matriz de códigos de desenrolamento das informações de desenrolamento primárias.

Você também poderá usar informações encadeadas para agrupar salvamentos de registro volátil. O compilador pode atrasar o salvamento de alguns registros voláteis até sair do prólogo de entrada da função. Você pode representá-las usando informações primárias de desempilhamento para a parte da função anterior ao código agrupado e, em seguida, configurar informações encadeadas cujo tamanho do prólogo seja diferente de zero, em que os códigos de desempilhamento nas informações encadeadas refletem os salvamentos dos registradores não voláteis. Nesse caso, os códigos de desenrolamento são todas instâncias de UWOP_SAVE_NONVOL. Não há suporte para uma agrupação que salva registros não voláteis com o uso de um PUSH ou modifica o registrador RSP com o uso de uma alocação adicional fixa de pilha.

Um item UNWIND_INFO que tenha UNW_FLAG_CHAININFO definido pode conter uma entrada RUNTIME_FUNCTION cujo item UNWIND_INFO também tenha UNW_FLAG_CHAININFO definido, às vezes denominado shrink-wrap múltiplo. Por fim, os ponteiros encadeados de informações de desempilhamento apontam para um item UNWIND_INFO com UNW_FLAG_CHAININFO desmarcado. Este é o item primário UNWIND_INFO, que aponta para o ponto de entrada real do procedimento.

Procedimento de desenrolar

A matriz de código de desenrolamento é classificada em ordem decrescente. Quando ocorre uma exceção, o sistema operacional armazena o contexto completo em um registro de contexto. Em seguida, a lógica de expedição de exceção é invocada, que executa repetidamente estas etapas para localizar um manipulador de exceção:

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

  2. Se a pesquisa não encontrar uma entrada de tabela de funções, o código será considerado parte de uma função folha e RSP abordará 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 a pesquisa encontrar uma entrada de tabela de funções, RIP poderá estar dentro de três regiões: a) em um epilog, b) no prolog ou c) no código que pode ser coberto por um manipulador de exceção.

    • Caso a) Se o RIP estiver dentro de um epílogo, o controle está saindo da função. Não pode haver nenhum manipulador de exceção associado a essa exceção para essa função. Os efeitos do epílogo devem continuar a computar o contexto da função chamadora. Para determinar se o RIP está dentro de um epílogo, o fluxo de código de RIP em diante é analisado. Se esse fluxo de código corresponde à porção final de um epílogo legítimo, ele está em um epílogo. A parte restante do epílogo é simulada, com o registro de contexto atualizado conforme cada instrução é processada. Após esse processamento, a etapa 1 será repetida.

      • Caso b) Se o RIP estiver dentro do prólogo, o controle ainda não entrou na função. Não pode haver nenhum manipulador de exceção associado a essa exceção para essa função. 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 do início da função até RIP for menor ou igual ao tamanho do prólogo codificado nas informações de desempilhamento. O mecanismo de desenrolamento percorre a matriz de códigos de desenrolamento até encontrar a primeira entrada com um deslocamento menor ou igual ao deslocamento do RIP em relação ao início da função e, em seguida, desfaz o efeito de todos os itens restantes na matriz de códigos de desenrolamento. Em seguida, a etapa 1 é repetida.
    • Caso c) Se o RIP não estiver em um prólogo ou epílogo, e a função tiver um manipulador de exceção (UNW_FLAG_EHANDLER está definido), o manipulador específico da linguagem será chamado. O manipulador examina seus dados e chama funções de filtro conforme apropriado. O manipulador específico da linguagem pode retornar que a exceção foi tratada ou que a pesquisa deve continuar. Ele também pode iniciar um desenrolamento diretamente.

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

  5. Se não houver um manipulador específico para o idioma, ou se o manipulador retornar um status de "continuar a busca", o registro de contexto deve ser restaurado ao estado do chamador. O desenrolador desfaz o efeito de cada elemento na matriz de código de desenrolamento. Em seguida, a etapa 1 é repetida.

Quando informações de desenrolamento encadeadas estão envolvidas, essas etapas básicas ainda são seguidas. A única diferença é que, ao percorrer o vetor de código de desempilhamento para desfazer os efeitos de um prólogo, quando o processo chega ao fim do vetor, ele faz referência às informações de desempilhamento pai e percorre todo o vetor de código de desempilhamento encontrado ali. Essa vinculação continua até chegar a uma informação de desenrolamento sem o UNW_CHAINED_INFO sinalizador e, em seguida, termina de percorrer sua matriz de código de desenrolamento.

O menor conjunto de dados de desenrolamento é de 8 bytes. Esse conjunto representa uma função que aloca apenas 128 bytes de pilha ou menos e possivelmente salva um registrador não volátil. Esse também é o tamanho de uma estrutura de informações de desenrolamento encadeada para um prólogo de comprimento zero sem códigos de desenrolamento.

Manipulador específico da linguagem

A estrutura UNWIND_INFO fornece o endereço relativo do manipulador específico do idioma quando os sinalizadores UNW_FLAG_EHANDLER ou UNW_FLAG_UHANDLER estão definidos. Conforme descrito na seção anterior, a pesquisa por um manipulador de exceção ou o processo de desenrolamento chama o manipulador específico do idioma. O manipulador usa 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 base da alocação de pilha fixa para essa função.

ContextRecord aponta para o contexto da exceção no momento em que a exceção foi gerada (no caso do manipulador de exceção) ou o contexto atual de "desenrolamento" (no caso do manipulador de encerramento).

DispatcherContext aponta para o contexto do dispatcher dessa função. Ele tem essa 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. Esse valor é um endereço de exceção ou o endereço onde o controle deixou a função de configuração. O RIP é usado para determinar se o controle está dentro de algum constructo protegido dentro dessa função, por exemplo, um __try bloco para__try/__except ou .__try/__finally

ImageBase é a base de imagem (endereço de carga) do módulo que contém essa função. Os deslocamentos de 32 bits usados na entrada da função e as informações de desenrolamento devem ser adicionados ao ImageBase para obter o endereço final.

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

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

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

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

LanguageHandler aponta para a rotina de manipulação de linguagem específica que está sendo chamada.

HandlerData aponta para os dados do manipulador específico da linguagem para essa função.

Auxiliares de desempacotamento para MASM

Para escrever rotinas de assembly adequadas, use um conjunto de pseudooperações juntamente com as instruções de assembly reais. Essas pseudo-operações criam o apropriado .pdata e .xdata. Além disso, use um conjunto de macros que simplificam o uso dessas pseudo-operações para seus usos mais comuns.

Pseudo-operações brutas

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

Ao usar o atributo FRAME, use-o seguido da 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 registro Gera uma entrada de código de desacoplamento UWOP_PUSH_NONVOL para o número de registro especificado usando o deslocamento atual no prólogo.

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

O operando size deve ser um múltiplo de 8.
.SAVEREG registrador, deslocamento Gera uma UWOP_SAVE_NONVOL ou uma UWOP_SAVE_NONVOL_FAR entrada de código de desenrolamento para o registro e deslocamento especificados usando o deslocamento de prólogo atual. O MASM escolhe a codificação mais eficiente.

O offset deve ser positivo e um múltiplo de 8. deslocamento é relativo à base do frame do procedimento, que geralmente está em RSP, ou, se estiver sendo usado um ponteiro de frame, ao ponteiro de frame sem escala.
. SAVEXMM128 registro, deslocamento Gera uma entrada de código de desempilhamento UWOP_SAVE_XMM128 ou UWOP_SAVE_XMM128_FAR para o registro XMM e o deslocamento especificado, com base no deslocamento atual do prólogo. O MASM escolhe a codificação mais eficiente.

O offset deve ser positivo e um múltiplo de 16. deslocamento é relativo à base do quadro do procedimento, que geralmente está em 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 unwind UWOP_PUSH_MACHFRAME. Se você especificar o código opcional, a entrada de código de desenrolamento obterá um modificador de 1. Caso contrário, o modificador será 0.
.ENDPROLOG Sinaliza o término das declarações do prólogo. Deve ocorrer nos primeiros 255 bytes da função.

Veja um prólogo de função de exemplo com o uso adequado da maioria dos opcodes:

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 epílogo, veja o Código de epílogo em Prólogo e epílogo do x64.

Macros MASM

Para simplificar o uso de pseudo-operações brutas, use o conjunto de macros definido em ksamd64.inc. Essas macros ajudam você a criar prólogos e epílogos de procedimento 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 o registrador não volátil reg na pilha no deslocamento RSPloc e emite as informações de desempilhamento apropriadas (.savereg reg, loc)
reg push_reg reg Empilha o registrador não volátil reg na pilha e emite as informações apropriadas de desempilhamento (.pushreg reg)
rex_push_reg reg Salva um registro não volátil na pilha por meio de um push de 2 bytes e emite as informações de desempilhamento apropriadas (.pushreg reg). Use essa macro se o push for a primeira instrução na função, para garantir que a função seja passível de aplicação de patch instantâneo.
save_xmm128 reg, loc Salva um XMM registrador não volátil reg na pilha, no deslocamento RSPloc, e emite as informações de desempilhamento apropriadas (.savexmm128 reg, loc)
set_frame reg, deslocamento Define o reg de registro de quadro como o RSP + deslocamento (usando um mov ou um lea) e emite as informações apropriadas de desenrolamento (.set_frame reg, offset)
push_eflags Envia por push as eflags usando uma pushfq instrução e emite as informações de desenrolamento apropriadas (.alloc_stack 8)

Aqui está um prólogo de função de exemplo 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

Expandir definições de dados em C

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

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))

Confira também

Convenções de software x64