2060
|
1 /* |
|
2 * @(#)dlfcn.c 1.10 revision of 96/02/15 17:42:44 |
|
3 * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH |
|
4 * 30159 Hannover, Germany |
|
5 */ |
|
6 |
2061
|
7 /* |
|
8 * Changes marked with `--jwe' were made on April 7 1996 by John W. Eaton |
|
9 * <jwe@bevo.che.wisc.edu> to support g++ and/or use with Octave. |
|
10 */ |
|
11 |
|
12 /* |
|
13 * This makes my life easier with Octave. --jwe |
|
14 */ |
|
15 #ifdef HAVE_CONFIG_H |
|
16 #include <config.h> |
|
17 #endif |
|
18 |
2060
|
19 #include <stdio.h> |
|
20 #include <errno.h> |
|
21 #include <string.h> |
|
22 #include <stdlib.h> |
|
23 #include <sys/types.h> |
|
24 #include <sys/ldr.h> |
|
25 #include <a.out.h> |
|
26 #include <ldfcn.h> |
|
27 #include "dlfcn.h" |
|
28 |
|
29 /* |
|
30 * We simulate dlopen() et al. through a call to load. Because AIX has |
|
31 * no call to find an exported symbol we read the loader section of the |
|
32 * loaded module and build a list of exported symbols and their virtual |
|
33 * address. |
|
34 */ |
|
35 |
|
36 typedef struct { |
|
37 char *name; /* the symbols's name */ |
|
38 void *addr; /* its relocated virtual address */ |
|
39 } Export, *ExportPtr; |
|
40 |
|
41 /* |
|
42 * xlC uses the following structure to list its constructors and |
|
43 * destructors. This is gleaned from the output of munch. |
|
44 */ |
|
45 typedef struct { |
|
46 void (*init)(void); /* call static constructors */ |
|
47 void (*term)(void); /* call static destructors */ |
|
48 } Cdtor, *CdtorPtr; |
|
49 |
2061
|
50 typedef void (*GccCDtorPtr)(void); |
|
51 |
2060
|
52 /* |
|
53 * The void * handle returned from dlopen is actually a ModulePtr. |
|
54 */ |
|
55 typedef struct Module { |
|
56 struct Module *next; |
|
57 char *name; /* module name for refcounting */ |
|
58 int refCnt; /* the number of references */ |
|
59 void *entry; /* entry point from load */ |
|
60 struct dl_info *info; /* optional init/terminate functions */ |
|
61 CdtorPtr cdtors; /* optional C++ constructors */ |
2061
|
62 GccCDtorPtr gcc_ctor; /* g++ constructors --jwe */ |
|
63 GccCDtorPtr gcc_dtor; /* g++ destructors --jwe */ |
2060
|
64 int nExports; /* the number of exports found */ |
|
65 ExportPtr exports; /* the array of exports */ |
|
66 } Module, *ModulePtr; |
|
67 |
|
68 /* |
|
69 * We keep a list of all loaded modules to be able to call the fini |
|
70 * handlers and destructors at atexit() time. |
|
71 */ |
|
72 static ModulePtr modList; |
|
73 |
|
74 /* |
|
75 * The last error from one of the dl* routines is kept in static |
|
76 * variables here. Each error is returned only once to the caller. |
|
77 */ |
|
78 static char errbuf[BUFSIZ]; |
|
79 static int errvalid; |
|
80 |
2061
|
81 /* |
|
82 * The `fixed' gcc header files on AIX 3.2.5 provide a prototype for |
|
83 * strdup(). --jwe |
|
84 */ |
|
85 #ifndef HAVE_STRDUP |
2060
|
86 extern char *strdup(const char *); |
2061
|
87 #endif |
2060
|
88 static void caterr(char *); |
|
89 static int readExports(ModulePtr); |
|
90 static void terminate(void); |
|
91 static void *findMain(void); |
|
92 |
|
93 void *dlopen(const char *path, int mode) |
|
94 { |
|
95 register ModulePtr mp; |
|
96 static void *mainModule; |
|
97 |
|
98 /* |
|
99 * Upon the first call register a terminate handler that will |
|
100 * close all libraries. Also get a reference to the main module |
|
101 * for use with loadbind. |
|
102 */ |
|
103 if (!mainModule) { |
|
104 if ((mainModule = findMain()) == NULL) |
|
105 return NULL; |
|
106 atexit(terminate); |
|
107 } |
|
108 /* |
|
109 * Scan the list of modules if we have the module already loaded. |
|
110 */ |
|
111 for (mp = modList; mp; mp = mp->next) |
|
112 if (strcmp(mp->name, path) == 0) { |
|
113 mp->refCnt++; |
|
114 return mp; |
|
115 } |
|
116 if ((mp = (ModulePtr)calloc(1, sizeof(*mp))) == NULL) { |
|
117 errvalid++; |
|
118 strcpy(errbuf, "calloc: "); |
|
119 strcat(errbuf, strerror(errno)); |
|
120 return NULL; |
|
121 } |
|
122 if ((mp->name = strdup(path)) == NULL) { |
|
123 errvalid++; |
|
124 strcpy(errbuf, "strdup: "); |
|
125 strcat(errbuf, strerror(errno)); |
|
126 free(mp); |
|
127 return NULL; |
|
128 } |
|
129 /* |
|
130 * load should be declared load(const char *...). Thus we |
|
131 * cast the path to a normal char *. Ugly. |
|
132 */ |
|
133 if ((mp->entry = (void *)load((char *)path, L_NOAUTODEFER, NULL)) == NULL) { |
|
134 free(mp->name); |
|
135 free(mp); |
|
136 errvalid++; |
|
137 strcpy(errbuf, "dlopen: "); |
|
138 strcat(errbuf, path); |
|
139 strcat(errbuf, ": "); |
|
140 /* |
|
141 * If AIX says the file is not executable, the error |
|
142 * can be further described by querying the loader about |
|
143 * the last error. |
|
144 */ |
|
145 if (errno == ENOEXEC) { |
|
146 char *tmp[BUFSIZ/sizeof(char *)]; |
|
147 if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) |
|
148 strcpy(errbuf, strerror(errno)); |
|
149 else { |
|
150 char **p; |
|
151 for (p = tmp; *p; p++) |
|
152 caterr(*p); |
|
153 } |
|
154 } else |
|
155 strcat(errbuf, strerror(errno)); |
|
156 return NULL; |
|
157 } |
|
158 mp->refCnt = 1; |
|
159 mp->next = modList; |
|
160 modList = mp; |
|
161 if (loadbind(0, mainModule, mp->entry) == -1) { |
|
162 dlclose(mp); |
|
163 errvalid++; |
|
164 strcpy(errbuf, "loadbind: "); |
|
165 strcat(errbuf, strerror(errno)); |
|
166 return NULL; |
|
167 } |
|
168 /* |
|
169 * If the user wants global binding, loadbind against all other |
|
170 * loaded modules. |
|
171 */ |
|
172 if (mode & RTLD_GLOBAL) { |
|
173 register ModulePtr mp1; |
|
174 for (mp1 = mp->next; mp1; mp1 = mp1->next) |
|
175 if (loadbind(0, mp1->entry, mp->entry) == -1) { |
|
176 dlclose(mp); |
|
177 errvalid++; |
|
178 strcpy(errbuf, "loadbind: "); |
|
179 strcat(errbuf, strerror(errno)); |
|
180 return NULL; |
|
181 } |
|
182 } |
|
183 if (readExports(mp) == -1) { |
|
184 dlclose(mp); |
|
185 return NULL; |
|
186 } |
|
187 /* |
|
188 * If there is a dl_info structure, call the init function. |
|
189 */ |
|
190 if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) { |
|
191 if (mp->info->init) |
|
192 (*mp->info->init)(); |
|
193 } else |
|
194 errvalid = 0; |
|
195 /* |
|
196 * If the shared object was compiled using xlC we will need |
|
197 * to call static constructors (and later on dlclose destructors). |
|
198 */ |
|
199 if (mp->cdtors = (CdtorPtr)dlsym(mp, "__cdtors")) { |
|
200 CdtorPtr cp = mp->cdtors; |
|
201 while (cp->init || cp->term) { |
|
202 if (cp->init && cp->init != (void (*)(void))0xffffffff) |
|
203 (*cp->init)(); |
|
204 cp++; |
|
205 } |
2061
|
206 /* |
|
207 * If the shared object was compiled using g++, we will need |
|
208 * to call global constructors using the _GLOBAL__DI function, |
|
209 * and later, global destructors using the _GLOBAL_DD |
|
210 * funciton. --jwe |
|
211 */ |
|
212 } else if (mp->gcc_ctor = (GccCDtorPtr)dlsym(mp, "_GLOBAL__DI")) { |
|
213 (*mp->gcc_ctor)(); |
|
214 mp->gcc_dtor = (GccCDtorPtr)dlsym(mp, "_GLOBAL__DD"); |
2060
|
215 } else |
|
216 errvalid = 0; |
|
217 return mp; |
|
218 } |
|
219 |
|
220 /* |
|
221 * Attempt to decipher an AIX loader error message and append it |
|
222 * to our static error message buffer. |
|
223 */ |
|
224 static void caterr(char *s) |
|
225 { |
|
226 register char *p = s; |
|
227 |
|
228 while (*p >= '0' && *p <= '9') |
|
229 p++; |
|
230 switch(atoi(s)) { |
|
231 case L_ERROR_TOOMANY: |
|
232 strcat(errbuf, "to many errors"); |
|
233 break; |
|
234 case L_ERROR_NOLIB: |
|
235 strcat(errbuf, "can't load library"); |
|
236 strcat(errbuf, p); |
|
237 break; |
|
238 case L_ERROR_UNDEF: |
|
239 strcat(errbuf, "can't find symbol"); |
|
240 strcat(errbuf, p); |
|
241 break; |
|
242 case L_ERROR_RLDBAD: |
|
243 strcat(errbuf, "bad RLD"); |
|
244 strcat(errbuf, p); |
|
245 break; |
|
246 case L_ERROR_FORMAT: |
|
247 strcat(errbuf, "bad exec format in"); |
|
248 strcat(errbuf, p); |
|
249 break; |
|
250 case L_ERROR_ERRNO: |
|
251 strcat(errbuf, strerror(atoi(++p))); |
|
252 break; |
|
253 default: |
|
254 strcat(errbuf, s); |
|
255 break; |
|
256 } |
|
257 } |
|
258 |
|
259 void *dlsym(void *handle, const char *symbol) |
|
260 { |
|
261 register ModulePtr mp = (ModulePtr)handle; |
|
262 register ExportPtr ep; |
|
263 register int i; |
|
264 |
|
265 /* |
|
266 * Could speed up the search, but I assume that one assigns |
|
267 * the result to function pointers anyways. |
|
268 */ |
|
269 for (ep = mp->exports, i = mp->nExports; i; i--, ep++) |
|
270 if (strcmp(ep->name, symbol) == 0) |
|
271 return ep->addr; |
|
272 errvalid++; |
|
273 strcpy(errbuf, "dlsym: undefined symbol "); |
|
274 strcat(errbuf, symbol); |
|
275 return NULL; |
|
276 } |
|
277 |
|
278 char *dlerror(void) |
|
279 { |
|
280 if (errvalid) { |
|
281 errvalid = 0; |
|
282 return errbuf; |
|
283 } |
|
284 return NULL; |
|
285 } |
|
286 |
|
287 int dlclose(void *handle) |
|
288 { |
|
289 register ModulePtr mp = (ModulePtr)handle; |
|
290 int result; |
|
291 register ModulePtr mp1; |
|
292 |
|
293 if (--mp->refCnt > 0) |
|
294 return 0; |
|
295 if (mp->info && mp->info->fini) |
|
296 (*mp->info->fini)(); |
|
297 if (mp->cdtors) { |
|
298 CdtorPtr cp = mp->cdtors; |
|
299 while (cp->init || cp->term) { |
|
300 if (cp->term && cp->init != (void (*)(void))0xffffffff) |
|
301 (*cp->term)(); |
|
302 cp++; |
|
303 } |
2061
|
304 /* |
|
305 * If the function to handle global destructors for g++ |
|
306 * exists, call it. --jwe |
|
307 */ |
|
308 } else if (mp->gcc_dtor) { |
|
309 (*mp->gcc_dtor)(); |
2060
|
310 } |
|
311 result = unload(mp->entry); |
|
312 if (result == -1) { |
|
313 errvalid++; |
|
314 strcpy(errbuf, strerror(errno)); |
|
315 } |
|
316 if (mp->exports) { |
|
317 register ExportPtr ep; |
|
318 register int i; |
|
319 for (ep = mp->exports, i = mp->nExports; i; i--, ep++) |
|
320 if (ep->name) |
|
321 free(ep->name); |
|
322 free(mp->exports); |
|
323 } |
|
324 if (mp == modList) |
|
325 modList = mp->next; |
|
326 else { |
|
327 for (mp1 = modList; mp1; mp1 = mp1->next) |
|
328 if (mp1->next == mp) { |
|
329 mp1->next = mp->next; |
|
330 break; |
|
331 } |
|
332 } |
|
333 free(mp->name); |
|
334 free(mp); |
|
335 return result; |
|
336 } |
|
337 |
|
338 static void terminate(void) |
|
339 { |
|
340 while (modList) |
|
341 dlclose(modList); |
|
342 } |
|
343 |
|
344 /* |
|
345 * Build the export table from the XCOFF .loader section. |
|
346 */ |
|
347 static int readExports(ModulePtr mp) |
|
348 { |
|
349 LDFILE *ldp = NULL; |
|
350 SCNHDR sh, shdata; |
|
351 LDHDR *lhp; |
|
352 char *ldbuf; |
|
353 LDSYM *ls; |
|
354 int i; |
|
355 ExportPtr ep; |
|
356 |
|
357 if ((ldp = ldopen(mp->name, ldp)) == NULL) { |
|
358 struct ld_info *lp; |
|
359 char *buf; |
|
360 int size = 4*1024; |
|
361 if (errno != ENOENT) { |
|
362 errvalid++; |
|
363 strcpy(errbuf, "readExports: "); |
|
364 strcat(errbuf, strerror(errno)); |
|
365 return -1; |
|
366 } |
|
367 /* |
|
368 * The module might be loaded due to the LIBPATH |
|
369 * environment variable. Search for the loaded |
|
370 * module using L_GETINFO. |
|
371 */ |
|
372 if ((buf = malloc(size)) == NULL) { |
|
373 errvalid++; |
|
374 strcpy(errbuf, "readExports: "); |
|
375 strcat(errbuf, strerror(errno)); |
|
376 return -1; |
|
377 } |
|
378 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { |
|
379 free(buf); |
|
380 size += 4*1024; |
|
381 if ((buf = malloc(size)) == NULL) { |
|
382 errvalid++; |
|
383 strcpy(errbuf, "readExports: "); |
|
384 strcat(errbuf, strerror(errno)); |
|
385 return -1; |
|
386 } |
|
387 } |
|
388 if (i == -1) { |
|
389 errvalid++; |
|
390 strcpy(errbuf, "readExports: "); |
|
391 strcat(errbuf, strerror(errno)); |
|
392 free(buf); |
|
393 return -1; |
|
394 } |
|
395 /* |
|
396 * Traverse the list of loaded modules. The entry point |
|
397 * returned by load() does actually point to the data |
|
398 * segment origin. |
|
399 */ |
|
400 lp = (struct ld_info *)buf; |
|
401 while (lp) { |
|
402 if (lp->ldinfo_dataorg == mp->entry) { |
|
403 ldp = ldopen(lp->ldinfo_filename, ldp); |
|
404 break; |
|
405 } |
|
406 if (lp->ldinfo_next == 0) |
|
407 lp = NULL; |
|
408 else |
|
409 lp = (struct ld_info *)((char *)lp + lp->ldinfo_next); |
|
410 } |
|
411 free(buf); |
|
412 if (!ldp) { |
|
413 errvalid++; |
|
414 strcpy(errbuf, "readExports: "); |
|
415 strcat(errbuf, strerror(errno)); |
|
416 return -1; |
|
417 } |
|
418 } |
|
419 if (TYPE(ldp) != U802TOCMAGIC) { |
|
420 errvalid++; |
|
421 strcpy(errbuf, "readExports: bad magic"); |
|
422 while(ldclose(ldp) == FAILURE) |
|
423 ; |
|
424 return -1; |
|
425 } |
|
426 /* |
|
427 * Get the padding for the data section. This is needed for |
|
428 * AIX 4.1 compilers. This is used when building the final |
|
429 * function pointer to the exported symbol. |
|
430 */ |
|
431 if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) { |
|
432 errvalid++; |
|
433 strcpy(errbuf, "readExports: cannot read data section header"); |
|
434 while(ldclose(ldp) == FAILURE) |
|
435 ; |
|
436 return -1; |
|
437 } |
|
438 if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) { |
|
439 errvalid++; |
|
440 strcpy(errbuf, "readExports: cannot read loader section header"); |
|
441 while(ldclose(ldp) == FAILURE) |
|
442 ; |
|
443 return -1; |
|
444 } |
|
445 /* |
|
446 * We read the complete loader section in one chunk, this makes |
|
447 * finding long symbol names residing in the string table easier. |
|
448 */ |
|
449 if ((ldbuf = (char *)malloc(sh.s_size)) == NULL) { |
|
450 errvalid++; |
|
451 strcpy(errbuf, "readExports: "); |
|
452 strcat(errbuf, strerror(errno)); |
|
453 while(ldclose(ldp) == FAILURE) |
|
454 ; |
|
455 return -1; |
|
456 } |
|
457 if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) { |
|
458 errvalid++; |
|
459 strcpy(errbuf, "readExports: cannot seek to loader section"); |
|
460 free(ldbuf); |
|
461 while(ldclose(ldp) == FAILURE) |
|
462 ; |
|
463 return -1; |
|
464 } |
|
465 if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) { |
|
466 errvalid++; |
|
467 strcpy(errbuf, "readExports: cannot read loader section"); |
|
468 free(ldbuf); |
|
469 while(ldclose(ldp) == FAILURE) |
|
470 ; |
|
471 return -1; |
|
472 } |
|
473 lhp = (LDHDR *)ldbuf; |
|
474 ls = (LDSYM *)(ldbuf+LDHDRSZ); |
|
475 /* |
|
476 * Count the number of exports to include in our export table. |
|
477 */ |
|
478 for (i = lhp->l_nsyms; i; i--, ls++) { |
|
479 if (!LDR_EXPORT(*ls)) |
|
480 continue; |
|
481 mp->nExports++; |
|
482 } |
|
483 if ((mp->exports = (ExportPtr)calloc(mp->nExports, sizeof(*mp->exports))) == NULL) { |
|
484 errvalid++; |
|
485 strcpy(errbuf, "readExports: "); |
|
486 strcat(errbuf, strerror(errno)); |
|
487 free(ldbuf); |
|
488 while(ldclose(ldp) == FAILURE) |
|
489 ; |
|
490 return -1; |
|
491 } |
|
492 /* |
|
493 * Fill in the export table. All entries are relative to |
|
494 * the entry point we got from load. |
|
495 */ |
|
496 ep = mp->exports; |
|
497 ls = (LDSYM *)(ldbuf+LDHDRSZ); |
|
498 for (i = lhp->l_nsyms; i; i--, ls++) { |
|
499 char *symname; |
|
500 char tmpsym[SYMNMLEN+1]; |
|
501 if (!LDR_EXPORT(*ls)) |
|
502 continue; |
|
503 if (ls->l_zeroes == 0) |
|
504 symname = ls->l_offset+lhp->l_stoff+ldbuf; |
|
505 else { |
|
506 /* |
|
507 * The l_name member is not zero terminated, we |
|
508 * must copy the first SYMNMLEN chars and make |
|
509 * sure we have a zero byte at the end. |
|
510 */ |
|
511 strncpy(tmpsym, ls->l_name, SYMNMLEN); |
|
512 tmpsym[SYMNMLEN] = '\0'; |
|
513 symname = tmpsym; |
|
514 } |
|
515 ep->name = strdup(symname); |
|
516 ep->addr = (void *)((unsigned long)mp->entry + |
|
517 ls->l_value - shdata.s_vaddr); |
|
518 ep++; |
|
519 } |
|
520 free(ldbuf); |
|
521 while(ldclose(ldp) == FAILURE) |
|
522 ; |
|
523 return 0; |
|
524 } |
|
525 |
|
526 /* |
|
527 * Find the main modules entry point. This is used as export pointer |
|
528 * for loadbind() to be able to resolve references to the main part. |
|
529 */ |
|
530 static void * findMain(void) |
|
531 { |
|
532 struct ld_info *lp; |
|
533 char *buf; |
|
534 int size = 4*1024; |
|
535 int i; |
|
536 void *ret; |
|
537 |
|
538 if ((buf = malloc(size)) == NULL) { |
|
539 errvalid++; |
|
540 strcpy(errbuf, "findMain: "); |
|
541 strcat(errbuf, strerror(errno)); |
|
542 return NULL; |
|
543 } |
|
544 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { |
|
545 free(buf); |
|
546 size += 4*1024; |
|
547 if ((buf = malloc(size)) == NULL) { |
|
548 errvalid++; |
|
549 strcpy(errbuf, "findMain: "); |
|
550 strcat(errbuf, strerror(errno)); |
|
551 return NULL; |
|
552 } |
|
553 } |
|
554 if (i == -1) { |
|
555 errvalid++; |
|
556 strcpy(errbuf, "findMain: "); |
|
557 strcat(errbuf, strerror(errno)); |
|
558 free(buf); |
|
559 return NULL; |
|
560 } |
|
561 /* |
|
562 * The first entry is the main module. The entry point |
|
563 * returned by load() does actually point to the data |
|
564 * segment origin. |
|
565 */ |
|
566 lp = (struct ld_info *)buf; |
|
567 ret = lp->ldinfo_dataorg; |
|
568 free(buf); |
|
569 return ret; |
|
570 } |