1 module llvm.functions.load;
2 
3 version (LLVM_Load):
4 
5 import std.traits;
6 import std.meta;
7 
8 import link = llvm.functions.link;
9 
10 private:
11 
12 version(Posix)
13 {
14     import core.sys.posix.dlfcn;
15 
16     alias SharedLibHandle = void*;
17 
18     version(OSX)
19     {
20         enum libPrefix = "lib";
21         enum libSuffix = "dylib";
22     }
23     else
24     {
25         enum libPrefix = "lib";
26         enum libSuffix = "so";
27     }
28 
29     pragma(lib, "dl");
30 }
31 else version(Windows)
32 {
33     import core.sys.windows.windows;
34     import std.path : dirName;
35 
36     alias SharedLibHandle = HMODULE;
37 
38     enum libPrefix = "";
39     enum libSuffix = "dll";
40 } else {
41     static assert(0, "Unsupported operating system");
42 }
43 
44 struct SharedLib
45 {
46     import std.string : fromStringz, toStringz;
47 private:
48     SharedLibHandle handle;
49 public:
50     @disable this(this);
51 
52     this(string filename)
53     {
54         version(Posix)
55         {
56             if((handle = dlopen(filename.toStringz(), RTLD_NOW)) is null)
57             {
58                 throw new SharedLibException("Failed to load library " ~ filename ~ ": " ~ dlerror().fromStringz().idup);
59             }
60         }
61         else version(Windows)
62         {
63             if((handle = LoadLibraryA(filename.toStringz())) is null)
64             {
65                 LPCSTR error;
66                 DWORD tchar_length = FormatMessageA(
67                     FORMAT_MESSAGE_ALLOCATE_BUFFER |
68                     FORMAT_MESSAGE_FROM_SYSTEM |
69                     FORMAT_MESSAGE_IGNORE_INSERTS,
70                     null,
71                     GetLastError(),
72                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
73                     cast(char*) &error,
74                     0,
75                     null);
76                 scope(exit) LocalFree(cast(HLOCAL) error);
77 
78                 throw new SharedLibException("Failed to load library " ~ filename ~ ": " ~ error[0..tchar_length].idup);
79             }
80         }
81         else static assert(0, "Unsupported operating system");
82     }
83 
84     ~this()
85     {
86              version (Posix)   alias close = dlclose;
87         else version (Windows) alias close = FreeLibrary;
88         else static assert(0, "Unsupported operating system");
89 
90         if (handle) {
91             close(handle);
92             handle = null;
93         }
94     }
95 
96     T getSymbol(T)(string symbol)
97     {
98              version (Posix)   alias get = dlsym;
99         else version (Windows) alias get = GetProcAddress;
100         else static assert(0, "Unsupported operating system");
101 
102         return cast(T) get(handle, symbol.toStringz());
103     }
104 }
105 
106 public:
107 
108 final class SharedLibException : Exception
109 {
110     this(string msg) @safe pure nothrow
111     {
112         super(msg);
113     }
114 }
115 
116 private
117 {
118     bool isSym(string m) { return m != "object" && m != "llvm" && m != "core" && m != "orEmpty"; }
119 
120     string declareSymPtr(string m) { return "typeof(link." ~ m ~ ")* " ~ m ~ ";"; }
121     string getSymPtr(string m)
122     {
123         string code;
124         code ~= "foreach (library; libraries) { ";
125         code ~= m ~ " = library.getSymbol!(typeof(" ~ m ~ "))(\"" ~ m ~ "\");";
126         code ~= "if (" ~ m ~ " !is null) break;";
127         code ~= " }";
128         return code;
129     }
130 }
131 
132 __gshared
133 {
134     mixin ({
135         string code;
136         foreach (m; __traits(allMembers, link)) if (m.isSym) {
137             code ~= m.declareSymPtr;
138         }
139         return code;
140     }());
141 }
142 
143 /// Container for holding the LLVM library and the load/unload functions.
144 public struct LLVM
145 {
146     import llvm.config : LLVM_VersionString;
147 private:
148     import std.typecons : RefCounted;
149 
150     __gshared static RefCounted!SharedLib[] libraries;
151 
152     static void getSymbols()
153     {
154         foreach (m; __traits(allMembers, link)) static if (m.isSym) {
155             mixin (m.getSymPtr);
156             debug {
157                 import std.stdio : stderr;
158                 if (!mixin(m)) stderr.writeln("Missing LLVM symbol: " ~ m);
159             }
160         }
161     }
162 public:
163     /// true iff the LLVM library is loaded
164     static bool loaded() @property { return libraries.length > 0; }
165 
166     /// Loads the LLVM libraries using the specified filenames
167     static void load(string[] filenames...)
168     {
169         if (filenames.length == 0) {
170             filenames ~= libPrefix ~ "LLVM" ~ "." ~ libSuffix;
171             filenames ~= libPrefix ~ "LTO" ~ "." ~ libSuffix;
172         }
173 
174         if (loaded) unload();
175 
176         foreach (filename; filenames) {
177             libraries ~= RefCounted!SharedLib(filename);
178         }
179         getSymbols();
180     }
181 
182     /// Unloads the LLVM library
183     static void unload()
184     {
185         libraries.length = 0;
186     }
187 }
188 
189 version (LLVM_Autoload)
190 {
191     shared static this()
192     {
193         LLVM.load();
194     }
195 
196     shared static ~this()
197     {
198         LLVM.unload();
199     }
200 }