Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

C++ Dynamic Shared Library on Linux

Asked 15 years, 2 months ago Modified 2 years, 10 months ago Viewed 287k times

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
190
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.

c++ linux shared-libraries

Share Improve this question Follow edited May 23, 2017 at 10:31 asked Jan 30, 2009 at 18:41
Community Bot Bill the Lizard
1 1 402k 210 570 885

I'm not sure I understand what you mean by "using" it, once a pointer to the object is returned, you could use it like
you use any other pointer to an object. – codelogic Jan 30, 2009 at 19:41

The article I linked to shows how to create a function pointer to an object factory function using dlsym. It doesn't
show the syntax for creating and using objects from the library. – Bill the Lizard Jan 30, 2009 at 19:54

1 You will need the header file describing the class. Why do you think you have to use "dlsym" instead of just letting
the OS find and link the library at load time? Let me know if you need a simple example. – nimrodm Jan 30, 2009
at 20:00

3 @nimrodm: What's the alternative to using "dlsym"? I'm (supposed to be) writing 3 C++ programs that will all use
the classes defined in the shared library. I also have 1 Perl script that will use it, but that's a whole other problem for
next week. – Bill the Lizard Jan 30, 2009 at 20:06

4 Answers Sorted by: Highest score (default)

myclass.h

184 #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.

Share Improve this answer Follow edited Jan 30, 2009 at 21:18 answered Jan 30, 2009 at 20:37
codelogic
72.5k 9 60 54

7 I'm in the process of trying this, but just have one question. Is it strictly necessary to use void*, or could the
create_object function return MyClass* instead? I'm not asking you to change this for me, I'd just like to know if
there's a reason to use one over the other. – Bill the Lizard Jan 30, 2009 at 21:05

1 It can be MyClass*, no reason for it to be void*, I've updated it. – codelogic Jan 30, 2009 at 21:19

1 Thanks, I tried this and it worked as is on Linux from the command line (once I made the change you suggested in
the code comments). I appreciate your time. – Bill the Lizard Jan 31, 2009 at 4:16

1 Is there any reason you would declare these with extern "C"? As this is compiled using a g++ compiler. Why would
you want to use c naming convention? C can not call c++. A wrapper interface written in c++ is the only way to call
this from c. – ant2009 Dec 11, 2012 at 11:25

7 @ant2009 you need the extern "C" because the dlsym function is a C function. And to dynamically load the
create_object function, it will use C-style linkage. If you wouldn't use the extern "C" , there would be no way
of knowing the name of the create_object function in the .so file, because of name-mangling in the C++
compiler. – kokx Jan 3, 2013 at 23:27

Looks like the cast in MyClass* myClass = (MyClass*)create(); isn't necessary (anymore). Somewhat
strange that none of the the previous 49K viewers noticed it :) Or am I wrong? Also, wouldn't it be nice to hint the
readers on why the client code will not even link had it tried to call the constructor, while calling a virtual function is
ok (the answer would also clarify the cryptic-looking comment on linker's tastes :))? – mlvljr Mar 17, 2013 at 21:33

1 Nice method, it's very similar to what someone would do on a Microsoft compiler. with a bit of #if #else work, you
can get a nice platform independent system – user365268 Jun 22, 2013 at 11:26

1 I know this is old (I nearly said "three years", but then I realized it's more like nearly 5 years), but it's important to
call dlclose on the handle. Please add that. – stefan Oct 4, 2013 at 9:05

The function ptrs are a bit messy – Peter Chaula Aug 7, 2016 at 7:42

is -fPIC necessary? – ar2015 Aug 12, 2018 at 14:32

Why is destroy_object function needed? Can we just call delete myClass in main ? – betteroutthanin Nov
5, 2018 at 11:33

No explanation whatsoever on the compilation commands and the flags. If someone could elaborate and edit the
answer it would be awesome. – Tony Tannous Jan 13, 2020 at 7:55

1 The -fPIC (Position Independent Code) flag is necessary because you can't require the shared library to provide
the things in its interface at a specific address. Havinc PIC allows for different versions of the library to work even
though all the addresses of functions etc are different. The -shared flag lets the linker know to make it a .so (shared
object) instead of an .a (static library). See gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options for details.
– danba Mar 9, 2020 at 6:05
1 You don't need to write extern "C" for every function. Just wrap all the functions in an extern "C" { ... }
block – Jakob Kenda Apr 14, 2022 at 17:00

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
62 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)

Share Improve this answer Follow edited Apr 9, 2013 at 15:54 answered Jan 30, 2009 at 20:16
ashgkwd nimrodm
195 11 23.5k 7 60 61

1 This appears (to my very untrained eye) to be statically linking libshared.so to your executable, rather than using
dynamic linking at run-time. Am I correct? – Bill the Lizard Jan 30, 2009 at 21:13

11 No. This is standard Unix (Linux) dynamic linking. A dynamic library has the extension ".so" (Shared Object) and is
linked with the executable (main in this case) at load time -- every time main is loaded. Static linking occurs at link
time and uses libraries with the extension ".a" (archive). – nimrodm Jan 30, 2009 at 21:49

10 This is dynamically linked at build time. In other words you need prior knowledge of the library you're linking
against (e.g. linking against 'dl' for dlopen). This is different from dynamically loading a library, based on say, a user
specified filename, where prior knowledge is not needed. – codelogic Jan 31, 2009 at 1:12

1 Build time? What exactly do you mean by build? The library is linked in when 'main' is loaded. This is handled by
the linux dynamic linker (ld-linux.so - last in the dependency list). Replacing libshared.so with a newer library will
work automatically. No need to recompile/re-link (rebuild?) – nimrodm Jan 31, 2009 at 6:54

Thank you, I did finally get this to work. I also verified that the shared library is loaded at run time. Pretty simple to
verify by just deleting the library and running main. :) – Bill the Lizard Jan 31, 2009 at 14:06

11 What I was trying to explain (badly) is that in this case, you need to know the name of the library at build time (you
need to pass -lshared to gcc). Usually, one uses dlopen() when that information is not available, i.e. the library's
name is discovered at runtime (eg: plugin enumeration). – codelogic Mar 2, 2009 at 19:46

3 Use -L. -lshared -Wl,-rpath=$$(ORIGIN) when linking and drop that LD_LIBRARY_PATH=. .
– Maxim Egorushkin May 31, 2018 at 18:04

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.
16
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

Share Improve this answer Follow edited Feb 20, 2019 at 22:52 answered Feb 7, 2019 at 21:33
Xavier Lamorlette
1,232 1 14 22

This is an excellent example. RAII is definitely the way to go. – David Steinhauer Mar 6, 2019 at 18:13

Why not std::unique_ptr<void, void(*)(void *)> handle (or an explicit dlcloser functor)? – Caleth
Oct 1, 2020 at 11:03

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,
9 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.

Share Improve this answer Follow answered Jan 30, 2009 at 20:20
Matt Lewis
531 1 3 6

1 The reason I need a dynamic shared library is that I'll also be calling it from Perl code. It may be a complete
misconception on my own part that I also need to call it dynamically from other C++ programs that I'm developing.
– Bill the Lizard Jan 30, 2009 at 21:44

I've never tried integrated perl and C++, but I think you need to use XS: johnkeiser.com/perl-xs-c++.html
– Matt Lewis Jan 30, 2009 at 23:15

You might also like