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 w开发者_如何转开发ith 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.
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.
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.
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 exceptionmyLib1.so
has the function that loadsmyLib.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 $@
精彩评论