3

When i use dlopen to dynamically load a library it seems i can not catch exceptions thrown by that library. As i understand it it's because dlopen is a C function.

Is there another way to dynamically load a library that makes it possible to catch exceptions thrown by the lib in GCC?

In Windows you can use LoadLibrary but for Linux i have only found dlopen but when using dlopen i can not catch exceptions.

Edit: I have tried void* handle = dlopen("myLib.so", RTLD_NOW | RTLD_GLOBAL); and I still cant catch exceptions thrown by myLib.so

Edit 2: I throw custom exceptions with its own namespace. I want to be able to catch these exceptions outside the library. I want to be able to compile on different compilers, for example GCC 3.2 and GCC 4.1.

In myLib2.so i throw exceptions, one example:

namespace MyNamespace {  
    void MyClass::function1() throw(Exception1) {  
        throw Exception1("Error message");  
    } 
}

In myLib1.so I want to catch that exception:

std::auto_ptr <MyNamespace::MyClass> obj = MyNamespace::getClass();
try {  
    obj->function1();  
} catch (MyNamespace::Exception1& e) {  
    std::cout << e.what();  //This is not caught for some reason.  
}

mylib1.so dynamically loads myLib2.so with:

void* handle = dlopen("myLib2.so", RTLDNOW | RTLDGLOBAL);

This works in Windows (to catch my exceptions) but there I dont use dlopen of course.

Edit 3: myLib1.so is dynamically linked.

6
  • Maybe something with different compile settings. Can you check results of objdump -TC mylib1.so | grep Exception1 with objdump -TC mylib2.so | grep Exception1? sizes of typeinfos should be equal Commented Nov 16, 2009 at 13:37
  • objdump -TC mylib1.so | grep Exception1 gives nothing while objdump -TC mylib2.so | grep Exception1 gives some. Commented Nov 16, 2009 at 13:56
  • This is weird, and probably the source of this problem. Maybe you have some linker options preventing symbols from being exported globally in mylib1? Commented Nov 16, 2009 at 14:18
  • My flags to build mylib1 is: -shared -fPIC -rdynamic -W1,--export-dynamic -W1,-soname,libMyLib.so My flags to build mylib2 is the same Commented Nov 16, 2009 at 14:37
  • Can it be that code in mylib1 just not get executed? What behavior do you see, call to std::terminate because of not catched exception? Commented Nov 16, 2009 at 14:54

3 Answers 3

8

You need to specify RTLD_GLOBAL flag to dlopen. This will allow correct weak symbol binding, so each typeinfo symbol for exception object will point at the same place, which is needed by exception processing ABI code.

Sign up to request clarification or add additional context in comments.

3 Comments

When i use the flag RTLD_GLOBAL to dlopen i get dlerror: invalid mode for dlopen(): Invalid argument. void* handle = dlopen("myLib.so", RTLD_GLOBAL); This dlopen statement is in a shared library that my code calls. My code -> shared lib -> dynamically loads another lib with dlopen("myLib.so", RTLD_GLOBAL);
I now use: void* handle = dlopen("myLib.so", RTLD_NOW | RTLD_GLOBAL); And it does compile but i still cant catch exceptions thrown by myLib.so
Which type of exception do you throw? Your custom one? Maybe you can update question with short sample code? And also with version of system and compiler
2

This depends on the version of GCC you are using.
First of all, make sure you compile everything with "-fPIC" and link with the "-rdynamic" flag.
RTLD_NOW flag is still needed.

1 Comment

I compile everything with -shared -fPIC -W1, --export-dynamic -W1, -soname, libTest.so But I am not sure at all about these flags. I have added -rdynamic and tested but i still cant catch exceptions: -shared -fPIC -W1, -rdynamic --export-dynamic -W1, -soname, libTest.so
0

The set up is unclear, so I improvised

There are multiple descriptions of your set up that are slightly conflicting. So, my description may not match up with what you have, but hopefully it addresses your question.

The setup I used

The stub implementations of the libs are included at the bottom of this answer, as is the Makefile. But the basic organization is:

  • myLib.so has the class with a method that will throw an exception
  • myLib1.so has the function that loads myLib.so and invokes the method

I then attempted various versions of the myTest.cpp to see what would work.

RTLD_GLOBAL does not work on myLib1.so

Invoking dlopen("./myLib1.so", RTLD_GLOBAL | RTLD_NOW) is just like trying to link it into the executable. My attempt to use the above call resulted in a NULL handler being returned. When I attempted to link it directly into the executable instead, I got a linker error.

g++ -g myTest.cpp ./myLib1.so -ldl -o myTest
./myLib1.so: undefined reference to `MyNamespace::MyClass::function1()'
./myLib1.so: undefined reference to `MyNamespace::getClass()'
collect2: error: ld returned 1 exit status

This is an explanation as to why using RTLD_GLOBAL failed. It could not resolve the symbols that are defined in myLib.so. I could fix this by including myLib.so in the link line too, but then there is no dynamic loading happening at all.

RTLD_LAZY can be made to work

If you are not insistent on being able to invoke the code directly, you can use RTLD_LAZY on myLib1.so instead. This would require d dlsym() call to find the entry point function to call. But that lookup will trigger a lazy symbol resolution mechanism that causes the inner code to succeed.

Below is the working myTest.cpp file I used.

#include "myLib1.h"
#include <dlfcn.h>

int main () {
    void * h = dlopen("./myLib1.so", RTLD_LAZY);
    void (*foo)() = (void(*)())dlsym(h, "foo");
    foo();
    dlclose(h);
}

The source files

myLib.h

#pragma once

namespace MyNamespace {

    struct Exception1 {
        const char *what_;
        Exception1(const char *what) : what_(what) {}
        const char * what () const { return what_; }
    };

    struct MyClass {
        friend MyClass * MyNamespace::getClass ();
        void function1 () throw(Exception1);
    private:
        MyClass () {}
    };

    MyClass * getClass ();

}

myLib.cpp

#include "myLib.h"

namespace MyNamespace {
    void MyClass::function1() throw(Exception1) {
        throw Exception1("Error message");
    }

    MyClass * getClass () { return new MyClass(); }
}

myLib1.h

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

void foo ();

#ifdef __cplusplus
}
#endif

myLib1.cpp

#include "myLib1.h"
#include "myLib.h"
#include <iostream>
#include <dlfcn.h>

void foo () {
    void *h = dlopen("./myLib.so", RTLD_GLOBAL | RTLD_NOW);
    MyNamespace::MyClass *p = MyNamespace::getClass();
    try {
        p->function1();
    } catch (MyNamespace::Exception1 e) {
        std::cout << e.what() << std::endl;
    }
    delete p;
    dlclose(h);
}

Makefile

all : myLib.so myLib1.so myTest
clean :
        rm -f *.o *.so myTest

%.so : %.o
        g++ -shared -o $@ $<

%.o : %.cpp
        g++ -std=c++03 -W -Wall -Werror -fPIC -rdynamic -O3 -g -c $<

myLib.so : myLib.h
myLib1.so : myLib1.h myLib.h

myTest : myTest.cpp myLib1.h
        g++ -g $< $(MYLIB) -ldl -o $@

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.