remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / lib / kafs / dlfcn.c
1 /*
2  * @(#)dlfcn.c  1.11 revision of 96/04/10  20:12:51
3  * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH
4  * 30159 Hannover, Germany
5  */
6
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
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
50 typedef void (*GccCDtorPtr)(void);
51
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 */
62         GccCDtorPtr     gcc_ctor;       /* g++ constructors  --jwe */
63         GccCDtorPtr     gcc_dtor;       /* g++ destructors  --jwe */
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
81 /*
82  * The `fixed' gcc header files on AIX 3.2.5 provide a prototype for
83  * strdup().  --jwe
84  */
85 #ifndef HAVE_STRDUP
86 extern char *strdup(const char *);
87 #endif
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         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                 snprintf (errbuf, sizeof(errbuf), "calloc: %s", strerror(errno));
119                 return NULL;
120         }
121         if ((mp->name = strdup(path)) == NULL) {
122                 errvalid++;
123                 snprintf (errbuf, sizeof(errbuf), "strdup: %s", strerror(errno));
124                 free(mp);
125                 return NULL;
126         }
127         /*
128          * load should be declared load(const char *...). Thus we
129          * cast the path to a normal char *. Ugly.
130          */
131         if ((mp->entry = (void *)load((char *)path, L_NOAUTODEFER, NULL)) == NULL) {
132                 free(mp->name);
133                 free(mp);
134                 errvalid++;
135                 snprintf (errbuf, sizeof(errbuf),
136                           "dlopen: %s: ", path);
137                 /*
138                  * If AIX says the file is not executable, the error
139                  * can be further described by querying the loader about
140                  * the last error.
141                  */
142                 if (errno == ENOEXEC) {
143                         char *tmp[BUFSIZ/sizeof(char *)];
144                         if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
145                                 strlcpy(errbuf,
146                                                 strerror(errno),
147                                                 sizeof(errbuf));
148                         else {
149                                 char **p;
150                                 for (p = tmp; *p; p++)
151                                         caterr(*p);
152                         }
153                 } else
154                         strlcat(errbuf,
155                                         strerror(errno),
156                                         sizeof(errbuf));
157                 return NULL;
158         }
159         mp->refCnt = 1;
160         mp->next = modList;
161         modList = mp;
162         if (loadbind(0, mainModule, mp->entry) == -1) {
163                 dlclose(mp);
164                 errvalid++;
165                 snprintf (errbuf, sizeof(errbuf),
166                           "loadbind: %s", strerror(errno));
167                 return NULL;
168         }
169         /*
170          * If the user wants global binding, loadbind against all other
171          * loaded modules.
172          */
173         if (mode & RTLD_GLOBAL) {
174                 ModulePtr mp1;
175                 for (mp1 = mp->next; mp1; mp1 = mp1->next)
176                         if (loadbind(0, mp1->entry, mp->entry) == -1) {
177                                 dlclose(mp);
178                                 errvalid++;
179                                 snprintf (errbuf, sizeof(errbuf),
180                                           "loadbind: %s",
181                                           strerror(errno));
182                                 return NULL;
183                         }
184         }
185         if (readExports(mp) == -1) {
186                 dlclose(mp);
187                 return NULL;
188         }
189         /*
190          * If there is a dl_info structure, call the init function.
191          */
192         if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) {
193                 if (mp->info->init)
194                         (*mp->info->init)();
195         } else
196                 errvalid = 0;
197         /*
198          * If the shared object was compiled using xlC we will need
199          * to call static constructors (and later on dlclose destructors).
200          */
201         if (mp->cdtors = (CdtorPtr)dlsym(mp, "__cdtors")) {
202                 CdtorPtr cp = mp->cdtors;
203                 while (cp->init || cp->term) {
204                         if (cp->init && cp->init != (void (*)(void))0xffffffff)
205                                 (*cp->init)();
206                         cp++;
207                 }
208         /*
209          * If the shared object was compiled using g++, we will need
210          * to call global constructors using the _GLOBAL__DI function,
211          * and later, global destructors using the _GLOBAL_DD
212          * funciton.  --jwe
213          */
214         } else if (mp->gcc_ctor = (GccCDtorPtr)dlsym(mp, "_GLOBAL__DI")) {
215                 (*mp->gcc_ctor)();
216                 mp->gcc_dtor = (GccCDtorPtr)dlsym(mp, "_GLOBAL__DD"); 
217         } else
218                 errvalid = 0;
219         return mp;
220 }
221
222 /*
223  * Attempt to decipher an AIX loader error message and append it
224  * to our static error message buffer.
225  */
226 static void caterr(char *s)
227 {
228         char *p = s;
229
230         while (*p >= '0' && *p <= '9')
231                 p++;
232         switch(atoi(s)) {
233         case L_ERROR_TOOMANY:
234                 strlcat(errbuf, "to many errors", sizeof(errbuf));
235                 break;
236         case L_ERROR_NOLIB:
237                 strlcat(errbuf, "can't load library", sizeof(errbuf));
238                 strlcat(errbuf, p, sizeof(errbuf));
239                 break;
240         case L_ERROR_UNDEF:
241                 strlcat(errbuf, "can't find symbol", sizeof(errbuf));
242                 strlcat(errbuf, p, sizeof(errbuf));
243                 break;
244         case L_ERROR_RLDBAD:
245                 strlcat(errbuf, "bad RLD", sizeof(errbuf));
246                 strlcat(errbuf, p, sizeof(errbuf));
247                 break;
248         case L_ERROR_FORMAT:
249                 strlcat(errbuf, "bad exec format in", sizeof(errbuf));
250                 strlcat(errbuf, p, sizeof(errbuf));
251                 break;
252         case L_ERROR_ERRNO:
253                 strlcat(errbuf, strerror(atoi(++p)), sizeof(errbuf));
254                 break;
255         default:
256                 strlcat(errbuf, s, sizeof(errbuf));
257                 break;
258         }
259 }
260
261 void *dlsym(void *handle, const char *symbol)
262 {
263         ModulePtr mp = (ModulePtr)handle;
264         ExportPtr ep;
265         int i;
266
267         /*
268          * Could speed up the search, but I assume that one assigns
269          * the result to function pointers anyways.
270          */
271         for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
272                 if (strcmp(ep->name, symbol) == 0)
273                         return ep->addr;
274         errvalid++;
275         snprintf (errbuf, sizeof(errbuf),
276                   "dlsym: undefined symbol %s", symbol);                  
277         return NULL;
278 }
279
280 char *dlerror(void)
281 {
282         if (errvalid) {
283                 errvalid = 0;
284                 return errbuf;
285         }
286         return NULL;
287 }
288
289 int dlclose(void *handle)
290 {
291         ModulePtr mp = (ModulePtr)handle;
292         int result;
293         ModulePtr mp1;
294
295         if (--mp->refCnt > 0)
296                 return 0;
297         if (mp->info && mp->info->fini)
298                 (*mp->info->fini)();
299         if (mp->cdtors) {
300                 CdtorPtr cp = mp->cdtors;
301                 while (cp->init || cp->term) {
302                         if (cp->term && cp->init != (void (*)(void))0xffffffff)
303                                 (*cp->term)();
304                         cp++;
305                 }
306         /*
307          * If the function to handle global destructors for g++
308          * exists, call it.  --jwe
309          */
310         } else if (mp->gcc_dtor) {
311                 (*mp->gcc_dtor)();
312         }
313         result = unload(mp->entry);
314         if (result == -1) {
315                 errvalid++;
316                 snprintf (errbuf, sizeof(errbuf),
317                           "%s", strerror(errno));
318         }
319         if (mp->exports) {
320                 ExportPtr ep;
321                 int i;
322                 for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
323                         if (ep->name)
324                                 free(ep->name);
325                 free(mp->exports);
326         }
327         if (mp == modList)
328                 modList = mp->next;
329         else {
330                 for (mp1 = modList; mp1; mp1 = mp1->next)
331                         if (mp1->next == mp) {
332                                 mp1->next = mp->next;
333                                 break;
334                         }
335         }
336         free(mp->name);
337         free(mp);
338         return result;
339 }
340
341 static void terminate(void)
342 {
343         while (modList)
344                 dlclose(modList);
345 }
346
347 /*
348  * Build the export table from the XCOFF .loader section.
349  */
350 static int readExports(ModulePtr mp)
351 {
352         LDFILE *ldp = NULL;
353         SCNHDR sh, shdata;
354         LDHDR *lhp;
355         char *ldbuf;
356         LDSYM *ls;
357         int i;
358         ExportPtr ep;
359
360         if ((ldp = ldopen(mp->name, ldp)) == NULL) {
361                 struct ld_info *lp;
362                 char *buf;
363                 int size = 4*1024;
364                 if (errno != ENOENT) {
365                         errvalid++;
366                         snprintf(errbuf, sizeof(errbuf),
367                                  "readExports: %s",
368                                  strerror(errno));
369                         return -1;
370                 }
371                 /*
372                  * The module might be loaded due to the LIBPATH
373                  * environment variable. Search for the loaded
374                  * module using L_GETINFO.
375                  */
376                 if ((buf = malloc(size)) == NULL) {
377                         errvalid++;
378                         snprintf(errbuf, sizeof(errbuf),
379                                  "readExports: %s",
380                                  strerror(errno));
381                         return -1;
382                 }
383                 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
384                         free(buf);
385                         size += 4*1024;
386                         if ((buf = malloc(size)) == NULL) {
387                                 errvalid++;
388                                 snprintf(errbuf, sizeof(errbuf),
389                                          "readExports: %s",
390                                          strerror(errno));
391                                 return -1;
392                         }
393                 }
394                 if (i == -1) {
395                         errvalid++;
396                         snprintf(errbuf, sizeof(errbuf),
397                                  "readExports: %s",
398                                  strerror(errno));
399                         free(buf);
400                         return -1;
401                 }
402                 /*
403                  * Traverse the list of loaded modules. The entry point
404                  * returned by load() does actually point to the data
405                  * segment origin.
406                  */
407                 lp = (struct ld_info *)buf;
408                 while (lp) {
409                         if (lp->ldinfo_dataorg == mp->entry) {
410                                 ldp = ldopen(lp->ldinfo_filename, ldp);
411                                 break;
412                         }
413                         if (lp->ldinfo_next == 0)
414                                 lp = NULL;
415                         else
416                                 lp = (struct ld_info *)((char *)lp + lp->ldinfo_next);
417                 }
418                 free(buf);
419                 if (!ldp) {
420                         errvalid++;
421                         snprintf (errbuf, sizeof(errbuf),
422                                   "readExports: %s", strerror(errno));
423                         return -1;
424                 }
425         }
426         if (TYPE(ldp) != U802TOCMAGIC) {
427                 errvalid++;
428                 snprintf(errbuf, sizeof(errbuf), "readExports: bad magic");
429                 while(ldclose(ldp) == FAILURE)
430                         ;
431                 return -1;
432         }
433         /*
434          * Get the padding for the data section. This is needed for
435          * AIX 4.1 compilers. This is used when building the final
436          * function pointer to the exported symbol.
437          */
438         if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
439                 errvalid++;
440                 snprintf(errbuf, sizeof(errbuf),
441                          "readExports: cannot read data section header");
442                 while(ldclose(ldp) == FAILURE)
443                         ;
444                 return -1;
445         }
446         if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
447                 errvalid++;
448                 snprintf(errbuf, sizeof(errbuf),
449                          "readExports: cannot read loader section header");
450                 while(ldclose(ldp) == FAILURE)
451                         ;
452                 return -1;
453         }
454         /*
455          * We read the complete loader section in one chunk, this makes
456          * finding long symbol names residing in the string table easier.
457          */
458         if ((ldbuf = (char *)malloc(sh.s_size)) == NULL) {
459                 errvalid++;
460                 snprintf (errbuf, sizeof(errbuf),
461                           "readExports: %s", strerror(errno));
462                 while(ldclose(ldp) == FAILURE)
463                         ;
464                 return -1;
465         }
466         if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
467                 errvalid++;
468                 snprintf(errbuf, sizeof(errbuf),
469                          "readExports: cannot seek to loader section");
470                 free(ldbuf);
471                 while(ldclose(ldp) == FAILURE)
472                         ;
473                 return -1;
474         }
475         if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
476                 errvalid++;
477                 snprintf(errbuf, sizeof(errbuf),
478                          "readExports: cannot read loader section");
479                 free(ldbuf);
480                 while(ldclose(ldp) == FAILURE)
481                         ;
482                 return -1;
483         }
484         lhp = (LDHDR *)ldbuf;
485         ls = (LDSYM *)(ldbuf+LDHDRSZ);
486         /*
487          * Count the number of exports to include in our export table.
488          */
489         for (i = lhp->l_nsyms; i; i--, ls++) {
490                 if (!LDR_EXPORT(*ls))
491                         continue;
492                 mp->nExports++;
493         }
494         if ((mp->exports = (ExportPtr)calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
495                 errvalid++;
496                 snprintf (errbuf, sizeof(errbuf),
497                           "readExports: %s", strerror(errno));
498                 free(ldbuf);
499                 while(ldclose(ldp) == FAILURE)
500                         ;
501                 return -1;
502         }
503         /*
504          * Fill in the export table. All entries are relative to
505          * the entry point we got from load.
506          */
507         ep = mp->exports;
508         ls = (LDSYM *)(ldbuf+LDHDRSZ);
509         for (i = lhp->l_nsyms; i; i--, ls++) {
510                 char *symname;
511                 char tmpsym[SYMNMLEN+1];
512                 if (!LDR_EXPORT(*ls))
513                         continue;
514                 if (ls->l_zeroes == 0)
515                         symname = ls->l_offset+lhp->l_stoff+ldbuf;
516                 else {
517                         /*
518                          * The l_name member is not zero terminated, we
519                          * must copy the first SYMNMLEN chars and make
520                          * sure we have a zero byte at the end.
521                          */
522                         strlcpy (tmpsym, ls->l_name,
523                                          SYMNMLEN + 1);
524                         symname = tmpsym;
525                 }
526                 ep->name = strdup(symname);
527                 ep->addr = (void *)((unsigned long)mp->entry +
528                                         ls->l_value - shdata.s_vaddr);
529                 ep++;
530         }
531         free(ldbuf);
532         while(ldclose(ldp) == FAILURE)
533                 ;
534         return 0;
535 }
536
537 /*
538  * Find the main modules entry point. This is used as export pointer
539  * for loadbind() to be able to resolve references to the main part.
540  */
541 static void * findMain(void)
542 {
543         struct ld_info *lp;
544         char *buf;
545         int size = 4*1024;
546         int i;
547         void *ret;
548
549         if ((buf = malloc(size)) == NULL) {
550                 errvalid++;
551                 snprintf (errbuf, sizeof(errbuf),
552                           "findMail: %s", strerror(errno));
553                 return NULL;
554         }
555         while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
556                 free(buf);
557                 size += 4*1024;
558                 if ((buf = malloc(size)) == NULL) {
559                         errvalid++;
560                         snprintf (errbuf, sizeof(errbuf),
561                                   "findMail: %s", strerror(errno));
562                         return NULL;
563                 }
564         }
565         if (i == -1) {
566                 errvalid++;
567                 snprintf (errbuf, sizeof(errbuf),
568                           "findMail: %s", strerror(errno));
569                 free(buf);
570                 return NULL;
571         }
572         /*
573          * The first entry is the main module. The entry point
574          * returned by load() does actually point to the data
575          * segment origin.
576          */
577         lp = (struct ld_info *)buf;
578         ret = lp->ldinfo_dataorg;
579         free(buf);
580         return ret;
581 }