Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Información general sobre el control estructurado de excepciones y las convenciones de codificación y el comportamiento del control de excepciones de C++ en x64. Para obtener información general sobre el control de excepciones, vea Control de excepciones en Microsoft C++.
Desenredado de datos para el control de excepciones y la compatibilidad con el depurador
Para recuperar registros no volátiles cuando se controla una excepción, las funciones que no son no hoja se anotan con datos estáticos. Estos datos, comúnmente denominados "Información de desenredado de funciones", describen cómo desenredar correctamente la función en una instrucción arbitraria. Estos datos se almacenan como pdata o datos de procedimiento, que a su vez hacen referencia a xdata, los datos de control de excepciones.
La información de desenredado de funciones se compone de varias estructuras de datos, que se describen a continuación.
Para obtener información de desenredado compatible con Intel APX (extensiones de rendimiento avanzada), consulte la especificación de versión preliminar de Desenredado V3.
estructura RUNTIME_FUNCTION
El control de excepciones basado en tablas requiere una entrada de tabla para todas las funciones que asignan espacio de pila o llaman a otra función (por ejemplo, funciones no hoja). Las entradas de la tabla de funciones tienen el formato:
| Tamaño | Valor |
|---|---|
| ULONG | Dirección de inicio de la función |
| ULONG | Dirección final de la función |
| ULONG | Dirección de información de desenlace |
La RUNTIME_FUNCTION estructura debe estar DWORD alineada en memoria. Todas las direcciones son relativas a la imagen, es decir, son desplazamientos de 32 bits con respecto a la dirección inicial de la imagen que contiene la entrada de la tabla de funciones. Estas entradas se ordenan y se colocan en la .pdata sección de una imagen PE32+. Para las funciones generadas dinámicamente [compiladores JIT], el tiempo de ejecución para admitir estas funciones debe usar RtlInstallFunctionTableCallback o RtlAddFunctionTable para proporcionar esta información al sistema operativo. No hacerlo da lugar a un manejo de excepciones y una depuración de procesos poco fiables.
estructura UNWIND_INFO
La estructura de información de desenrollado registra los efectos que una función tiene sobre el puntero de pila y dónde se almacenan en la pila los registros no volátiles:
| Tamaño | Valor |
|---|---|
| UBYTE: 3 | Versión |
| UBYTE: 5 | Banderas |
| UBYTE | Tamaño del prólogo |
| UBYTE | Recuento de códigos de desenredado |
| UBYTE: 4 | Registro de marco |
| UBYTE: 4 | Registro de marco desplazado (a escala) |
| USHORT * n | Matriz de códigos de desempaquetado |
| variable | Puede tener el formato (1) o (2) siguiente |
(1) Controlador de excepciones
| Tamaño | Valor |
|---|---|
| ULONG | Dirección del controlador de excepciones |
| variable | Datos del controlador específicos del idioma (opcional) |
(2) Información de desenredado encadenada
| Tamaño | Valor |
|---|---|
| ULONG | Dirección de inicio de la función |
| ULONG | Dirección final de la función |
| ULONG | Dirección de información de desenlace |
La estructura UNWIND_INFO debe estar alineada DWORD en memoria. Este es el significado de cada fila:
Versión
Número de versión de los datos de desenlace, que actualmente es 1.
Banderas
Actualmente hay tres marcas definidas:
Marca Descripción UNW_FLAG_EHANDLERLa función tiene un controlador de excepciones al que llama el sistema operativo para examinar el estado de la excepción y controlarlo potencialmente. Las características del lenguaje, como la cláusula C __try, registran este controlador.UNW_FLAG_UHANDLERLa función tiene un controlador de terminación al que llama el sistema operativo al desenredar la pila. Este controlador podría liberar los recursos asignados por la función en código seguro de excepciones. Las funcionalidades del lenguaje, como los destructores de objetos locales de C++ y las cláusulas de C __finally, registran dicho controlador de terminación.UNW_FLAG_CHAININFOEsta estructura de información de desenredado no es la principal del procedimiento. En su lugar, la entrada de información de desenredado encadenada es el contenido de una entrada anterior RUNTIME_FUNCTION. Para obtener información, vea Estructuras encadenadas de información de desenredado. Si se establece este indicador, los indicadoresUNW_FLAG_EHANDLERyUNW_FLAG_UHANDLERdeben desactivarse. Además, el registro de marco y los campos de asignación de pila fija deben tener los mismos valores que en la información de desenredado principal.Tamaño del prólogo
Longitud del prólogo de la función en bytes.
Recuento de códigos de desempaquetado
Número de ranuras de la matriz de códigos de desenredado. Algunos códigos de desenredado, como
UWOP_SAVE_NONVOL, requieren más de una ranura en la matriz.Registro de marco
Si no es cero, la función usa un puntero de marco (FP) y este campo es el número del registro no volátil que se usa como puntero de marco, utilizando la misma codificación para el campo de información de operación de
UNWIND_CODElos nodos.Desplazamiento del registro de cuadro (escalado)
Este campo es un desplazamiento escalado entre el valor del registro
RSPy el valor del registro de puntero de marco (FP) seleccionado. El registro FP seleccionado se configura comoRSPmás 16 * este número, lo que significa que puede usar desplazamientos entre 0 y 240. Este desplazamiento sitúa el registro FP en medio de la reserva local en la pila para marcos de pila dinámicos, lo que permite obtener una mejor densidad de código gracias a instrucciones más cortas. (Es decir, más instrucciones pueden usar el formato de desplazamiento de 8 bits con signo).Matriz de códigos de desenrollado
Un conjunto de elementos que explica el efecto del prólogo sobre los registros no volátiles y
RSP. Consulte la sección sobre el código de operaciones de desenredado para conocer los significados de los elementos individuales. Para mantener la alineación de datos adecuada, esta matriz siempre contiene un número par de entradas y la entrada final puede no usarse. En ese caso, la matriz es una longitud superior a la indicada por el campo de recuento de códigos de desenredado.Dirección del controlador de excepciones
Puntero relativo a la imagen a la excepción o al controlador de terminación específico del lenguaje de la función, si la marca
UNW_FLAG_CHAININFOestá desactivada y una de las marcasUNW_FLAG_EHANDLERoUNW_FLAG_UHANDLERse establece.Datos del controlador específico del lenguaje
Datos específicos del lenguaje del controlador de excepciones de la función. El formato de estos datos no está especificado y se determina en su totalidad por el controlador de excepciones específico en uso.
Datos de desenrollado encadenado
Si se establece la marca
UNW_FLAG_CHAININFO, laUNWIND_INFOestructura termina con tresUWORDs. EstosUWORDrepresentan laRUNTIME_FUNCTIONinformación sobre la función del desapilado encadenado.
Estructura UNWIND_CODE
Utilice la matriz de códigos de desenrollado para registrar la secuencia de operaciones en el prólogo que afectan a los registros no volátiles y RSP. Cada elemento de código tiene este formato:
| Tamaño | Valor |
|---|---|
| UBYTE | Desplazamiento en el prólogo |
| UBYTE: 4 | Código de operación de desenredado |
| UBYTE: 4 | Información de la operación |
La matriz se ordena por orden descendente de desplazamiento en el prólogo.
Desplazamiento en el prólogo
Desplazamiento (con respecto al principio del prólogo) del final de la instrucción que realiza esta operación, más 1 (es decir, el desplazamiento del comienzo de la instrucción siguiente).
Código de operación de desenredado
Ciertos códigos de operación requieren un desplazamiento sin signo respecto de un valor en el marco de pila local. Este desplazamiento se realiza con respecto al principio, es decir, la dirección más baja de la asignación de pila fija. Si el campo Registro de tramas de UNWIND_INFO es cero, este desplazamiento se mide desde RSP. Si el campo Registro de trama es distinto de cero, este desplazamiento se mide desde la ubicación en la que estaba RSP cuando se estableció el registro FP. Es igual al registro FP menos el desplazamiento del registro FP (16 * el desplazamiento escalado del registro de trama en el UNWIND_INFO). Si se usa un registro de FP, el código de desenlace que toma un desplazamiento solo se debe utilizar después de que en el prólogo se haya establecido el registro de FP.
En todos los códigos de operación excepto UWOP_SAVE_XMM128 y UWOP_SAVE_XMM128_FAR, el desplazamiento es siempre un múltiplo de 8, ya que todos los valores relevantes de la pila se almacenan en límites de 8 bytes (la propia pila siempre tiene una alineación de 16 bytes). Para los códigos de operación que toman un desplazamiento corto (menor que 512K), el final USHORT de los nodos de este código contiene el desplazamiento dividido por 8. Para los códigos de operación que tardan un desplazamiento largo (512K <= desplazamiento < de 4 GB), los dos USHORT nodos finales para este código contienen el desplazamiento (en formato little-endian).
Para los opcodes UWOP_SAVE_XMM128 y UWOP_SAVE_XMM128_FAR, el desplazamiento siempre es un múltiplo de 16, ya que todas las operaciones XMM de 128 bits deben realizarse sobre memoria alineada a 16 bytes. Por tanto, se usa un factor de escala de 16 para UWOP_SAVE_XMM128, lo que permite desplazamientos de menos de 1 millón.
El código de operación de desenredado es uno de estos valores:
UWOP_PUSH_NONVOL(0) 1 nodoInserte un registro entero no volátil, disminuyendo
RSPen 8. La información de la operación es el número del registro. Debido a las restricciones en los epílogos, los códigos de desenredado deUWOP_PUSH_NONVOLdeben aparecer en primer lugar en el prólogo y, en consecuencia, en el último lugar de la matriz de códigos de desenredado. Esta ordenación relativa se aplica a todos los demás códigos de desenredado exceptoUWOP_PUSH_MACHFRAME.UWOP_ALLOC_LARGE(1) 2 o 3 nodosAsigne un área de gran tamaño en la pila. Hay dos formularios. Si la información de la operación es igual a 0, el tamaño de la asignación dividido entre 8 se registra en la siguiente ranura, lo que permite una asignación de hasta 512K - 8. Si la información de la operación es igual a 1, el tamaño no escalado de la asignación se almacena en los dos espacios siguientes en formato little-endian, lo que permite asignaciones de hasta 4GB - 8.
UWOP_ALLOC_SMALL(2) 1 nodoAsigne un área de pequeño tamaño en la pila. El tamaño de la asignación es el campo de información de la operación * 8 + 8, lo que permite asignaciones de entre 8 y 128 bytes.
El código de desenredado de una asignación de pila siempre debe usar la codificación más corta posible:
Tamaño de la asignación Código de desbobinado De 8 a 128 bytes UWOP_ALLOC_SMALLDe 136 a 512K-8 bytes UWOP_ALLOC_LARGE, información de la operación = 0De 512K a 4G - 8 bytes UWOP_ALLOC_LARGE, información de la operación = 1UWOP_SET_FPREG(3) 1 nodoEstablezca el registro de puntero de trama configurándolo con un desplazamiento respecto al valor actual de
RSP. El desplazamiento es igual al campo de desplazamiento del registro de trama (escalado) enUNWIND_INFO, multiplicado por 16, lo que permite desplazamientos entre 0 y 240. El uso de un desplazamiento permite establecer un puntero de marco que apunta al centro de la asignación de pila fija, lo que ayuda a la densidad del código al permitir que más accesos usen formatos cortos de instrucción. El campo de información de la operación está reservado y no se debe usar.UWOP_SAVE_NONVOL(4) 2 nodosGuarde un registro entero no volátil en la pila mediante MOV en lugar de PUSH. Este código se usa principalmente para la optimización de ajuste, donde un registro no volátil se guarda en la pila en una posición asignada previamente. La información de la operación es el número del registro. El desplazamiento de la pila a escala por 8 se registra en la siguiente ranura de código de operación de desenredado, como se describe en la nota anterior.
UWOP_SAVE_NONVOL_FAR(5) 3 nodosGuarde un registro entero no volátil en la pila con un desplazamiento largo usando MOV en lugar de PUSH. Este código se usa principalmente para la optimización de ajuste, donde un registro no volátil se guarda en la pila en una posición asignada previamente. La información de la operación es el número del registro. El desplazamiento de la pila sin escala se registra en las dos ranuras de código de operación de desenredado siguientes, como se describe en la nota anterior.
UWOP_SAVE_XMM128(8) 2 nodosGuarde los 128 bits de un registro no volátil
XMMen la pila. La información de la operación es el número del registro. El desplazamiento de la pila escalado por 16 se registra en la ranura siguiente.UWOP_SAVE_XMM128_FAR(9) 3 nodosGuarde los 128 bits de un registro no volátil
XMMen la pila con un desplazamiento largo. La información de la operación es el número del registro. El desplazamiento de la pila sin escala se registra en las dos ranuras siguientes.UWOP_PUSH_MACHFRAME(10) 1 nodoInserte un marco de máquina. Este código de desenrollado registra el efecto de una interrupción o excepción de hardware. Tiene dos formas. Un valor de 0, indica que el hardware ha insertado un marco como este en la pila:
Ubicación Valor RSP+32SSRSP+24Antiguo RSPRSP+16EFLAGSRSP+8CSRSPRIPUn valor de 1 indica que el hardware ha insertado un marco como este en la pila:
Ubicación Valor RSP+40SSRSP+32Antiguo RSPRSP+24EFLAGSRSP+16CSRSP+8RIPRSPCódigo de error Este código de desenrollado siempre aparece en un prólogo simulado, que en realidad no se ejecuta nunca, sino que aparece antes del punto de entrada real de una rutina de interrupción y solo existe para proporcionar un lugar a fin de simular la inserción de un marco de máquina.
UWOP_PUSH_MACHFRAMEregistra la simulación, que indica que el equipo ha realizado conceptualmente esta operación:Extraiga
RIPla dirección de retorno de la parte superior de la pila y colóquela en TempInserción
SSInsertar antiguo
RSPInserción
EFLAGSInserción
CSEmpujar Temp
Codificación de error de envío (si la información de operación es igual a 1)
La operación simulada
UWOP_PUSH_MACHFRAMEreduceRSPen 40 (si la información de la operación es igual a 0) o en 48 (si la información de la operación es igual a 1).
Información de la operación
El significado de los bits de información de la operación depende del código de la operación. Para codificar un registro entero de propósito general, se utiliza este mapeo:
| bit | Registro |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | RSP |
| 5 | RBP |
| 6 | RSI |
| 7 | RDI |
| 8 a 15 |
R8 a R15 |
Estructuras de información de desenredado encadenada
Si se establece el UNW_FLAG_CHAININFO indicador, entonces una estructura de información de desenrollado es secundaria, y el campo de dirección del controlador de excepciones compartido o de información encadenada contiene la información de desenrollado principal. Este código de ejemplo recupera la información primaria de desapilado, suponiendo que unwindInfo es la estructura que tiene establecido el indicador UNW_FLAG_CHAININFO.
PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo->UnwindCode[( unwindInfo->CountOfCodes + 1 ) & ~1]);
La información encadenada es útil en dos situaciones. En primer lugar, se puede usar para segmentos de código no contiguos. Mediante el uso de información encadenada, puede reducir el tamaño de la información de desenredado necesaria, ya que no tiene que duplicar la matriz de códigos de desenredado de la información de desenredado principal.
También puede usar la información encadenada para agrupar el almacenamiento de registros volátiles. El compilador podría retrasar el guardado de algunos registros volátiles hasta que esté fuera del prólogo de entrada de función. Puede representarlos disponiendo de información principal de desapilado para la parte de la función anterior al código agrupado y luego configurar información encadenada con un tamaño de prólogo distinto de cero, donde los códigos de desapilado de la información encadenada reflejen las operaciones de guardado de los registros no volátiles. En ese caso, los códigos de desenredado son todas las instancias de UWOP_SAVE_NONVOL. No se admite una agrupación que guarde registros no volátiles mediante PUSH o que modifique el registro RSP mediante una reserva fija adicional en la pila.
Un UNWIND_INFO elemento que tiene UNW_FLAG_CHAININFO establecido puede contener una entrada RUNTIME_FUNCTION cuyo elemento UNWIND_INFO también tiene UNW_FLAG_CHAININFO establecido, lo que a veces se denomina empaquetado retráctil múltiple. Finalmente, los punteros de información de desenrollado encadenados llegan a un elemento UNWIND_INFO que tiene UNW_FLAG_CHAININFO desactivado. Este elemento es el elemento principal UNWIND_INFO , que apunta al punto de entrada del procedimiento real.
Procedimiento de desenrollado
La matriz de códigos de desempaquetado se ordena en orden descendente. Cuando se produce una excepción, el sistema operativo almacena el contexto completo en un registro de contexto. Después, se invoca la lógica de envío de excepciones, que ejecuta repetidamente estos pasos para encontrar un controlador de excepciones:
Use el actual
RIPalmacenado en el registro de contexto para buscar una entrada de la tablaRUNTIME_FUNCTIONque describa la función actual (o parte de la función, para entradasUNWIND_INFOencadenadas).Si la búsqueda no encuentra una entrada de tabla de funciones, se supone que el código forma parte de una función hoja y
RSPdirecciona directamente el puntero de retorno. El puntero de retorno en [RSP] se almacena en el contexto actualizado, el simuladoRSPse incrementa en 8 y se repite el paso 1.Si la búsqueda encuentra una entrada de tabla de funciones,
RIPpuede encontrarse dentro de tres regiones: a) en un epílogo, b) en el prólogo o c) en el código que podría estar cubierto por un controlador de excepciones.Caso a) Si
RIPestá dentro de un epílogo, el control está saliendo de la función. No puede haber ningún controlador de excepciones asociado a esta excepción para esta función. Los efectos del epílogo deben continuar calculando el contexto de la función del autor de la llamada. Para determinar siRIPse encuentra dentro de un epílogo, se examina la secuencia de código a partir deRIP. Si esa secuencia de código coincide con el tramo final de un epílogo legítimo, está en un epílogo. La parte restante del epílogo se simula, con el registro de contexto actualizado a medida que se procesa cada instrucción. Tras este procesamiento, se repite el paso 1.- Caso b) Si el
RIPse encuentra dentro del prólogo, el control no ha entrado en la función. No puede haber ningún controlador de excepciones asociado a esta excepción para esta función. Los efectos del prólogo deben revertirse para calcular el contexto de la función que llama. ElRIPestá dentro del prólogo si la distancia desde el inicio de la función hasta elRIPes menor o igual al tamaño del prólogo codificado en la información de desenrollado. El mecanismo de desenrollado recorre hacia delante la tabla de códigos de desenrollado hasta encontrar la primera entrada con un desplazamiento menor o igual que el deRIPrespecto del inicio de la función y, a continuación, deshace el efecto de todos los elementos restantes de la tabla de códigos de desenrollado. Después se repite el paso 1.
- Caso b) Si el
Caso c) Si
RIPno está dentro de un prólogo o epílogo, y la función tiene un controlador de excepciones (UNW_FLAG_EHANDLERse establece), se llama al controlador específico del lenguaje. El manejador examina sus datos y llama a las funciones de filtro según sea necesario. El controlador específico del lenguaje puede devolver que la excepción se ha controlado o que se va a continuar la búsqueda. También puede iniciar un desenrollado directamente.
Si el controlador específico del lenguaje devuelve un estado controlado, la ejecución continúa usando el registro de contexto original.
Si no hay ningún controlador específico del idioma o el controlador devuelve un estado de "continuar la búsqueda", el registro de contexto debe restablecerse al estado del llamador. El desapilador revierte el efecto de cada elemento de la secuencia de códigos de desapilado. Después se repite el paso 1.
Cuando interviene información de desenredado encadenada, todavía se siguen estos pasos básicos. La única diferencia es que, al recorrer la matriz de códigos de desapilado para deshacer los efectos de un prólogo, una vez que el proceso llega al final de la matriz, se enlaza con la información de desapilado primaria y recorre toda la matriz de códigos de desapilado que se encuentra allí. Este encadenamiento continúa hasta llegar a una información de desenrollado sin la marca UNW_CHAINED_INFO, y a continuación termina de recorrer su matriz de códigos de desenrollado.
El conjunto más pequeño de datos de desempaquetado consta de 8 bytes. Este conjunto representa una función que solo asignó 128 bytes de pila o menos, y posiblemente guardó un registro no volátil. También es el tamaño de una estructura de información de desenredado encadenada para un prólogo de longitud cero sin códigos de desenredado.
Controlador específico del lenguaje
La estructura UNWIND_INFO proporciona la dirección relativa del controlador específico del idioma cuando se establece cualquiera de las marcas UNW_FLAG_EHANDLER o UNW_FLAG_UHANDLER. Como se describe en la sección anterior, la búsqueda de un controlador de excepciones o el proceso de desenredado llama al controlador específico del lenguaje. El controlador usa este prototipo:
typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
IN PEXCEPTION_RECORD ExceptionRecord,
IN ULONG64 EstablisherFrame,
IN OUT PCONTEXT ContextRecord,
IN OUT PDISPATCHER_CONTEXT DispatcherContext
);
ExceptionRecord proporciona un puntero a un registro de excepción, que tiene la definición estándar de Win64.
EstablisherFrame es la dirección de la base de la asignación de pila fija para esta función.
ContextRecord apunta al contexto de la excepción en el momento de generar la excepción (en el caso del controlador de excepciones), o bien al contexto de "desenredado" actual (en el caso del controlador de finalización).
DispatcherContext apunta al contexto del distribuidor para esta función. Tiene esta definición:
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 es el valor de RIP dentro de esta función. Este valor es una dirección de excepción o la dirección en la que el control ha abandonado la función de establecimiento.
RIP se usa para determinar si el control está dentro de alguna construcción protegida dentro de esta función, por ejemplo, un __try bloque para__try/__except o .__try/__finally
ImageBase es la base de imagen (dirección de carga) del módulo que contiene esta función. Los desplazamientos de 32 bits usados en la entrada de función y la información de desenredado deben agregarse a ImageBase para obtener la dirección final.
FunctionEntry proporciona un puntero a la entrada de función RUNTIME_FUNCTION que contiene la función y desenreda las direcciones relativas a imágenes base de información para esta función.
EstablisherFrame es la dirección de la base de la asignación de pila fija para esta función.
TargetIp proporciona una dirección de instrucción opcional que especifica la dirección en la que continúa el desenrollado. Esta dirección se omite si no se especifica EstablisherFrame.
ContextRecord apunta al contexto de la excepción, utilizado por el código de distribución/desenlace de excepciones del sistema.
LanguageHandler señala a la rutina del controlador de idioma específico que se está llamando.
HandlerData apunta a los datos del controlador específico de lenguaje para esta función.
Asistentes de desenrollado para MASM
Para escribir rutinas de ensamblado adecuadas, use un conjunto de pseudooperaciones junto con las instrucciones de ensamblado reales. Estas pseudooperaciones crean los valores adecuados .pdata y .xdata. Además, use un conjunto de macros que simplifican el uso de estas pseudooperaciones para sus usos más comunes.
Operaciones primitivas
| Pseudo-operación | Descripción |
|---|---|
| MARCO PROC [:ehandler] | Hace que MASM genere una entrada de tabla de funciones en .pdata y desenrede la información en .xdata para el comportamiento de desenredado estructurado de control de excepciones de una función. Si ehandler está presente, este procedimiento se especifica en .xdata como el controlador específico del lenguaje.Cuando use el atributo FRAME, a continuación incluya la directiva .ENDPROLOG. Si la función es una función hoja (tal como se define en Tipos de función), el atributo FRAME no es necesario, al igual que el resto de estas pseudooperaciones. |
| .PUSHREG registro | Genera una entrada de código de desenrollado UWOP_PUSH_NONVOL para el número de registro especificado usando el desplazamiento actual del prólogo.Úselo solo con registros de enteros no volátiles. Para las instrucciones push de registros volátiles, utilice .ALLOCSTACK 8 en su lugar. |
| .SETFRAME registro, desplazamiento | Rellena el campo de registro de marco y el desplazamiento en la información de desenredado con el registro y el desplazamiento especificados. El desplazamiento debe ser un múltiplo de 16 y menor o igual que 240. Esta directiva genera también una entrada de código de desenrollado para el registro especificado UWOP_SET_FPREG utilizando el desplazamiento actual del prólogo. |
| .ALLOCSTACK tamaño | Genera un UWOP_ALLOC_SMALL o un UWOP_ALLOC_LARGE con el tamaño especificado en el desplazamiento actual del prólogo.El operando tamaño debe ser un múltiplo de 8. |
| .SAVEREG registro, desplazamiento | Genera una entrada de código de desenrollado UWOP_SAVE_NONVOL o UWOP_SAVE_NONVOL_FAR para el registro y el desplazamiento especificados usando el desplazamiento actual del prólogo. MASM elige la codificación más eficaz.desplazamiento debe ser positivo y un múltiplo de 8. offset es relativo a la base del marco del procedimiento, que generalmente se encuentra en RSP, o, si se usa un puntero de marco, en el puntero de marco sin escalar. |
| .SAVEXMM128 registro, desplazamiento | Genera una entrada de código de desenrollado UWOP_SAVE_XMM128 o UWOP_SAVE_XMM128_FAR para el registro XMM y el desplazamiento especificados, utilizando el desplazamiento actual del prólogo. MASM elige la codificación más eficaz.offset debe ser un número positivo y un múltiplo de 16. offset es relativo a la base del marco del procedimiento, que suele estar en RSPo, si se usa un puntero de marco, el puntero de marco sin escalar. |
| .PUSHFRAME [código] | Genera una entrada de código de desenrollado UWOP_PUSH_MACHFRAME. Si especifica el código opcional, la entrada de código de desenredado obtiene un modificador de 1. De lo contrario, el modificador es 0. |
| .ENDPROLOG | Señala el final de las declaraciones de prólogo. Debe aparecer en los primeros 255 bytes de la función. |
Este es un ejemplo de prólogo de función con el uso correcto de la mayoría de los códigos de operación:
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 obtener más información sobre el ejemplo de epílogo, vea Código de epílogo en Prólogo y epílogo de x64.
Macros de MASM
Para simplificar el uso de pseudooperaciones en bruto, utilice el conjunto de macros definidas en ksamd64.inc. Estas macros le ayudan a crear prólogo y epílogos de procedimientos típicos.
| Macro | Descripción |
|---|---|
| alloc_stack(n) | Asigna un marco de pila de n bytes (mediante sub rsp, n), y emite la información de desenredado adecuada (.allocstack n) |
| save_reg reg, loc | Guarda un registro no volátil reg en la pila con un desplazamiento de loc en RSP, y emite la información de desapilado adecuada (.savereg reg, loc) |
| push_reg reg | Inserta un registro no volátil reg en la pila y emite la información de desenredado adecuada (.pushreg reg) |
| rex_push_reg reg | Guarda un registro no volátil en la pila mediante una operación push de 2 bytes y emite la información de desapilado adecuada (.pushreg reg). Use esta macro si el 'push' es la primera instrucción de la función, para asegurarse de que la función sea actualizable en caliente. |
| save_xmm128 reg, loc | Guarda un registro no volátil XMMreg en la pila con desplazamiento RSPloc y emite la información de desenrollado adecuada (.savexmm128 reg, loc) |
| set_frame reg, desplazamiento | Establece el registro de marco reg con el RSP + desplazamiento (usando un mov o un lea) y emite la información de desapilado adecuada (.set_frame reg, offset) |
| push_eflags | Inserta los eflags mediante una pushfq instrucción y emite la información de desenredado adecuada (.alloc_stack 8) |
Este es un prólogo de funciones de ejemplo con el uso adecuado de las 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
Desenrollar definiciones de datos en C
A continuación se muestra una descripción en C de los datos de desenrollado:
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))