xref: /petsc/src/sys/dll/dlimpl.c (revision d5b43468fb8780a8feea140ccd6fa3e6a50411cc)
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(0);
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(0);
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     PetscErrorPrintf("Error closing dynamic library:\n  Error message from FreeLibrary() %s\n", buff);
155     LocalFree(buff);
156     #else
157     PetscErrorPrintf("Error closing dynamic library:\n  Error message from FreeLibrary() %s\n", "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     PetscErrorPrintf("Error closing dynamic library:\n  Error message from dlclose() %s\n", 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(0);
189 }
190 
191 /*@C
192    PetscDLSym - finds a symbol in a dynamic library
193 
194    Not Collective
195 
196    Input Parameters:
197 +   handle - obtained with `PetscDLOpen()` or NULL
198 -   symbol - name of symbol
199 
200    Output Parameter:
201 .   value - pointer to the function, NULL if not found
202 
203    Level: developer
204 
205   Note:
206    If handle is NULL, the symbol is looked for in the main executable's dynamic symbol table.
207    In order to be dynamically loadable, the symbol has to be exported as such.  On many UNIX-like
208    systems this requires platform-specific linker flags.
209 
210 .seealso: `PetscDLClose()`, `PetscDLOpen()`, `PetscDLAddr()`
211 @*/
212 PetscErrorCode PetscDLSym(PetscDLHandle handle, const char symbol[], void **value)
213 {
214   PETSC_UNUSED dlhandle_t dlhandle;
215   dlsymbol_t              dlsymbol;
216 
217   PetscValidCharPointer(symbol, 2);
218   PetscValidPointer(value, 3);
219 
220   dlhandle = (dlhandle_t)0;
221   dlsymbol = (dlsymbol_t)0;
222   *value   = (void *)0;
223 
224   /*
225      --- GetProcAddress ---
226   */
227 #if defined(PETSC_HAVE_WINDOWS_H)
228   #if defined(PETSC_HAVE_GETPROCADDRESS)
229   if (handle) dlhandle = (dlhandle_t)handle;
230   else dlhandle = (dlhandle_t)GetCurrentProcess();
231   dlsymbol = (dlsymbol_t)GetProcAddress(dlhandle, symbol);
232     #if defined(PETSC_HAVE_SETLASTERROR)
233   SetLastError((DWORD)0); /* clear any previous error */
234     #endif
235   #endif /* !PETSC_HAVE_GETPROCADDRESS */
236 
237   /*
238      --- dlsym ---
239   */
240 #elif defined(PETSC_HAVE_DLFCN_H)
241   #if defined(PETSC_HAVE_DLSYM)
242   if (handle) dlhandle = (dlhandle_t)handle;
243   else {
244 
245     #if defined(PETSC_HAVE_DLOPEN)
246     /* Attempt to retrieve the main executable's dlhandle. */
247     {
248       int dlflags1 = 0, dlflags2 = 0;
249       #if defined(PETSC_HAVE_RTLD_LAZY)
250       dlflags1 = RTLD_LAZY;
251       #endif
252       if (!dlflags1) {
253       #if defined(PETSC_HAVE_RTLD_NOW)
254         dlflags1 = RTLD_NOW;
255       #endif
256       }
257       #if defined(PETSC_HAVE_RTLD_LOCAL)
258       dlflags2 = RTLD_LOCAL;
259       #endif
260       if (!dlflags2) {
261       #if defined(PETSC_HAVE_RTLD_GLOBAL)
262         dlflags2 = RTLD_GLOBAL;
263       #endif
264       }
265       #if defined(PETSC_HAVE_DLERROR)
266       if (!(PETSC_RUNNING_ON_VALGRIND)) { dlerror(); /* clear any previous error; valgrind does not like this */ }
267       #endif
268           /* Attempt to open the main executable as a dynamic library. */
269       #if defined(PETSC_HAVE_RTDL_DEFAULT)
270       dlhandle = RTLD_DEFAULT;
271       #else
272       dlhandle = dlopen(NULL, dlflags1 | dlflags2);
273         #if defined(PETSC_HAVE_DLERROR)
274       {
275         const char *e = (const char *)dlerror();
276         PetscCheck(!e, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Error opening main executable as a dynamic library:\n  Error message from dlopen(): '%s'", e);
277       }
278         #endif
279       #endif
280     }
281     #endif
282   #endif /* PETSC_HAVE_DLOPEN && PETSC_HAVE_DYNAMIC_LIBRARIES */
283   }
284   #if defined(PETSC_HAVE_DLERROR)
285   dlerror(); /* clear any previous error */
286   #endif
287   dlsymbol = (dlsymbol_t)dlsym(dlhandle, symbol);
288   /*
289      --- unimplemented ---
290   */
291 #else
292   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
293 #endif
294 
295   *value = *((void **)&dlsymbol);
296 
297 #if defined(PETSC_SERIALIZE_FUNCTIONS)
298   if (*value) PetscCall(PetscFPTAdd(*value, symbol));
299 #endif
300   return (0);
301 }
302 
303 /*@C
304   PetscDLAddr - find the name of a symbol in a dynamic library
305 
306   Not Collective
307 
308   Input Parameters:
309 + handle - obtained with `PetscDLOpen()` or NULL
310 - func   - pointer to the function, NULL if not found
311 
312   Output Parameter:
313 . name   - name of symbol, or NULL if name lookup is not supported.
314 
315   Level: developer
316 
317   Notes:
318   The caller must free the returned name.
319 
320   In order to be dynamically loadable, the symbol has to be exported as such.  On many UNIX-like
321   systems this requires platform-specific linker flags.
322 
323 .seealso: `PetscDLClose()`, `PetscDLSym()`, `PetscDLOpen()`
324 @*/
325 PetscErrorCode PetscDLAddr(void (*func)(void), char **name)
326 {
327   PetscFunctionBegin;
328   PetscValidPointer(name, 2);
329   *name = NULL;
330 #if defined(PETSC_HAVE_DLADDR) && !(defined(__cray__) && defined(__clang__))
331   dlerror(); /* clear any previous error */
332   {
333     Dl_info info;
334 
335     PetscCheck(dladdr(*(void **)&func, &info), PETSC_COMM_SELF, PETSC_ERR_LIB, "Failed to lookup symbol: %s", dlerror());
336   #ifdef PETSC_HAVE_CXX
337     PetscCall(PetscDemangleSymbol(info.dli_sname, name));
338   #else
339     PetscCall(PetscStrallocpy(info.dli_sname, name));
340   #endif
341   }
342 #endif
343   PetscFunctionReturn(0);
344 }
345