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