[Mono-devel-list] AMD64, PInvoke + Native Exceptions

Willibald Krenn Willibald.Krenn at gmx.at
Wed Oct 27 15:05:05 EDT 2004


Hi!

> This could be implemented, I guess. The implementation might be based on
> the one in gcj.

While I've no clue of how gcj catches native exceptions (I assume it 
uses the standard C library), I tried to find some 'hack' that works for 
a JIT compiler that is dynamically emitting wrapper methods on AMD64.

First Problem: On AMD64 C-exception handling is based on unwind 
information available in the .eh_frame(?) section (which is read only) 
of the ELF file. So generating that unwind information is obviously out 
of question for several reasons.

Consequently there is probably only one way to go: Trick the subroutine 
into thinking the caller was some function with a try-catch block.
Interestingly enough, this does work - and seems to be not that costly. 
(At least one additional jump and either one callee saved register or a 
stack slot is being used. Plus the calculation of the correct return 
address..)

As this won't do anything about the LMF structures that need to be 
saved, I've tried to implement a signal handler that just throws a C 
exception and see if it reaches the catch block. Well, it does: The 
catch block will be executed and all callee saved registers have the 
correct values.
But I dunno, if this is the way to go, because my short search on signal 
handlers and exceptions on google revealed that everyone seems to 
discourage that approach. ("Just set a flag in a signal handler..")
In that case, the LMF structure clearly has an advantage because it's 
completely unimportant what's on the runtime stack. But then again: I 
read in the Summit Notes that the exception on sigsev will eventually be 
disabled and after a SIGSEV it's not guaranteed that the stack will be 
'ok'?!

For the curious here's what i've done:

void ExHandler(void)
{
   /*The faked try-catch block*/
   try
   {
    /*...skipping some 'tricks' for gcc...*/
FakeReturn:
    __asm__ __volatile__(
     /* "jmp *%r15\n\t" */      /*callee saved reg or*/
      "ret\n\t"                 /*stack slot*/
    );
   }
   /*If the native function throws an exception, we'll start
    * _here_ with exception handling! So basically this would be the
    * entrypoint of a custom JIT exception handling routine.
    * */
   catch(...)
   {
     /* RAX => pointer to _Unwind_Exception
      * R15 => where normal execution should have continued (if not on
      *        stack)
      * RBX, RBP, RSP, R12..R14 restored
      * */

     /*set myException pointer*/
     __asm__ __volatile__ ("movq	$myException, %r11\n\t"
                           "movq	%rax,(%r11)\n\t"
                          );
     /*__asm__ __volatile__ ("int3");*/
   }
}

The call into a native function that might throw an exception in psuedo asm:
    __asm__ (
       "push	($AfterCallPtr)"
     /*"movq     ($AfterCallPtr), %r15"*/
       "push	($FakeReturnPtr)"
       "jmp	ToBeCalled\n\t");
AfterCall:



Looking forward for comments,
  Willi




More information about the Mono-devel-list mailing list