Hi,

i've tested libunwind's exception handling routines under linux-x86 (compiled into libunwind via --enable-cxx-exceptions). Initially my test program segfaulted, so i've patched the code of libunwind a little bit. Two patches are attached.

The first patch addresses a difference between gcc and libunwind: the size of exception_class is different - libunwind uses a 32-bit value whereas gcc uses and expects a 64-bit value (which equals to 'GNUCC++\0' or 'GNUCC++\x01')

The second patch is just a quick'n'dirty fix to get the testing executable running correctly. The problem is, that gcc's personality function (eh_personailty.cc) uses __builtin_eh_return_data_regno(0) (== 0) and __builtin_eh_return_data_regno(1) (== 2) to set data registers for exception handling with _Unwind_SetGR. It expects EAX and EDX to be set, respectively. But libunwind sets EAX and ECX. So, how should this problem be addressed cleanly? Should the registers be renumbered (via UNW_REG_* enums in libunwind-x86.h)? Or should _Unwind_SetGR and _Unwind_GetGR be patched to renumber the registers? Or do you have any other idea?

My test program can simply be compiled and executed by calling test-eh.sh. On my linux machine it fails with gcc's own exception handling routines, while libunwind's ones work great with these patches.

Greetings
Stefan
diff --git a/include/unwind.h b/include/unwind.h
index d9a767d..ed01435 100644
--- a/include/unwind.h
+++ b/include/unwind.h
@@ -26,6 +26,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 #ifndef _UNWIND_H
 #define _UNWIND_H
 
+/* uint64_t */
+#include <stdint.h>
+uint64_t x;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -62,7 +66,7 @@ typedef void (*_Unwind_Exception_Cleanup_Fn) 
(_Unwind_Reason_Code,
                                              struct _Unwind_Exception *);
 
 typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) (int, _Unwind_Action,
-                                               unsigned long,
+                                               uint64_t,
                                                struct _Unwind_Exception *,
                                                struct _Unwind_Context *,
                                                void *);
@@ -71,14 +75,18 @@ typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) (int, 
_Unwind_Action,
    be of type uint64 and the entire structure to be
    double-word-aligned, but that seems a bit overly IA-64-specific.
    Using "unsigned long" instead should give us the desired effect on
-   IA-64, while being more general.  */
+   IA-64, while being more general.
+
+   To be compatible with gcc, exception_class stays 64-bit even on 32-bit 
machines.
+   See gcc/unwind-generic.h.
+   */
 struct _Unwind_Exception
   {
-    unsigned long exception_class;
+    uint64_t exception_class;
     _Unwind_Exception_Cleanup_Fn exception_cleanup;
     unsigned long private_1;
     unsigned long private_2;
-  };
+  } __attribute__((__aligned__));
 
 extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *);
 extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *,
diff --git a/src/unwind/RaiseException.c b/src/unwind/RaiseException.c
index 5533876..66a681d 100644
--- a/src/unwind/RaiseException.c
+++ b/src/unwind/RaiseException.c
@@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 PROTECTED _Unwind_Reason_Code
 _Unwind_RaiseException (struct _Unwind_Exception *exception_object)
 {
-  unsigned long exception_class = exception_object->exception_class;
+  uint64_t exception_class = exception_object->exception_class;
   _Unwind_Personality_Fn personality;
   struct _Unwind_Context context;
   _Unwind_Reason_Code reason;
diff --git a/src/unwind/unwind-internal.h b/src/unwind/unwind-internal.h
index 169bad5..130cb81 100644
--- a/src/unwind/unwind-internal.h
+++ b/src/unwind/unwind-internal.h
@@ -38,7 +38,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 #define _U_VERSION     1
 
 typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)
-       (int, _Unwind_Action, unsigned long, struct _Unwind_Exception *,
+       (int, _Unwind_Action, uint64_t, struct _Unwind_Exception *,
         struct _Unwind_Context *);
 
 struct _Unwind_Context {
@@ -59,7 +59,7 @@ _Unwind_Phase2 (struct _Unwind_Exception *exception_object,
                struct _Unwind_Context *context)
 {
   _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) exception_object->private_1;
-  unsigned long exception_class = exception_object->exception_class;
+  uint64_t exception_class = exception_object->exception_class;
   void *stop_parameter = (void *) exception_object->private_2;
   _Unwind_Personality_Fn personality;
   _Unwind_Reason_Code reason;
diff --git a/src/unwind/SetGR.c b/src/unwind/SetGR.c
index 056bfc7..e684065 100644
--- a/src/unwind/SetGR.c
+++ b/src/unwind/SetGR.c
@@ -29,6 +29,16 @@ PROTECTED void
 _Unwind_SetGR (struct _Unwind_Context *context, int index,
               unsigned long new_value)
 {
+  // quidk'n'dirty workaround for gcc compatibility
+  // gcc uses __builtin_eh_return_data_regno(0) == 0 as index for first 
parameter (EAX)
+  // and __builtin_eh_return_data_regno(1) == 2 as index for second parameter 
(EDX)
+  if (index == __builtin_eh_return_data_regno(0)) {
+    index = 0;
+  }
+  if (index == __builtin_eh_return_data_regno(1)) {
+    index = 1;
+  }
+
   unw_set_reg (&context->cursor, index, new_value);
 #ifdef UNW_TARGET_IA64
   if (index >= UNW_IA64_GR && index <= UNW_IA64_GR + 127)
void foo()
{
    bar();
}
#include <assert.h>

struct Test
{
public: // --- ctor/dtor ---
    Test() { ++counter_; }
    ~Test() { -- counter_; }
    Test(const Test&) { ++counter_; }

public: // --- static members ---
    static int counter_;
};

int Test::counter_ = 0;

// Called by foo
extern "C" void bar()
{
    Test t;
    try {
        Test t;
        throw 5;
    } catch (...) {
        Test t;
        throw;
    }
}

// External C function, which calls bar() and
// doens't contain C++ exception handling code.
// libgcc can't handle this, but libunwind can.
extern "C" void foo();

int main()
{
    try {
        Test t;
        foo();
    } catch (int) {
        // Dtor of all Test-object has to be called.
        assert(Test::counter_ == 0);
        return Test::counter_;
    } catch (...) {
        // An int was thrown - we should not get here.
        assert(false);
        return 1;
    }
    assert(false);
    return 1;
}

Attachment: test-eh.sh
Description: Bourne shell script

_______________________________________________
Libunwind-devel mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/libunwind-devel

Reply via email to