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