xref: /petsc/src/sys/dll/dlimpl.c (revision 76be6f4ff3bd4e251c19fc00ebbebfd58b6e7589)
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 {
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,
74                   NULL,erc,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&buff,0,NULL);
75     PetscCall(PetscError(PETSC_COMM_SELF,__LINE__,PETSC_FUNCTION_NAME,__FILE__,PETSC_ERR_FILE_OPEN,PETSC_ERROR_REPEAT,
76                          "Unable to open dynamic library:\n  %s\n  Error message from LoadLibrary() %s\n",name,buff));
77     LocalFree(buff);
78     PetscFunctionReturn(0);
79 #else
80     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open dynamic library:\n  %s\n  Error message from LoadLibrary() %s",name,"unavailable");
81 #endif
82   }
83 
84   /*
85      --- dlopen ---
86   */
87 #elif defined(PETSC_HAVE_DLFCN_H) && defined(PETSC_HAVE_DLOPEN)
88   /*
89       Mode indicates symbols required by symbol loaded with dlsym()
90      are only loaded when required (not all together) also indicates
91      symbols required can be contained in other libraries also opened
92      with dlopen()
93   */
94 #if defined(PETSC_HAVE_RTLD_LAZY)
95   dlflags1 = RTLD_LAZY;
96 #endif
97 #if defined(PETSC_HAVE_RTLD_NOW)
98   if (mode & PETSC_DL_NOW) dlflags1 = RTLD_NOW;
99 #endif
100 #if defined(PETSC_HAVE_RTLD_GLOBAL)
101   dlflags2 = RTLD_GLOBAL;
102 #endif
103 #if defined(PETSC_HAVE_RTLD_LOCAL)
104   if (mode & PETSC_DL_LOCAL) dlflags2 = RTLD_LOCAL;
105 #endif
106 #if defined(PETSC_HAVE_DLERROR)
107   dlerror(); /* clear any previous error */
108 #endif
109   dlhandle = dlopen(name,dlflags1|dlflags2);
110   if (!dlhandle) {
111 #if defined(PETSC_HAVE_DLERROR)
112     const char *errmsg = dlerror();
113 #else
114     const char *errmsg = "unavailable";
115 #endif
116     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open dynamic library:\n  %s\n  Error message from dlopen() %s",name,errmsg);
117   }
118   /*
119      --- unimplemented ---
120   */
121 #else
122   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
123 #endif
124 
125   *handle = (PetscDLHandle) dlhandle;
126   PetscFunctionReturn(0);
127 }
128 
129 /*@C
130    PetscDLClose -  closes a dynamic library
131 
132    Not Collective
133 
134   Input Parameter:
135 .   handle - the handle for the library obtained with PetscDLOpen()
136 
137   Level: developer
138 
139 .seealso: `PetscDLOpen()`, `PetscDLSym()`, `PetscDLAddr()`
140 @*/
141 PetscErrorCode  PetscDLClose(PetscDLHandle *handle)
142 {
143   PetscFunctionBegin;
144   PetscValidPointer(handle,1);
145 
146   /*
147      --- FreeLibrary ---
148   */
149 #if defined(PETSC_HAVE_WINDOWS_H)
150 #if defined(PETSC_HAVE_FREELIBRARY)
151   if (FreeLibrary((dlhandle_t)*handle) == 0) {
152 #if defined(PETSC_HAVE_GETLASTERROR)
153     char  *buff = NULL;
154     DWORD erc   = GetLastError();
155     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,erc,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&buff,0,NULL);
156     PetscErrorPrintf("Error closing dynamic library:\n  Error message from FreeLibrary() %s\n",buff);
157     LocalFree(buff);
158 #else
159     PetscErrorPrintf("Error closing dynamic library:\n  Error message from FreeLibrary() %s\n","unavailable");
160 #endif
161   }
162 #endif /* !PETSC_HAVE_FREELIBRARY */
163 
164   /*
165      --- dclose ---
166   */
167 #elif defined(PETSC_HAVE_DLFCN_H)
168 #if defined(PETSC_HAVE_DLCLOSE)
169 #if defined(PETSC_HAVE_DLERROR)
170   dlerror(); /* clear any previous error */
171 #endif
172   if (dlclose((dlhandle_t)*handle) < 0) {
173 #if defined(PETSC_HAVE_DLERROR)
174     const char *errmsg = dlerror();
175 #else
176     const char *errmsg = "unavailable";
177 #endif
178     PetscErrorPrintf("Error closing dynamic library:\n  Error message from dlclose() %s\n", errmsg);
179   }
180 #endif /* !PETSC_HAVE_DLCLOSE */
181 
182   /*
183      --- unimplemented ---
184   */
185 #else
186   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
187 #endif
188 
189   *handle = NULL;
190   PetscFunctionReturn(0);
191 }
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   Notes:
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   PetscValidCharPointer(symbol,2);
220   PetscValidPointer(value,3);
221 
222   dlhandle = (dlhandle_t) 0;
223   dlsymbol = (dlsymbol_t) 0;
224   *value   = (void*) 0;
225 
226   /*
227      --- GetProcAddress ---
228   */
229 #if defined(PETSC_HAVE_WINDOWS_H)
230 #if defined(PETSC_HAVE_GETPROCADDRESS)
231   if (handle) dlhandle = (dlhandle_t) handle;
232   else dlhandle = (dlhandle_t) GetCurrentProcess();
233   dlsymbol = (dlsymbol_t) GetProcAddress(dlhandle,symbol);
234 #if defined(PETSC_HAVE_SETLASTERROR)
235   SetLastError((DWORD)0); /* clear any previous error */
236 #endif
237 #endif /* !PETSC_HAVE_GETPROCADDRESS */
238 
239   /*
240      --- dlsym ---
241   */
242 #elif defined(PETSC_HAVE_DLFCN_H)
243 #if defined(PETSC_HAVE_DLSYM)
244   if (handle) dlhandle = (dlhandle_t) handle;
245   else {
246 
247 #if defined(PETSC_HAVE_DLOPEN)
248     /* Attempt to retrieve the main executable's dlhandle. */
249     { int dlflags1 = 0, dlflags2 = 0;
250 #if defined(PETSC_HAVE_RTLD_LAZY)
251       dlflags1 = RTLD_LAZY;
252 #endif
253       if (!dlflags1) {
254 #if defined(PETSC_HAVE_RTLD_NOW)
255         dlflags1 = RTLD_NOW;
256 #endif
257       }
258 #if defined(PETSC_HAVE_RTLD_LOCAL)
259       dlflags2 = RTLD_LOCAL;
260 #endif
261       if (!dlflags2) {
262 #if defined(PETSC_HAVE_RTLD_GLOBAL)
263         dlflags2 = RTLD_GLOBAL;
264 #endif
265       }
266 #if defined(PETSC_HAVE_DLERROR)
267       if (!(PETSC_RUNNING_ON_VALGRIND)) {
268         dlerror(); /* clear any previous error; valgrind does not like this */
269       }
270 #endif
271       /* Attempt to open the main executable as a dynamic library. */
272 #if defined(PETSC_HAVE_RTDL_DEFAULT)
273       dlhandle = RTLD_DEFAULT;
274 #else
275       dlhandle = dlopen(NULL, dlflags1|dlflags2);
276 #if defined(PETSC_HAVE_DLERROR)
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
281 #endif
282     }
283 #endif
284 #endif /* PETSC_HAVE_DLOPEN && PETSC_HAVE_DYNAMIC_LIBRARIES */
285   }
286 #if defined(PETSC_HAVE_DLERROR)
287   dlerror(); /* clear any previous error */
288 #endif
289   dlsymbol = (dlsymbol_t) dlsym(dlhandle,symbol);
290   /*
291      --- unimplemented ---
292   */
293 #else
294   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP_SYS, "Cannot use dynamic libraries on this platform");
295 #endif
296 
297   *value = *((void**)&dlsymbol);
298 
299 #if defined(PETSC_SERIALIZE_FUNCTIONS)
300   if (*value) PetscCall(PetscFPTAdd(*value,symbol));
301 #endif
302   return(0);
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(__USE_GNU)
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(0);
346 }
347