2

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:

  • After compiling with clang++: undefined symbol: _ZN9BaseClassD2Ev
  • After compiling with g++: 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.

8
  • 1
    use command echo _ZTI9BaseClass | c++filt to demangle that name Commented Aug 17, 2020 at 14:20
  • 1
    You need to provide a definition for the BaseClass destructor. Commented Aug 17, 2020 at 14:22
  • @Slava Thanks! This is a really helpful command, I didn't know about it. Commented Aug 17, 2020 at 14:22
  • @G.M. I have now done that, but the missing item now is still _ZTI9BaseClass, which appears to be the type info for the base class. Commented Aug 17, 2020 at 14:23
  • 2
    Is BaseClass::function implemented? Commented Aug 17, 2020 at 14:28

1 Answer 1

3

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!

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

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.