1 module elfhook.library;
2 
3 import core.sys.posix.unistd;
4 import core.sys.posix.dlfcn;
5 
6 import core.stdc.errno;
7 import core.stdc..string;
8 
9 import std.exception;
10 import std..string : toStringz;
11 
12 version(Posix):
13 
14 immutable int RTLD_LOCAL = 0;
15 immutable int RTLD_LAZY = 1;
16 immutable int RTLD_NOW = 2;
17 immutable int RTLD_GLOBAL = 3;
18 
19 /**
20    This is a wrapper of UNIX-specified dynamic loading.
21    See `man 3 dlopen`.
22  */
23 struct SharedLibrary
24 {
25     void* handle;
26 
27     this(in string filename, int flags)
28     {
29         handle = dlopen(filename.toStringz, flags);
30         if (handle is null)
31         {
32             const errorMsg = dlerror();
33             if (errorMsg !is null)
34                 errnoEnforce(false, cast(string) errorMsg[0 .. strlen(errorMsg)]);
35             errnoEnforce(false, "failed to dlopen(3) by unknown reason.");
36         }
37     }
38 
39 
40     ~this()
41     {
42         if (handle !is null)
43             close();
44     }
45 
46 
47     void close()
48     {
49         const ret = dlclose(handle);
50         if (ret != 0)
51         {
52             const errorMsg = dlerror();
53             if (errorMsg !is null)
54                 errnoEnforce(false, cast(string) errorMsg[0 .. strlen(errorMsg)]);
55             errnoEnforce(false, "failed to dlclose(3) by unknown reason.");
56         }
57     }
58 
59 
60     auto get(in string symbolName)
61     {
62         const symbol = dlsym(handle, symbolName.toStringz);
63         if (symbol is null)
64         {
65             const errorMsg = dlerror();
66             if (errorMsg !is null)
67                 errnoEnforce(false, cast(string) errorMsg[0 .. strlen(errorMsg)]);
68             errnoEnforce(false, "failed to dlsym(3) by unknown reason.");
69         }
70         return symbol;
71     }
72 
73     // utility for getting the adress of library loaded.
74     void* getLoadedAddr()
75     {
76         return cast(void*) *cast(const size_t*) handle;
77     }
78 }
79 
80 
81 unittest
82 {
83     string libm;
84 
85     version(linux)
86     {
87         // Using libm.so gots invalid ELF Header.
88         libm = "libm-2.24.so";
89     }
90     else version(OSX)
91     {
92         libm = "libm.dylib";
93     }
94     else static assert(false, "Not support your platform.");
95 
96     {
97         auto lib = new SharedLibrary(libm, RTLD_NOW);
98         auto ceil = cast(double function(double)) lib.get("ceil");
99         assert(ceil(0.45) == 1);
100     }
101 
102     {
103         auto lib = new SharedLibrary(libm, RTLD_NOW);
104         auto addr = lib.getLoadedAddr();
105         assert(addr !is null);
106     }
107 }