Repository navigation
Expand file tree
/
Copy pathurll.cpp
More file actions
118 lines (104 loc) · 4.26 KB
/
Copy pathurll.cpp
File metadata and controls
118 lines (104 loc) · 4.26 KB
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
#include "urll.h"
#include <cstdio>
#ifdef _WIN32
// On Unix ::dlerror() only reports failures raised by the loader API itself and clears the error
// when it's read. GetLastError() is process-wide state that any unrelated Win32 call can
// overwrite, so instead of reading it directly in dlerror() we record it here, at the point where
// an URLL operation actually fails. Both are thread_local, matching glibc's thread-local
// ::dlerror() and making the buffer returned by dlerror() free of data races.
thread_local static DWORD urllLastError = 0;
thread_local static char urllErrorBuffer[512];
// Win32 functions only guarantee a meaningful last-error value after a failure, hence the
// fallback for the rare case where a function fails without setting one
static void recordLastError(const DWORD fallback) noexcept
{
const DWORD id = GetLastError();
urllLastError = (id == 0 ? fallback : id);
}
#endif
void* URLL::dlopen(const char* location) noexcept
{
// Due to restrictions from Windows libraries are always loaded in lazy mode!
#ifdef _WIN32
// Emulates the Unix behaviour of dlopen(nullptr), which returns a handle to the main program.
// Unlike LoadLibrary, GetModuleHandle doesn't increment the module's reference count, which is
// why dlclose has to skip it
if (location == nullptr)
{
void* handle = (void*)GetModuleHandleA(nullptr);
if (handle == nullptr)
recordLastError(ERROR_MOD_NOT_FOUND);
return handle;
}
// LoadLibraryA instead of LoadLibrary, since the TCHAR-generic macro resolves to the wide
// version in UNICODE builds, which doesn't accept our char* argument
SetLastError(0);
void* handle = (void*)LoadLibraryA(location);
if (handle == nullptr)
recordLastError(ERROR_MOD_NOT_FOUND);
return handle;
#else
return ::dlopen(location, RTLD_LAZY);
#endif
}
void* URLL::dlsym(void* handle, const char* name) noexcept
{
#ifdef _WIN32
SetLastError(0);
// The cast is required because GetProcAddress returns a function pointer, which doesn't
// implicitly convert to void* outside of Microsoft's extensions
void* ret = (void*)GetProcAddress((HMODULE)handle, name);
if (ret == nullptr)
recordLastError(ERROR_PROC_NOT_FOUND);
return ret;
#else
return ::dlsym(handle, name);
#endif
}
int URLL::dlclose(void* handle) noexcept
{
#ifdef _WIN32
// The main program handle handed out by dlopen(nullptr) was never reference counted, so
// releasing it would decrement a count we never incremented. A caller that instead loaded the
// executable by path gets that same handle back with its count incremented, and leaks the one
// reference here, which costs nothing given the main image is never unloaded anyway
if (handle == (void*)GetModuleHandleA(nullptr))
return 0;
SetLastError(0);
if (FreeLibrary((HMODULE)handle) == 0)
{
recordLastError(ERROR_INVALID_HANDLE);
return -1;
}
return 0;
#else
return ::dlclose(handle);
#endif
}
char* URLL::dlerror() noexcept
{
#ifdef _WIN32
const DWORD id = urllLastError;
if (id == 0)
return nullptr;
// Unix clears the error once it has been read
urllLastError = 0;
// FormatMessageA instead of FormatMessage, since the TCHAR-generic macro resolves to the wide
// version in UNICODE builds and would fill our char* buffer with UTF-16 data. The language is
// left at 0 so the system walks its own fallback order, instead of failing outright whenever
// the message table happens to lack one specific language
DWORD len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, id, 0, urllErrorBuffer, sizeof(urllErrorBuffer), nullptr);
// System messages come terminated with a CRLF, which would break the line of every log
// message that embeds the error
while (len > 0 && (urllErrorBuffer[len - 1] == '\r' || urllErrorBuffer[len - 1] == '\n'))
--len;
urllErrorBuffer[len] = '\0';
// Losing the error entirely because the system has no message for it is worse than an
// ugly message
if (len == 0)
std::snprintf(urllErrorBuffer, sizeof(urllErrorBuffer), "Unknown error %lu", static_cast<unsigned long>(id));
return urllErrorBuffer;
#else
return ::dlerror();
#endif
}