Dynamic

This API provides a clean, cross-platform abstraction for working with dynamic/shared libraries in C and C++, enabling applications to load, unload, and interact with external modules at runtime. It wraps platform-specific mechanisms such as LoadLibrary on Windows and dlopen on POSIX systems behind a unified interface, while exposing a lightweight descriptor (fossil_sys_dynamic_lib_t) to track library identity, path, and state. Beyond simple loading, the API emphasizes safety and introspection, offering explicit status tracking, symbol resolution, and error reporting. The accompanying C++ wrapper (fossil::sys::Dynamic) builds on this foundation with RAII semantics, move safety, and basic path validation to reduce misuse and improve reliability, making it well-suited for plugin systems, modular architectures, or runtime feature extension.

HEADER REFERENCE #

#ifndef FOSSIL_SYS_DYNAMIC_H
#define FOSSIL_SYS_DYNAMIC_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stddef.h>

/* ------------------------------------------------------
 * Platform abstraction
 * ----------------------------------------------------- */

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
typedef HMODULE fossil_sys_dynamic_handle_t;
#else
typedef void* fossil_sys_dynamic_handle_t;
#endif

/* ------------------------------------------------------
 * Types
 * ----------------------------------------------------- */

/* Dynamic library descriptor */
typedef struct {
    const char* id;                    /* string ID for AI/meta tracking */
    const char* path;                  /* file path to library */
    fossil_sys_dynamic_handle_t handle;
    int status;                        /* loaded / unloaded / error */
} fossil_sys_dynamic_lib_t;


/* ------------------------------------------------------
 * Lifecycle
 * ----------------------------------------------------- */

/**
 * @brief Load a dynamic library from the specified path.
 *
 * Attempts to load a shared library (.so, .dll, .dylib) and initializes
 * the fossil_sys_dynamic_lib_t structure with the library handle.
 * Sets the status field to indicate success or failure.
 *
 * @param path      Path to the dynamic library file.
 * @param out_lib   Pointer to library descriptor (will be populated).
 * @return          true if library loaded successfully, false otherwise.
 */
bool fossil_sys_dynamic_load(
    const char* path,
    fossil_sys_dynamic_lib_t* out_lib);

/**
 * @brief Unload a previously loaded dynamic library.
 *
 * Releases the library handle and associated resources. Sets the handle
 * to NULL and updates the status field. Safe to call on unloaded libraries.
 *
 * @param lib       Pointer to library descriptor to unload.
 * @return          true if unload succeeded, false on error.
 */
bool fossil_sys_dynamic_unload(
    fossil_sys_dynamic_lib_t* lib);


/* ------------------------------------------------------
 * Symbol resolution
 * ----------------------------------------------------- */

/**
 * @brief Retrieve a function or symbol pointer from the loaded library.
 *
 * Looks up a symbol by name and returns its address. The caller must
 * cast the returned pointer to the appropriate function signature.
 * Returns NULL if the symbol is not found or library is not loaded.
 *
 * @param lib           Pointer to loaded library descriptor.
 * @param symbol_name   Name of the symbol to resolve.
 * @return              Pointer to the symbol, or NULL on failure.
 */
void* fossil_sys_dynamic_symbol(
    fossil_sys_dynamic_lib_t* lib,
    const char* symbol_name);


/* ------------------------------------------------------
 * Introspection / diagnostics
 * ----------------------------------------------------- */

/**
 * @brief Check whether a library is currently loaded.
 *
 * Inspects the library descriptor to determine if the handle is valid
 * and the library is active in memory.
 *
 * @param lib   Pointer to library descriptor.
 * @return      true if library is loaded, false otherwise.
 */
bool fossil_sys_dynamic_is_loaded(
    const fossil_sys_dynamic_lib_t* lib);

/**
 * @brief Retrieve the last error message from dynamic library operations.
 *
 * Returns a platform-specific error string describing the most recent
 * load, unload, or symbol resolution failure. Message is valid until
 * the next dynamic library operation.
 *
 * @return  Error message string, or empty string if no error occurred.
 */
const char* fossil_sys_dynamic_error(void);


#ifdef __cplusplus
}

namespace fossil::sys {

class Dynamic {
private:
    fossil_sys_dynamic_lib_t lib_{};
    bool loaded_ = false;

    /* ----------------------------------------------
     * Internal validation hook
     * (You can expand this later)
     * ---------------------------------------------- */
    static bool validate_path(const char* path) {
        if (!path || !*path) return false;

        /* block obvious traversal attempts */
        if (strstr(path, "..")) return false;

        /* optional: forbid relative paths entirely */
        if (path[0] == '.') return false;

        return true;
    }

public:
    /* ----------------------------------------------
     * Constructors / Destructor
     * ---------------------------------------------- */

    Dynamic() = default;

    explicit Dynamic(const char* path) {
        load(path);
    }

    Dynamic(const char* id, const char* path) {
        lib_.id = id;
        load(path);
    }

    ~Dynamic() {
        if (loaded_) {
            fossil_sys_dynamic_unload(&lib_);
            loaded_ = false;
        }
    }

    /* ----------------------------------------------
     * Non-copyable
     * ---------------------------------------------- */
    Dynamic(const Dynamic&) = delete;
    Dynamic& operator=(const Dynamic&) = delete;

    /* ----------------------------------------------
     * Movable (safe transfer)
     * ---------------------------------------------- */
    Dynamic(Dynamic&& other) noexcept {
        lib_ = other.lib_;
        loaded_ = other.loaded_;

        other.lib_.handle = nullptr;
        other.loaded_ = false;
    }

    Dynamic& operator=(Dynamic&& other) noexcept {
        if (this != &other) {
            if (loaded_)
                fossil_sys_dynamic_unload(&lib_);

            lib_ = other.lib_;
            loaded_ = other.loaded_;

            other.lib_.handle = nullptr;
            other.loaded_ = false;
        }
        return *this;
    }

    /* ----------------------------------------------
     * Secure load
     * ---------------------------------------------- */
    bool load(const char* path) {
        if (loaded_) return false;            /* already loaded */
        if (!validate_path(path)) return false;

        lib_.path = path;

        if (!fossil_sys_dynamic_load(path, &lib_))
            return false;

        loaded_ = true;
        return true;
    }

    /* ----------------------------------------------
     * Secure unload
     * ---------------------------------------------- */
    bool unload() {
        if (!loaded_) return false;

        if (!fossil_sys_dynamic_unload(&lib_))
            return false;

        loaded_ = false;
        return true;
    }

    /* ----------------------------------------------
     * Safe symbol lookup
     * ---------------------------------------------- */
    void* symbol(const char* name) {
        if (!loaded_ || !name || !*name)
            return nullptr;

        return fossil_sys_dynamic_symbol(&lib_, name);
    }

    /* ----------------------------------------------
     * State / diagnostics
     * ---------------------------------------------- */
    bool is_loaded() const { return loaded_; }

    const char* error() const {
        return fossil_sys_dynamic_error();
    }

    const fossil_sys_dynamic_lib_t* raw() const { return &lib_; }
    fossil_sys_dynamic_lib_t* raw() { return &lib_; }
};

}
#endif

#endif /* FOSSIL_SYS_DYNAMIC_H */

SAMPLE CODE C #

#include "fossil/sys/dynamic.h"
#include <stdio.h>

/* Example function signature from loaded library */
typedef int (*plugin_func_t)(int);

int main(void)
{
    fossil_sys_dynamic_lib_t lib;

    /* Load library */
    if (!fossil_sys_dynamic_load("./libplugin.so", &lib)) {
        printf("Load failed: %s\n", fossil_sys_dynamic_error());
        return 1;
    }

    /* Resolve symbol */
    plugin_func_t func = (plugin_func_t)
        fossil_sys_dynamic_symbol(&lib, "plugin_entry");

    if (!func) {
        printf("Symbol not found: %s\n", fossil_sys_dynamic_error());
        fossil_sys_dynamic_unload(&lib);
        return 1;
    }

    /* Call function */
    int result = func(42);
    printf("Plugin returned: %d\n", result);

    /* Unload */
    fossil_sys_dynamic_unload(&lib);

    return 0;
}

SAMPLE CODE C++ #

#include "fossil/sys/dynamic.h"
#include <iostream>

using namespace fossil::sys;

/* Function signature expected from plugin */
using plugin_func_t = int(*)(int);

int main()
{
    Dynamic lib;

    /* Load safely */
    if (!lib.load("/usr/lib/libplugin.so")) {
        std::cerr << "Load failed: " << lib.error() << "\n";
        return 1;
    }

    /* Lookup symbol */
    auto func = reinterpret_cast<plugin_func_t>(
        lib.symbol("plugin_entry"));

    if (!func) {
        std::cerr << "Symbol error: " << lib.error() << "\n";
        return 1;
    }

    /* Execute */
    int result = func(123);
    std::cout << "Plugin returned: " << result << "\n";

    /* Automatic unload via destructor (RAII) */
    return 0;
}

What are your feelings

Updated on March 24, 2026