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