xref: /petsc/src/sys/dll/dlimpl.c (revision a28344a5144139f565b3604164979954db19997e)
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