I have a C++ program which I need to open a shared object (.so) and load a C++ class from it. I have seen this done on the internet and I have loaded many things using dlsym() in both C and C++, so I know how everything works. However, there is a slight difference I need to make. The class I am loading with dlsym() must be a child class of another class which is defined in a header used by both the shared object and the main program. The idea is to load a bunch of different custom versions of this class at runtime. Linking with the object at compile-time is not an option. Here is what I have done (heavily abbreviated, but all the important parts are there):
BaseClass.h:
class BaseClass {
public:
virtual ~BaseClass();
virtual int function(int, int);
};
ChildClass.h:
extern "C" BaseClass* makechild();
ChildClass.cpp:
class ChildClass : public BaseClass {
public:
int function(int a, int b) override { return a + b; }
};
BaseClass* makechild() {
return new ChildClass();
}
main.cpp:
std::function<BaseClass*(void)> make;
// note that in the actual program I throw a std::runtime_error with
// dlerror() as its message when this goes wrong, and I don't handle it
make = reinterpret_cast<BaseClass*(*)(void)>(dlsym(handle, "makechild"));
BaseClass* myclass = make();
I then compile the ChildClass.cpp as ChildClass.so (using -shared -fpic
) and I compile main.cpp with -ldl
and some other warning-related flags and such. Using g++ and clang++ I get two different errors at runtime:
undefined symbol: _ZN9BaseClassD2Ev
undefined symbol: _ZTI9BaseClass
After checking the .so file with objdump and readelf, I confirm that this symbol is indeed undefined:
...
0000000000000000 *UND* 0000000000000000 _ZN9BaseClassD2Ev
...
0000000000000000 *UND* 0000000000000000 _ZTI9BaseClass
...
I am not sure what these symbols mean exactly. After a little bit of reading of the ABI docs, it seems the first might be a dtor or something, and the second is probably the ctor or the class itself, something like that. In any case, I am not explicitly trying to load these, but I am not sure why they are undefined. Is it possible to do this polymorphism/inheritance setup with dlsym() and C++?
Edit: Thanks to a comment, I have used the C++filt program to find out that the destructor was missing (I just added virtual ~BaseClass() = default;
to fix that). Now the one thing missing is the _ZTI
one, which is the typeinfo for the class.
As it turns out, the destructor and the typeinfo were the symbols which were undefined. Given how many times I've done things like this, I should have seen that, but nobody's perfect. Anyway, all I did to fix this was add = default
for BaseClass::~BaseClass
and = 0
for BaseClass::function
. Thanks a lot to user G.M. in the comments on my original post for bringing this up!