Real registers to emulated ones, in C on x86

My work project involves generating machine code and running it, and to make debugging the code generator easier, I wrote an emulator for the subset of the x86 that we output for. To make the emulation run in the same context that the real CPU would see when running that code, I put some inline assembler at the start of the emulator routine. It saves all registers to a block of memory that C sees as a struct. Here’s the code snippet, along with the struct definition, in case it’s useful to anyone wanting to get real register contents.  I wrote it as a run of separate asm statements for ease of commenting and to get proper line numbering in gdb; you could merge them all into one.  It’s self-contained, and doesn’t leave any registers changed.

typedef struct x86emu_state {
/* CPU registers: */
int32_t registers[8];        /* 0..3 4..7 8..11 12..15 16..19 20..23 24..27 28..31 */
double fp_stack[8];        /* 32..39 40..47 48..55 56..63 64..71 72..79 80..87 88..95  */
uint32_t status;        /* 96..99 */
const uint8_t *ip;        /* 100..103 */
/* Other state */
verbosity_level verbosity;
int call_depth;
uint8_t *original_sp;
const uint8_t *ip_end;
} x86emu_state;

/*
* Copy the real register values into our emulation registers.
*
* We use AX as the pointer to the structure containing the
* emulation registers, and we need a second register, say CX, to
* save the value of AX.  So we push the original values of AX and
* CX, as we'll want them back by the end of this block of inline
* assembler.


* GCC's inline mechanism is meant to be able to choose a register
* for you, as the working register, but I found that in practice it
* wasn't actually loading it with the value needed (the pointer to
* the emulated register block).  Maybe this was because it's a
* parameter?  Anyway the code below is self-contained in such
* respects, although it does embody an assumption about where the
* first argument of the current function is.
*/

asm("push %%eax\n\t" : : : "memory");
asm("push %%ecx\n\t" : : : "memory");

/* From our stack frame, we pick the first argument, which is the
state (containing the memory holding the emulation registers). */

asm("mov 8(%%ebp), %%eax\n\t" : : : "eax" );
/* We get the original value of AX from where we pushed it onto the
stack, and we save it into the emulated registers: */
asm("mov 4(%%esp), %%ecx\n\t" : : : "ecx");
asm("mov %%ecx, 0(%%eax)\n\t" : : : "memory" );
/* Now we only need one working register, so pop CX and store it
into emulation: */

asm("pop %%ecx\n\t" : : : "ecx");
/* Still using AX as the pointer, we save the remaining integer
registers: */
asm("mov %%ecx, 4(%%eax)\n\t" : : : "memory" );
asm("mov %%edx, 8(%%eax)\n\t" : : : "memory" );
asm("mov %%ebx, 12(%%eax)\n\t" : : : "memory" );
asm("mov %%esp, 16(%%eax)\n\t" : : : "memory" );
asm("mov %%ebp, 20(%%eax)\n\t" : : : "memory" );
asm("mov %%esi, 24(%%eax)\n\t" : : : "memory" );
asm("mov %%edi, 28(%%eax)\n\t" : : : "memory" );
/* And we use AX as the pointer for copying the FP registers: */
asm("fstpl 32(%%eax)\n\t" : : : "memory" );
asm("fstpl 40(%%eax)\n\t" : : : "memory" );
asm("fstpl 48(%%eax)\n\t" : : : "memory" );
asm("fstpl 56(%%eax)\n\t" : : : "memory" );
asm("fstpl 64(%%eax)\n\t" : : : "memory" );
asm("fstpl 72(%%eax)\n\t" : : : "memory" );
asm("fstpl 80(%%eax)\n\t" : : : "memory" );
asm("fstpl 88(%%eax)\n\t" : : : "memory" );

/* Finally, we get AX back to its original value (if anyone wants
it, which I don't think they do), and the stack back to its
original level (which is probably more important): */
asm("pop %%eax\n\t" : : : "eax");

Advertisements

Post a Comment

Required fields are marked *

*
*

%d bloggers like this: