Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/unix/tclLoadAix.c @ 52

Last change on this file since 52 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 12.9 KB
Line 
1/*
2 * tclLoadAix.c --
3 *
4 *      This file implements the dlopen and dlsym APIs under the AIX operating
5 *      system, to enable the Tcl "load" command to work. This code was
6 *      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 been
10 *      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: tclLoadAix.c,v 1.6 2007/04/16 13:36:36 dkf Exp $
21 *
22 * Note: this file has been altered from the original in a few ways in order
23 * 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 no call
44 * to find an exported symbol we read the loader section of the loaded module
45 * and build a list of exported symbols and their virtual address.
46 */
47
48typedef struct {
49    char *name;                 /* The symbols's name. */
50    void *addr;                 /* Its relocated virtual address. */
51} Export, *ExportPtr;
52
53/*
54 * xlC uses the following structure to list its constructors and destructors.
55 * This is gleaned from the output of munch.
56 */
57
58typedef 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
67typedef struct Module {
68    struct Module *next;
69    char *name;                 /* module name for refcounting */
70    int refCnt;                 /* the number of references */
71    void *entry;                /* entry point from load */
72    struct dl_info *info;       /* optional init/terminate functions */
73    CdtorPtr cdtors;            /* optional C++ constructors */
74    int nExports;               /* the number of exports found */
75    ExportPtr exports;          /* the array of exports */
76} Module, *ModulePtr;
77
78/*
79 * We keep a list of all loaded modules to be able to call the fini handlers
80 * and destructors at atexit() time.
81 */
82
83static ModulePtr modList;
84
85/*
86 * The last error from one of the dl* routines is kept in static variables
87 * here. Each error is returned only once to the caller.
88 */
89
90static char errbuf[BUFSIZ];
91static int errvalid;
92
93static void caterr(char *);
94static int readExports(ModulePtr);
95static void terminate(void);
96static void *findMain(void);
97
98void *
99dlopen(
100    const char *path,
101    int mode)
102{
103    register ModulePtr mp;
104    static void *mainModule;
105
106    /*
107     * Upon the first call register a terminate handler that will close all
108     * libraries. Also get a reference to the main module for use with
109     * loadbind.
110     */
111
112    if (!mainModule) {
113        mainModule = findMain();
114        if (mainModule == NULL) {
115            return NULL;
116        }
117        atexit(terminate);
118    }
119
120    /*
121     * Scan the list of modules if we have the module already loaded.
122     */
123
124    for (mp = modList; mp; mp = mp->next) {
125        if (strcmp(mp->name, path) == 0) {
126            mp->refCnt++;
127            return (void *) mp;
128        }
129    }
130
131    mp = (ModulePtr) calloc(1, sizeof(*mp));
132    if (mp == NULL) {
133        errvalid++;
134        strcpy(errbuf, "calloc: ");
135        strcat(errbuf, strerror(errno));
136        return NULL;
137    }
138
139    mp->name = malloc((unsigned) (strlen(path) + 1));
140    strcpy(mp->name, path);
141
142    /*
143     * load should be declared load(const char *...). Thus we cast the path to
144     * a normal char *. Ugly.
145     */
146
147    mp->entry = (void *) load((char *)path, L_NOAUTODEFER, NULL);
148    if (mp->entry == NULL) {
149        free(mp->name);
150        free(mp);
151        errvalid++;
152        strcpy(errbuf, "dlopen: ");
153        strcat(errbuf, path);
154        strcat(errbuf, ": ");
155
156        /*
157         * If AIX says the file is not executable, the error can be further
158         * described by querying the loader about the last error.
159         */
160
161        if (errno == ENOEXEC) {
162            char *tmp[BUFSIZ/sizeof(char *)], **p;
163
164            if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) {
165                strcpy(errbuf, strerror(errno));
166            } else {
167                for (p=tmp ; *p ; p++) {
168                    caterr(*p);
169                }
170            }
171        } else {
172            strcat(errbuf, strerror(errno));
173        }
174        return NULL;
175    }
176
177    mp->refCnt = 1;
178    mp->next = modList;
179    modList = mp;
180
181    if (loadbind(0, mainModule, mp->entry) == -1) {
182    loadbindFailure:
183        dlclose(mp);
184        errvalid++;
185        strcpy(errbuf, "loadbind: ");
186        strcat(errbuf, strerror(errno));
187        return NULL;
188    }
189
190    /*
191     * If the user wants global binding, loadbind against all other loaded
192     * modules.
193     */
194
195    if (mode & RTLD_GLOBAL) {
196        register ModulePtr mp1;
197
198        for (mp1 = mp->next; mp1; mp1 = mp1->next) {
199            if (loadbind(0, mp1->entry, mp->entry) == -1) {
200                goto loadbindFailure;
201            }
202        }
203    }
204
205    if (readExports(mp) == -1) {
206        dlclose(mp);
207        return NULL;
208    }
209
210    /*
211     * If there is a dl_info structure, call the init function.
212     */
213
214    if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) {
215        if (mp->info->init) {
216            (*mp->info->init)();
217        }
218    } else {
219        errvalid = 0;
220    }
221
222    /*
223     * If the shared object was compiled using xlC we will need to call static
224     * constructors (and later on dlclose destructors).
225     */
226
227    if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) {
228        while (mp->cdtors->init) {
229            (*mp->cdtors->init)();
230            mp->cdtors++;
231        }
232    } else {
233        errvalid = 0;
234    }
235
236    return (void *) mp;
237}
238
239/*
240 * Attempt to decipher an AIX loader error message and append it to our static
241 * error message buffer.
242 */
243
244static void
245caterr(
246    char *s)
247{
248    register char *p = s;
249
250    while (*p >= '0' && *p <= '9') {
251        p++;
252    }
253    switch (atoi(s)) {          /* INTL: "C", UTF safe. */
254    case L_ERROR_TOOMANY:
255        strcat(errbuf, "to many errors");
256        break;
257    case L_ERROR_NOLIB:
258        strcat(errbuf, "can't load library");
259        strcat(errbuf, p);
260        break;
261    case L_ERROR_UNDEF:
262        strcat(errbuf, "can't find symbol");
263        strcat(errbuf, p);
264        break;
265    case L_ERROR_RLDBAD:
266        strcat(errbuf, "bad RLD");
267        strcat(errbuf, p);
268        break;
269    case L_ERROR_FORMAT:
270        strcat(errbuf, "bad exec format in");
271        strcat(errbuf, p);
272        break;
273    case L_ERROR_ERRNO:
274        strcat(errbuf, strerror(atoi(++p)));    /* INTL: "C", UTF safe. */
275        break;
276    default:
277        strcat(errbuf, s);
278        break;
279    }
280}
281
282void *
283dlsym(
284    void *handle,
285    const char *symbol)
286{
287    register ModulePtr mp = (ModulePtr)handle;
288    register ExportPtr ep;
289    register int i;
290
291    /*
292     * Could speed up the search, but I assume that one assigns the result to
293     * function pointers anyways.
294     */
295
296    for (ep = mp->exports, i = mp->nExports; i; i--, ep++) {
297        if (strcmp(ep->name, symbol) == 0) {
298            return ep->addr;
299        }
300    }
301
302    errvalid++;
303    strcpy(errbuf, "dlsym: undefined symbol ");
304    strcat(errbuf, symbol);
305    return NULL;
306}
307
308char *
309dlerror(void)
310{
311    if (errvalid) {
312        errvalid = 0;
313        return errbuf;
314    }
315    return NULL;
316}
317
318int
319dlclose(
320    void *handle)
321{
322    register ModulePtr mp = (ModulePtr)handle;
323    int result;
324    register ModulePtr mp1;
325
326    if (--mp->refCnt > 0) {
327        return 0;
328    }
329
330    if (mp->info && mp->info->fini) {
331        (*mp->info->fini)();
332    }
333
334    if (mp->cdtors) {
335        while (mp->cdtors->term) {
336            (*mp->cdtors->term)();
337            mp->cdtors++;
338        }
339    }
340
341    result = unload(mp->entry);
342    if (result == -1) {
343        errvalid++;
344        strcpy(errbuf, strerror(errno));
345    }
346
347    if (mp->exports) {
348        register ExportPtr ep;
349        register int i;
350        for (ep = mp->exports, i = mp->nExports; i; i--, ep++) {
351            if (ep->name) {
352                free(ep->name);
353            }
354        }
355        free(mp->exports);
356    }
357
358    if (mp == modList) {
359        modList = mp->next;
360    } else {
361        for (mp1 = modList; mp1; mp1 = mp1->next) {
362            if (mp1->next == mp) {
363                mp1->next = mp->next;
364                break;
365            }
366        }
367    }
368
369    free(mp->name);
370    free(mp);
371    return result;
372}
373
374static void
375terminate(void)
376{
377    while (modList) {
378        dlclose(modList);
379    }
380}
381
382/*
383 * Build the export table from the XCOFF .loader section.
384 */
385
386static int
387readExports(
388    ModulePtr mp)
389{
390    LDFILE *ldp = NULL;
391    SCNHDR sh, shdata;
392    LDHDR *lhp;
393    char *ldbuf;
394    LDSYM *ls;
395    int i;
396    ExportPtr ep;
397    const char *errMsg;
398
399#define Error(msg) do{errMsg=(msg);goto error;}while(0)
400#define SysErr() Error(strerror(errno))
401
402    ldp = ldopen(mp->name, ldp);
403    if (ldp == NULL) {
404        struct ld_info *lp;
405        char *buf;
406        int size = 0;
407
408        if (errno != ENOENT) {
409            SysErr();
410        }
411
412        /*
413         * The module might be loaded due to the LIBPATH environment variable.
414         * Search for the loaded module using L_GETINFO.
415         */
416
417        while (1) {
418            size += 4 * 1024;
419            buf = malloc(size);
420            if (buf == NULL) {
421                SysErr();
422            }
423
424            i = loadquery(L_GETINFO, buf, size);
425
426            if (i != -1) {
427                break;
428            }
429            free(buf);
430            if (errno != ENOMEM) {
431                SysErr();
432            }
433        }
434
435        /*
436         * Traverse the list of loaded modules. The entry point returned by
437         * load() does actually point to the data segment origin.
438         */
439
440        lp = (struct ld_info *) buf;
441        while (lp) {
442            if (lp->ldinfo_dataorg == mp->entry) {
443                ldp = ldopen(lp->ldinfo_filename, ldp);
444                break;
445            }
446            if (lp->ldinfo_next == 0) {
447                lp = NULL;
448            } else {
449                lp = (struct ld_info *)((char *)lp + lp->ldinfo_next);
450            }
451        }
452
453        free(buf);
454
455        if (!ldp) {
456            SysErr();
457        }
458    }
459
460    if (TYPE(ldp) != U802TOCMAGIC) {
461        Error("bad magic");
462    }
463
464    /*
465     * Get the padding for the data section. This is needed for AIX 4.1
466     * compilers. This is used when building the final function pointer to the
467     * exported symbol.
468     */
469
470    if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
471        Error("cannot read data section header");
472    }
473
474    if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
475        Error("cannot read loader section header");
476    }
477
478    /*
479     * We read the complete loader section in one chunk, this makes finding
480     * long symbol names residing in the string table easier.
481     */
482
483    ldbuf = (char *) malloc(sh.s_size);
484    if (ldbuf == NULL) {
485        SysErr();
486    }
487
488    if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
489        free(ldbuf);
490        Error("cannot seek to loader section");
491    }
492
493    if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
494        free(ldbuf);
495        Error("cannot read loader section");
496    }
497
498    lhp = (LDHDR *) ldbuf;
499    ls = (LDSYM *)(ldbuf + LDHDRSZ);
500
501    /*
502     * Count the number of exports to include in our export table.
503     */
504
505    for (i = lhp->l_nsyms; i; i--, ls++) {
506        if (!LDR_EXPORT(*ls)) {
507            continue;
508        }
509        mp->nExports++;
510    }
511
512    mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports));
513    if (mp->exports == NULL) {
514        free(ldbuf);
515        SysErr();
516    }
517
518    /*
519     * Fill in the export table. All entries are relative to the entry point
520     * we got from load.
521     */
522
523    ep = mp->exports;
524    ls = (LDSYM *)(ldbuf + LDHDRSZ);
525    for (i=lhp->l_nsyms ; i!=0 ; i--,ls++) {
526        char *symname;
527        char tmpsym[SYMNMLEN+1];
528
529        if (!LDR_EXPORT(*ls)) {
530            continue;
531        }
532
533        if (ls->l_zeroes == 0) {
534            symname = ls->l_offset + lhp->l_stoff + ldbuf;
535        } else {
536            /*
537             * The l_name member is not zero terminated, we must copy the
538             * first SYMNMLEN chars and make sure we have a zero byte at the
539             * end.
540             */
541
542            strncpy(tmpsym, ls->l_name, SYMNMLEN);
543            tmpsym[SYMNMLEN] = '\0';
544            symname = tmpsym;
545        }
546        ep->name = malloc((unsigned) (strlen(symname) + 1));
547        strcpy(ep->name, symname);
548        ep->addr = (void *)((unsigned long)
549                mp->entry + ls->l_value - shdata.s_vaddr);
550        ep++;
551    }
552    free(ldbuf);
553    while (ldclose(ldp) == FAILURE) {
554        /* Empty body */
555    }
556    return 0;
557
558    /*
559     * This is a factoring out of the error-handling code to make the rest of
560     * the function much simpler to read.
561     */
562
563  error:
564    errvalid++;
565    strcpy(errbuf, "readExports: ");
566    strcat(errbuf, errMsg);
567
568    if (ldp != NULL) {
569        while (ldclose(ldp) == FAILURE) {
570            /* Empty body */
571        }
572    }
573    return -1;
574}
575
576/*
577 * Find the main modules entry point. This is used as export pointer for
578 * loadbind() to be able to resolve references to the main part.
579 */
580
581static void *
582findMain(void)
583{
584    struct ld_info *lp;
585    char *buf;
586    int size = 4*1024;
587    int i;
588    void *ret;
589
590    buf = malloc(size);
591    if (buf == NULL) {
592        goto error;
593    }
594
595    while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
596        free(buf);
597        size += 4*1024;
598        buf = malloc(size);
599        if (buf == NULL) {
600            goto error;
601        }
602    }
603
604    if (i == -1) {
605        free(buf);
606        goto error;
607    }
608
609    /*
610     * The first entry is the main module. The entry point returned by load()
611     * does actually point to the data segment origin.
612     */
613
614    lp = (struct ld_info *) buf;
615    ret = lp->ldinfo_dataorg;
616    free(buf);
617    return ret;
618
619  error:
620    errvalid++;
621    strcpy(errbuf, "findMain: ");
622    strcat(errbuf, strerror(errno));
623    return NULL;
624}
625
626/*
627 * Local Variables:
628 * mode: c
629 * c-basic-offset: 4
630 * fill-column: 78
631 * End:
632 */
Note: See TracBrowser for help on using the repository browser.