Search code examples
c++dlopenshared-objectsundefined-symbol

Using objects of an app from shared object


I'm a little confused right now. Yesterday I had undefined symbols even if I used -rdynamic with g++. But now I don't have any error and that is even more disturbing.

To explain a bit my case, I want to do some sort of plugin like shared object. I haven't decided yet which is the best way to do.

A) My shared objects all have a function called register which will be called with a parameter. This will be a plugin manager.

B) My shared object will define a class and will create and instance of that class at load time. In the constructor of that class it will try to get a static singleton from the app and auto register himself.

As far as I can tell my first attempts weren't so great so far.

main.cpp

#include "main.hpp"
#include <iostream>
#include <cstdio>
#include <dlfcn.h>

int S::shared = 0;

int main(int argc, char** argv){
    std::cout << "In main -> " << S::shared << "\n";

    void* triangle = dlopen("./libtwo.so", RTLD_LAZY);

    if(triangle == NULL){
        std::cout << "Error while loading so file\n" << dlerror() << "\n";
    }

    std::cout << "In main -> " << S::shared << "\n" << triangle;
    return 0;
}

main.hpp

class S {
    public:
    static int shared;

    S(){
        S::shared = 0;
    };
};

two.cpp

#include "main.hpp"
#include <iostream>

class MyObject {
    public:
    MyObject(){
        std::cout << "In two -> " << S::shared  << "\n";
    }
};

MyObject t();

In that sample S::shared is the static object I would share. For this simple test I only use a int but in the futur it would be an instance of a class.

My only attempt at case A) was a segfault... I really don't know what I missed.

//Results so far (today)

piplup@vika:~/dev/WebDesign/Scproci$ scons -Q
g++ -o two.os -c -fPIC two.cpp
g++ -o libtwo.so -shared two.os
g++ -o main.o -c -fPIC main.cpp
g++ -o main -Wl,--export-dynamic main.o -ldl
piplup@vika:~/dev/WebDesign/Scproci$ ./main
In main -> 0
In main -> 0

Solution

  • #include "main.hpp" 
    #include <iostream>  
    
    class MyObject {     
        public:     
            MyObject(){         
                std::cout << "In two -> " << S::shared  << "\n";     
            } 
    };  
    
    MyObject* t;
    
    __attribute__((constructor))
    void init_two()
    {
        t = new MyObject();
    }
    
    __attribute__((destructor))
    void uninit_two()
    {
        delete t;
    }
    

    This should produce what is expected. Pointers are used as it is easier to deal with them explicitly in shared objects as regular initialization doesn't happen automatically. If you don't want to use the pointers provide an explicit initialization for your class and call it in the shared library initialzation function.

    * EDIT *

    I did some additional experimentation and it appears that if you are using the default constructor, use it implicitly and it will be called, if you are using a non-default constructor then as normal.

    so you could change your:

    MyObject t();
    

    call to:

    MyObject t;
    

    and it wil work without the explicit initialization functions being defined.

    or

    class MyObject {
    public:
       MyObject() { /* as before */ };
       MyObject(int val)
       {
            S::shared = val;
            std::cout << "In two -> " << S::shared << "\n";
       }
    };
    
    MyObject t(10);
    

    It appears that the compiler gets confused as to whether the MyObject t(); is a variable declaration, or a function declaration at the global scope and treats it as a function declaration.