xref: /petsc/src/sys/dll/dl.c (revision 2d30e087755efd99e28fdfe792ffbeb2ee1ea928)
1 /*
2       Routines for opening dynamic link libraries (DLLs), keeping a searchable
3    path of DLLs, obtaining remote DLLs via a URL and opening them locally.
4 */
5 
6 #include <petsc/private/petscimpl.h>
7 
8 /* ------------------------------------------------------------------------------*/
9 /*
10       Code to maintain a list of opened dynamic libraries and load symbols
11 */
12 struct _n_PetscDLLibrary {
13   PetscDLLibrary next;
14   PetscDLHandle  handle;
15   char           libname[PETSC_MAX_PATH_LEN];
16 };
17 
18 PetscErrorCode PetscDLLibraryPrintPath(PetscDLLibrary libs) {
19   PetscFunctionBegin;
20   while (libs) {
21     PetscErrorPrintf("  %s\n", libs->libname);
22     libs = libs->next;
23   }
24   PetscFunctionReturn(0);
25 }
26 
27 /*@C
28    PetscDLLibraryRetrieve - Copies a PETSc dynamic library from a remote location
29      (if it is remote), indicates if it exits and its local name.
30 
31      Collective
32 
33    Input Parameters:
34 +   comm - processors that are opening the library
35 -   libname - name of the library, can be relative or absolute
36 
37    Output Parameters:
38 +   name - actual name of file on local filesystem if found
39 .   llen - length of the name buffer
40 -   found - true if the file exists
41 
42    Level: developer
43 
44    Notes:
45    [[<http,ftp>://hostname]/directoryname/]filename[.so.1.0]
46 
47    ${PETSC_ARCH}, ${PETSC_DIR}, ${PETSC_LIB_DIR}, or ${any environmental variable}
48    occurring in directoryname and filename will be replaced with appropriate values.
49 @*/
50 PetscErrorCode PetscDLLibraryRetrieve(MPI_Comm comm, const char libname[], char *lname, size_t llen, PetscBool *found) {
51   char  *buf, *par2, suffix[16], *gz, *so;
52   size_t len;
53 
54   PetscFunctionBegin;
55   /*
56      make copy of library name and replace $PETSC_ARCH etc
57      so we can add to the end of it to look for something like .so.1.0 etc.
58   */
59   PetscCall(PetscStrlen(libname, &len));
60   len = PetscMax(4 * len, PETSC_MAX_PATH_LEN);
61   PetscCall(PetscMalloc1(len, &buf));
62   par2 = buf;
63   PetscCall(PetscStrreplace(comm, libname, par2, len));
64 
65   /* temporarily remove .gz if it ends library name */
66   PetscCall(PetscStrrstr(par2, ".gz", &gz));
67   if (gz) {
68     PetscCall(PetscStrlen(gz, &len));
69     if (len != 3) gz = NULL; /* do not end (exactly) with .gz */
70     else *gz = 0;            /* ends with .gz, so remove it   */
71   }
72   /* strip out .a from it if user put it in by mistake */
73   PetscCall(PetscStrlen(par2, &len));
74   if (par2[len - 1] == 'a' && par2[len - 2] == '.') par2[len - 2] = 0;
75 
76   PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found));
77   if (!(*found)) {
78     /* see if library name does already not have suffix attached */
79     PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
80     PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
81     PetscCall(PetscStrrstr(par2, suffix, &so));
82     /* and attach the suffix if it is not there */
83     if (!so) PetscCall(PetscStrcat(par2, suffix));
84 
85     /* restore the .gz suffix if it was there */
86     if (gz) PetscCall(PetscStrcat(par2, ".gz"));
87 
88     /* and finally retrieve the file */
89     PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found));
90   }
91 
92   PetscCall(PetscFree(buf));
93   PetscFunctionReturn(0);
94 }
95 
96 /*@C
97    PetscDLLibraryOpen - Opens a PETSc dynamic link library
98 
99      Collective
100 
101    Input Parameters:
102 +   comm - processors that are opening the library
103 -   path - name of the library, can be relative or absolute
104 
105    Output Parameter:
106 .   entry - a PETSc dynamic link library entry
107 
108    Level: developer
109 
110    Notes:
111    [[<http,ftp>://hostname]/directoryname/]libbasename[.so.1.0]
112 
113    If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
114    when the library is opened.
115 
116    ${PETSC_ARCH} occurring in directoryname and filename
117    will be replaced with the appropriate value.
118 
119 .seealso: `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`
120 @*/
121 PetscErrorCode PetscDLLibraryOpen(MPI_Comm comm, const char path[], PetscDLLibrary *entry) {
122   PetscBool     foundlibrary, match;
123   char          libname[PETSC_MAX_PATH_LEN], par2[PETSC_MAX_PATH_LEN], suffix[16], *s;
124   char         *basename, registername[128];
125   PetscDLHandle handle;
126   PetscErrorCode (*func)(void) = NULL;
127 
128   PetscFunctionBegin;
129   PetscValidCharPointer(path, 2);
130   PetscValidPointer(entry, 3);
131 
132   *entry = NULL;
133 
134   /* retrieve the library */
135   PetscCall(PetscInfo(NULL, "Retrieving %s\n", path));
136   PetscCall(PetscDLLibraryRetrieve(comm, path, par2, PETSC_MAX_PATH_LEN, &foundlibrary));
137   PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate dynamic library:\n  %s", path);
138   /* Eventually ./configure should determine if the system needs an executable dynamic library */
139 #define PETSC_USE_NONEXECUTABLE_SO
140 #if !defined(PETSC_USE_NONEXECUTABLE_SO)
141   PetscCall(PetscTestFile(par2, 'x', &foundlibrary));
142   PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Dynamic library is not executable:\n  %s\n  %s", path, par2);
143 #endif
144 
145   /* copy path and setup shared library suffix  */
146   PetscCall(PetscStrncpy(libname, path, PETSC_MAX_PATH_LEN));
147   PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
148   PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
149   /* remove wrong suffixes from libname */
150   PetscCall(PetscStrrstr(libname, ".gz", &s));
151   if (s && s[3] == 0) s[0] = 0;
152   PetscCall(PetscStrrstr(libname, ".a", &s));
153   if (s && s[2] == 0) s[0] = 0;
154   /* remove shared suffix from libname */
155   PetscCall(PetscStrrstr(libname, suffix, &s));
156   if (s) s[0] = 0;
157 
158   /* open the dynamic library */
159   PetscCall(PetscInfo(NULL, "Opening dynamic library %s\n", libname));
160   PetscCall(PetscDLOpen(par2, PETSC_DL_DECIDE, &handle));
161 
162   /* look for [path/]libXXXXX.YYY and extract out the XXXXXX */
163   PetscCall(PetscStrrchr(libname, '/', &basename)); /* XXX Windows ??? */
164   if (!basename) basename = libname;
165   PetscCall(PetscStrncmp(basename, "lib", 3, &match));
166   if (match) basename = basename + 3;
167   else PetscCall(PetscInfo(NULL, "Dynamic library %s does not have lib prefix\n", libname));
168   for (s = basename; *s; s++)
169     if (*s == '-') *s = '_';
170   PetscCall(PetscStrncpy(registername, "PetscDLLibraryRegister_", sizeof(registername)));
171   PetscCall(PetscStrlcat(registername, basename, sizeof(registername)));
172   PetscCall(PetscDLSym(handle, registername, (void **)&func));
173   if (func) {
174     PetscCall(PetscInfo(NULL, "Loading registered routines from %s\n", libname));
175     PetscCall((*func)());
176   } else {
177     PetscCall(PetscInfo(NULL, "Dynamic library %s does not have symbol %s\n", libname, registername));
178   }
179 
180   PetscCall(PetscNew(entry));
181   (*entry)->next   = NULL;
182   (*entry)->handle = handle;
183   PetscCall(PetscStrcpy((*entry)->libname, libname));
184   PetscFunctionReturn(0);
185 }
186 
187 /*@C
188    PetscDLLibrarySym - Load a symbol from the dynamic link libraries.
189 
190    Collective
191 
192    Input Parameters:
193 +  comm - communicator that will open the library
194 .  outlist - list of already open libraries that may contain symbol (can be NULL and only the executable is searched for the function)
195 .  path     - optional complete library name (if provided checks here before checking outlist)
196 -  insymbol - name of symbol
197 
198    Output Parameter:
199 .  value - if symbol not found then this value is set to NULL
200 
201    Level: developer
202 
203    Notes:
204     Symbol can be of the form
205         [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional
206 
207         Will attempt to (retrieve and) open the library if it is not yet been opened.
208 
209 @*/
210 PetscErrorCode PetscDLLibrarySym(MPI_Comm comm, PetscDLLibrary *outlist, const char path[], const char insymbol[], void **value) {
211   char           libname[PETSC_MAX_PATH_LEN], suffix[16], *symbol, *s;
212   PetscDLLibrary nlist, prev, list = NULL;
213 
214   PetscFunctionBegin;
215   if (outlist) PetscValidPointer(outlist, 2);
216   if (path) PetscValidCharPointer(path, 3);
217   PetscValidCharPointer(insymbol, 4);
218   PetscValidPointer(value, 5);
219 
220   if (outlist) list = *outlist;
221   *value = NULL;
222 
223   PetscCall(PetscStrchr(insymbol, '(', &s));
224   if (s) {
225     /* make copy of symbol so we can edit it in place */
226     PetscCall(PetscStrallocpy(insymbol, &symbol));
227     /* If symbol contains () then replace with a NULL, to support functionname() */
228     PetscCall(PetscStrchr(symbol, '(', &s));
229     s[0] = 0;
230   } else symbol = (char *)insymbol;
231 
232   /*
233        Function name does include library
234        -------------------------------------
235   */
236   if (path && path[0] != '\0') {
237     /* copy path and remove suffix from libname */
238     PetscCall(PetscStrncpy(libname, path, PETSC_MAX_PATH_LEN));
239     PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
240     PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
241     PetscCall(PetscStrrstr(libname, suffix, &s));
242     if (s) s[0] = 0;
243     /* Look if library is already opened and in path */
244     prev  = NULL;
245     nlist = list;
246     while (nlist) {
247       PetscBool match;
248       PetscCall(PetscStrcmp(nlist->libname, libname, &match));
249       if (match) goto done;
250       prev  = nlist;
251       nlist = nlist->next;
252     }
253     /* open the library and append it to path */
254     PetscCall(PetscDLLibraryOpen(comm, path, &nlist));
255     PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", path));
256     if (prev) prev->next = nlist;
257     else {
258       if (outlist) *outlist = nlist;
259     }
260 
261   done:;
262     PetscCall(PetscDLSym(nlist->handle, symbol, value));
263     if (*value) PetscCall(PetscInfo(NULL, "Loading function %s from dynamic library %s\n", insymbol, path));
264 
265     /*
266          Function name does not include library so search path
267          -----------------------------------------------------
268     */
269   } else {
270     while (list) {
271       PetscCall(PetscDLSym(list->handle, symbol, value));
272       if (*value) {
273         PetscCall(PetscInfo(NULL, "Loading symbol %s from dynamic library %s\n", symbol, list->libname));
274         break;
275       }
276       list = list->next;
277     }
278     if (!*value) {
279       PetscCall(PetscDLSym(NULL, symbol, value));
280       if (*value) PetscCall(PetscInfo(NULL, "Loading symbol %s from object code\n", symbol));
281     }
282   }
283 
284   if (symbol != insymbol) PetscCall(PetscFree(symbol));
285   PetscFunctionReturn(0);
286 }
287 
288 /*@C
289      PetscDLLibraryAppend - Appends another dynamic link library to the seach list, to the end
290                 of the search path.
291 
292      Collective
293 
294      Input Parameters:
295 +     comm - MPI communicator
296 -     path - name of the library
297 
298      Output Parameter:
299 .     outlist - list of libraries
300 
301      Level: developer
302 
303      Note:
304     if library is already in path will not add it.
305 
306   If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
307       when the library is opened.
308 
309 .seealso: `PetscDLLibraryOpen()`
310 @*/
311 PetscErrorCode PetscDLLibraryAppend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[]) {
312   PetscDLLibrary list, prev;
313   size_t         len;
314   PetscBool      match, dir;
315   char           program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN];
316   char          *libname, suffix[16], *s;
317   PetscToken     token;
318 
319   PetscFunctionBegin;
320   PetscValidPointer(outlist, 2);
321 
322   /* is path a directory? */
323   PetscCall(PetscTestDirectory(path, 'r', &dir));
324   if (dir) {
325     PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
326     PetscCall(PetscStrncpy(program, path, sizeof(program)));
327     PetscCall(PetscStrlen(program, &len));
328     if (program[len - 1] == '/') {
329       PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
330     } else {
331       PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
332     }
333     PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));
334 
335     PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
336     if (!dir) PetscFunctionReturn(0);
337   } else {
338     PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
339   }
340   PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
341   PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
342 
343   PetscCall(PetscTokenCreate(found, '\n', &token));
344   PetscCall(PetscTokenFind(token, &libname));
345   while (libname) {
346     /* remove suffix from libname */
347     PetscCall(PetscStrrstr(libname, suffix, &s));
348     if (s) s[0] = 0;
349     /* see if library was already open then we are done */
350     list = prev = *outlist;
351     match       = PETSC_FALSE;
352     while (list) {
353       PetscCall(PetscStrcmp(list->libname, libname, &match));
354       if (match) break;
355       prev = list;
356       list = list->next;
357     }
358     /* restore suffix from libname */
359     if (s) s[0] = '.';
360     if (!match) {
361       /* open the library and add to end of list */
362       PetscCall(PetscDLLibraryOpen(comm, libname, &list));
363       PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", libname));
364       if (!*outlist) *outlist = list;
365       else prev->next = list;
366     }
367     PetscCall(PetscTokenFind(token, &libname));
368   }
369   PetscCall(PetscTokenDestroy(&token));
370   PetscFunctionReturn(0);
371 }
372 
373 /*@C
374      PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of
375                  the search path.
376 
377      Collective
378 
379      Input Parameters:
380 +     comm - MPI communicator
381 -     path - name of the library
382 
383      Output Parameter:
384 .     outlist - list of libraries
385 
386      Level: developer
387 
388      Note:
389     If library is already in path will remove old reference.
390 
391 @*/
392 PetscErrorCode PetscDLLibraryPrepend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[]) {
393   PetscDLLibrary list, prev;
394   size_t         len;
395   PetscBool      match, dir;
396   char           program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN];
397   char          *libname, suffix[16], *s;
398   PetscToken     token;
399 
400   PetscFunctionBegin;
401   PetscValidPointer(outlist, 2);
402 
403   /* is path a directory? */
404   PetscCall(PetscTestDirectory(path, 'r', &dir));
405   if (dir) {
406     PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
407     PetscCall(PetscStrncpy(program, path, sizeof(program)));
408     PetscCall(PetscStrlen(program, &len));
409     if (program[len - 1] == '/') {
410       PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
411     } else {
412       PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
413     }
414     PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));
415 
416     PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
417     if (!dir) PetscFunctionReturn(0);
418   } else {
419     PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
420   }
421 
422   PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
423   PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
424 
425   PetscCall(PetscTokenCreate(found, '\n', &token));
426   PetscCall(PetscTokenFind(token, &libname));
427   while (libname) {
428     /* remove suffix from libname */
429     PetscCall(PetscStrstr(libname, suffix, &s));
430     if (s) s[0] = 0;
431     /* see if library was already open and move it to the front */
432     prev  = NULL;
433     list  = *outlist;
434     match = PETSC_FALSE;
435     while (list) {
436       PetscCall(PetscStrcmp(list->libname, libname, &match));
437       if (match) {
438         PetscCall(PetscInfo(NULL, "Moving %s to begin of dynamic library search path\n", libname));
439         if (prev) prev->next = list->next;
440         if (prev) list->next = *outlist;
441         *outlist = list;
442         break;
443       }
444       prev = list;
445       list = list->next;
446     }
447     /* restore suffix from libname */
448     if (s) s[0] = '.';
449     if (!match) {
450       /* open the library and add to front of list */
451       PetscCall(PetscDLLibraryOpen(comm, libname, &list));
452       PetscCall(PetscInfo(NULL, "Prepending %s to dynamic library search path\n", libname));
453       list->next = *outlist;
454       *outlist   = list;
455     }
456     PetscCall(PetscTokenFind(token, &libname));
457   }
458   PetscCall(PetscTokenDestroy(&token));
459   PetscFunctionReturn(0);
460 }
461 
462 /*@C
463      PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries.
464 
465     Collective on list
466 
467     Input Parameter:
468 .     head - library list
469 
470      Level: developer
471 
472 @*/
473 PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list) {
474   PetscBool      done = PETSC_FALSE;
475   PetscDLLibrary prev, tail;
476 
477   PetscFunctionBegin;
478   if (!list) PetscFunctionReturn(0);
479   /* traverse the list in reverse order */
480   while (!done) {
481     if (!list->next) done = PETSC_TRUE;
482     prev = tail = list;
483     while (tail->next) {
484       prev = tail;
485       tail = tail->next;
486     }
487     prev->next = NULL;
488     /* close the dynamic library and free the space in entry data-structure*/
489     PetscCall(PetscInfo(NULL, "Closing dynamic library %s\n", tail->libname));
490     PetscCall(PetscDLClose(&tail->handle));
491     PetscCall(PetscFree(tail));
492   }
493   PetscFunctionReturn(0);
494 }
495