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