1

I'm trying to create a shared library containing a base class so that it could be derived:

base.h

class Base
{
public:

    virtual ~Base () {}
    virtual void Function ();
};

base.cpp

#include <stdio.h>
#include "base.h"

void Base::Function ()
{
    printf ("base function\n");
}

mybase.so

g++ -fpic -g -shared base.cpp -o libbase.so

main.cpp

#include "base.h"

class Derived : public Base
{
};

int main (int argc, char** argv)
{
    Derived* d = new Derived ();

    d->Function ();

    delete d;

    return 1;
}

I also want to avoid linking the executable with the shared library, so I create it by ignoring unresolved symbols:

test

g++ -fpic -rdynamic -Wl,--unresolved-symbols=ignore-in-object-files -g main.cpp -o test

Finally I use LD_PRELOAD environment variable for preloading the shared library before execution

LD_PRELOAD=./libbase.so ./test
Segmentation fault (core dumped)

I've noticed the problem is that virtual table for Derived object is undefined for "Function":

(gdb) info vtbl d
vtable for 'Derived' @ 0x601030 (subobject @ 0x602010):
[0]: 0x400c7e <Derived::~Derived()>
[1]: 0x400cc0 <Derived::~Derived()>
[2]: 0x0

My guess is that when executable gets loaded, the dynamic linker cannot resolve the vtable entries since the shared library has not been loaded yet.

So my question is: Is there a way of making this work? Maybe forcing somehow to load the shared library before the executable...

BTW: By making "Function" non virtual everything works OK, since no vtable is needed for Derived class.

UPDATE 1: Using an object instead a pointer makes main to work:

int main (int argc, char** argv)
{
    Derived d;

    d.Function ();  // prints "base function"

    return 1;
}

UPDATE 2: Doing the same as in main but in a second shared library also works:

mylib.cpp

#include "base.h"

class DerivedLib : public Base
{
};

extern "C" void do_function()
{
    DerivedLib* d = new DerivedLib();

    d->Function(); 

    delete d;
}

mylib.so

g++ -fPIC -g -shared lib.cpp -o libmylib.so 

main.cpp

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

class Derived : public Base
{
};

int main (int argc, char** argv)
{
    void* handle = dlopen("libmylib.so", RTLD_LAZY);

    void (*do_function)();

    do_function = (void (*)())dlsym(handle, "do_function");

    do_function();  // prints "base function"

    Derived* d = new Derived();

    d->Function (); // <- crashes

    delete d;

    return 1;
}

So definitely problem arises when a new instance pointer is created inside the executable

Josema
  • 11
  • 4
  • Why don't you want to link to the shared library? – Alan Birtles Sep 14 '18 at 06:59
  • Because I want to maintain unchanged the executable (same hash value) and continue developing the shared library – Josema Sep 14 '18 at 07:03
  • I'm not sure what you mean by that but as long as you don't change your declaration of `Base` there shouldn't be any problem using the same executable with different versions of the shared library. If you do change the declaration of `Base` you'll have to recompile your executable anyway – Alan Birtles Sep 14 '18 at 07:11
  • You should take a look at the discussion : https://stackoverflow.com/questions/496664/c-dynamic-shared-library-on-linux?rq=1 – tunglt Sep 14 '18 at 07:35
  • Alan: If I link the executable to the shared library and I change the library, for instance, by adding a new class (i.e. without modifying Base), executable will be different... and I want to avoid this – Josema Sep 14 '18 at 07:48

2 Answers2

0

If the reason you are attempting not to link to the shared library is that you want to keep changing the shared library without breaking the executable, really, as long as you don't change the public interface of the library class you're using. If you do change that, you're going to need to recompile the executable whatever you do. Keep in mind a few things.

  1. Try to keep as much of the implementation as possible out of the header files included by your executable. Changing the header files will force a recompile that's not always necessary.

  2. You shouldn't need to recompile the executable if you add a class to the shared library. That just shouldn't be an issue.

OrdoFlammae
  • 721
  • 4
  • 13
0

The solution (where available) is creating a PIE executable.

test:

g++ -fpic -pie -fpie -rdynamic -Wl,--unresolved-symbols=ignore-in-object-files -g main.cpp -o test

LD_PRELOAD=./libbase.so ./test
base function
Josema
  • 11
  • 4