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