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;
}
test-eh.sh
Description: Bourne shell script
_______________________________________________ Libunwind-devel mailing list [email protected] http://lists.nongnu.org/mailman/listinfo/libunwind-devel
