aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/dynlib.cpp
blob: 3ce77e5540e80408f51041817802c8cfcb50cf1a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "dynlib.h"

#include "guard.h"
#include "mutex.h"
#include <util/generic/singleton.h>
#include <util/generic/yexception.h>

#ifdef _win32_
    #include "winint.h"

    #define DLLOPEN(path, flags) LoadLibrary(path)
    #define DLLCLOSE(hndl) FreeLibrary(hndl)
    #define DLLSYM(hndl, name) GetProcAddress(hndl, name)
#else
    #include <dlfcn.h>

    #ifndef RTLD_GLOBAL
        #define RTLD_GLOBAL (0)
    #endif

using HINSTANCE = void*;

    #define DLLOPEN(path, flags) dlopen(path, flags)
    #define DLLCLOSE(hndl) dlclose(hndl)
    #define DLLSYM(hndl, name) dlsym(hndl, name)
#endif

inline TString DLLERR() {
#ifdef _unix_
    return dlerror();
#endif

#ifdef _win32_
    char* msg = 0;
    DWORD cnt = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                              nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, nullptr); 
    if (!msg)
        return "DLLERR() unknown error";
    while (cnt && isspace(msg[cnt - 1]))
        --cnt;
    TString err(msg, 0, cnt);
    LocalFree(msg);
    return err;
#endif
}

class TDynamicLibrary::TImpl {
private:
    inline TImpl(const char* path, int flags)
        : Module(DLLOPEN(path, flags))
        , Unloadable(true)
    {
        (void)flags;

        if (!Module) {
            ythrow yexception() << DLLERR().data();
        }
    }

    class TCreateMutex: public TMutex {
    };

public:
    static inline TImpl* SafeCreate(const char* path, int flags) {
        auto guard = Guard(*Singleton<TCreateMutex>());

        return new TImpl(path, flags);
    }

    inline ~TImpl() {
        if (Module && Unloadable) {
            DLLCLOSE(Module);
        }
    }

    inline void* SymOptional(const char* name) noexcept {
        return (void*)DLLSYM(Module, name);
    }

    inline void* Sym(const char* name) {
        void* symbol = SymOptional(name);

        if (symbol == nullptr) { 
            ythrow yexception() << DLLERR().data();
        }

        return symbol;
    }

    inline void SetUnloadable(bool unloadable) {
        Unloadable = unloadable;
    }

private:
    HINSTANCE Module;
    bool Unloadable;
};

TDynamicLibrary::TDynamicLibrary() noexcept {
}

TDynamicLibrary::TDynamicLibrary(const TString& path, int flags) {
    Open(path.data(), flags);
}

TDynamicLibrary::~TDynamicLibrary() = default;

void TDynamicLibrary::Open(const char* path, int flags) {
    Impl_.Reset(TImpl::SafeCreate(path, flags));
}

void TDynamicLibrary::Close() noexcept {
    Impl_.Destroy();
}

void* TDynamicLibrary::SymOptional(const char* name) noexcept {
    if (!IsLoaded()) {
        return nullptr; 
    }

    return Impl_->SymOptional(name);
}

void* TDynamicLibrary::Sym(const char* name) {
    if (!IsLoaded()) {
        ythrow yexception() << "library not loaded";
    }

    return Impl_->Sym(name);
}

bool TDynamicLibrary::IsLoaded() const noexcept {
    return (bool)Impl_.Get();
}

void TDynamicLibrary::SetUnloadable(bool unloadable) {
    Impl_->SetUnloadable(unloadable);
}