
Todos aquellos programadores que hayan programado en C o C++ conocen los típicos depuradores de código disponibles para el código máquina generado por el compilador de dichos lenguajes.
Dependiendo del sistema operativo estaremos acostumbrados a gdb y sus amigos (ddd, kgdb, etc, etc), LLDB, WinDbg, OllyDbg, etc. No vamos a entrar en cual de estos depuradores es mejor sino que vamos a hacer un pequeño recorrido por su funcionamiento interno.
Para entender el funcionamiento de un depurador, primero debemos entender el funcionamiento del procesador y de la memoria.
Con la aparición de x86_64 (o amd64) se extendió su número hasta dieciséis registros, añadiéndose a los ya citados la siguiente lista: R8, R9, R10, R11, R12, R13, R14, R15. También se añadieron ocho registros de 128 bits de tipo XMM (SEE) pero eso va más allá de las pretensiones de este artículo.
Vamos a suponer que tenemos una arquitectura de 32 bits para simplificar un poco el asunto, a todos los efectos, y salvando algunas diferencias, para 64 bits es exactamente igual. Cada uno de los ocho registros de propósito general está diseñado para un uso específico.
Muchas instrucciones optimizadas en el juego de instrucciones de x86 han sido diseñadas para mover información hacia y desde este registro y realizar cálculos con esa información. Operaciones como la adición, reducción, y la comparación están optimizadas para utilizar el registro EAX.
Algunas operaciones más especializadas como la multiplicación o la división solo pueden tener lugar en el registro EAX. Como este registro es también utilizado para devolver el valor de retorno de una función, es fácil saber si una función ha fallado o no y cual es su valor, esto es de gran importancia a la hora de depurar una aplicación.
while(i < 100)
{
...
}
Trasladado a código ensamblador, el registro ECX contendría un valor inicial de 100 en la primera iteración e iría reduciéndose en uno en cada iteración por lo que en la segunda iteración su valor sería de 99 y no al contrario.
El registro EDI apunta a la dirección donde se almacena el resultado de una operación de datos o al indice de destino. Una forma sencilla de recordar esto es que ESI se usa para leer y EDI para escribir.
Al usar los registros de índice de origen y destino se mejora el rendimiento de la aplicación en ejecución.
El registro ESP apunta a la parte superior de la pila y por lo tanto a la dirección de retorno. El registro EBP apunta a la parte inferior de la pila. Algunos compiladores pueden usar optimizaciones para eliminar el registro EBP como puntero de marco de pila y poder ser usado como cualquier otro registro de propósito general.
En el siguiente artículo hablaremos sobre la pila.
Más información | Listado de instrucciones del procesador x86