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