Exceptions abort if libcxa loaded after startup (Linux)

Exceptions abort if libcxa loaded after startup (Linux)

I've been investigating a problem with C++ exceptions in a shared object loaded by the Python interpreter. It turns out to be a more general issue which I can reproduce with a small driver program, and causes the affected program to abort.

The program is deliberately linked without any dependency on libcxaa, and then loads via dlopen a shared object which does have such a dependency (effectively what happens with Python). This causes libcxa to be loaded after program startup, instead of during initial loading of the image. In this situation, a C++ exception (even one with a suitable catch) aborts execution. The traceback looks like this:

Program received signal SIGABRT, Aborted.
0x42028cc1 in kill () from /lib/i686/libc.so.6
(gdb) where
#0 0x42028cc1 in kill () from /lib/i686/libc.so.6
#1 0x42028ac8 in raise () from /lib/i686/libc.so.6
#2 0x4202a019 in abort () from /lib/i686/libc.so.6
#3 0x40015680 in GetCurrentFrame32 () from ./throw_and_catch.so
#4 0x40014ee4 in _Unwind_RaiseException () from ./throw_and_catch.so
#5 0x4005f48b in __cxa_throw ()
from /opt/intel/compiler70/ia32/lib/libcxa.so.3
#6 0x40014e03 in throw_and_catch () at throw_and_catch.cpp:4
#7 0x08048503 in main () at test_throw_lazy.c:18
#8 0x420158d4 in __libc_start_main () from /lib/i686/libc.so.6

I assume that libcxa does not initialize exception handling correctly if loaded after startup. Is this a known (or already fixed) problem?

Looking for a workaround, is there any way to force libcxa to get loaded with the executable before start-up, short of actually relinking the executable?

Regards,
Raoul Gough.

----

Demonstration transcript follows:

$ cat test_throw_lazy.c
#include 
#include 

int main () {
  void *handle1;
  void (*throw_and_catch)();

  handle1 = dlopen ("throw_and_catch.so", RTLD_LAZY);

  if (!handle1) {
    fprintf (stderr, "%s
", dlerror());
    return 2;
  }

  throw_and_catch = (void (*)()) dlsym (handle1, "throw_and_catch");
  if (dlerror() != (void *)0) return 3;

  throw_and_catch();
  return 0;
}
$ cat throw_and_catch.cpp
extern "C" {
  void throw_and_catch() {
    try {
      throw 0;
    } catch (int) {
    }
  }
}
$ icc -g -o test_throw_lazy.o -c test_throw_lazy.c
$ icc -g -shared -o throw_and_catch.so throw_and_catch.cpp
$ # Use gcc to link without libcxa. Same result can
$ # be achieved with icc -nostdlib
$ gcc -o test_throw_lazy test_throw_lazy.o -ldl
$ ./test_throw_lazy
Abort
$ ldd test_throw_lazy
        libdl.so.2 => /lib/libdl.so.2 (0x40021000)
        libc.so.6 => /lib/i686/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
$ # Try linking with icc, to include libcxa directly
$ icc -o test_throw_lazy test_throw_lazy.o -ldl
$ ./test_throw_lazy
$ ldd test_throw_lazy
        libdl.so.2 => /lib/libdl.so.2 (0x40021000)
        libm.so.6 => /lib/i686/libm.so.6 (0x40025000)
        libcxa.so.3 => /opt/intel/compiler70/ia32/lib/libcxa.so.3 (0x40047000)
        libc.so.6 => /lib/i686/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
$ ldd throw_and_catch.so
        libm.so.6 => /lib/i686/libm.so.6 (0x40013000)
        libcxa.so.3 => /opt/intel/compiler70/ia32/lib/libcxa.so.3 (0x40036000)
        libc.so.6 => /lib/i686/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)
$ icc -V
Intel C++ Compiler f
or 32-bit applications, Version 7.1   Build 20030307Z
Copyright (C) 1985-2003 Intel Corporation.  All rights reserved.
FOR NON-COMMERCIAL USE ONLY

GNU ld version 2.13.90.0.2 20020802
  Supported emulations:
   elf_i386
   i386linux
   elf_i386_glibc21
/usr/lib/crt1.o: In function `_start':
/usr/lib/crt1.o(.text+0x18): undefined reference to `main'
2 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

I've found a workaround for this problem - pre-loading the libunwind shared library.

$ ./test_throw_lazy
Aborted
$ LD_PRELOAD=/opt/intel/compiler70/ia32/lib/libunwind.so.3 ./test_throw_lazy
$ echo $?
0

This does seem a little strange to me, since ldd did not report any dependency on libunwind for the throw_and_catch.so shared object.

Regards,
Raoul Gough.

Login to leave a comment.