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 }