Suppose I have a shared library with a class that defines public non-virtual methods, and I want to import said shared library but re-defining some of those class methods without creating a new class, in a way that the library would call the methods I redefine when those are used internally within the shared library.
From some experiments with GCC in linux, everything seems to work as intended, but I am wondering if I am just getting lucky or if what I am doing is guaranteed to work correctly in other environments.
Example:
shared.h:
class Myclass {
public:
int val;
void set_myval(); //I want to override this
void set_myval_v2();
void print_myval();
};
shared.cpp:
#include <iostream>
#include "shared.h"
void Myclass::set_myval() {
this->val = 100;
}
void Myclass::set_myval_v2() {
this->set_myval();
}
void Myclass::print_myval() {
std::cout << "val:" << this->val << std::endl;
}
app.cpp:
#include "shared.h"
void Myclass::set_myval() {
this->val = 200;
}
int main()
{
Myclass obj;
obj.set_myval();
obj.print_myval();
obj.set_myval_v2();
obj.print_myval();
return 0;
}
I then compile it and run as follows:
g++ -c -fPIC shared.cpp -o shared.o
gcc shared.o -shared -o libshared.so
g++ app.cpp -L<path> -l:libshared.so -Wl,-rpath=<path>
./a.out
val:200
val:200
Is this guaranteed to work when done in other compilers/OSes/etc.?
No, this is a violation of C++ ODR rule and is handled in different ways on different platforms. It is not portable even for GCC - interposition will not work if e.g. the symbols in code have protected visibility or library has been linked with -Wl,-Bsymbolic or with -fno-semantic-interposition.
Related
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
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.
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.
You shouldn't need to recompile the executable if you add a class to the shared library. That just shouldn't be an issue.
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
I wanted to test what happens if executable and library share different versions of a library,
i.e. different classes with the same name. Idea: make a test function which
is called once from the executable directly, and once with the code from the library:
MWE:
base.h
defines an abstract plugin class, which can generate a port object (of type base)
struct base
{
virtual void accept(struct visitor& ) {}
virtual void test() = 0;
virtual ~base() {}
};
struct visitor
{
virtual void visit(struct base& ) {}
};
struct plugin
{
virtual ~plugin() {}
virtual base& port() = 0;
virtual void test() = 0;
};
typedef plugin* (*loader_t) (void);
plugin.cpp
defines a derived plugin class, which can return a derived port (mport)
#include <iostream>
#include "base.h"
struct mport : public base
{
void accept(struct visitor& ) override {}
void test() override { std::cout << "plugin:test" << std::endl; }
virtual ~mport() = default;
};
struct port_failure_plugin : public plugin
{
void test() override final { inp.test(); }
virtual ~port_failure_plugin() {}
private:
mport inp;
base& port() override { return inp; }
};
extern "C" {
const plugin* get_plugin() { return new port_failure_plugin; }
}
host.cpp
defines a derived port class with the same name (mport)
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <dlfcn.h>
#include "base.h"
struct mport : public base
{
#ifdef ACCEPT_EXTERN
void accept(struct visitor& ) override;
#else
void accept(struct visitor& ) override {}
#endif
void test() override { std::cout << "host:test" << std::endl; }
};
#ifdef ACCEPT_EXTERN
void mport::accept(struct visitor& ) {}
#endif
int main(int argc, char** argv)
{
assert(argc > 1);
const char* library_name = argv[1];
loader_t loader;
void* lib = dlopen(library_name, RTLD_LAZY | RTLD_LOCAL);
assert(lib);
*(void **) (&loader) = dlsym(lib, "get_plugin");
assert(loader);
plugin* plugin = (*loader)();
base& host_ref = plugin->port();
host_ref.test(); // expected output: "host:test"
plugin->test(); // expected output: "plugin:test"
return EXIT_SUCCESS;
}
Compile e.g.:
g++ -std=c++11 -DACCEPT_EXTERN -shared -fPIC plugin.cpp -o libplugin.so
g++ -std=c++11 -DACCEPT_EXTERN -ldl -rdynamic host.cpp -o host
The complete code is on github (try make help)
In order to let the host run test "like the plugin does",
it calls a virtual function, which is implemented in the plugin. So I expect that test is called
once from the object code of the host executable (expectation: "host:test")
once from the object code of the plugin library (expectation: "plugin:test")
The reality looks different:
In all (of the following) cases, both outputs are equal (2x"host:test" or 2x"plugin:test")
Compile host.cpp with -rdynamic, and without -DACCEPT_EXTERN the test calls output "plugin:test"
Compile host.cpp with -rdynamic, and with -DACCEPT_EXTERN (see Makefile), and the test calls call "host:test"
Compile host.cpp without -rdynamic, and the test calls output plugin:test (both intern and extern)
Questions:
Is it even possible to call both versions of mport::test (e.g. executable and library)?
Why does -rdynamic change the behavior?
Why does -DACCEPT_EXTERN affect the behavior?
The thing here is you are violating the one definition rule.
Your two versions of mport::test have the same declaration, yet they do not have the same definition.
But you are doing it a dynamic linking time. Now, the C++ standard does not concern itself with dynamic loading. We have to turn to the x86 ELF ABI for further detail.
Long story short, the ABI supports a technique known as symbol interposition, which allows substituting symbols dynamically and still see consistent behavior. This is what you are doing here, though inadvertently.
You can check it manually:
spectras#etherhop$ objdump -R libplugin.so |grep test
0000000000202cf0 R_X86_64_64 _ZN19port_failure_plugin4testEv##Base
0000000000202d10 R_X86_64_64 _ZN5mport4testEv##Base
0000000000203028 R_X86_64_JUMP_SLOT _ZN5mport4testEv##Base
Here you see that, in the shared object, all uses of mport::test have a relocation entry. All calls go through the PLT.
spectras#etherhop$ objdump -t host |grep test
0000000000001328 w F .text 0000000000000037 _ZN5mport4testEv
Here you see that host does export the symbol (because of -rdynamic). So when dynamic linking libplugin.so, the dynamic linker will use the mport::test of your main program.
That's the basic mechanism. That's also why you don't see this without -rdynamic: the host no longer exports its own version, so the plugin uses its own.
How to fix?
You can avoid all this leakage by hiding symbols (a good practice in general, it makes for faster loading and avoids name clashes).
add -fvisibility=hidden when compiling.
manually export your get_plugin function by prepending __attribute__ ((visibility("default"))) to the line. Tip: that's compiler-specific stuff, it's a good idea to make it a macro somewhere.
#define EXPORT __attribute__((visibility("default")))
// Later:
EXPORT const plugin* get_plugin() { /* stuff here */ }
Thus:
spectras#etherhop$ g++ -std=c++11 -fvisibility=hidden -shared -fPIC plugin.cpp -o libplugin.so
spectras#etherhop$ g++ -std=c++11 -fvisibility=hidden -rdynamic host.cpp -ldl -o host
spectras#etherhop$ ./host ./libplugin.so
plugin:test
plugin:test
Another option would be to simply make the class static by enclosing it within an anonymous namespace. This will work in your simple case, but it is not good if your plugin is made of multiple translation units.
As for your expectations of having a different result on your two lines, you're getting a base-reference to a derived class, why would you expect anything other than the appropriate virtual override to be called?
The following code represents the strategy design pattern.
Consider the following program structure:
//base.hpp
#ifndef BASE_HPP
#define BASE_HPP
class Base {
public:
virtual ~Base() {};
virtual int solve() = 0;
};
#endif
//derived.hpp
#include "base.hpp"
class Derived: public Base {
public:
virtual int solve() { return 77; }
};
I have a client.cpp which receives a context of Derived
#include "client.hpp"
#include <iostream>
void Client::operate() {
std::cout << solver_.solve() << std::endl;
}
This is the header file of client:
#include "base.hpp"
class Client {
public:
Client(Base& b): solver_(b) {}
void operate();
private:
Base& solver_;
};
My main: test.cpp
#include "client.hpp"
#include "derived.hpp"
int main() {
Derived d;
Client c(d);
c.operate() ;
}
I would expect it to print 77, but the program doesn't print anything at all.
Client receives a context to Derived by it's constructor and stores it as Base& solver_;
First, the operate method of Client is called, which calls the appropriate solve method of class that derives from Base. In this case, Derived.
Right, that should output 77, but it doesn't. The program compiles fine and no errors, just clean exit. Any ideas ?
I compiled it using following command:
g++ -o program.exe client.cpp test.cpp
I'm using GCC version 5.3.0
I found the solution. The problem was that some of the libraries were not linked by g++, namely libstdc++-6.dll.
This problem can be fixed by using -static-libgcc -static-libstdc++ options
The definition gives:
"When the g++ program is used to link a C++ program, it normally automatically links against libstdc++. If libstdc++ is available as a shared library, and the -static option is not used, then this links against the shared version of libstdc++. That is normally fine. However, it is sometimes useful to freeze the version of libstdc++ used by the program without going all the way to a fully static link. The -static-libstdc++ option directs the g++ driver to link libstdc++ statically, without necessarily linking other libraries statically."
See: libstdc++-6.dll not found
I'm trying to build a shared library using gcc 4.6 on Linux, which is dynamically loaded. As described in many articles on the web as well as in previous questions, I provide c-style factory methods in the library to create and destroy objects. The code - in minimal form - looks like this:
base.h:
class base {
public:
base();
virtual ~base();
virtual int value() = 0;
};
base.cpp:
#include "base.h"
base::base() {}
base::~base() {}
main.cpp:
#include "base.h"
#include <dlfcn.h>
#include <iostream>
int main() {
void* handle = dlopen("liblib.so", RTLD_NOW);
if(handle == NULL) std::cout << dlerror() << std::endl;
// dlsym, ...
}
lib.cpp:
class derived : public base {
public:
derived() {}
virtual ~derived() {}
virtual int value() { return 42; }
};
extern "C" derived* create_object() {
return new derived();
}
It compiles fine with:
g++ -shared -fPIC lib.cpp -o liblib.so
g++ base.cpp main.cpp -ldl -o app
At runtime however it crashes because of a missing typeinfo symbol
liblib.so: undefined symbol: _ZTI4base
In the previous questions I found here, this error was usually due to either some missing "= 0;" or a missing definition of a virtual function. In the example above however, base::value is pure virtual and the destructor has a definition. Strangely enough nm reports _ZTI4base as definied in app:
$ nm app | grep _ZTI4base
0000000000601050 V _ZTI4base
So why isn't the linker using this definition?
The only way I found so far to get the code working is to implement constructur and destructor in the header file. After doing this however, the corresponding symbols for base are reported in liblib.so by nm and totally disappear from app, which probably means, that their definitions were compiled into the library and not into app, which is not what I wanted to achieve. Has anyone an idea how to get the above working without doing this?
You need the -rdynamic option when linking the program, to export its symbols and make them available to libraries loaded with dlopen().
This is a follow-up to Dynamic Shared Library compilation with g++.
I'm trying to create a shared class library in C++ on Linux. I'm able to get the library to compile, and I can call some of the (non-class) functions using the tutorials that I found here and here. My problems start when I try to use the classes that are defined in the library. The second tutorial that I linked to shows how to load the symbols for creating objects of the classes defined in the library, but stops short of using those objects to get any work done.
Does anyone know of a more complete tutorial for creating shared C++ class libraries that also shows how to use those classes in a separate executable? A very simple tutorial that shows object creation, use (simple getters and setters would be fine), and deletion would be fantastic. A link or a reference to some open source code that illustrates the use of a shared class library would be equally good.
Although the answers from codelogic and nimrodm do work, I just wanted to add that I picked up a copy of Beginning Linux Programming since asking this question, and its first chapter has example C code and good explanations for creating and using both static and shared libraries. These examples are available through Google Book Search in an older edition of that book.
myclass.h
#ifndef __MYCLASS_H__
#define __MYCLASS_H__
class MyClass
{
public:
MyClass();
/* use virtual otherwise linker will try to perform static linkage */
virtual void DoSomething();
private:
int x;
};
#endif
myclass.cc
#include "myclass.h"
#include <iostream>
using namespace std;
extern "C" MyClass* create_object()
{
return new MyClass;
}
extern "C" void destroy_object( MyClass* object )
{
delete object;
}
MyClass::MyClass()
{
x = 20;
}
void MyClass::DoSomething()
{
cout<<x<<endl;
}
class_user.cc
#include <dlfcn.h>
#include <iostream>
#include "myclass.h"
using namespace std;
int main(int argc, char **argv)
{
/* on Linux, use "./myclass.so" */
void* handle = dlopen("myclass.so", RTLD_LAZY);
MyClass* (*create)();
void (*destroy)(MyClass*);
create = (MyClass* (*)())dlsym(handle, "create_object");
destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");
MyClass* myClass = (MyClass*)create();
myClass->DoSomething();
destroy( myClass );
}
On Mac OS X, compile with:
g++ -dynamiclib -flat_namespace myclass.cc -o myclass.so
g++ class_user.cc -o class_user
On Linux, compile with:
g++ -fPIC -shared myclass.cc -o myclass.so
g++ class_user.cc -ldl -o class_user
If this were for a plugin system, you would use MyClass as a base class and define all the required functions virtual. The plugin author would then derive from MyClass, override the virtuals and implement create_object and destroy_object. Your main application would not need to be changed in any way.
The following shows an example of a shared class library shared.[h,cpp] and a main.cpp module using the library. It's a very simple example and the makefile could be made much better. But it works and may help you:
shared.h defines the class:
class myclass {
int myx;
public:
myclass() { myx=0; }
void setx(int newx);
int getx();
};
shared.cpp defines the getx/setx functions:
#include "shared.h"
void myclass::setx(int newx) { myx = newx; }
int myclass::getx() { return myx; }
main.cpp uses the class,
#include <iostream>
#include "shared.h"
using namespace std;
int main(int argc, char *argv[])
{
myclass m;
cout << m.getx() << endl;
m.setx(10);
cout << m.getx() << endl;
}
and the makefile that generates libshared.so and links main with the shared library:
main: libshared.so main.o
$(CXX) -o main main.o -L. -lshared
libshared.so: shared.cpp
$(CXX) -fPIC -c shared.cpp -o shared.o
$(CXX) -shared -Wl,-soname,libshared.so -o libshared.so shared.o
clean:
$rm *.o *.so
To actual run 'main' and link with libshared.so you will probably need to specify the load path (or put it in /usr/local/lib or similar).
The following specifies the current directory as the search path for libraries and runs main (bash syntax):
export LD_LIBRARY_PATH=.
./main
To see that the program is linked with libshared.so you can try ldd:
LD_LIBRARY_PATH=. ldd main
Prints on my machine:
~/prj/test/shared$ LD_LIBRARY_PATH=. ldd main
linux-gate.so.1 => (0xb7f88000)
libshared.so => ./libshared.so (0xb7f85000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e74000)
libm.so.6 => /lib/libm.so.6 (0xb7e4e000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb7e41000)
libc.so.6 => /lib/libc.so.6 (0xb7cfa000)
/lib/ld-linux.so.2 (0xb7f89000)
On top of previous answers, I'd like to raise awareness about the fact that you should use the RAII (Resource Acquisition Is Initialisation) idiom to be safe about handler destruction.
Here is a complete working example:
Interface declaration: Interface.hpp:
class Base {
public:
virtual ~Base() {}
virtual void foo() const = 0;
};
using Base_creator_t = Base *(*)();
Shared library content:
#include "Interface.hpp"
class Derived: public Base {
public:
void foo() const override {}
};
extern "C" {
Base * create() {
return new Derived;
}
}
Dynamic shared library handler: Derived_factory.hpp:
#include "Interface.hpp"
#include <dlfcn.h>
class Derived_factory {
public:
Derived_factory() {
handler = dlopen("libderived.so", RTLD_NOW);
if (! handler) {
throw std::runtime_error(dlerror());
}
Reset_dlerror();
creator = reinterpret_cast<Base_creator_t>(dlsym(handler, "create"));
Check_dlerror();
}
std::unique_ptr<Base> create() const {
return std::unique_ptr<Base>(creator());
}
~Derived_factory() {
if (handler) {
dlclose(handler);
}
}
private:
void * handler = nullptr;
Base_creator_t creator = nullptr;
static void Reset_dlerror() {
dlerror();
}
static void Check_dlerror() {
const char * dlsym_error = dlerror();
if (dlsym_error) {
throw std::runtime_error(dlsym_error);
}
}
};
Client code:
#include "Derived_factory.hpp"
{
Derived_factory factory;
std::unique_ptr<Base> base = factory.create();
base->foo();
}
Note:
I put everything in header files for conciseness. In real life you should of course split your code between .hpp and .cpp files.
To simplify, I ignored the case where you want to handle a new/delete overload.
Two clear articles to get more details:
C++ dlopen mini how-to
C++ Dynamic Loading of Shared Objects at Runtime
Basically, you should include the class' header file in the code where you want to use the class in the shared library. Then, when you link, use the '-l' flag to link your code with the shared library. Of course, this requires the .so to be where the OS can find it. See 3.5. Installing and Using a Shared Library
Using dlsym is for when you don't know at compile time which library you want to use. That doesn't sound like it's the case here. Maybe the confusion is that Windows calls the dynamically loaded libraries whether you do the linking at compile or run-time (with analogous methods)? If so, then you can think of dlsym as the equivalent of LoadLibrary.
If you really do need to dynamically load the libraries (i.e., they're plug-ins), then this FAQ should help.