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