Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
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_EHANDLERA 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_UHANDLERA 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_CHAININFOEssa 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 sinalizadoresUNW_FLAG_EHANDLEReUNW_FLAG_UHANDLERdevem 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
RSPe o valor do registrador Ponteiro de Quadro (FP) selecionado. O registro FP selecionado é definido comoRSP+ 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_CHAININFOestiver limpo e um dos sinalizadoresUNW_FLAG_EHANDLERouUNW_FLAG_UHANDLERestiver 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_CHAININFOestiver definido, aUNWIND_INFOestrutura terminará com trêsUWORDs. EssesUWORDs representam as informaçõesRUNTIME_FUNCTIONda 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
RSPem 8. A informação da operação é o número do registro. Devido às restrições em epílogos, os códigos de desenrolamentoUWOP_PUSH_NONVOLdevem 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', excetoUWOP_PUSH_MACHFRAME.UWOP_ALLOC_LARGE(1) 2 ou 3 nósAlocar 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_SMALL136 a 512 K - 8 bytes UWOP_ALLOC_LARGE, informações da operação = 0512K a 4 G - 8 bytes UWOP_ALLOC_LARGE, informações da operação = 1UWOP_SET_FPREG(3) 1 nóDefina o registrador de ponteiro de quadro configurando-o com um deslocamento em relação ao
RSPatual. O deslocamento é igual ao campo de deslocamento do registrador de quadro (escalado) emUNWIND_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ósSalvar 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ósSalvar 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ósSalvar todos os 128 bits de um registro não volátil
XMMna 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ósSalva todos os 128 bits de um registro não volátil
XMMna 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+32SSRSP+24Velho RSPRSP+16EFLAGSRSP+8CSRSPRIPUm valor de 1 indica que o hardware empilhou um quadro como este na pilha:
Localidade Valor RSP+40SSRSP+32Velho RSPRSP+24EFLAGSRSP+16CSRSP+8RIPRSPCó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_MACHFRAMEregistra essa simulação, que indica que o computador fez essa operação conceitualmente:Remova
RIPo endereço de retorno do topo da pilha e coloque-o em TempPressione
SSEfetuar push antigo
RSPPressionar
EFLAGSPressione
CSPush Temp
Código de Erro de Push (se op info for igual a 1)
A operação simulada
UWOP_PUSH_MACHFRAMEdecrementaRSPem 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:
Use o
RIPatual armazenado no registro de contexto para procurar uma entrada na tabelaRUNTIME_FUNCTIONque descreva a função atual (ou parte da função, para entradasUNWIND_INFOencadeadas).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
RSPabordará diretamente o ponteiro de retorno. O ponteiro de retorno em [RSP] é armazenado no contexto atualizado, oRSPsimulado é incrementado em 8, e a etapa 1 é repetida.Se a pesquisa encontrar uma entrada de tabela de funções,
RIPpoderá 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
RIPestiver 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 oRIPestá dentro de um epílogo, o fluxo de código deRIPem 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
RIPestiver 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. ORIPestá dentro do prólogo se a distância do início da função atéRIPfor 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 doRIPem 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 b) Se o
Caso c) Se o
RIPnão estiver em um prólogo ou epílogo, e a função tiver um manipulador de exceção (UNW_FLAG_EHANDLERestá 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.
Se o manipulador específico do idioma retornar um status manipulado, a execução continuará usando o registro de contexto original.
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))