1 module dast.net.dlfcn;
2 
3 version (Posix)
4 	public import core.sys.posix.dlfcn : dlclose, dlopen, dlsym, RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL;
5 else version (Windows){
6 	import core.sys.windows.windows;
7 	enum {
8 		RTLD_LAZY = 0x00001,
9 		RTLD_NOW = 0x00002,
10 		RTLD_GLOBAL = 0x00100,
11 		RTLD_LOCAL = 0,
12 	}
13 }else
14 	static assert(0, "unimplemented");
15 
16 nothrow @nogc:
17 
18 string dlerr() @trusted {
19 	import std.string : fromStringz;
20 	version (Posix) {
21 		import core.sys.posix.dlfcn : dlerror;
22 
23 		return cast(string)fromStringz(dlerror());
24 	} else version (Windows) {
25 		if (errorOccurred) {
26 			errorOccurred = false;
27 			return cast(string)fromStringz(errorBuffer.ptr);
28 		}
29 		return null;
30 	}
31 }
32 
33 version (Windows):
34 
35 import core.stdc.string : memcpy, strlen;
36 
37 void* dlopen(const char* filename, int) @trusted {
38 	errorOccurred = false;
39 
40 	HMODULE handle = LoadLibraryA(filename);
41 	if (handle is null)
42 		saveErrStr(filename, GetLastError());
43 
44 	return handle;
45 }
46 
47 alias dlsym = GetProcAddress;
48 
49 int dlclose(HMODULE handle) @trusted {
50 	errorOccurred = false;
51 
52 	BOOL ret = FreeLibrary(handle);
53 	if (!ret)
54 		saveErrPtrStr(handle, GetLastError());
55 
56 	/* dlclose's return value in inverted in relation to FreeLibrary's. */
57 	return !ret;
58 }
59 
60 private:
61 // From https://github.com/dlfcn-win32/dlfcn-win32/blob/048bff80f2bd00bb651bcc3357cb6f76e3d76fd5/src/dlfcn.c#L176
62 
63 /* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
64  * static buffer.
65  * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
66  * the limit.
67  */
68 __gshared {
69 	char[65_535] errorBuffer = void;
70 	bool errorOccurred;
71 }
72 
73 void saveErrStr(const char* str, uint dwMessageId) {
74 	size_t len = strlen(str);
75 	if (len > errorBuffer.sizeof - 5)
76 		len = errorBuffer.sizeof - 5;
77 
78 	// Format error message to:
79 	// "<argument to function that failed>": <Windows localized error message>
80 	size_t pos = 0;
81 	errorBuffer[pos++] = '"';
82 	memcpy(errorBuffer.ptr + pos, str, len);
83 	pos += len;
84 	errorBuffer[pos++] = '"';
85 	errorBuffer[pos++] = ':';
86 	errorBuffer[pos++] = ' ';
87 
88 	uint ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, dwMessageId,
89 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
90 		errorBuffer.ptr + pos, cast(uint)(errorBuffer.sizeof - pos), null);
91 	pos += ret;
92 
93 	// When FormatMessageA() fails it returns zero and does not touch buffer
94 	// so add trailing null byte
95 	if (ret == 0)
96 		errorBuffer[pos] = '\0';
97 
98 	if (pos > 1) {
99 		// POSIX says the string must not have trailing <newline>
100 		if (errorBuffer[pos - 2] == '\r' && errorBuffer[pos - 1] == '\n')
101 			errorBuffer[pos - 2] = '\0';
102 	}
103 
104 	errorOccurred = true;
105 }
106 
107 void saveErrPtrStr(const void* ptr, uint dwMessageId) {
108 	char[2 + 2 * ptr.sizeof + 1] ptrBuf = void;
109 
110 	ptrBuf[0] = '0';
111 	ptrBuf[1] = 'x';
112 
113 	for (size_t i; i < 2 * ptr.sizeof; i++) {
114 		const num = cast(ulong)ptr >> (8 * ptr.sizeof - 4 * (i + 1)) & 0xF;
115 		ptrBuf[2 + i] = cast(char)(num + (num < 0xA ? '0' : 'A' - 0xA));
116 	}
117 
118 	ptrBuf[2 + 2 * ptr.sizeof] = 0;
119 
120 	saveErrStr(ptrBuf.ptr, dwMessageId);
121 }