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