xref: /petsc/src/sys/dll/dl.c (revision 8fb5bd83c3955fefcf33a54e3bb66920a9fa884b)
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 {
171     PetscCall(PetscInfo(NULL,"Dynamic library %s does not have lib prefix\n",libname));
172   }
173   for (s=basename; *s; s++) if (*s == '-') *s = '_';
174   PetscCall(PetscStrncpy(registername,"PetscDLLibraryRegister_",sizeof(registername)));
175   PetscCall(PetscStrlcat(registername,basename,sizeof(registername)));
176   PetscCall(PetscDLSym(handle,registername,(void**)&func));
177   if (func) {
178     PetscCall(PetscInfo(NULL,"Loading registered routines from %s\n",libname));
179     PetscCall((*func)());
180   } else {
181     PetscCall(PetscInfo(NULL,"Dynamic library %s does not have symbol %s\n",libname,registername));
182   }
183 
184   PetscCall(PetscNew(entry));
185   (*entry)->next   = NULL;
186   (*entry)->handle = handle;
187   PetscCall(PetscStrcpy((*entry)->libname,libname));
188   PetscFunctionReturn(0);
189 }
190 
191 /*@C
192    PetscDLLibrarySym - Load a symbol from the dynamic link libraries.
193 
194    Collective
195 
196    Input Parameters:
197 +  comm - communicator that will open the library
198 .  outlist - list of already open libraries that may contain symbol (can be NULL and only the executable is searched for the function)
199 .  path     - optional complete library name (if provided checks here before checking outlist)
200 -  insymbol - name of symbol
201 
202    Output Parameter:
203 .  value - if symbol not found then this value is set to NULL
204 
205    Level: developer
206 
207    Notes:
208     Symbol can be of the form
209         [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional
210 
211         Will attempt to (retrieve and) open the library if it is not yet been opened.
212 
213 @*/
214 PetscErrorCode  PetscDLLibrarySym(MPI_Comm comm,PetscDLLibrary *outlist,const char path[],const char insymbol[],void **value)
215 {
216   char           libname[PETSC_MAX_PATH_LEN],suffix[16],*symbol,*s;
217   PetscDLLibrary nlist,prev,list = NULL;
218 
219   PetscFunctionBegin;
220   if (outlist) PetscValidPointer(outlist,2);
221   if (path) PetscValidCharPointer(path,3);
222   PetscValidCharPointer(insymbol,4);
223   PetscValidPointer(value,5);
224 
225   if (outlist) list = *outlist;
226   *value = NULL;
227 
228   PetscCall(PetscStrchr(insymbol,'(',&s));
229   if (s) {
230     /* make copy of symbol so we can edit it in place */
231     PetscCall(PetscStrallocpy(insymbol,&symbol));
232     /* If symbol contains () then replace with a NULL, to support functionname() */
233     PetscCall(PetscStrchr(symbol,'(',&s));
234     s[0] = 0;
235   } else symbol = (char*)insymbol;
236 
237   /*
238        Function name does include library
239        -------------------------------------
240   */
241   if (path && path[0] != '\0') {
242     /* copy path and remove suffix from libname */
243     PetscCall(PetscStrncpy(libname,path,PETSC_MAX_PATH_LEN));
244     PetscCall(PetscStrncpy(suffix,".",sizeof(suffix)));
245     PetscCall(PetscStrlcat(suffix,PETSC_SLSUFFIX,sizeof(suffix)));
246     PetscCall(PetscStrrstr(libname,suffix,&s));
247     if (s) s[0] = 0;
248     /* Look if library is already opened and in path */
249     prev  = NULL;
250     nlist = list;
251     while (nlist) {
252       PetscBool match;
253       PetscCall(PetscStrcmp(nlist->libname,libname,&match));
254       if (match) goto done;
255       prev  = nlist;
256       nlist = nlist->next;
257     }
258     /* open the library and append it to path */
259     PetscCall(PetscDLLibraryOpen(comm,path,&nlist));
260     PetscCall(PetscInfo(NULL,"Appending %s to dynamic library search path\n",path));
261     if (prev) prev->next = nlist;
262     else {if (outlist) *outlist   = nlist;}
263 
264 done:;
265     PetscCall(PetscDLSym(nlist->handle,symbol,value));
266     if (*value) {
267       PetscCall(PetscInfo(NULL,"Loading function %s from dynamic library %s\n",insymbol,path));
268     }
269 
270     /*
271          Function name does not include library so search path
272          -----------------------------------------------------
273     */
274   } else {
275     while (list) {
276       PetscCall(PetscDLSym(list->handle,symbol,value));
277       if (*value) {
278         PetscCall(PetscInfo(NULL,"Loading symbol %s from dynamic library %s\n",symbol,list->libname));
279         break;
280       }
281       list = list->next;
282     }
283     if (!*value) {
284       PetscCall(PetscDLSym(NULL,symbol,value));
285       if (*value) {
286         PetscCall(PetscInfo(NULL,"Loading symbol %s from object code\n",symbol));
287       }
288     }
289   }
290 
291   if (symbol != insymbol) {
292     PetscCall(PetscFree(symbol));
293   }
294   PetscFunctionReturn(0);
295 }
296 
297 /*@C
298      PetscDLLibraryAppend - Appends another dynamic link library to the seach list, to the end
299                 of the search path.
300 
301      Collective
302 
303      Input Parameters:
304 +     comm - MPI communicator
305 -     path - name of the library
306 
307      Output Parameter:
308 .     outlist - list of libraries
309 
310      Level: developer
311 
312      Notes:
313     if library is already in path will not add it.
314 
315   If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
316       when the library is opened.
317 
318 .seealso: `PetscDLLibraryOpen()`
319 @*/
320 PetscErrorCode  PetscDLLibraryAppend(MPI_Comm comm,PetscDLLibrary *outlist,const char path[])
321 {
322   PetscDLLibrary list,prev;
323   size_t         len;
324   PetscBool      match,dir;
325   char           program[PETSC_MAX_PATH_LEN],found[8*PETSC_MAX_PATH_LEN];
326   char           *libname,suffix[16],*s;
327   PetscToken     token;
328 
329   PetscFunctionBegin;
330   PetscValidPointer(outlist,2);
331 
332   /* is path a directory? */
333   PetscCall(PetscTestDirectory(path,'r',&dir));
334   if (dir) {
335     PetscCall(PetscInfo(NULL,"Checking directory %s for dynamic libraries\n",path));
336     PetscCall(PetscStrncpy(program,path,sizeof(program)));
337     PetscCall(PetscStrlen(program,&len));
338     if (program[len-1] == '/') {
339       PetscCall(PetscStrlcat(program,"*.",sizeof(program)));
340     } else {
341       PetscCall(PetscStrlcat(program,"/*.",sizeof(program)));
342     }
343     PetscCall(PetscStrlcat(program,PETSC_SLSUFFIX,sizeof(program)));
344 
345     PetscCall(PetscLs(comm,program,found,8*PETSC_MAX_PATH_LEN,&dir));
346     if (!dir) PetscFunctionReturn(0);
347   } else {
348     PetscCall(PetscStrncpy(found,path,PETSC_MAX_PATH_LEN));
349   }
350   PetscCall(PetscStrncpy(suffix,".",sizeof(suffix)));
351   PetscCall(PetscStrlcat(suffix,PETSC_SLSUFFIX,sizeof(suffix)));
352 
353   PetscCall(PetscTokenCreate(found,'\n',&token));
354   PetscCall(PetscTokenFind(token,&libname));
355   while (libname) {
356     /* remove suffix from libname */
357     PetscCall(PetscStrrstr(libname,suffix,&s));
358     if (s) s[0] = 0;
359     /* see if library was already open then we are done */
360     list  = prev = *outlist;
361     match = PETSC_FALSE;
362     while (list) {
363       PetscCall(PetscStrcmp(list->libname,libname,&match));
364       if (match) break;
365       prev = list;
366       list = list->next;
367     }
368     /* restore suffix from libname */
369     if (s) s[0] = '.';
370     if (!match) {
371       /* open the library and add to end of list */
372       PetscCall(PetscDLLibraryOpen(comm,libname,&list));
373       PetscCall(PetscInfo(NULL,"Appending %s to dynamic library search path\n",libname));
374       if (!*outlist) *outlist   = list;
375       else           prev->next = list;
376     }
377     PetscCall(PetscTokenFind(token,&libname));
378   }
379   PetscCall(PetscTokenDestroy(&token));
380   PetscFunctionReturn(0);
381 }
382 
383 /*@C
384      PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of
385                  the search path.
386 
387      Collective
388 
389      Input Parameters:
390 +     comm - MPI communicator
391 -     path - name of the library
392 
393      Output Parameter:
394 .     outlist - list of libraries
395 
396      Level: developer
397 
398      Notes:
399     If library is already in path will remove old reference.
400 
401 @*/
402 PetscErrorCode  PetscDLLibraryPrepend(MPI_Comm comm,PetscDLLibrary *outlist,const char path[])
403 {
404   PetscDLLibrary list,prev;
405   size_t         len;
406   PetscBool      match,dir;
407   char           program[PETSC_MAX_PATH_LEN],found[8*PETSC_MAX_PATH_LEN];
408   char           *libname,suffix[16],*s;
409   PetscToken     token;
410 
411   PetscFunctionBegin;
412   PetscValidPointer(outlist,2);
413 
414   /* is path a directory? */
415   PetscCall(PetscTestDirectory(path,'r',&dir));
416   if (dir) {
417     PetscCall(PetscInfo(NULL,"Checking directory %s for dynamic libraries\n",path));
418     PetscCall(PetscStrncpy(program,path,sizeof(program)));
419     PetscCall(PetscStrlen(program,&len));
420     if (program[len-1] == '/') {
421       PetscCall(PetscStrlcat(program,"*.",sizeof(program)));
422     } else {
423       PetscCall(PetscStrlcat(program,"/*.",sizeof(program)));
424     }
425     PetscCall(PetscStrlcat(program,PETSC_SLSUFFIX,sizeof(program)));
426 
427     PetscCall(PetscLs(comm,program,found,8*PETSC_MAX_PATH_LEN,&dir));
428     if (!dir) PetscFunctionReturn(0);
429   } else {
430     PetscCall(PetscStrncpy(found,path,PETSC_MAX_PATH_LEN));
431   }
432 
433   PetscCall(PetscStrncpy(suffix,".",sizeof(suffix)));
434   PetscCall(PetscStrlcat(suffix,PETSC_SLSUFFIX,sizeof(suffix)));
435 
436   PetscCall(PetscTokenCreate(found,'\n',&token));
437   PetscCall(PetscTokenFind(token,&libname));
438   while (libname) {
439     /* remove suffix from libname */
440     PetscCall(PetscStrstr(libname,suffix,&s));
441     if (s) s[0] = 0;
442     /* see if library was already open and move it to the front */
443     prev  = NULL;
444     list  = *outlist;
445     match = PETSC_FALSE;
446     while (list) {
447       PetscCall(PetscStrcmp(list->libname,libname,&match));
448       if (match) {
449         PetscCall(PetscInfo(NULL,"Moving %s to begin of dynamic library search path\n",libname));
450         if (prev) prev->next = list->next;
451         if (prev) list->next = *outlist;
452         *outlist = list;
453         break;
454       }
455       prev = list;
456       list = list->next;
457     }
458     /* restore suffix from libname */
459     if (s) s[0] = '.';
460     if (!match) {
461       /* open the library and add to front of list */
462       PetscCall(PetscDLLibraryOpen(comm,libname,&list));
463       PetscCall(PetscInfo(NULL,"Prepending %s to dynamic library search path\n",libname));
464       list->next = *outlist;
465       *outlist   = list;
466     }
467     PetscCall(PetscTokenFind(token,&libname));
468   }
469   PetscCall(PetscTokenDestroy(&token));
470   PetscFunctionReturn(0);
471 }
472 
473 /*@C
474      PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries.
475 
476     Collective on PetscDLLibrary
477 
478     Input Parameter:
479 .     head - library list
480 
481      Level: developer
482 
483 @*/
484 PetscErrorCode  PetscDLLibraryClose(PetscDLLibrary list)
485 {
486   PetscBool      done = PETSC_FALSE;
487   PetscDLLibrary prev,tail;
488 
489   PetscFunctionBegin;
490   if (!list) PetscFunctionReturn(0);
491   /* traverse the list in reverse order */
492   while (!done) {
493     if (!list->next) done = PETSC_TRUE;
494     prev = tail = list;
495     while (tail->next) {
496       prev = tail;
497       tail = tail->next;
498     }
499     prev->next = NULL;
500     /* close the dynamic library and free the space in entry data-structure*/
501     PetscCall(PetscInfo(NULL,"Closing dynamic library %s\n",tail->libname));
502     PetscCall(PetscDLClose(&tail->handle));
503     PetscCall(PetscFree(tail));
504   }
505   PetscFunctionReturn(0);
506 }
507