-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
/
loader_lib.c
194 lines (176 loc) · 6.4 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
#include "loader.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Bring in helper functions for windows without libgcc. */
#ifdef _OS_WINDOWS_
#include "loader_win_utils.c"
#endif
/*
* DEP_LIBS is our list of dependent libraries that must be loaded before `libjulia`.
* Note that order matters, as each entry will be opened in-order. We define here a
* dummy value just so this file compiles on its own, and also so that developers can
* see what this value should look like. Note that the last entry must always be
* `libjulia`, and that all paths should be relative to this loader `.exe` path.
*/
#if !defined(DEP_LIBS)
#define DEP_LIBS "../lib/example.so:../lib/libjulia.so"
#endif
static char dep_libs[256] = DEP_LIBS;
void print_stderr(const char * msg)
{
fputs(msg, stderr);
}
// I use three arguments a lot.
void print_stderr3(const char * msg1, const char * msg2, const char * msg3)
{
print_stderr(msg1);
print_stderr(msg2);
print_stderr(msg3);
}
/* Absolute path to the path of the current executable, gets filled in by `get_exe_path()` */
static void * load_library(const char * rel_path, const char * src_dir) {
char path[2*PATH_MAX + 1] = {0};
strncat(path, src_dir, sizeof(path) - 1);
strncat(path, PATHSEPSTRING, sizeof(path) - 1);
strncat(path, rel_path, sizeof(path) - 1);
void * handle = NULL;
#if defined(_OS_WINDOWS_)
wchar_t wpath[2*PATH_MAX + 1] = {0};
if (!utf8_to_wchar(path, wpath, 2*PATH_MAX)) {
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 | RTLD_GLOBAL);
#endif
if (handle == NULL) {
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);
print_stderr3("Message:", err, "\n");
#else
print_stderr3("Message:", dlerror(), "\n");
#endif
exit(1);
}
return handle;
}
char exe_dir[PATH_MAX];
const char * get_exe_dir()
{
#if defined(_OS_WINDOWS_)
// On Windows, we use GetModuleFileName()
wchar_t julia_path[PATH_MAX];
if (!GetModuleFileName(NULL, julia_path, PATH_MAX)) {
print_stderr("ERROR: GetModuleFileName() failed\n");
exit(1);
}
if (!wchar_to_utf8(julia_path, exe_dir, PATH_MAX)) {
print_stderr("ERROR: Unable to convert julia path to UTF-8\n");
exit(1);
}
#elif defined(_OS_DARWIN_)
// On MacOS, we use _NSGetExecutablePath(), followed by realpath()
char nonreal_exe_path[PATH_MAX + 1];
uint32_t exe_path_len = PATH_MAX;
int ret = _NSGetExecutablePath(nonreal_exe_path, &exe_path_len);
if (ret != 0) {
print_stderr("ERROR: _NSGetExecutablePath() failed\n");
exit(1);
}
if (realpath(nonreal_exe_path, exe_dir) == NULL) {
print_stderr("ERROR: realpath() failed\n");
exit(1);
}
#elif defined(_OS_LINUX_)
// On Linux, we read from /proc/self/exe
int num_bytes = readlink("/proc/self/exe", exe_dir, PATH_MAX);
if (num_bytes == -1) {
print_stderr("ERROR: readlink(/proc/self/exe) failed\n");
exit(1);
}
exe_dir[num_bytes] = '\0';
#elif defined(_OS_FREEBSD_)
// On FreeBSD, we use the KERN_PROC_PATHNAME sysctl:
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
unsigned long exe_dir_len = PATH_MAX - 1;
int ret = sysctl(mib, 4, &exe_dir, &exe_dir_len, NULL, 0);
if (ret) {
print_stderr("ERROR: sysctl(KERN_PROC_PATHNAME) failed\n");
exit(1);
}
exe_dir[exe_dir_len] = '\0';
#endif
// Finally, convert to dirname
const char * new_dir = dirname(exe_dir);
if (new_dir != exe_dir) {
// On some plateforms, dirname() mutates. On others, it does not.
memcpy(exe_dir, new_dir, strlen(new_dir)+1);
}
return exe_dir;
}
// Load libjulia and run the REPL with the given arguments (in UTF-8 format)
int load_repl(const char * exe_dir, int argc, char * argv[])
{
// Pre-load libraries that libjulia needs.
int deps_len = strlen(dep_libs);
char * curr_dep = &dep_libs[0];
while (1) {
// try to find next colon character, if we can't, escape out.
char * colon = strchr(curr_dep, ':');
if (colon == NULL)
break;
// Chop the string at the colon, load this library.
*colon = '\0';
load_library(curr_dep, exe_dir);
// Skip ahead to next dependency
curr_dep = colon + 1;
}
// Last dependency is `libjulia`, so load that and we're done with `dep_libs`!
void * libjulia = load_library(curr_dep, exe_dir);
// Next, if we're on Linux/FreeBSD, set up fast TLS.
#if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_)
void (*jl_set_ptls_states_getter)(void *) = dlsym(libjulia, "jl_set_ptls_states_getter");
if (jl_set_ptls_states_getter == NULL) {
print_stderr("ERROR: Cannot find jl_set_ptls_states_getter() function within libjulia!\n");
exit(1);
}
void * (*fptr)(void) = dlsym(NULL, "jl_get_ptls_states_static");
if (fptr == NULL) {
print_stderr("ERROR: Cannot find jl_get_ptls_states_static(), must define this symbol within calling executable!\n");
exit(1);
}
jl_set_ptls_states_getter((void *)fptr);
#endif
// Load the repl entrypoint symbol and jump into it!
int (*entrypoint)(int, char **) = NULL;
#ifdef _OS_WINDOWS_
entrypoint = (int (*)(int, char **))GetProcAddress((HMODULE) libjulia, "repl_entrypoint");
#else
entrypoint = (int (*)(int, char **))dlsym(libjulia, "repl_entrypoint");
#endif
if (entrypoint == NULL) {
print_stderr("ERROR: Unable to find `repl_entrypoint()` within libjulia!\n");
exit(1);
}
return entrypoint(argc, (char **)argv);
}
// Empty DLL main entrypoint to silence warning
#ifdef _OS_WINDOWS_
int __stdcall DllMainCRTStartup(void* instance, unsigned reason, void* reserved) {
}
#endif
#ifdef __cplusplus
} // extern "C"
#endif