| Programando en 64bits I |
|
|
|
| Escrito por Guan |
| Lunes, 04 de Abril de 2011 09:22 |
|
Bueno lo prometido es deuda, en este artículo voy a intentar explicar las cosas mas significativas que han cambiado en la programación de 64bits y como el jwasm versión 2.05 nos permite trabajar como si la programación de 32bit se tratara. Lo primero es el cambio en los registros. Los registros de 64bits empiezan con la letra 'r' por lo que tenemos: rax, rbx, rcx, rdx, rdi, rsi, rip, rbp, rsp. Además de estos se han incorporado una serie de registros nuevos también de 64bits, r8, r9, r10, r11, r12, r13, r14 y r15.
Por su puestos, tenemos acceso a las partes bajas de los registros como antes: rax <= 64bits eax <= 32bits ax <= 16bits al / ah <= 8 bits
Para el caso de los registros nuevos el acceso es r8 <= 64 bits r8d <= 32 bits r8w <= 16 bits r8b <= 8 bits (el byte menos significativo)
La devolución de las funciones se realiza por el registro rax o si es un número real por el registro xmm0. Uno de los grandes cambios es la convección de llamada, ya no se usa el stdcall sino el fastcall. En fastcall los 4 primeros parámetros se pasan por registros, y los siguientes por pila, de tal forma que:
1º Argumento <= rcx 2º Argumento <= rdx 3º Argumento <= r8 4º Argumento <= r9
Seguimos contando cosas nuevas, y es que el programador de la función es el encargado de pasar a la pila el valor de estos primeros argumentos que son pasados por registro, esto argumentos son los llamados "Shadow variables". El programador al llamar a una función SIEMPRE debe de reservar espacio en la pila para estos argumentos, aun cuando la función no reciba argumentos, y es también el responsable de liberar ese espacio de la pila cuando se retorna de la función. Desde el punto de vista de la creación de una función, todo lo que sería reserva de espacio para las variables locales, así como la memoria necesaria para alojar los parámetros de las funciones que se van a llamar dentro de la función, es lo que se conoce como marco de pila (stack frame) Esto en MASM64 se debería de tratar usando las macros:
.ALLOCSTACK .ENDPROLOG .PUSHFRAME .PUSHREG .SAVEREG .SETFRAME
Un ejemplo extraído del MSDN ; ml64 frmex2.asm /link /entry:frmex2 /SUBSYSTEM:CONSOLE _text SEGMENT frmex2 PROC FRAME push rbp .pushreg rbp sub rsp, 010h .allocstack 010h mov rbp, rsp .setframe rbp, 0 .endprolog ; modify the stack pointer outside of the prologue sub rsp, 060h ; we can unwind from the following AV because of the frame pointer mov rax, 0 mov rax, [rax] ; AV! add rsp, 060h add rsp, 010h pop rbp ret frmex2 ENDP _text ENDS END
Como pueden ver si lo comparamos con la programación de 32bits esto es un poco lio. Otro problema que se presentan en los SO de 64 a partir del Vista (solo he trabajado en 64bits en Windows desconozco esto bajo linux) es que antes de llamar a una función/API, la pila debe de estar alineada a un tamaño de QWORD, en caso contrario el programa corre el riesgo de romper por una excepción.
Pues bien todo esto, y los problemas con las macros del ml64.exe, lo soluciona el compilador jwasm mediante el uso de la directiva option. Usando option frame:auto nos ahorramos el tener que preocuparnos con los problemas de alineamiento de pila y el stack frame, el compilador lo hace por nosotros.
Y usando option win64:1 el copilador asigna el valor de los registros correspondientes que se usan para pasar los argumentos en una función a las Shadow Variables por nosotros.
Con esto ya estamos ante un entorno de trabajo parecido al que teníamos al trabajar bajo 32bits. Una cosa mas a tener en cuenta en los 64bits, es que hay ciertos registros llamados "inalterados". Estos registros no deben de variar su valor tras salir de una función, estos registros son: rdi, rsi, rbx, r12, r13, r14 y r15 Para eso en la declaración de las funciones podemos poner
function proc uses rdi ... function endp
Con esto rdi es guardado en la pila al inicio y retoma su valor al salir. Según los registros que usemos en nuestras funciones los deberemos de salvar para evitar posibles excepciones.
Equivalente a lo que hemos comentado pasa con los registros para el uso de la coma flotante. Si la función a crear utiliza parámetros reales, los registros a usar son: XMM0 <= 1º Argumento XMM1 <= 2º Argumento XMM2 <= 3º Argumento XMM3 <= 4º Argumento Y si tocamos alguno de los registros XMM en nuestro código, los que deben de conservar su valor son: XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14 y XMM15 Faltaría hablar de la gestión de excepciones la cual ha cambiado completamente, pero es un tema que a la fecha del presente artículo aun no controlo, eso lo veremos mas adelante en un futuro post. |
| Última actualización el Miércoles, 06 de Abril de 2011 11:34 |








Comentarios
Revisa mi artículo sobre jwasm, ahí tienes los enlaces al kit de programación que uso para trabajar en asm de 64bits.