1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2006-2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 /*
22 * Interface functions to the dynamic linker using dl* functions.
23 * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb)
24 */
25
26 #include <windows.h>
27
28 #define GET_ERTS_ALC_TEST
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 #include "sys.h"
33 #include "global.h"
34 #include "erl_alloc.h"
35
36 #include "erl_driver.h"
37 #include "erl_win_dyn_driver.h"
38
39 #include "erl_nif.h"
40
41 #define EXT_LEN 4
42 #define FILE_EXT_WCHAR L".dll"
43
44 static DWORD tls_index = 0;
45 static TWinDynDriverCallbacks wddc;
46 static TWinDynNifCallbacks nif_callbacks;
47
erl_sys_ddll_init(void)48 void erl_sys_ddll_init(void) {
49 WCHAR cwd_buffer[MAX_PATH];
50
51 tls_index = TlsAlloc();
52 ERL_INIT_CALLBACK_STRUCTURE(wddc);
53
54 /* LOAD_WITH_ALTERED_SEARCH_PATH removes the startup directory from the
55 * search path, so we add it separately to be backwards compatible. */
56 if (GetCurrentDirectoryW(sizeof(cwd_buffer), cwd_buffer)) {
57 SetDllDirectoryW(cwd_buffer);
58 }
59
60 #define ERL_NIF_API_FUNC_DECL(RET,NAME,ARGS) nif_callbacks.NAME = NAME
61 #include "erl_nif_api_funcs.h"
62 #undef ERL_NIF_API_FUNC_DECL
63 nif_callbacks.erts_alc_test = erts_alc_test;
64
65 return;
66 }
67
68 /*
69 * Open a shared object
70 * Expecting 'full_name' as an UTF-8 string.
71 */
erts_sys_ddll_open(const char * full_name,void ** handle,ErtsSysDdllError * err)72 int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* err)
73 {
74 HINSTANCE hinstance;
75 int len;
76 wchar_t* wcp;
77 Sint used;
78 int code;
79
80 if ((len = sys_strlen(full_name)) >= MAXPATHLEN - EXT_LEN) {
81 if (err != NULL) {
82 err->str = "Library name too long";
83 }
84 return ERL_DE_LOAD_ERROR_NAME_TO_LONG;
85 }
86
87 wcp = (wchar_t*)erts_convert_filename_to_wchar((byte*)full_name, len,
88 NULL, 0,
89 ERTS_ALC_T_TMP, &used, EXT_LEN);
90 wcscpy(&wcp[used/2 - 1], FILE_EXT_WCHAR);
91
92 /* LOAD_WITH_ALTERED_SEARCH_PATH adds the specified DLL's directory to the
93 * dependency search path. This also removes the directory we started in,
94 * but we've explicitly added that in in erl_sys_ddll_init. */
95 if ((hinstance = LoadLibraryExW(wcp, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) == NULL) {
96 code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
97 if (err != NULL) {
98 err->str = erts_sys_ddll_error(code);
99 }
100 }
101 else {
102 code = ERL_DE_NO_ERROR;
103 *handle = (void *) hinstance;
104 }
105 erts_free(ERTS_ALC_T_TMP, wcp);
106 return code;
107 }
108
erts_sys_ddll_open_noext(char * dlname,void ** handle,ErtsSysDdllError * err)109 int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
110 {
111 HINSTANCE hinstance;
112
113 if ((hinstance = LoadLibrary(dlname)) == NULL) {
114 int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
115 if (err != NULL) {
116 err->str = erts_sys_ddll_error(code);
117 }
118 return code;
119 } else {
120 *handle = (void *) hinstance;
121 return ERL_DE_NO_ERROR;
122 }
123 }
124
125 /*
126 * Find a symbol in the shared object
127 */
erts_sys_ddll_sym2(void * handle,const char * func_name,void ** function,ErtsSysDdllError * err)128 int erts_sys_ddll_sym2(void *handle, const char *func_name, void **function,
129 ErtsSysDdllError* err)
130 {
131 FARPROC proc;
132 if ((proc = GetProcAddress( (HINSTANCE) handle, func_name)) == NULL) {
133 int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
134 if (err != NULL) {
135 err->str = erts_sys_ddll_error(code);
136 }
137 return code;
138 }
139 *function = (void *) proc;
140 return ERL_DE_NO_ERROR;
141 }
142
143 /* XXX:PaN These two will be changed with new driver interface! */
144
145 /*
146 * Load the driver init function, might appear under different names depending on object arch...
147 */
148
erts_sys_ddll_load_driver_init(void * handle,void ** function)149 int erts_sys_ddll_load_driver_init(void *handle, void **function)
150 {
151 void *fn;
152 int res;
153 if ((res = erts_sys_ddll_sym(handle, "driver_init", &fn)) != ERL_DE_NO_ERROR) {
154 return res;
155 }
156 *function = fn;
157 return res;
158 }
159
erts_sys_ddll_load_nif_init(void * handle,void ** function,ErtsSysDdllError * err)160 int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
161 {
162 void *fn;
163 int res;
164 if ((res = erts_sys_ddll_sym2(handle, "nif_init", &fn, err)) != ERL_DE_NO_ERROR) {
165 return res;
166 }
167 *function = fn;
168 return res;
169 }
170
171
172 /*
173 * Call the driver_init function, whatever it's really called, simple on unix...
174 */
erts_sys_ddll_call_init(void * function)175 void *erts_sys_ddll_call_init(void *function) {
176 void *(*initfn)(TWinDynDriverCallbacks *) = function;
177 return (*initfn)(&wddc);
178 }
179
erts_sys_ddll_call_nif_init(void * function)180 void *erts_sys_ddll_call_nif_init(void *function) {
181 void *(*initfn)(TWinDynNifCallbacks *) = function;
182 return (*initfn)(&nif_callbacks);
183 }
184
185
186 /*
187 * Close a chared object
188 */
erts_sys_ddll_close2(void * handle,ErtsSysDdllError * err)189 int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err)
190 {
191 if (!FreeLibrary((HINSTANCE) handle)) {
192 int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
193 if (err != NULL) {
194 err->str = erts_sys_ddll_error(code);
195 }
196 return code;
197 }
198 return ERL_DE_NO_ERROR;
199 }
200
201 /*
202 * Return string that describes the (current) error
203 */
204 #define MAX_ERROR 255
erts_sys_ddll_error(int code)205 char *erts_sys_ddll_error(int code)
206 {
207 int actual_code;
208 char *local_ptr;
209 if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) {
210 return "Unspecified error";
211 }
212 actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET);
213
214 local_ptr = TlsGetValue(tls_index);
215 if (local_ptr == NULL) {
216 local_ptr = erts_alloc(ERTS_ALC_T_DDLL_ERRCODES, MAX_ERROR);
217 TlsSetValue(tls_index,local_ptr);
218 }
219 if (!FormatMessage(
220 FORMAT_MESSAGE_FROM_SYSTEM,
221 NULL,
222 (DWORD) actual_code,
223 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
224 local_ptr,
225 MAX_ERROR, NULL )) {
226 return "Unspecified error";
227 } else {
228 char *ptr = local_ptr + strlen(local_ptr) - 1;
229 while (ptr >= local_ptr && (*ptr == '\r' || *ptr == '\n')) {
230 *ptr-- = '\0';
231 }
232 }
233 return local_ptr;
234 }
235
erts_sys_ddll_free_error(ErtsSysDdllError * err)236 void erts_sys_ddll_free_error(ErtsSysDdllError* err)
237 {
238 /* err->str may be either a static string or reused as thread local data,
239 * so wo don't bother free it.
240 */
241 }
242
243