Add support for JIT exceptions on Darwin. Since we're dealing with libgcc,
authorNicolas Geoffray <nicolas.geoffray@lip6.fr>
Thu, 28 Aug 2008 22:34:49 +0000 (22:34 +0000)
committerNicolas Geoffray <nicolas.geoffray@lip6.fr>
Thu, 28 Aug 2008 22:34:49 +0000 (22:34 +0000)
whose darwin code was written after the ability to dynamically register frames,
we need to do special hacks to make things work.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@55507 91177308-0d34-0410-b5e6-96231b3b80d8

lib/ExecutionEngine/JIT/JIT.cpp

index 9a3bd3bda1ee49369d49b96a107b19d856406e36..39ecc2796c77dcbc4a263d403043284dabcf6acc 100644 (file)
@@ -64,9 +64,127 @@ namespace llvm {
   }
 }
 
+
 #if defined (__GNUC__)
+// libgcc defines the __register_frame function to dynamically register new
+// dwarf frames for exception handling. This functionality is not portable
+// across compilers and is only provided by GCC. We use the __register_frame
+// function here so that code generated by the JIT cooperates with the unwinding
+// runtime of libgcc. When JITting with exception handling enable, LLVM
+// generates dwarf frames and registers it to libgcc with __register_frame.
+//
+// The __register_frame function works with Linux.
+//
+// Unfortunately, this functionality seems to be in libgcc after the unwinding
+// library of libgcc for darwin was written. The code for darwin overwrites the
+// value updated by __register_frame with a value fetched with "keymgr".
+// "keymgr" is an obsolete functionality, which should be rewritten some day.
+// In the meantime, since "keymgr" is on all libgccs shipped with apple-gcc, we
+// need a workaround in LLVM which uses the "keymgr" to dynamically modify the
+// values of an opaque key, used by libgcc to find dwarf tables.
+
 extern "C" void __register_frame(void*);
-#endif
+
+#if defined (__APPLE__)
+
+namespace {
+
+// LibgccObject - This is the structure defined in libgcc. There is no #include
+// provided for this structure, so we also define it here. libgcc calls it
+// "struct object". The structure is undocumented in libgcc.
+struct LibgccObject {
+  void *unused1;
+  void *unused2;
+  void *unused3;
+  
+  /// frame - Pointer to the exception table.
+  void *frame;
+  
+  /// encoding -  The encoding of the object?
+  union {
+    struct {
+      unsigned long sorted : 1;
+      unsigned long from_array : 1;
+      unsigned long mixed_encoding : 1;
+      unsigned long encoding : 8;
+      unsigned long count : 21; 
+    } b;
+    size_t i;
+  } encoding;
+  
+  /// fde_end - libgcc defines this field only if some macro is defined. We
+  /// include this field even if it may not there, to make libgcc happy.
+  char *fde_end;
+  
+  /// next - At least we know it's a chained list!
+  struct LibgccObject *next;
+};
+
+// "kemgr" stuff. Apparently, all frame tables are stored there.
+extern "C" void _keymgr_set_and_unlock_processwide_ptr(int, void *);
+extern "C" void *_keymgr_get_and_lock_processwide_ptr(int);
+#define KEYMGR_GCC3_DW2_OBJ_LIST        302     /* Dwarf2 object list  */
+
+/// LibgccObjectInfo - libgcc defines this struct as km_object_info. It
+/// probably contains all dwarf tables that are loaded.
+struct LibgccObjectInfo {
+
+  /// seenObjects - LibgccObjects already parsed by the unwinding runtime.
+  ///
+  struct LibgccObject* seenObjects;
+
+  /// unseenObjects - LibgccObjects not parsed yet by the unwinding runtime.
+  ///
+  struct LibgccObject* unseenObjects;
+  
+  unsigned unused[2];
+};
+
+// for DW_EH_PE_omit
+#include "llvm/Support/Dwarf.h"
+
+/// darwin_register_frame - Since __register_frame does not work with darwin's
+/// libgcc,we provide our own function, which "tricks" libgcc by modifying the
+/// "Dwarf2 object list" key.
+void DarwinRegisterFrame(void* FrameBegin) {
+  // Get the key.
+  struct LibgccObjectInfo* LOI = (struct LibgccObjectInfo*)
+    _keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
+  
+  // Allocate a new LibgccObject to represent this frame. Deallocation of this
+  // object may be impossible: since darwin code in libgcc was written after
+  // the ability to dynamically register frames, things may crash if we
+  // deallocate it.
+  struct LibgccObject* ob = (struct LibgccObject*)
+    malloc(sizeof(struct LibgccObject));
+  
+  // Do like libgcc for the values of the field.
+  ob->unused1 = (void *)-1;
+  ob->unused2 = 0;
+  ob->unused3 = 0;
+  ob->frame = FrameBegin;
+  ob->encoding.i = 0; 
+  ob->encoding.b.encoding = llvm::dwarf::DW_EH_PE_omit;
+  
+  // Put the info on both places, as libgcc uses the first or the the second
+  // field. Note that we rely on having two pointers here. If fde_end was a
+  // char, things would get complicated.
+  ob->fde_end = (char*)LOI->unseenObjects;
+  ob->next = LOI->unseenObjects;
+  
+  // Update the key's unseenObjects list.
+  LOI->unseenObjects = ob;
+  
+  // Finally update the "key". Apparently, libgcc requires it. 
+  _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST,
+                                         LOI);
+
+}
+
+}
+#endif // __APPLE__
+#endif // __GNUC__
 
 /// createJIT - This is the factory method for creating a JIT for the current
 /// machine, it does not fall back to the interpreter.  This takes ownership
@@ -108,8 +226,23 @@ JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji,
   
   // Register routine for informing unwinding runtime about new EH frames
 #if defined(__GNUC__)
+#if defined(__APPLE__)
+  struct LibgccObjectInfo* LOI = (struct LibgccObjectInfo*)
+    _keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
+  
+  // The key is created on demand, and libgcc creates it the first time an
+  // exception occurs. Since we need the key to register frames, we create
+  // it now.
+  if (!LOI) {
+    LOI = (LibgccObjectInfo*)malloc(sizeof(struct LibgccObjectInfo)); 
+    _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST,
+                                           LOI);
+  }
+  InstallExceptionTableRegister(DarwinRegisterFrame);
+#else
   InstallExceptionTableRegister(__register_frame);
-#endif
+#endif // __APPLE__
+#endif // __GNUC__
   
   // Initialize passes.
   PM.doInitialization();