Xavier Lamorlette

Bibliothèques dynamiques en C++

Types de bibliothèques

Bibliothèques statiques

Fichiers .a : archive.
Créés par ar : « concaténation » de fichiers objets .o.
Windows : .lib.

Bibliothèques dynamiques / partagées

Fichiers .so : shared object
Windows : .dll : dynamic link libraries
ELF : Executable and Linking Format
DSO : Dynamic Shared Object

Il faut compiler avec l'option -fPIC : Position Independent Code.

À l'édition de lien, on peut donner le chemin vers la bibliothèque avec l'option -L, sinon il faudra le mettre dans le LD_LIBRARY_PATH.

Outils

ar

Liste les fichiers objets d'une bibliothèque archive.

ldd

Liste les dépendances d'un exécutable.

nm

Liste les symboles d'un fichier objet, d'une bibliothèque archive ou d'une bibliothèque partagée.

nm -u : liste les symboles non définis.

objdump

Liste les informations d'un fichier objet.

readelf

Liste les symboles d'une biliothèque partagée.

Chargement dynamique

Fonctions de base

Références :

En C++ moderne

Interface :

class Base {
public:
    virtual ~Base() {}
    virtual void foo() const = 0;
};

using Base_creator_t = Base *(*)();

Bibliothèque :

class Derived: public Base {
public:
    void foo() const override {}
};

extern "C" {
Base * create() {
    return new Derived;
}
}

Client :

#ifdef _WINDOWS

#include <windows.h>

class Derived_factory {
public:
    Derived_factory() {
        handler = LoadLibrary("derived.dll");
        if (! handler) {
            throw std::runtime_error(Get_error_message());
        }
        FARPROC farproc = GetProcAddress(handler, "create");
        if (! farproc) {
            throw std::runtime_error(Get_error_message());
        }
        creator = reinterpret_cast<Base_creator_t>(farproc);
    }

    std::unique_ptr<Base> create() const {
        return std::unique_ptr<Base>(creator());
    }

    ~Derived_factory() {
        if (handler) {
            FreeLibrary(handler);
        }
    }

private:
    HINSTANCE handler = nullptr;
    Base_creator_t creator = nullptr;

    static std::string Get_error_message() {
        LPVOID message_buffer;
        DWORD dw = GetLastError();
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, dw, 0, (LPTSTR) & message_buffer, 0, NULL);
        std::string error_message(static_cast<char *>(message_buffer));
        LocalFree(message_buffer);
        return error_message;
    }
};

#else

#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);
        }
    }
};

#endif

{
    Derived_factory factory;
    std::unique_ptr<Base> base = factory.create();
    base->foo();
}

La dernière mise à jour de cette page date de février 2019.

Le contenu de ce site est, en tant qu'œuvre originale de l'esprit, protégé par le droit d'auteur.
Pour tout commentaire, vous pouvez m'écrire à xavier.lamorlette@gmail.com.