Desde que empecé con las pruebas de recompilación dinámica, un par de personas me han preguntado cómo me las apaño “para depurar eso”.

Para depurarlo uso un desensamblador, como era de esperar. Yo recomiendo la demo del IDA, que aunque está limitada para esto puede ser suficiente.

La idea es sencilla: para la ejecución cuando se llegue al bloque generador dinámciamente e ir paso a paso viendo si hace lo que se espera. Para llegar a este punto, se puede hacer manualmente. O como hago yo en este caso: detecto si hay un depurador vinculado al ejecutable, y si lo hay emito la interrupción 3 justo antes de empezar el bloque. La interrupción 3 es un software breakpoint. El debugger se para ahí automáticamente, con lo que no es necesario conocer la dirección de antemano.

IsDebuggerPresent

if (addBreakPointWhenStart && (PC == StartPC)) {  
    if (IsDebuggerPresent()) emiter.INT3(); // Debugger  
    emiter.MOV(Register32.EDX, PC); // Just for debugging.  
}  

Esto me permite con el mismo ejecutable (sin necesidad de recompilar) poder depurar. La pega es que la interrupción se produce en cada bloque nativo. Por el momento es lo que me interesa, pero más adelante lo que interesará será poner breakpoints con el debugger que haré. Cuando esté el debugger, para parar la ejecución en un sitio concreto con el dynarec, bastará con machacar la instrucción donde queremos que se pare con un código que parar la ejecución. Habiendo guardado previamente los bytes que machacamos y restaurándolos cuando se retome la ejecución.

.text  

;loop_test:  
;j loop_test  

li t0, 128  

loop_color:  
    li a0, 0x04000000  
    li a1, 0x88000  
    loop_write:  
        addiu a0, a0, 1  
        addiu a1, a1, -1  
        sb t0, 0(a0)  
    bne a1, zr, loop_write  
    nop  
    addi t0, t0, 1  
    syscall 0x2147 ; sceDisplay.sceDisplayWaitVblankStart  
j loop_color  
nop