/[ascend]/trunk/base/generic/utilities/ascDynaLoad.c
ViewVC logotype

Contents of /trunk/base/generic/utilities/ascDynaLoad.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1598 - (show annotations) (download) (as text)
Fri Aug 17 06:44:45 2007 UTC (17 years, 2 months ago) by jpye
File MIME type: text/x-csrc
File size: 18497 byte(s)
All platforms will now use the lib<name>_ascend.so convention for ASCEND external libraries.
(it makes it simpler for people writing such things).
1 /* ASCEND modelling environment
2 Copyright (C) 2006 Carnegie Mellon University
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18 *//**
19 @file
20
21 This file *should* support unix/linux-style systems (dlfcn.h)
22 and Windows.
23
24 Note that under many systems, profiling does not work
25 with dynamic libraries!
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include "ascConfig.h"
32 #include "error.h"
33 #include "ascPrint.h"
34 #include "ascPanic.h"
35 #include "ascMalloc.h"
36 #include "ascDynaLoad.h"
37 #include "ascEnvVar.h"
38
39 #include <general/env.h>
40 #include <general/ospath.h>
41 #include <compiler/instance_enum.h>
42 #include <general/list.h>
43 #include <compiler/extfunc.h>
44 #include <compiler/importhandler.h>
45
46 typedef int (*ExternalLibraryRegister_fptr_t)(void);
47
48 /*--------------------------------------
49 GENERIC STUFF
50 */
51
52 struct ascend_dlrecord {
53 char *path; /* library name */
54 void *dlreturn; /* return from dlopen */
55 struct ascend_dlrecord *next;
56 };
57
58 /* Linked list of library names & dlopen() return values. */
59 static struct ascend_dlrecord *g_ascend_dllist = NULL;
60
61 /*
62 * Adds a record of the path and handle to the list.
63 * If it fails to do this, returns 1, else 0.
64 */
65 static int AscAddRecord(void *dlreturn, CONST char *path){
66 struct ascend_dlrecord *new;
67 char *keeppath;
68 if (dlreturn == NULL || path == NULL) {
69 return 1;
70 }
71 keeppath = ASC_STRDUP((char *)path);
72 if (keeppath==NULL) return 1;
73 new = ASC_NEW(struct ascend_dlrecord);
74 if (new==NULL) {
75 ascfree(keeppath);
76 return 1;
77 }
78 new->next = g_ascend_dllist; /* insert at head */
79 g_ascend_dllist = new;
80 new->path = keeppath;
81 new->dlreturn = dlreturn;
82 return 0;
83 }
84
85 /*
86 * Finds a record of the path given and returns the associated handle.
87 * If it fails to do this, returns NULL.
88 */
89 static
90 void *AscFindDLRecord(CONST char *path)
91 {
92 struct ascend_dlrecord *new;
93 if (path == NULL) {
94 return NULL;
95 }
96 new = g_ascend_dllist;
97 while (new != NULL && strcmp(new->path,path) != 0) {
98 /* advance new until no more new or new with path found */
99 new = new->next;
100 }
101 return (new != NULL) ? new->dlreturn : NULL;
102 }
103
104 /*
105 * Finds and returns the handle to path, if one matches, and
106 * deletes the record from the list. Returns NULL if not found.
107 */
108 static
109 void *AscDeleteRecord(CONST char *path)
110 {
111 struct ascend_dlrecord *nextptr, *lastptr, *old;
112 void *dlreturn = NULL;
113
114 if ((g_ascend_dllist == NULL) || (NULL == path)) return NULL;
115
116 if (strcmp(path,g_ascend_dllist->path)==0) {
117 /* head case */
118 old = g_ascend_dllist;
119 g_ascend_dllist = old->next;
120 dlreturn = old->dlreturn;
121 ascfree(old->path);
122 ascfree(old);
123 } else {
124 lastptr = g_ascend_dllist;
125 nextptr = lastptr->next;
126 while (nextptr != NULL && strcmp(nextptr->path,path) != 0) {
127 lastptr = nextptr;
128 nextptr = nextptr->next;
129 }
130 /* so either nextptr is NULL and not in list, or nextptr is
131 * what we want to delete and lastptr is the link to it.
132 */
133 if (nextptr != NULL) {
134 old = nextptr;
135 lastptr->next = nextptr->next;
136 dlreturn = old->dlreturn;
137 ascfree(old->path);
138 ascfree(old);
139 }
140 }
141 return dlreturn;
142 }
143
144 /*
145 * Checks the list for a conflicting handle so we can issue
146 * a more helpful warning, if need be, than the standard message.
147 */
148 static
149 void AscCheckDuplicateLoad(CONST char *path)
150 {
151 struct ascend_dlrecord *r;
152
153 if (NULL == path) {
154 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Null path");
155 return;
156 }
157
158 r = g_ascend_dllist;
159 while (r != NULL) {
160 if (strcmp(path,r->path)==0) {
161 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Attempt to load already loaded '%s'.",path);
162 return;
163 }
164 r = r->next;
165 }
166 }
167
168
169 /*-----------------------------------------------
170 WINDOWS
171 */
172 #if defined(__WIN32__)
173 # include <windows.h>
174
175 int Asc_DynamicLoad(CONST char *path, CONST char *initFun){
176 HINSTANCE xlib;
177 ExternalLibraryRegister_fptr_t install = NULL;
178
179 if (NULL == path) {
180 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null path\n");
181 return 1;
182 }
183
184 AscCheckDuplicateLoad(path); /* whine if we've see it before */
185 /*
186 * If the named library does not exist, if it's not loadable or if
187 * it does not define the named install proc, report an error
188 */
189
190 xlib = LoadLibrary(path);
191 if (xlib == NULL) {
192 ERROR_REPORTER_HERE(ASC_PROG_ERR,"LoadLibrary failed\n'%s'",path);
193 return 1;
194 }
195 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"LoadLibrary succeeded, '%s'\n",path);
196
197 if (NULL != initFun) {
198 install = (int (*)(void))GetProcAddress(xlib,initFun);
199 if (install == NULL) {
200 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Required function '%s' not found", initFun);
201 (void)FreeLibrary(xlib);
202 return 1;
203 #if 0
204 }else{
205 FPRINTF(ASCERR,"FOUND INITFCN %s AT %d\n",initFun,install);
206 #endif
207 }
208 }
209 if (0 != AscAddRecord(xlib,path)) {
210 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to record library (%s)\n",path);
211 }
212 return (install == NULL) ? 0 : (*install)();
213 }
214 #define ASCDL_OK /* this line should appear inside each Asc_DynamicLoad */
215
216 # define UNLOAD FreeLibrary
217 # define DLLSYM GetProcAddress
218 # define DLL_CAST (HINSTANCE)
219 # define ASC_DLERRSTRING "unknown"
220 # define UNLOAD_SUCCESS TRUE
221
222 #endif /* __WIN32__ */
223
224 /*-----------------------------------------------
225 UNIX/LINUX
226 */
227 /*
228 SOLARIS and LINUX
229 */
230 /* NOTE, added defined(__unix__) here, not sure if that's a bad thing or not -- johnpye */
231 /*
232 From a quick Google, it appears that AIX 5.1 now provides dlfcn.h,
233 so I'll remove the code that was emulating it here. -- johnpye
234 */
235 #if (defined(sun) || defined(linux) || defined(__unix__) || defined(solaris) || defined(_AIX) || defined(_SGI_SOURCE))
236 # ifndef MACH
237 # include <dlfcn.h>
238 # else
239 # error "MACH unsupported"
240 # endif /* mach */
241
242 int Asc_DynamicLoad(CONST char *path, CONST char *initFun)
243 {
244 void *xlib;
245 ExternalLibraryRegister_fptr_t install = NULL;
246
247 if (NULL == path) {
248 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: null path");
249 return 1;
250 }
251
252 AscCheckDuplicateLoad(path); /* whine if we've see it before */
253
254 /*
255 * If the named library does not exist, if it's not loadable or if
256 * it does not define the named install proc, report an error
257 */
258 xlib = dlopen(path, 1);
259 if (xlib == NULL) {
260 ERROR_REPORTER_HERE(ASC_PROG_ERR,"%s",(char *)dlerror());
261
262 #ifdef linux
263 ERROR_REPORTER_HERE(ASC_PROG_ERR,"LD_LIBRARY_PATH = \"%s\"",getenv("LD_LIBRARY_PATH"));
264 #endif
265
266 return 1;
267 }
268 if (NULL != initFun) {
269 /* https://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=2252 */
270 *(void**)(&install) = dlsym(xlib, initFun);
271 if (install == NULL) {
272 ERROR_REPORTER_HERE(ASC_PROG_ERR,"%s",(char *)dlerror());
273 dlclose(xlib);
274 return 1;
275 }
276 }
277
278 if (0 != AscAddRecord(xlib,path)) {
279 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to record library (%s)",path);
280 }
281 return (install == NULL) ? 0 : (*install)();
282 }
283 #define ASCDL_OK /* this line should appear inside each Asc_DynamicLoad */
284
285 # define UNLOAD dlclose
286 # define DLLSYM dlsym
287 # define DLL_CAST (void *)
288 # define ASC_DLERRSTRING dlerror()
289 # define UNLOAD_SUCCESS 0
290
291 #endif /* posix: linux, unix, solaris,sgi */
292
293 /*-----------------------------------------------
294 HPUX
295 */
296 #ifdef __hpux
297 /*
298 Kirk Abbott last fiddled with the following, which was
299 originally put in place my Michael Moore for an
300 HP/UX 9.X Operating Sys back in 1993. Arrr. No idea if
301 it still works.
302 */
303
304 # include <dl.h>
305 # include <errno.h>
306
307 int Asc_DynamicLoad(CONST char *path, CONST char *initFun)
308 {
309 shl_t xlib;
310 ExternalLibraryRegister_fptr_t install = NULL;
311 int i;
312
313 if (NULL == path) {
314 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null path");
315 return 1;
316 }
317
318 AscCheckDuplicateLoad(path); /* whine if we've see it before */
319
320 /*
321 * If the named library does not exist, if it's not loadable or if
322 * it does not define the named install proc, report an error
323 */
324 xlib = shl_load(path, BIND_IMMEDIATE | BIND_VERBOSE, 0L);
325 if (xlib == (shl_t) NULL) {
326 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to load shared library: %s",strerror(errno));
327 return 1;
328 }
329 if (NULL != initFun) {
330 i = shl_findsym(&xlib, initFun, TYPE_PROCEDURE, &install);
331 if (i == -1) {
332 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find needed symbol '%s': %s",
333 initFun, strerror(errno));
334 shl_unload(xlib); /* baa */
335 return 1;
336 }
337 if(install == NULL) {
338 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find needed symbol '%s'. Error type unknown",initFun);
339 shl_unload(xlib); /* baa */
340 return 1;
341 }
342 }
343 if (0 != AscAddRecord(xlib,path)) {
344 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to record library (%s)",path);
345 }
346 return (install == NULL) ? 0 : (*install)();
347 }
348 # define ASCDL_OK /* this line should appear inside each Asc_DynamicLoad */
349
350 # define UNLOAD shl_unload
351 # define DLL_CAST (shl_t)
352 # define ASC_DLERRSTRING "NULL definition"
353 # define UNLOAD_SUCCESS 0
354
355 #endif /* __hpux */
356
357 /*-----------------------------------------------
358 Did we get something from the above?
359 */
360
361 #ifndef ASCDL_OK
362 # error "Unable to define an Asc_DynamicLoad function. Check your compiler options and installed system libraries."
363 #endif
364
365 /**-----------------------------------------------
366 DYNAMIC UNLOADING
367 */
368
369 int Asc_DynamicUnLoad(CONST char *path)
370 {
371 void *dlreturn;
372 int retval;
373
374 if (NULL == path) {
375 ERROR_REPORTER_HERE(ASC_PROG_ERR, "Failed: Null path");
376 return -3;
377 }
378
379 dlreturn = AscDeleteRecord(path);
380 if (dlreturn == NULL) {
381 ERROR_REPORTER_HERE(ASC_PROG_ERR, "Unable to remember or unload %s", path);
382 return -3;
383 }
384 CONSOLE_DEBUG("Asc_DynamicUnLoad: forgetting & unloading %s", path);
385 /*
386 * dlclose() returns 0 on success, FreeLibrary() returns TRUE.
387 * A uniform convention is preferable, so trap and return 0 on success.
388 */
389 retval = UNLOAD(DLL_CAST dlreturn);
390 return (retval == UNLOAD_SUCCESS) ? 0 : retval;
391 }
392
393 /**-----------------------------------------------
394 DYNAMIC VARIABLE LINKING
395 */
396 void *Asc_DynamicVariable(CONST char *libname, CONST char *symbol)
397 {
398 void *dlreturn;
399 void *symreturn;
400 #ifdef __hpux
401 int i;
402 #endif
403
404 if (libname == NULL) {
405 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null libname");
406 return NULL;
407 }
408 if (symbol == NULL) {
409 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null symbol");
410 return NULL;
411 }
412
413 dlreturn = AscFindDLRecord(libname);
414 if (dlreturn == NULL) {
415 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested library %s", libname);
416 return NULL;
417 }
418 #ifdef __hpux
419 i = shl_findsym(&dlreturn, symbol, TYPE_UNDEFINED, &symreturn);
420 if (i == -1) {
421 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested symbol '%s' in %s (%s)",
422 symbol, libname, strerror(errno));
423 symreturn = NULL;
424 }
425 #elif defined(__WIN32__)
426 /*
427 * Here's a bit of possibly-misdirected casting horror.
428 * ISO C forbids casting between function and data pointers, so, naturally,
429 * we cast between function and data pointers. Well, we don't have much
430 * choice. GetProcAddress() returns a function pointer for both functions
431 * and variables so we have to do the cast for variables. This is ok on
432 * 32 bit Windows since the pointers are compatible. Then, to avoid
433 * being reminded by the compiler that we're doing something illegal,
434 * we apply convoluted casting to shut it up.
435 * Oh, the crap you can find on the internet... JDS
436 */
437 *(FARPROC*)(&symreturn) = GetProcAddress((HINSTANCE)dlreturn, symbol);
438 #else
439 /* no problem on POSIX systems - dlsym() returns a void *. */
440 symreturn = dlsym(dlreturn, symbol);
441 #endif
442 if (symreturn == NULL) {
443 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested symbol '%s' in %s",symbol,libname);
444 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Error type: %s",ASC_DLERRSTRING);
445 }
446 return symreturn;
447 }
448
449 /**-----------------------------------------------
450 DYNAMIC FUNCTION LINKING
451 */
452 DynamicF Asc_DynamicFunction(CONST char *libname, CONST char *symbol)
453 {
454 void *dlreturn;
455 DynamicF symreturn;
456 #ifdef __hpux
457 int i;
458 #endif
459
460 if (libname == NULL) {
461 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: null library name");
462 return NULL;
463 }
464 if (symbol == NULL) {
465 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: null function name");
466 return NULL;
467 }
468
469 dlreturn = AscFindDLRecord(libname);
470 if (dlreturn == NULL) {
471 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested library %s", libname);
472 return NULL;
473 }
474 #ifdef __hpux
475 i = shl_findsym(&dlreturn, symbol, TYPE_UNDEFINED, &symreturn);
476 if (i == -1) {
477 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested function '%s' in %s (%s)",
478 symbol, libname, strerror(errno));
479 symreturn = NULL;
480 }
481 #elif defined(__WIN32__)
482 /* no problem on Windows - GetProcAddress() returns a function pointer. */
483 symreturn = (DynamicF)GetProcAddress((HINSTANCE)dlreturn, symbol);
484 #else
485 /*
486 * Here's the corresponding bit of possibly-misdirected casting horror.
487 * ISO C forbids casting between function and data pointers, so, naturally,
488 * we cast between function and data pointers. Well, we don't have much
489 * choice. dlsym() returns a void* for both variables and functions so we
490 * have to do the cast for functions. This is ok on POSIX systems since the
491 * pointer types are compatible. Then, to avoid being reminded by the
492 * compiler that we're doing something illegal, we apply convoluted casting
493 * to shut it up. Oh, the crap you can find on the internet... JDS
494 */
495 *(void**)(&symreturn) = dlsym(dlreturn, symbol);
496 #endif
497 if (symreturn == NULL) {
498 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested function '%s' in %s",symbol,libname);
499 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Error type: %s",ASC_DLERRSTRING);
500 }
501 return symreturn;
502 }
503
504
505 /*-----------------------------------------------------------------------------
506 SEARCHING FOR LIBRARIES
507 */
508
509
510 /**
511 Create a library filename according to platform standard naming.
512
513 @param partialname The partial filename (eg 'mylib')
514 @return Complete filename (eg 'libmylib.so' or 'mylib.dlll', etc)
515
516 Basically just adds ASC_SHLIBPREFIX to start and ASC_SHLIBSUFFIX to end.
517
518 No allowance made for 'soname' suffixes eg 'libsomething.so.1' etc. But
519 that's probably OK as those methods aren't really applicable to dlopened
520 libraries (eg the soname symlink mechanism breaks down).
521 */
522 char *dynaload_lib_filename(const char *partialname){
523 char *buffer;
524 buffer = ASC_NEW_ARRAY(char,PATH_MAX);
525 #if !defined(ASC_SHLIBSUFFIX) || !defined(ASC_SHLIBPREFIX)
526 # error "ASC_SHLIBSUFFIX and ASC_SHLIBPREFIX are not defined"
527 #endif
528 snprintf(buffer,PATH_MAX,"%s%s%s",ASC_SHLIBPREFIX,partialname,ASC_SHLIBSUFFIX);
529 return buffer;
530 }
531
532
533 /**
534 A little structure to help with searching for libraries
535
536 @see test_librarysearch
537 */
538 struct LibrarySearch{
539 struct FilePath *partialpath;
540 char fullpath[PATH_MAX];
541 };
542
543 FilePathTestFn test_librarysearch;
544
545 /**
546 A 'test' function for passing to the ospath_searchpath_iterate function.
547 This test function will return a match when a library having the required
548 name is present in the fully resolved path.
549 */
550 int test_librarysearch(struct FilePath *path, void *userdata){
551 /* user data = the relative path, plus a place
552 to store the full path when found */
553 FILE *f;
554 struct LibrarySearch *ls;
555 struct FilePath *fp;
556
557 ls = (struct LibrarySearch *)userdata;
558 fp = ospath_concat(path,ls->partialpath);
559 if(fp==NULL){
560 char *tmp;
561 tmp = ospath_str(path);
562 CONSOLE_DEBUG("Unable to concatenate '%s'...",tmp);
563 ospath_free_str(tmp);
564 tmp = ospath_str(ls->partialpath);
565 CONSOLE_DEBUG("... and '%s'...",tmp);
566 ospath_free_str(tmp);
567 return 0;
568 }
569
570 ospath_strncpy(fp,ls->fullpath,PATH_MAX);
571 /* CONSOLE_DEBUG("SEARCHING FOR %s",ls->fullpath); */
572
573 f = ospath_fopen(fp,"r");
574 if(f==NULL){
575 ospath_free(fp);
576 return 0;
577 }
578 fclose(f);
579
580 /* ERROR_REPORTER_HERE(ASC_PROG_NOTE,"FOUND! %s\n",ls->fullpath); */
581 ospath_free(fp);
582 return 1;
583 }
584
585 /**
586 @DEPRECATED this function needs to be rewritten to use 'ImportHandler'
587 functionality.
588 */
589 char *SearchArchiveLibraryPath(CONST char *name, char *dpath, const char *envv){
590 struct FilePath *fp1, *fp2, *fp3; /* relative path */
591 char *s1;
592 char *buffer;
593
594 struct LibrarySearch ls;
595 struct FilePath **sp;
596 char *path, *foundpath;
597 ospath_stat_t buf;
598 FILE *f;
599
600 fp1 = ospath_new_noclean(name);
601 if(fp1==NULL){
602 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Invalid partial path '%s'",name);
603 ospath_free(fp1);
604 return NULL;
605 }
606
607 s1 = ospath_getfilestem(fp1);
608 if(s1==NULL){
609 /* not a file, so fail... */
610 return NULL;
611 }
612
613 fp2 = ospath_getdir(fp1);
614 if(fp2==NULL){
615 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to retrieve file dir");
616 return NULL;
617 }
618
619 buffer = dynaload_lib_filename(s1);
620
621 fp3 = ospath_new(buffer);
622 ASC_FREE(buffer);
623 ospath_free(fp1);
624 fp1 = ospath_concat(fp2,fp3);
625 ospath_free(fp2);
626 ospath_free(fp3);
627 ospath_free_str(s1);
628
629 /* attempt to open "name" directly */
630 if(0==ospath_stat(fp1,&buf) && NULL!=(f = ospath_fopen(fp1,"r")) ){
631 char *tmp;
632 tmp = ospath_str(fp1);
633 CONSOLE_DEBUG("Library '%s' opened directly, without path search",tmp);
634 ospath_free_str(tmp);
635 fp2 = ospath_getabs(fp1);
636 foundpath = ospath_str(fp2);
637 ospath_free(fp2);
638 fclose(f);
639 }else{
640
641 ls.partialpath = fp1;
642
643 path=Asc_GetEnv(envv);
644 if(path==NULL){
645 /* CONSOLE_DEBUG("Library search path env var '%s' not found, using default path '%s'",envv,dpath); */
646 path=dpath;
647 }
648
649 /* CONSOLE_DEBUG("SEARCHPATH IS %s",path); */
650 sp = ospath_searchpath_new(path);
651
652 if(NULL==ospath_searchpath_iterate(sp,&test_librarysearch,&ls)){
653 ospath_free(fp1);
654 ospath_searchpath_free(sp);
655 return NULL;
656 }
657
658 foundpath = ASC_NEW_ARRAY(char,strlen(ls.fullpath)+1);
659 strcpy(foundpath,ls.fullpath);
660 ospath_searchpath_free(sp);
661 }
662
663 ospath_free(fp1);
664 return foundpath;
665 }

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22