How to port the libgcj signal handling code

Introduction

When a signal is sent to a Java program, the Java runtime code should raise an exception which can be caught by Java application code. Examples of such signals are SIGSEGV (segment violation) and SIGFPE (arithmetic exception). It is not always clear which Java exceptions should be raised, but in these cases it is fairly obvious: NullPointerException and ArithmeticException.

Unfortunately, there is no portable way to turn a signal into an exception: this depends on the processor architecture and operating system being used. As a consequence of this, some porting work may be necessary in order to catch signals.

The problem is that a signal handler may be called from the OS kernel with a stack frame which cannot be walked by signal handling code. In order to provide throw() with a stack which it can walk, the signal handler may need to alter the stack frame to make it appear that the handler has been directly called from the code which faulted and caused the signal.

Details

Firstly, you must determine the address of the instruction which caused the signal to be sent: the point at which the Java program was interrupted. There is no standard way to do this, so you will need to read the documentation of your OS, or possibly even the kernel sources to discover where the interrupted program counter (PC) and frame pointer (FP) are saved. You may have to reverse engineer the kernel trap, or use gdb to see what is on the stack at the time a signal handler is called.

Once you have done this, you'll need to write a little assembler code to take the PC and FP at the time of call and rewrite the current stack frame to make it appear that the signal handler was directly called from the faulting code.

Here's an i386 Linux example of code which does this:

#define MAKE_THROW_FRAME						\
{									\
  void **_p = (void **)&_dummy;						\
  struct sigcontext_struct *_regs = (struct sigcontext_struct *)++_p;	\
									\
  register unsigned long _ebp = _regs->ebp;				\
  register unsigned long _eip = _regs->eip;				\
  									\
  asm volatile ("mov %0, (%%ebp); mov %1, 4(%%ebp)"			\
		: : "r"(_ebp), "r"(_eip));				\
}

Here, dummy is the int argument passed to a signal handler. Immediately below this on the stack is a struct sigcontext_struct which contains the registers at the point the fault occurred. The assembly language instructions rewrite the current stack frame to point to the code which was interrupted.

Once you've written this code for processor foo, create a new foo-signal.h file and put it in the libjava/include directory. Add a reference to foo-signal.h to the list in libjava/configure.in:

case "${host}" in
  i?86-*-linux*)
    SIGNAL_HANDLER=include/i386-signal.h
	;;
  sparc-sun-solaris*)
    SIGNAL_HANDLER=include/sparc-signal.h
	;;
  *)
    SIGNAL_HANDLER=include/default-signal.h
	;;
esac

Specimen signal handling code for other processors is in the libjava/include directory.

The actual signal handlers are in libjava/prims.cc, and look like this:

static java::lang::NullPointerException *nullp;
SIGNAL_HANDLER (catch_segv)
{
  MAKE_THROW_FRAME;
  _Jv_Throw (nullp);
}

You must ensure that the pointer to the exception has been initialized to point to a Java exception before _Jv_Throw() is called: a good place to do this is in JvRunMain, before the application is started.

GCJ

GCJ Home
GCC Home
Status
FAQ
Documentation
Contributing
Done with GCJ

About GCC
Mission Statement
Releases
Snapshots
Mailing lists
Contributors
Steering Committee
@gnutools on Twitter@gnutools
Documentation
Installation
· Platforms
· Testing
Manual
FAQ
Wiki
Further Readings
Download
Mirror sites
Binaries
"Live" Sources
SVN read access
Git read access
Rsync read access
SVN write access
Development
Development Plan
· Tentative Timeline
Contributing
Why contribute?
Open projects
Front ends
Back ends
Extensions
Benchmarks
Build Robot
Translations
Bugs
Known bugs
How to report
Bug database
· Management