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