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

Contents of /trunk/ascend/utilities/ascDynaLoad.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2353 - (show annotations) (download) (as text)
Sat Jan 8 11:21:09 2011 UTC (11 years, 5 months ago) by jpye
File MIME type: text/x-csrc
File size: 18526 byte(s)
fixed bug in destruction of importhandler_library. More work still to be done there.
Improved parser message for failed IMPORT.
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 <ascend/general/platform.h>
32 #include "error.h"
33 #include "ascPrint.h"
34 #include <ascend/general/panic.h>
35 #include <ascend/general/ascMalloc.h>
36 #include "ascDynaLoad.h"
37 #include "ascEnvVar.h"
38
39 #include <ascend/general/env.h>
40 #include <ascend/general/ospath.h>
41 #include <ascend/general/list.h>
42
43 typedef int (*ExternalLibraryRegister_fptr_t)(void);
44
45 /*--------------------------------------
46 GENERIC STUFF
47 */
48
49 struct ascend_dlrecord {
50 char *path; /* library name */
51 void *dlreturn; /* return from dlopen */
52 struct ascend_dlrecord *next;
53 };
54
55 /* Linked list of library names & dlopen() return values. */
56 static struct ascend_dlrecord *g_ascend_dllist = NULL;
57
58 /*
59 * Adds a record of the path and handle to the list.
60 * If it fails to do this, returns 1, else 0.
61 */
62 static int AscAddRecord(void *dlreturn, CONST char *path){
63 struct ascend_dlrecord *new;
64 char *keeppath;
65 if (dlreturn == NULL || path == NULL) {
66 return 1;
67 }
68 keeppath = ASC_STRDUP((char *)path);
69 if (keeppath==NULL) return 1;
70 new = ASC_NEW(struct ascend_dlrecord);
71 if (new==NULL) {
72 ascfree(keeppath);
73 return 1;
74 }
75 new->next = g_ascend_dllist; /* insert at head */
76 g_ascend_dllist = new;
77 new->path = keeppath;
78 new->dlreturn = dlreturn;
79 return 0;
80 }
81
82 /*
83 * Finds a record of the path given and returns the associated handle.
84 * If it fails to do this, returns NULL.
85 */
86 static
87 void *AscFindDLRecord(CONST char *path)
88 {
89 struct ascend_dlrecord *new;
90 if (path == NULL) {
91 return NULL;
92 }
93 new = g_ascend_dllist;
94 while (new != NULL && strcmp(new->path,path) != 0) {
95 /* advance new until no more new or new with path found */
96 new = new->next;
97 }
98 return (new != NULL) ? new->dlreturn : NULL;
99 }
100
101 /*
102 * Finds and returns the handle to path, if one matches, and
103 * deletes the record from the list. Returns NULL if not found.
104 */
105 static
106 void *AscDeleteRecord(CONST char *path)
107 {
108 struct ascend_dlrecord *nextptr, *lastptr, *old;
109 void *dlreturn = NULL;
110
111 if ((g_ascend_dllist == NULL) || (NULL == path)) return NULL;
112
113 if (strcmp(path,g_ascend_dllist->path)==0) {
114 /* head case */
115 old = g_ascend_dllist;
116 g_ascend_dllist = old->next;
117 dlreturn = old->dlreturn;
118 ascfree(old->path);
119 ascfree(old);
120 } else {
121 lastptr = g_ascend_dllist;
122 nextptr = lastptr->next;
123 while (nextptr != NULL && strcmp(nextptr->path,path) != 0) {
124 lastptr = nextptr;
125 nextptr = nextptr->next;
126 }
127 /* so either nextptr is NULL and not in list, or nextptr is
128 * what we want to delete and lastptr is the link to it.
129 */
130 if (nextptr != NULL) {
131 old = nextptr;
132 lastptr->next = nextptr->next;
133 dlreturn = old->dlreturn;
134 ascfree(old->path);
135 ascfree(old);
136 }
137 }
138 return dlreturn;
139 }
140
141 /*
142 * Checks the list for a conflicting handle so we can issue
143 * a more helpful warning, if need be, than the standard message.
144 */
145 static
146 void AscCheckDuplicateLoad(CONST char *path)
147 {
148 struct ascend_dlrecord *r;
149
150 if (NULL == path) {
151 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Null path");
152 return;
153 }
154
155 r = g_ascend_dllist;
156 while (r != NULL) {
157 if (strcmp(path,r->path)==0) {
158 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Attempt to load already loaded '%s'.",path);
159 return;
160 }
161 r = r->next;
162 }
163 }
164
165
166 /*-----------------------------------------------
167 WINDOWS
168 */
169 #if defined(__WIN32__)
170 # include <windows.h>
171
172 int Asc_DynamicLoad(CONST char *path, CONST char *initFun){
173 HINSTANCE xlib;
174 ExternalLibraryRegister_fptr_t install = NULL;
175
176 if (NULL == path) {
177 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null path\n");
178 return 1;
179 }
180
181 AscCheckDuplicateLoad(path); /* whine if we've see it before */
182 /*
183 * If the named library does not exist, if it's not loadable or if
184 * it does not define the named install proc, report an error
185 */
186
187 xlib = LoadLibrary(path);
188 if (xlib == NULL) {
189 ERROR_REPORTER_HERE(ASC_PROG_ERR,"LoadLibrary failed\n'%s'",path);
190 return 1;
191 }
192 #if 0
193 ERROR_REPORTER_HERE(ASC_PROG_NOTE,"LoadLibrary succeeded, '%s'\n",path);
194 #endif
195
196 if (NULL != initFun) {
197 install = (int (*)(void))GetProcAddress(xlib,initFun);
198 if (install == NULL) {
199 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Required function '%s' not found", initFun);
200 (void)FreeLibrary(xlib);
201 return 1;
202 #if 0
203 }else{
204 FPRINTF(ASCERR,"FOUND INITFCN %s AT %d\n",initFun,install);
205 #endif
206 }
207 }
208 if (0 != AscAddRecord(xlib,path)) {
209 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to record library (%s)\n",path);
210 }
211 return (install == NULL) ? 0 : (*install)();
212 }
213 #define ASCDL_OK /* this line should appear inside each Asc_DynamicLoad */
214
215 # define UNLOAD FreeLibrary
216 # define DLLSYM GetProcAddress
217 # define DLL_CAST (HINSTANCE)
218 # define ASC_DLERRSTRING "unknown"
219 # define UNLOAD_SUCCESS TRUE
220
221 #endif /* __WIN32__ */
222
223 /*-----------------------------------------------
224 UNIX/LINUX
225 */
226 /*
227 SOLARIS and LINUX
228 */
229 /* NOTE, added defined(__unix__) here, not sure if that's a bad thing or not -- johnpye */
230 /*
231 From a quick Google, it appears that AIX 5.1 now provides dlfcn.h,
232 so I'll remove the code that was emulating it here. -- johnpye
233 */
234 #if (defined(sun) || defined(linux) || defined(__unix__) || defined(solaris) \
235 || defined(_AIX) || defined(_SGI_SOURCE) || defined(__APPLE__))
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,"While attempting to run '%s' in '%s': %s",initFun, path, (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 @NOTE this function makes use of ospath search functionality.
587 */
588 char *SearchArchiveLibraryPath(CONST char *name, char *dpath, const char *envv){
589 struct FilePath *fp1, *fp2, *fp3; /* relative path */
590 char *s1;
591 char *buffer;
592
593 struct LibrarySearch ls;
594 struct FilePath **sp;
595 char *path, *foundpath;
596 ospath_stat_t buf;
597 FILE *f;
598
599 fp1 = ospath_new_noclean(name);
600 if(fp1==NULL){
601 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Invalid partial path '%s'",name);
602 ospath_free(fp1);
603 return NULL;
604 }
605
606 s1 = ospath_getfilestem(fp1);
607 if(s1==NULL){
608 /* not a file, so fail... */
609 return NULL;
610 }
611
612 fp2 = ospath_getdir(fp1);
613 if(fp2==NULL){
614 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to retrieve file dir");
615 return NULL;
616 }
617
618 buffer = dynaload_lib_filename(s1);
619
620 fp3 = ospath_new(buffer);
621 ASC_FREE(buffer);
622 ospath_free(fp1);
623 fp1 = ospath_concat(fp2,fp3);
624 ospath_free(fp2);
625 ospath_free(fp3);
626 ospath_free_str(s1);
627
628 /* attempt to open "name" directly */
629 if(0==ospath_stat(fp1,&buf) && NULL!=(f = ospath_fopen(fp1,"r")) ){
630 char *tmp;
631 tmp = ospath_str(fp1);
632 CONSOLE_DEBUG("Library '%s' opened directly, without path search",tmp);
633 ospath_free_str(tmp);
634 fp2 = ospath_getabs(fp1);
635 foundpath = ospath_str(fp2);
636 ospath_free(fp2);
637 fclose(f);
638 }else{
639
640 ls.partialpath = fp1;
641
642 path=Asc_GetEnv(envv);
643 if(path==NULL){
644 /* CONSOLE_DEBUG("Library search path env var '%s' not found, using default path '%s'",envv,dpath); */
645 path=dpath;
646 }
647
648 /* CONSOLE_DEBUG("SEARCHPATH IS %s",path); */
649 sp = ospath_searchpath_new(path);
650
651 if(NULL==ospath_searchpath_iterate(sp,&test_librarysearch,&ls)){
652 ospath_free(fp1);
653 ospath_searchpath_free(sp);
654 return NULL;
655 }
656
657 foundpath = ASC_NEW_ARRAY(char,strlen(ls.fullpath)+1);
658 strcpy(foundpath,ls.fullpath);
659 ospath_searchpath_free(sp);
660 }
661
662 ospath_free(fp1);
663 return foundpath;
664 }

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