1 /*
2 Low-level routines for managing dynamic link libraries (DLLs).
3 */
4
5 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for dlopen() */
6 #include <petsc/private/petscimpl.h>
7
8 #if defined(PETSC_HAVE_WINDOWS_H)
9 #include <windows.h>
10 #endif
11 #if defined(PETSC_HAVE_DLFCN_H)
12 #include <dlfcn.h>
13 #endif
14
15 #if defined(PETSC_HAVE_WINDOWS_H)
16 typedef HMODULE dlhandle_t;
17 typedef FARPROC dlsymbol_t;
18 #elif defined(PETSC_HAVE_DLFCN_H)
19 typedef void *dlhandle_t;
20 typedef void *dlsymbol_t;
21 #else
22 typedef void *dlhandle_t;
23 typedef void *dlsymbol_t;
24 #endif
25
26 /*@C
27 PetscDLOpen - opens a dynamic library
28
29 Not Collective, No Fortran Support
30
31 Input Parameters:
32 + name - name of library
33 - mode - options on how to open library
34
35 Output Parameter:
36 . handle - opaque pointer to be used with `PetscDLSym()`
37
38 Level: developer
39
40 .seealso: `PetscDLClose()`, `PetscDLSym()`, `PetscDLAddr()`, `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`,
41 `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `PetscDLLibraryClose()`, `PetscDLLibrarySym()`
42 @*/
PetscDLOpen(const char name[],PetscDLMode mode,PetscDLHandle * handle)43 PetscErrorCode PetscDLOpen(const char name[], PetscDLMode mode, PetscDLHandle *handle)
44 {
45 PETSC_UNUSED int dlflags1, dlflags2; /* There are some preprocessor paths where these variables are set, but not used */
46 dlhandle_t dlhandle;
47
48 PetscFunctionBegin;
49 PetscAssertPointer(name, 1);
50 PetscAssertPointer(handle, 3);
51
52 dlflags1 = 0;
53 dlflags2 = 0;
54 dlhandle = (dlhandle_t)0;
55 *handle = (PetscDLHandle)0;
56
57 /*
58 --- LoadLibrary ---
59 */
60 #if defined(PETSC_HAVE_WINDOWS_H) && defined(PETSC_HAVE_LOADLIBRARY)
61 dlhandle = LoadLibrary(name);
62 if (!dlhandle) {
63 /* TODO: Seem to need fixing, why not just return with an error with SETERRQ() */
64 #if defined(PETSC_HAVE_GETLASTERROR)
65 DWORD erc;
66 char *buff = NULL;
67 erc = GetLastError();
68 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, erc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buff, 0, NULL);
69 PetscCall(PetscError(PETSC_COMM_SELF, __LINE__, PETSC_FUNCTION_NAME, __FILE__, PETSC_ERR_FILE_OPEN, PETSC_ERROR_REPEAT, "Unable to open dynamic library:\n %s\n Error message from LoadLibrary() %s\n", name, buff));
70 LocalFree(buff);
71 PetscFunctionReturn(PETSC_SUCCESS);
72 #else
73 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open dynamic library:\n %s\n Error message from LoadLibrary() %s", name, "unavailable");
74 #endif
75 }
76
77 /*
78 --- dlopen ---
79 */
80 #elif defined(PETSC_HAVE_DLFCN_H) && defined(PETSC_HAVE_DLOPEN)
81 /*
82 Mode indicates symbols required by symbol loaded with dlsym()
83 are only loaded when required (not all together) also indicates
84 symbols required can be contained in other libraries also opened
85 with dlopen()
86 */
87 #if defined(PETSC_HAVE_RTLD_LAZY)
88 dlflags1 = RTLD_LAZY;
89 #endif
90 #if defined(PETSC_HAVE_RTLD_NOW)
91 if (mode & PETSC_DL_NOW) dlflags1 = RTLD_NOW;
92 #endif
93 #if defined(PETSC_HAVE_RTLD_GLOBAL)
94 dlflags2 = RTLD_GLOBAL;
95 #endif
96 #if defined(PETSC_HAVE_RTLD_LOCAL)
97 if (mode & PETSC_DL_LOCAL) dlflags2 = RTLD_LOCAL;
98 #endif
99 #if defined(PETSC_HAVE_DLERROR)
100 dlerror(); /* clear any previous error */
101 #endif
102 dlhandle = dlopen(name, dlflags1 | dlflags2);
103 if (!dlhandle) {
104 #if defined(PETSC_HAVE_DLERROR)
105 const char *errmsg = dlerror();
106 #else
107 const char *errmsg = "unavailable";
108 #endif
109 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open dynamic library:\n %s\n Error message from dlopen() %s", name, errmsg);
110 }
111 /*
112 --- unimplemented ---
113 */
114 #else
115 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
116 #endif
117
118 *handle = (PetscDLHandle)dlhandle;
119 PetscFunctionReturn(PETSC_SUCCESS);
120 }
121
122 /*@C
123 PetscDLClose - closes a dynamic library
124
125 Not Collective, No Fortran Support
126
127 Input Parameter:
128 . handle - the handle for the library obtained with `PetscDLOpen()`
129
130 Level: developer
131
132 .seealso: `PetscDLOpen()`, `PetscDLSym()`, `PetscDLAddr()`
133 @*/
PetscDLClose(PetscDLHandle * handle)134 PetscErrorCode PetscDLClose(PetscDLHandle *handle)
135 {
136 PetscFunctionBegin;
137 PetscAssertPointer(handle, 1);
138
139 /*
140 --- FreeLibrary ---
141 */
142 #if defined(PETSC_HAVE_WINDOWS_H)
143 #if defined(PETSC_HAVE_FREELIBRARY)
144 if (FreeLibrary((dlhandle_t)*handle) == 0) {
145 #if defined(PETSC_HAVE_GETLASTERROR)
146 char *buff = NULL;
147 DWORD erc = GetLastError();
148 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, erc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buff, 0, NULL);
149 PetscCall(PetscErrorPrintf("Error closing dynamic library:\n Error message from FreeLibrary() %s\n", buff));
150 LocalFree(buff);
151 #else
152 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error closing dynamic library:\n Error message from FreeLibrary() %s", "unavailable");
153 #endif
154 }
155 #endif /* !PETSC_HAVE_FREELIBRARY */
156
157 /*
158 --- dclose ---
159 */
160 #elif defined(PETSC_HAVE_DLFCN_H)
161 #if defined(PETSC_HAVE_DLCLOSE)
162 #if defined(PETSC_HAVE_DLERROR)
163 dlerror(); /* clear any previous error */
164 #endif
165 if (dlclose((dlhandle_t)*handle) < 0) {
166 #if defined(PETSC_HAVE_DLERROR)
167 const char *errmsg = dlerror();
168 #else
169 const char *errmsg = "unavailable";
170 #endif
171 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error closing dynamic library:\n Error message from dlclose() %s", errmsg);
172 }
173 #endif /* !PETSC_HAVE_DLCLOSE */
174
175 /*
176 --- unimplemented ---
177 */
178 #else
179 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
180 #endif
181
182 *handle = NULL;
183 PetscFunctionReturn(PETSC_SUCCESS);
184 }
185
186 // clang-format off
187 /*@C
188 PetscDLSym - finds a symbol in a dynamic library
189
190 Not Collective, No Fortran Support
191
192 Input Parameters:
193 + handle - obtained with `PetscDLOpen()` or `NULL`
194 - symbol - name of symbol
195
196 Output Parameter:
197 . value - pointer to the function, `NULL` if not found
198
199 Level: developer
200
201 Note:
202 If handle is `NULL`, the symbol is looked for in the main executable's dynamic symbol table.
203 In order to be dynamically loadable, the symbol has to be exported as such. On many UNIX-like
204 systems this requires platform-specific linker flags.
205
206 .seealso: `PetscDLClose()`, `PetscDLOpen()`, `PetscDLAddr()`, `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`,
207 `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `PetscDLLibraryClose()`, `PetscDLLibrarySym()`
208 @*/
PetscDLSym(PetscDLHandle handle,const char symbol[],void ** value)209 PetscErrorCode PetscDLSym(PetscDLHandle handle, const char symbol[], void **value)
210 {
211 dlhandle_t dlhandle;
212 dlsymbol_t dlsymbol;
213
214 PetscFunctionBegin;
215 PetscAssertPointer(symbol, 2);
216 PetscAssertPointer(value, 3);
217
218 dlhandle = (dlhandle_t)0;
219 dlsymbol = (dlsymbol_t)0;
220 *value = NULL;
221
222 /*
223 --- GetProcAddress ---
224 */
225 #if defined(PETSC_HAVE_WINDOWS_H)
226 #if defined(PETSC_HAVE_GETPROCADDRESS)
227 if (handle) dlhandle = (dlhandle_t)handle;
228 else dlhandle = (dlhandle_t)GetCurrentProcess();
229 dlsymbol = (dlsymbol_t)GetProcAddress(dlhandle, symbol);
230 #if defined(PETSC_HAVE_SETLASTERROR)
231 SetLastError((DWORD)0); /* clear any previous error */
232 #endif /* PETSC_HAVE_SETLASTERROR */
233 #endif /* !PETSC_HAVE_GETPROCADDRESS */
234
235 /*
236 --- dlsym ---
237 */
238 #elif defined(PETSC_HAVE_DLFCN_H) /* PETSC_HAVE_WINDOWS_H */
239 #if defined(PETSC_HAVE_DLSYM)
240 if (handle) dlhandle = (dlhandle_t)handle;
241 else {
242 #if defined(PETSC_HAVE_DLOPEN)
243 /* Attempt to retrieve the main executable's dlhandle. */
244 {
245 #if !defined(PETSC_HAVE_RTLD_DEFAULT)
246 int dlflags1 = 0, dlflags2 = 0;
247 #if defined(PETSC_HAVE_RTLD_LAZY)
248 dlflags1 = RTLD_LAZY;
249 #endif /* PETSC_HAVE_RTLD_LAZY */
250 #if defined(PETSC_HAVE_RTLD_NOW)
251 if (!dlflags1) dlflags1 = RTLD_NOW;
252 #endif /* PETSC_HAVE_RTLD_NOW */
253 #if defined(PETSC_HAVE_RTLD_LOCAL)
254 dlflags2 = RTLD_LOCAL;
255 #endif /* PETSC_HAVE_RTLD_LOCAL */
256 #if defined(PETSC_HAVE_RTLD_GLOBAL)
257 if (!dlflags2) dlflags2 = RTLD_GLOBAL;
258 #endif /* PETSC_HAVE_RTLD_GLOBAL */
259 #endif /* !PETSC_HAVE_RTLD_DEFAULT */
260 #if defined(PETSC_HAVE_DLERROR)
261 if (!(PETSC_RUNNING_ON_VALGRIND)) dlerror(); /* clear any previous error, valgrind does not like this */
262 #endif /* PETSC_HAVE_DLERROR */
263 #if defined(PETSC_HAVE_RTLD_DEFAULT)
264 dlhandle = RTLD_DEFAULT;
265 #else /* PETSC_HAVE_RTLD_DEFAULT */
266 /* Attempt to open the main executable as a dynamic library. */
267 dlhandle = dlopen(NULL, dlflags1 | dlflags2);
268 #if defined(PETSC_HAVE_DLERROR)
269 {
270 const char *e = (const char *)dlerror();
271 PetscCheck(!e, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Error opening main executable as a dynamic library: error message from dlopen(): '%s'", e);
272 }
273 #endif /* PETSC_HAVE_DLERROR */
274 #endif /* !PETSC_HAVE_RTLD_DEFAULT */
275 }
276 #endif /* PETSC_HAVE_DLOPEN */
277 }
278 #if defined(PETSC_HAVE_DLERROR)
279 dlerror(); /* clear any previous error */
280 #endif /* PETSC_HAVE_DLERROR */
281 dlsymbol = (dlsymbol_t)dlsym(dlhandle, symbol);
282 #else /* PETSC_HAVE_DLSYM */
283 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
284 #endif /* PETSC_HAVE_DLSYM */
285 #else /* PETSC_HAVE_DLFCN_H */
286 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
287 #endif /* PETSC_HAVE_WINDOWS_H */
288 // clang-format on
289
290 *value = *((void **)&dlsymbol);
291
292 #if defined(PETSC_SERIALIZE_FUNCTIONS)
293 if (*value) PetscCall(PetscFPTAdd(*value, symbol));
294 #endif /* PETSC_SERIALIZE_FUNCTIONS */
295 PetscFunctionReturn(PETSC_SUCCESS);
296 }
297
298 /*@C
299 PetscDLAddr - find the name of a symbol in a dynamic library
300
301 Not Collective, No Fortran Support
302
303 Input Parameters:
304 . func - pointer to the function, `NULL` if not found
305
306 Output Parameter:
307 . name - name of symbol, or `NULL` if name lookup is not supported.
308
309 Level: developer
310
311 Notes:
312 The caller must free the returned name.
313
314 In order to be dynamically loadable, the symbol has to be exported as such. On many UNIX-like
315 systems this requires platform-specific linker flags.
316
317 .seealso: `PetscDLClose()`, `PetscDLSym()`, `PetscDLOpen()`, `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`,
318 `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `PetscDLLibraryClose()`, `PetscDLLibrarySym()`
319 @*/
PetscDLAddr(PetscVoidFn * func,char * name[])320 PetscErrorCode PetscDLAddr(PetscVoidFn *func, char *name[])
321 {
322 PetscFunctionBegin;
323 PetscAssertPointer(name, 2);
324 *name = NULL;
325 #if defined(PETSC_HAVE_DLADDR) && !(defined(__cray__) && defined(__clang__))
326 dlerror(); /* clear any previous error */
327 {
328 Dl_info info;
329
330 PetscCheck(dladdr(*(void **)&func, &info), PETSC_COMM_SELF, PETSC_ERR_LIB, "Failed to lookup symbol: %s", dlerror());
331 PetscCall(PetscDemangleSymbol(info.dli_sname, name));
332 }
333 #endif
334 PetscFunctionReturn(PETSC_SUCCESS);
335 }
336