OSDN Git Service

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