Linux Dynamic Loading of shared libraries

Steve Teale steve.teale at britseyeview.com
Mon Mar 10 04:59:19 PDT 2014


On Sunday, 9 March 2014 at 12:07:22 UTC, Steve Teale wrote:

> Now suppose that my D shared library contains a class, rather 
> that just module ctors/dtors, how do I go about creating an 
> instance of that class and using its methods?
>
After wandering down several dead-end paths, and help from other 
contributors, I have finally come up with something that looks 
like the basis of a plugin pattern for Linux DMD using shared 
objects (.so files). This is somewhat long for a forum post. You 
can download this readme and the associated files from 
britseyeview.com/plugin101.tar.bz2

To get started, you need a base class that provides declarations 
for all functions that the plugin will be allowed to use 
externally. Why base class, and not interface? Well I guess 
because interfaces don't provide any information about data. If 
you create a shared library based on an interface, then all the 
shared object methods that reference data in the class that 
implements the interface fail miserably. I'm sure someone will 
explain why - probably some obvious thing I have overlooked.

OK, so my base class is:

module plugin;

class Plugin
{
    int n;
    this(int _n) { n = _n; }

    int foo() { return int.min; }
    void bar() {}
}


The class that implements this base in the shared library is:

module exta;
import plugin;
import std.stdio;
import std.math;

class ExtA: Plugin
{
    double d;
    this(int n) { super(n); d = PI; }

    override int foo() { return ++n; }
    override void bar() { writefln("Done my thing (%f)", d); }
}

Plugin getInstance(int n)
{
    return new ExtA(n);
}

shared static this() {
   writeln("exta.so shared static this");
}

shared static ~this() {
   writeln("exta.so shared static ~this");
}

The module ctor/dtor are included because that has become 
conventional in discussions about dynamic loading. Otherwise, the 
so has the class implementation - ExtA, and a shared method to 
create an instance of same. It includes references to methods in 
Phobos.

The test program is as follows:

module main;
import core.runtime;
import std.stdio;
import plugin;

extern(C) void* dlsym(void*, const char*);

alias Plugin function(int) pfi;

Plugin getPlugin(string name)
{
    void* lib = Runtime.loadLibrary(name~".so");
    if (lib is null)
    {
       writeln("failed to load plugin shared object");
       return null;
    }

    void* vp = dlsym(lib, 
"_D4exta11getInstanceFiZC6plugin6Plugin\0".ptr);
    if (vp is null)
    {
       writeln("plugin creator function not found");
       return null;
    }
    pfi f = cast(pfi) vp;
    Plugin x = f(42);
    if (x is null)
    {
       writeln("creation of plugin failed");
       return null;
    }
    return x;
}

void main()
{
    Plugin x = getPlugin("exta");
    int n = x.foo();
    writefln("n = %d", n);
    x.bar();
}

The long symbol name used in the dlsym() call is of course from 
the .map file generated when the .so file is created

These can be built using the following primitive makefile, whose 
main purpose is to spell out the required compiler flags:

main :
	dmd -c plugin.d
	dmd -c -shared -fPIC exta.d
	dmd exta.o -shared -defaultlib=libphobos2.so -map
	dmd -c main.d
	dmd main.o plugin.o -L-ldl -defaultlib=libphobos2.so -L-rpath=.

This assumes that the plugins will be in the same directory as 
the executable (rpath=.).

Note that there is no call to Runtime.unloadLibrary(). The 
assumption her is that once the plugin has been loaded it will be 
there for the duration of the program. If you want to unload it 
you'll probably have to make sure the plugin object is purged 
from memory first, and I have not discovered how to do that yet 
;=(

Steve


More information about the Digitalmars-d-learn mailing list