Well, the assembly-coded EXC_CallHandler works (to my surprise), with a little modification from what I described because after all, no plan survives contact with the enemy.
However, we're still getting hit by one of the landmines in Shrinker. The awful thing is that I can't debug this easily with gdb. The first exception that's supposed to take place is a write fault to the code area. But if I set a breakpoint in the process's code area, gdb is going to remove the write protection from the code area because, I guess, it needs to put a breakpoint in there (I thought it would use breakpoint registers instead of int $3?)
Anyway, I don't want to release the EXC_CallHandler code until I find out which landmine we're hitting, since I can't be certain the rest of EXC_CallHandler works -- the stuff past calling the exception handler.
But if you're interested in a morbid "I-love-assembler" way, here it is:
static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame, CONTEXT *context, EXCEPTION_FRAME **dispatcher, PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler) { DWORD tmp = 0;
__asm__ __volatile__( "push %1\n\t" // ebp-1c = nested_handler "push %2\n\t" // ebp-18 = handler "push %3\n\t" // ebp-14 = dispatcher "push %4\n\t" // ebp-10 = context "push %5\n\t" // ebp-c = frame "push %6\n\t" // ebp-8 = record "push %%ebp\n\t" // ebp-4 "push %%ecx\n\t" // ebp-0 "movl %%esp,%%ebp\n\t" "movl 0x18(%%ebp),%%ecx\n\t" "pushl 0x1c(%%ebp)\n\t" "pushl %%fs:0\n\t" "movl %%esp,%%fs:0\n\t" "pushl 0x14(%%ebp)\n\t" "pushl 0x10(%%ebp)\n\t" "pushl 0x0c(%%ebp)\n\t" "pushl 0x08(%%ebp)\n\t" "call *%%ecx\n\t" "movl %%fs:0,%%esp\n\t" "popl %%fs:0\n\t" "movl %%ebp,%%esp\n\t" "popl %%ecx\n\t" "popl %%ebp\n\t" "popl %0\n\t" "popl %0\n\t" "popl %0\n\t" "popl %0\n\t" "popl %0\n\t" "popl %0\n\t" "movl %%eax,%0\n\t" : "=&g" (tmp) : "g" (nested_handler), "g" (handler), "g" (dispatcher), "g" (context), "g" (frame), "g" (record) : "memory" ); return tmp; }
--Rob