/[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 1597 - (show annotations) (download) (as text)
Fri Aug 17 05:26:05 2007 UTC (17 years, 2 months ago) by jpye
File MIME type: text/x-csrc
File size: 18390 byte(s)
New external library naming scheme on Windows: 'name_ascend.dll' instead of 'name.dll'.
This applies to solvers as well as external methods, extpy, import handlers, etc.
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_NOLINE(ASC_PROG_ERR,"%s",(char *)dlerror());
261 return 1;
262 }
263 if (NULL != initFun) {
264 /* https://www.opengroup.org/sophocles/show_mail.tpl?source=L&listname=austin-review-l&id=2252 */
265 *(void**)(&install) = dlsym(xlib, initFun);
266 if (install == NULL) {
267 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"%s",(char *)dlerror());
268 dlclose(xlib);
269 return 1;
270 }
271 }
272
273 if (0 != AscAddRecord(xlib,path)) {
274 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to record library (%s)",path);
275 }
276 return (install == NULL) ? 0 : (*install)();
277 }
278 #define ASCDL_OK /* this line should appear inside each Asc_DynamicLoad */
279
280 # define UNLOAD dlclose
281 # define DLLSYM dlsym
282 # define DLL_CAST (void *)
283 # define ASC_DLERRSTRING dlerror()
284 # define UNLOAD_SUCCESS 0
285
286 #endif /* posix: linux, unix, solaris,sgi */
287
288 /*-----------------------------------------------
289 HPUX
290 */
291 #ifdef __hpux
292 /*
293 Kirk Abbott last fiddled with the following, which was
294 originally put in place my Michael Moore for an
295 HP/UX 9.X Operating Sys back in 1993. Arrr. No idea if
296 it still works.
297 */
298
299 # include <dl.h>
300 # include <errno.h>
301
302 int Asc_DynamicLoad(CONST char *path, CONST char *initFun)
303 {
304 shl_t xlib;
305 ExternalLibraryRegister_fptr_t install = NULL;
306 int i;
307
308 if (NULL == path) {
309 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null path");
310 return 1;
311 }
312
313 AscCheckDuplicateLoad(path); /* whine if we've see it before */
314
315 /*
316 * If the named library does not exist, if it's not loadable or if
317 * it does not define the named install proc, report an error
318 */
319 xlib = shl_load(path, BIND_IMMEDIATE | BIND_VERBOSE, 0L);
320 if (xlib == (shl_t) NULL) {
321 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to load shared library: %s",strerror(errno));
322 return 1;
323 }
324 if (NULL != initFun) {
325 i = shl_findsym(&xlib, initFun, TYPE_PROCEDURE, &install);
326 if (i == -1) {
327 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find needed symbol '%s': %s",
328 initFun, strerror(errno));
329 shl_unload(xlib); /* baa */
330 return 1;
331 }
332 if(install == NULL) {
333 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find needed symbol '%s'. Error type unknown",initFun);
334 shl_unload(xlib); /* baa */
335 return 1;
336 }
337 }
338 if (0 != AscAddRecord(xlib,path)) {
339 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to record library (%s)",path);
340 }
341 return (install == NULL) ? 0 : (*install)();
342 }
343 # define ASCDL_OK /* this line should appear inside each Asc_DynamicLoad */
344
345 # define UNLOAD shl_unload
346 # define DLL_CAST (shl_t)
347 # define ASC_DLERRSTRING "NULL definition"
348 # define UNLOAD_SUCCESS 0
349
350 #endif /* __hpux */
351
352 /*-----------------------------------------------
353 Did we get something from the above?
354 */
355
356 #ifndef ASCDL_OK
357 # error "Unable to define an Asc_DynamicLoad function. Check your compiler options and installed system libraries."
358 #endif
359
360 /**-----------------------------------------------
361 DYNAMIC UNLOADING
362 */
363
364 int Asc_DynamicUnLoad(CONST char *path)
365 {
366 void *dlreturn;
367 int retval;
368
369 if (NULL == path) {
370 ERROR_REPORTER_HERE(ASC_PROG_ERR, "Failed: Null path");
371 return -3;
372 }
373
374 dlreturn = AscDeleteRecord(path);
375 if (dlreturn == NULL) {
376 ERROR_REPORTER_HERE(ASC_PROG_ERR, "Unable to remember or unload %s", path);
377 return -3;
378 }
379 CONSOLE_DEBUG("Asc_DynamicUnLoad: forgetting & unloading %s", path);
380 /*
381 * dlclose() returns 0 on success, FreeLibrary() returns TRUE.
382 * A uniform convention is preferable, so trap and return 0 on success.
383 */
384 retval = UNLOAD(DLL_CAST dlreturn);
385 return (retval == UNLOAD_SUCCESS) ? 0 : retval;
386 }
387
388 /**-----------------------------------------------
389 DYNAMIC VARIABLE LINKING
390 */
391 void *Asc_DynamicVariable(CONST char *libname, CONST char *symbol)
392 {
393 void *dlreturn;
394 void *symreturn;
395 #ifdef __hpux
396 int i;
397 #endif
398
399 if (libname == NULL) {
400 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null libname");
401 return NULL;
402 }
403 if (symbol == NULL) {
404 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: Null symbol");
405 return NULL;
406 }
407
408 dlreturn = AscFindDLRecord(libname);
409 if (dlreturn == NULL) {
410 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested library %s", libname);
411 return NULL;
412 }
413 #ifdef __hpux
414 i = shl_findsym(&dlreturn, symbol, TYPE_UNDEFINED, &symreturn);
415 if (i == -1) {
416 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested symbol '%s' in %s (%s)",
417 symbol, libname, strerror(errno));
418 symreturn = NULL;
419 }
420 #elif defined(__WIN32__)
421 /*
422 * Here's a bit of possibly-misdirected casting horror.
423 * ISO C forbids casting between function and data pointers, so, naturally,
424 * we cast between function and data pointers. Well, we don't have much
425 * choice. GetProcAddress() returns a function pointer for both functions
426 * and variables so we have to do the cast for variables. This is ok on
427 * 32 bit Windows since the pointers are compatible. Then, to avoid
428 * being reminded by the compiler that we're doing something illegal,
429 * we apply convoluted casting to shut it up.
430 * Oh, the crap you can find on the internet... JDS
431 */
432 *(FARPROC*)(&symreturn) = GetProcAddress((HINSTANCE)dlreturn, symbol);
433 #else
434 /* no problem on POSIX systems - dlsym() returns a void *. */
435 symreturn = dlsym(dlreturn, symbol);
436 #endif
437 if (symreturn == NULL) {
438 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested symbol '%s' in %s",symbol,libname);
439 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Error type: %s",ASC_DLERRSTRING);
440 }
441 return symreturn;
442 }
443
444 /**-----------------------------------------------
445 DYNAMIC FUNCTION LINKING
446 */
447 DynamicF Asc_DynamicFunction(CONST char *libname, CONST char *symbol)
448 {
449 void *dlreturn;
450 DynamicF symreturn;
451 #ifdef __hpux
452 int i;
453 #endif
454
455 if (libname == NULL) {
456 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: null library name");
457 return NULL;
458 }
459 if (symbol == NULL) {
460 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed: null function name");
461 return NULL;
462 }
463
464 dlreturn = AscFindDLRecord(libname);
465 if (dlreturn == NULL) {
466 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested library %s", libname);
467 return NULL;
468 }
469 #ifdef __hpux
470 i = shl_findsym(&dlreturn, symbol, TYPE_UNDEFINED, &symreturn);
471 if (i == -1) {
472 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested function '%s' in %s (%s)",
473 symbol, libname, strerror(errno));
474 symreturn = NULL;
475 }
476 #elif defined(__WIN32__)
477 /* no problem on Windows - GetProcAddress() returns a function pointer. */
478 symreturn = (DynamicF)GetProcAddress((HINSTANCE)dlreturn, symbol);
479 #else
480 /*
481 * Here's the corresponding bit of possibly-misdirected casting horror.
482 * ISO C forbids casting between function and data pointers, so, naturally,
483 * we cast between function and data pointers. Well, we don't have much
484 * choice. dlsym() returns a void* for both variables and functions so we
485 * have to do the cast for functions. This is ok on POSIX systems since the
486 * pointer types are compatible. Then, to avoid being reminded by the
487 * compiler that we're doing something illegal, we apply convoluted casting
488 * to shut it up. Oh, the crap you can find on the internet... JDS
489 */
490 *(void**)(&symreturn) = dlsym(dlreturn, symbol);
491 #endif
492 if (symreturn == NULL) {
493 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to find requested function '%s' in %s",symbol,libname);
494 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Error type: %s",ASC_DLERRSTRING);
495 }
496 return symreturn;
497 }
498
499
500 /*-----------------------------------------------------------------------------
501 SEARCHING FOR LIBRARIES
502 */
503
504
505 /**
506 Create a library filename according to platform standard naming.
507
508 @param partialname The partial filename (eg 'mylib')
509 @return Complete filename (eg 'libmylib.so' or 'mylib.dlll', etc)
510
511 Basically just adds ASC_SHLIBPREFIX to start and ASC_SHLIBSUFFIX to end.
512
513 No allowance made for 'soname' suffixes eg 'libsomething.so.1' etc. But
514 that's probably OK as those methods aren't really applicable to dlopened
515 libraries (eg the soname symlink mechanism breaks down).
516 */
517 char *dynaload_lib_filename(const char *partialname){
518 char *buffer;
519 buffer = ASC_NEW_ARRAY(char,PATH_MAX);
520 #if !defined(ASC_SHLIBSUFFIX) || !defined(ASC_SHLIBPREFIX)
521 # error "ASC_SHLIBSUFFIX and ASC_SHLIBPREFIX are not defined"
522 #endif
523 snprintf(buffer,PATH_MAX,"%s%s%s",ASC_SHLIBPREFIX,partialname,ASC_SHLIBSUFFIX);
524 return buffer;
525 }
526
527
528 /**
529 A little structure to help with searching for libraries
530
531 @see test_librarysearch
532 */
533 struct LibrarySearch{
534 struct FilePath *partialpath;
535 char fullpath[PATH_MAX];
536 };
537
538 FilePathTestFn test_librarysearch;
539
540 /**
541 A 'test' function for passing to the ospath_searchpath_iterate function.
542 This test function will return a match when a library having the required
543 name is present in the fully resolved path.
544 */
545 int test_librarysearch(struct FilePath *path, void *userdata){
546 /* user data = the relative path, plus a place
547 to store the full path when found */
548 FILE *f;
549 struct LibrarySearch *ls;
550 struct FilePath *fp;
551
552 ls = (struct LibrarySearch *)userdata;
553 fp = ospath_concat(path,ls->partialpath);
554 if(fp==NULL){
555 char *tmp;
556 tmp = ospath_str(path);
557 CONSOLE_DEBUG("Unable to concatenate '%s'...",tmp);
558 ospath_free_str(tmp);
559 tmp = ospath_str(ls->partialpath);
560 CONSOLE_DEBUG("... and '%s'...",tmp);
561 ospath_free_str(tmp);
562 return 0;
563 }
564
565 ospath_strncpy(fp,ls->fullpath,PATH_MAX);
566 /* CONSOLE_DEBUG("SEARCHING FOR %s",ls->fullpath); */
567
568 f = ospath_fopen(fp,"r");
569 if(f==NULL){
570 ospath_free(fp);
571 return 0;
572 }
573 fclose(f);
574
575 /* ERROR_REPORTER_HERE(ASC_PROG_NOTE,"FOUND! %s\n",ls->fullpath); */
576 ospath_free(fp);
577 return 1;
578 }
579
580 /**
581 @DEPRECATED this function needs to be rewritten to use 'ImportHandler'
582 functionality.
583 */
584 char *SearchArchiveLibraryPath(CONST char *name, char *dpath, const char *envv){
585 struct FilePath *fp1, *fp2, *fp3; /* relative path */
586 char *s1;
587 char *buffer;
588
589 struct LibrarySearch ls;
590 struct FilePath **sp;
591 char *path, *foundpath;
592 ospath_stat_t buf;
593 FILE *f;
594
595 fp1 = ospath_new_noclean(name);
596 if(fp1==NULL){
597 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Invalid partial path '%s'",name);
598 ospath_free(fp1);
599 return NULL;
600 }
601
602 s1 = ospath_getfilestem(fp1);
603 if(s1==NULL){
604 /* not a file, so fail... */
605 return NULL;
606 }
607
608 fp2 = ospath_getdir(fp1);
609 if(fp2==NULL){
610 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to retrieve file dir");
611 return NULL;
612 }
613
614 buffer = dynaload_lib_filename(s1);
615
616 fp3 = ospath_new(buffer);
617 ASC_FREE(buffer);
618 ospath_free(fp1);
619 fp1 = ospath_concat(fp2,fp3);
620 ospath_free(fp2);
621 ospath_free(fp3);
622 ospath_free_str(s1);
623
624 /* attempt to open "name" directly */
625 if(0==ospath_stat(fp1,&buf) && NULL!=(f = ospath_fopen(fp1,"r")) ){
626 char *tmp;
627 tmp = ospath_str(fp1);
628 CONSOLE_DEBUG("Library '%s' opened directly, without path search",tmp);
629 ospath_free_str(tmp);
630 fp2 = ospath_getabs(fp1);
631 foundpath = ospath_str(fp2);
632 ospath_free(fp2);
633 fclose(f);
634 }else{
635
636 ls.partialpath = fp1;
637
638 path=Asc_GetEnv(envv);
639 if(path==NULL){
640 /* CONSOLE_DEBUG("Library search path env var '%s' not found, using default path '%s'",envv,dpath); */
641 path=dpath;
642 }
643
644 /* CONSOLE_DEBUG("SEARCHPATH IS %s",path); */
645 sp = ospath_searchpath_new(path);
646
647 if(NULL==ospath_searchpath_iterate(sp,&test_librarysearch,&ls)){
648 ospath_free(fp1);
649 ospath_searchpath_free(sp);
650 return NULL;
651 }
652
653 foundpath = ASC_NEW_ARRAY(char,strlen(ls.fullpath)+1);
654 strcpy(foundpath,ls.fullpath);
655 ospath_searchpath_free(sp);
656 }
657
658 ospath_free(fp1);
659 return foundpath;
660 }

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