-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
Copy pathloader_lib.c
292 lines (262 loc) · 10.5 KB
/
loader_lib.c
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// This file is a part of Julia. License is MIT: https://julialang.org/license
// This file defines an RPATH-style relative path loader for all platforms
#include "loader.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Bring in definitions of symbols exported from libjulia. */
#include "jl_exports.h"
/* Bring in helper functions for windows without libgcc. */
#ifdef _OS_WINDOWS_
#include "loader_win_utils.c"
#endif
// Save DEP_LIBS to a variable that is explicitly sized for expansion
static char dep_libs[1024] = DEP_LIBS;
JL_DLLEXPORT void jl_loader_print_stderr(const char * msg)
{
fputs(msg, stderr);
}
// I use three arguments a lot.
void jl_loader_print_stderr3(const char * msg1, const char * msg2, const char * msg3)
{
jl_loader_print_stderr(msg1);
jl_loader_print_stderr(msg2);
jl_loader_print_stderr(msg3);
}
/* Wrapper around dlopen(), with extra relative pathing thrown in*/
static void * load_library(const char * rel_path, const char * src_dir, int err) {
void * handle = NULL;
// See if a handle is already open to the basename
const char *basename = rel_path + strlen(rel_path);
while (basename-- > rel_path)
if (*basename == PATHSEPSTRING[0] || *basename == '/')
break;
basename++;
#if defined(_OS_WINDOWS_)
if ((handle = GetModuleHandleA(basename)))
return handle;
#else
// if err == 0 the library is optional, so don't allow global lookups to see it
if ((handle = dlopen(basename, RTLD_NOLOAD | RTLD_NOW | (err ? RTLD_GLOBAL : RTLD_LOCAL))))
return handle;
#endif
char path[2*JL_PATH_MAX + 1] = {0};
strncat(path, src_dir, sizeof(path) - 1);
strncat(path, PATHSEPSTRING, sizeof(path) - 1);
strncat(path, rel_path, sizeof(path) - 1);
#if defined(_OS_WINDOWS_)
wchar_t wpath[2*JL_PATH_MAX + 1] = {0};
if (!utf8_to_wchar(path, wpath, 2*JL_PATH_MAX)) {
jl_loader_print_stderr3("ERROR: Unable to convert path ", path, " to wide string!\n");
exit(1);
}
handle = (void *)LoadLibraryExW(wpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
#else
handle = dlopen(path, RTLD_NOW | (err ? RTLD_GLOBAL : RTLD_LOCAL));
#endif
if (handle == NULL) {
if (!err)
return NULL;
jl_loader_print_stderr3("ERROR: Unable to load dependent library ", path, "\n");
#if defined(_OS_WINDOWS_)
LPWSTR wmsg = TEXT("");
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, GetLastError(),
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPWSTR)&wmsg, 0, NULL);
char err[256] = {0};
wchar_to_utf8(wmsg, err, 255);
jl_loader_print_stderr3("Message:", err, "\n");
#else
char *dlerr = dlerror();
if (dlerr != NULL) {
jl_loader_print_stderr3("Message:", dlerr, "\n");
}
#endif
exit(1);
}
return handle;
}
static void * lookup_symbol(const void * lib_handle, const char * symbol_name) {
#ifdef _OS_WINDOWS_
return GetProcAddress((HMODULE) lib_handle, symbol_name);
#else
return dlsym((void *)lib_handle, symbol_name);
#endif
}
// Find the location of libjulia.
char lib_dir[JL_PATH_MAX];
JL_DLLEXPORT const char * jl_get_libdir()
{
// Reuse the path if this is not the first call.
if (lib_dir[0] != 0) {
return lib_dir;
}
#if defined(_OS_WINDOWS_)
// On Windows, we use GetModuleFileNameW
wchar_t libjulia_path[JL_PATH_MAX];
HMODULE libjulia = NULL;
// Get a handle to libjulia.
if (!utf8_to_wchar(LIBJULIA_NAME, libjulia_path, JL_PATH_MAX)) {
jl_loader_print_stderr3("ERROR: Unable to convert path ", LIBJULIA_NAME, " to wide string!\n");
exit(1);
}
libjulia = LoadLibraryW(libjulia_path);
if (libjulia == NULL) {
jl_loader_print_stderr3("ERROR: Unable to load ", LIBJULIA_NAME, "!\n");
exit(1);
}
if (!GetModuleFileNameW(libjulia, libjulia_path, JL_PATH_MAX)) {
jl_loader_print_stderr("ERROR: GetModuleFileName() failed\n");
exit(1);
}
if (!wchar_to_utf8(libjulia_path, lib_dir, JL_PATH_MAX)) {
jl_loader_print_stderr("ERROR: Unable to convert julia path to UTF-8\n");
exit(1);
}
#else
// On all other platforms, use dladdr()
Dl_info info;
if (!dladdr(&jl_get_libdir, &info)) {
jl_loader_print_stderr("ERROR: Unable to dladdr(&jl_get_libdir)!\n");
char *dlerr = dlerror();
if (dlerr != NULL) {
jl_loader_print_stderr3("Message:", dlerr, "\n");
}
exit(1);
}
strcpy(lib_dir, info.dli_fname);
#endif
// Finally, convert to dirname
const char * new_dir = dirname(lib_dir);
if (new_dir != lib_dir) {
// On some platforms, dirname() mutates. On others, it does not.
memcpy(lib_dir, new_dir, strlen(new_dir)+1);
}
return lib_dir;
}
void * libjulia_internal = NULL;
__attribute__((constructor)) void jl_load_libjulia_internal(void) {
// Only initialize this once
if (libjulia_internal != NULL) {
return;
}
// Introspect to find our own path
const char * lib_dir = jl_get_libdir();
// Pre-load libraries that libjulia-internal needs.
int deps_len = strlen(dep_libs);
char * curr_dep = &dep_libs[0];
// We keep track of "special" libraries names (ones whose name is prefixed with `@`)
// which are libraries that we want to load in some special, custom way, such as
// `libjulia-internal` or `libjulia-codegen`.
int special_idx = 0;
char * special_library_names[2] = {NULL};
while (1) {
// try to find next colon character; if we can't, break out
char * colon = strchr(curr_dep, ':');
if (colon == NULL)
break;
// Chop the string at the colon so it's a valid-ending-string
*colon = '\0';
// If this library name starts with `@`, don't open it here (but mark it as special)
if (curr_dep[0] == '@') {
if (special_idx > sizeof(special_library_names)/sizeof(char *)) {
jl_loader_print_stderr("ERROR: Too many special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
exit(1);
}
special_library_names[special_idx] = curr_dep + 1;
special_idx += 1;
} else {
load_library(curr_dep, lib_dir, 1);
}
// Skip ahead to next dependency
curr_dep = colon + 1;
}
if (special_idx != sizeof(special_library_names)/sizeof(char *)) {
jl_loader_print_stderr("ERROR: Too few special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
exit(1);
}
// Unpack our special library names. This is why ordering of library names matters.
libjulia_internal = load_library(special_library_names[0], lib_dir, 1);
void *libjulia_codegen = load_library(special_library_names[1], lib_dir, 0);
const char * const * codegen_func_names;
const char *codegen_liberr;
if (libjulia_codegen == NULL) {
// if codegen is not available, use fallback implementation in libjulia-internal
libjulia_codegen = libjulia_internal;
codegen_func_names = jl_codegen_fallback_func_names;
codegen_liberr = " from libjulia-internal\n";
}
else {
codegen_func_names = jl_codegen_exported_func_names;
codegen_liberr = " from libjulia-codegen\n";
}
// Once we have libjulia-internal loaded, re-export its symbols:
for (unsigned int symbol_idx=0; jl_runtime_exported_func_names[symbol_idx] != NULL; ++symbol_idx) {
void *addr = lookup_symbol(libjulia_internal, jl_runtime_exported_func_names[symbol_idx]);
if (addr == NULL) {
jl_loader_print_stderr3("ERROR: Unable to load ", jl_runtime_exported_func_names[symbol_idx], " from libjulia-internal\n");
exit(1);
}
(*jl_runtime_exported_func_addrs[symbol_idx]) = addr;
}
// jl_options must be initialized very early, in case an embedder sets some
// values there before calling jl_init
((void (*)(void))jl_init_options_addr)();
for (unsigned int symbol_idx=0; codegen_func_names[symbol_idx] != NULL; ++symbol_idx) {
void *addr = lookup_symbol(libjulia_codegen, codegen_func_names[symbol_idx]);
if (addr == NULL) {
jl_loader_print_stderr3("ERROR: Unable to load ", codegen_func_names[symbol_idx], codegen_liberr);
exit(1);
}
(*jl_codegen_exported_func_addrs[symbol_idx]) = addr;
}
// Next, if we're on Linux/FreeBSD, set up fast TLS.
#if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_)
void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey");
if (jl_pgcstack_setkey == NULL) {
jl_loader_print_stderr("ERROR: Cannot find jl_pgcstack_setkey() function within libjulia-internal!\n");
exit(1);
}
void *fptr = lookup_symbol(RTLD_DEFAULT, "jl_get_pgcstack_static");
void *(*key)(void) = lookup_symbol(RTLD_DEFAULT, "jl_pgcstack_addr_static");
if (fptr != NULL && key != NULL)
jl_pgcstack_setkey(fptr, key);
#endif
// jl_options must be initialized very early, in case an embedder sets some
// values there before calling jl_init
((void (*)(void))jl_init_options_addr)();
}
// Load libjulia and run the REPL with the given arguments (in UTF-8 format)
JL_DLLEXPORT int jl_load_repl(int argc, char * argv[]) {
// Some compilers/platforms are known to have `__attribute__((constructor))` issues,
// so we have a fallback call of `jl_load_libjulia_internal()` here.
if (libjulia_internal == NULL) {
jl_load_libjulia_internal();
if (libjulia_internal == NULL) {
jl_loader_print_stderr("ERROR: libjulia-internal could not be loaded!\n");
exit(1);
}
}
// Load the repl entrypoint symbol and jump into it!
int (*entrypoint)(int, char **) = (int (*)(int, char **))lookup_symbol(libjulia_internal, "jl_repl_entrypoint");
if (entrypoint == NULL) {
jl_loader_print_stderr("ERROR: Unable to find `jl_repl_entrypoint()` within libjulia-internal!\n");
exit(1);
}
return entrypoint(argc, (char **)argv);
}
#ifdef _OS_WINDOWS_
int __stdcall DllMainCRTStartup(void* instance, unsigned reason, void* reserved) {
setup_stdio();
// Because we override DllMainCRTStartup, we have to manually call our constructor methods
jl_load_libjulia_internal();
return 1;
}
#endif
#ifdef __cplusplus
} // extern "C"
#endif