tclLoadAix.c

Go to the documentation of this file.
00001 /*
00002  * tclLoadAix.c --
00003  *
00004  *      This file implements the dlopen and dlsym APIs under the AIX operating
00005  *      system, to enable the Tcl "load" command to work. This code was
00006  *      provided by Jens-Uwe Mager.
00007  *
00008  *      This file is subject to the following copyright notice, which is
00009  *      different from the notice used elsewhere in Tcl. The file has been
00010  *      modified to incorporate the file dlfcn.h in-line.
00011  *
00012  *      Copyright (c) 1992,1993,1995,1996, Jens-Uwe Mager, Helios Software GmbH
00013  *      Not derived from licensed software.
00014  *
00015  *      Permission is granted to freely use, copy, modify, and redistribute
00016  *      this software, provided that the author is not construed to be liable
00017  *      for any results of using the software, alterations are clearly marked
00018  *      as such, and this notice is not modified.
00019  *
00020  * RCS: @(#) $Id: tclLoadAix.c,v 1.6 2007/04/16 13:36:36 dkf Exp $
00021  *
00022  * Note: this file has been altered from the original in a few ways in order
00023  * to work properly with Tcl.
00024  */
00025 
00026 /*
00027  * @(#)dlfcn.c  1.7 revision of 95/08/14  19:08:38
00028  * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH
00029  * 30159 Hannover, Germany
00030  */
00031 
00032 #include <stdio.h>
00033 #include <errno.h>
00034 #include <string.h>
00035 #include <stdlib.h>
00036 #include <sys/types.h>
00037 #include <sys/ldr.h>
00038 #include <a.out.h>
00039 #include <ldfcn.h>
00040 #include "../compat/dlfcn.h"
00041 
00042 /*
00043  * We simulate dlopen() et al. through a call to load. Because AIX has no call
00044  * to find an exported symbol we read the loader section of the loaded module
00045  * and build a list of exported symbols and their virtual address.
00046  */
00047 
00048 typedef struct {
00049     char *name;                 /* The symbols's name. */
00050     void *addr;                 /* Its relocated virtual address. */
00051 } Export, *ExportPtr;
00052 
00053 /*
00054  * xlC uses the following structure to list its constructors and destructors.
00055  * This is gleaned from the output of munch.
00056  */
00057 
00058 typedef struct {
00059     void (*init)(void);         /* call static constructors */
00060     void (*term)(void);         /* call static destructors */
00061 } Cdtor, *CdtorPtr;
00062 
00063 /*
00064  * The void * handle returned from dlopen is actually a ModulePtr.
00065  */
00066 
00067 typedef struct Module {
00068     struct Module *next;
00069     char *name;                 /* module name for refcounting */
00070     int refCnt;                 /* the number of references */
00071     void *entry;                /* entry point from load */
00072     struct dl_info *info;       /* optional init/terminate functions */
00073     CdtorPtr cdtors;            /* optional C++ constructors */
00074     int nExports;               /* the number of exports found */
00075     ExportPtr exports;          /* the array of exports */
00076 } Module, *ModulePtr;
00077 
00078 /*
00079  * We keep a list of all loaded modules to be able to call the fini handlers
00080  * and destructors at atexit() time.
00081  */
00082 
00083 static ModulePtr modList;
00084 
00085 /*
00086  * The last error from one of the dl* routines is kept in static variables
00087  * here. Each error is returned only once to the caller.
00088  */
00089 
00090 static char errbuf[BUFSIZ];
00091 static int errvalid;
00092 
00093 static void caterr(char *);
00094 static int readExports(ModulePtr);
00095 static void terminate(void);
00096 static void *findMain(void);
00097 
00098 void *
00099 dlopen(
00100     const char *path,
00101     int mode)
00102 {
00103     register ModulePtr mp;
00104     static void *mainModule;
00105 
00106     /*
00107      * Upon the first call register a terminate handler that will close all
00108      * libraries. Also get a reference to the main module for use with
00109      * loadbind.
00110      */
00111 
00112     if (!mainModule) {
00113         mainModule = findMain();
00114         if (mainModule == NULL) {
00115             return NULL;
00116         }
00117         atexit(terminate);
00118     }
00119 
00120     /*
00121      * Scan the list of modules if we have the module already loaded.
00122      */
00123 
00124     for (mp = modList; mp; mp = mp->next) {
00125         if (strcmp(mp->name, path) == 0) {
00126             mp->refCnt++;
00127             return (void *) mp;
00128         }
00129     }
00130 
00131     mp = (ModulePtr) calloc(1, sizeof(*mp));
00132     if (mp == NULL) {
00133         errvalid++;
00134         strcpy(errbuf, "calloc: ");
00135         strcat(errbuf, strerror(errno));
00136         return NULL;
00137     }
00138 
00139     mp->name = malloc((unsigned) (strlen(path) + 1));
00140     strcpy(mp->name, path);
00141 
00142     /*
00143      * load should be declared load(const char *...). Thus we cast the path to
00144      * a normal char *. Ugly.
00145      */
00146 
00147     mp->entry = (void *) load((char *)path, L_NOAUTODEFER, NULL);
00148     if (mp->entry == NULL) {
00149         free(mp->name);
00150         free(mp);
00151         errvalid++;
00152         strcpy(errbuf, "dlopen: ");
00153         strcat(errbuf, path);
00154         strcat(errbuf, ": ");
00155 
00156         /*
00157          * If AIX says the file is not executable, the error can be further
00158          * described by querying the loader about the last error.
00159          */
00160 
00161         if (errno == ENOEXEC) {
00162             char *tmp[BUFSIZ/sizeof(char *)], **p;
00163 
00164             if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) {
00165                 strcpy(errbuf, strerror(errno));
00166             } else {
00167                 for (p=tmp ; *p ; p++) {
00168                     caterr(*p);
00169                 }
00170             }
00171         } else {
00172             strcat(errbuf, strerror(errno));
00173         }
00174         return NULL;
00175     }
00176 
00177     mp->refCnt = 1;
00178     mp->next = modList;
00179     modList = mp;
00180 
00181     if (loadbind(0, mainModule, mp->entry) == -1) {
00182     loadbindFailure:
00183         dlclose(mp);
00184         errvalid++;
00185         strcpy(errbuf, "loadbind: ");
00186         strcat(errbuf, strerror(errno));
00187         return NULL;
00188     }
00189 
00190     /*
00191      * If the user wants global binding, loadbind against all other loaded
00192      * modules.
00193      */
00194 
00195     if (mode & RTLD_GLOBAL) {
00196         register ModulePtr mp1;
00197 
00198         for (mp1 = mp->next; mp1; mp1 = mp1->next) {
00199             if (loadbind(0, mp1->entry, mp->entry) == -1) {
00200                 goto loadbindFailure;
00201             }
00202         }
00203     }
00204 
00205     if (readExports(mp) == -1) {
00206         dlclose(mp);
00207         return NULL;
00208     }
00209 
00210     /*
00211      * If there is a dl_info structure, call the init function.
00212      */
00213 
00214     if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) {
00215         if (mp->info->init) {
00216             (*mp->info->init)();
00217         }
00218     } else {
00219         errvalid = 0;
00220     }
00221 
00222     /*
00223      * If the shared object was compiled using xlC we will need to call static
00224      * constructors (and later on dlclose destructors).
00225      */
00226 
00227     if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) {
00228         while (mp->cdtors->init) {
00229             (*mp->cdtors->init)();
00230             mp->cdtors++;
00231         }
00232     } else {
00233         errvalid = 0;
00234     }
00235 
00236     return (void *) mp;
00237 }
00238 
00239 /*
00240  * Attempt to decipher an AIX loader error message and append it to our static
00241  * error message buffer.
00242  */
00243 
00244 static void
00245 caterr(
00246     char *s)
00247 {
00248     register char *p = s;
00249 
00250     while (*p >= '0' && *p <= '9') {
00251         p++;
00252     }
00253     switch (atoi(s)) {          /* INTL: "C", UTF safe. */
00254     case L_ERROR_TOOMANY:
00255         strcat(errbuf, "to many errors");
00256         break;
00257     case L_ERROR_NOLIB:
00258         strcat(errbuf, "can't load library");
00259         strcat(errbuf, p);
00260         break;
00261     case L_ERROR_UNDEF:
00262         strcat(errbuf, "can't find symbol");
00263         strcat(errbuf, p);
00264         break;
00265     case L_ERROR_RLDBAD:
00266         strcat(errbuf, "bad RLD");
00267         strcat(errbuf, p);
00268         break;
00269     case L_ERROR_FORMAT:
00270         strcat(errbuf, "bad exec format in");
00271         strcat(errbuf, p);
00272         break;
00273     case L_ERROR_ERRNO:
00274         strcat(errbuf, strerror(atoi(++p)));    /* INTL: "C", UTF safe. */
00275         break;
00276     default:
00277         strcat(errbuf, s);
00278         break;
00279     }
00280 }
00281 
00282 void *
00283 dlsym(
00284     void *handle,
00285     const char *symbol)
00286 {
00287     register ModulePtr mp = (ModulePtr)handle;
00288     register ExportPtr ep;
00289     register int i;
00290 
00291     /*
00292      * Could speed up the search, but I assume that one assigns the result to
00293      * function pointers anyways.
00294      */
00295 
00296     for (ep = mp->exports, i = mp->nExports; i; i--, ep++) {
00297         if (strcmp(ep->name, symbol) == 0) {
00298             return ep->addr;
00299         }
00300     }
00301 
00302     errvalid++;
00303     strcpy(errbuf, "dlsym: undefined symbol ");
00304     strcat(errbuf, symbol);
00305     return NULL;
00306 }
00307 
00308 char *
00309 dlerror(void)
00310 {
00311     if (errvalid) {
00312         errvalid = 0;
00313         return errbuf;
00314     }
00315     return NULL;
00316 }
00317 
00318 int
00319 dlclose(
00320     void *handle)
00321 {
00322     register ModulePtr mp = (ModulePtr)handle;
00323     int result;
00324     register ModulePtr mp1;
00325 
00326     if (--mp->refCnt > 0) {
00327         return 0;
00328     }
00329 
00330     if (mp->info && mp->info->fini) {
00331         (*mp->info->fini)();
00332     }
00333 
00334     if (mp->cdtors) {
00335         while (mp->cdtors->term) {
00336             (*mp->cdtors->term)();
00337             mp->cdtors++;
00338         }
00339     }
00340 
00341     result = unload(mp->entry);
00342     if (result == -1) {
00343         errvalid++;
00344         strcpy(errbuf, strerror(errno));
00345     }
00346 
00347     if (mp->exports) {
00348         register ExportPtr ep;
00349         register int i;
00350         for (ep = mp->exports, i = mp->nExports; i; i--, ep++) {
00351             if (ep->name) {
00352                 free(ep->name);
00353             }
00354         }
00355         free(mp->exports);
00356     }
00357 
00358     if (mp == modList) {
00359         modList = mp->next;
00360     } else {
00361         for (mp1 = modList; mp1; mp1 = mp1->next) {
00362             if (mp1->next == mp) {
00363                 mp1->next = mp->next;
00364                 break;
00365             }
00366         }
00367     }
00368 
00369     free(mp->name);
00370     free(mp);
00371     return result;
00372 }
00373 
00374 static void
00375 terminate(void)
00376 {
00377     while (modList) {
00378         dlclose(modList);
00379     }
00380 }
00381 
00382 /*
00383  * Build the export table from the XCOFF .loader section.
00384  */
00385 
00386 static int
00387 readExports(
00388     ModulePtr mp)
00389 {
00390     LDFILE *ldp = NULL;
00391     SCNHDR sh, shdata;
00392     LDHDR *lhp;
00393     char *ldbuf;
00394     LDSYM *ls;
00395     int i;
00396     ExportPtr ep;
00397     const char *errMsg;
00398 
00399 #define Error(msg) do{errMsg=(msg);goto error;}while(0)
00400 #define SysErr() Error(strerror(errno))
00401 
00402     ldp = ldopen(mp->name, ldp);
00403     if (ldp == NULL) {
00404         struct ld_info *lp;
00405         char *buf;
00406         int size = 0;
00407 
00408         if (errno != ENOENT) {
00409             SysErr();
00410         }
00411 
00412         /*
00413          * The module might be loaded due to the LIBPATH environment variable.
00414          * Search for the loaded module using L_GETINFO.
00415          */
00416 
00417         while (1) {
00418             size += 4 * 1024;
00419             buf = malloc(size);
00420             if (buf == NULL) {
00421                 SysErr();
00422             }
00423 
00424             i = loadquery(L_GETINFO, buf, size);
00425 
00426             if (i != -1) {
00427                 break;
00428             }
00429             free(buf);
00430             if (errno != ENOMEM) {
00431                 SysErr();
00432             }
00433         }
00434 
00435         /*
00436          * Traverse the list of loaded modules. The entry point returned by
00437          * load() does actually point to the data segment origin.
00438          */
00439 
00440         lp = (struct ld_info *) buf;
00441         while (lp) {
00442             if (lp->ldinfo_dataorg == mp->entry) {
00443                 ldp = ldopen(lp->ldinfo_filename, ldp);
00444                 break;
00445             }
00446             if (lp->ldinfo_next == 0) {
00447                 lp = NULL;
00448             } else {
00449                 lp = (struct ld_info *)((char *)lp + lp->ldinfo_next);
00450             }
00451         }
00452 
00453         free(buf);
00454 
00455         if (!ldp) {
00456             SysErr();
00457         }
00458     }
00459 
00460     if (TYPE(ldp) != U802TOCMAGIC) {
00461         Error("bad magic");
00462     }
00463 
00464     /*
00465      * Get the padding for the data section. This is needed for AIX 4.1
00466      * compilers. This is used when building the final function pointer to the
00467      * exported symbol.
00468      */
00469 
00470     if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
00471         Error("cannot read data section header");
00472     }
00473 
00474     if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
00475         Error("cannot read loader section header");
00476     }
00477 
00478     /*
00479      * We read the complete loader section in one chunk, this makes finding
00480      * long symbol names residing in the string table easier.
00481      */
00482 
00483     ldbuf = (char *) malloc(sh.s_size);
00484     if (ldbuf == NULL) {
00485         SysErr();
00486     }
00487 
00488     if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
00489         free(ldbuf);
00490         Error("cannot seek to loader section");
00491     }
00492 
00493     if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
00494         free(ldbuf);
00495         Error("cannot read loader section");
00496     }
00497 
00498     lhp = (LDHDR *) ldbuf;
00499     ls = (LDSYM *)(ldbuf + LDHDRSZ);
00500 
00501     /*
00502      * Count the number of exports to include in our export table.
00503      */
00504 
00505     for (i = lhp->l_nsyms; i; i--, ls++) {
00506         if (!LDR_EXPORT(*ls)) {
00507             continue;
00508         }
00509         mp->nExports++;
00510     }
00511 
00512     mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports));
00513     if (mp->exports == NULL) {
00514         free(ldbuf);
00515         SysErr();
00516     }
00517 
00518     /*
00519      * Fill in the export table. All entries are relative to the entry point
00520      * we got from load.
00521      */
00522 
00523     ep = mp->exports;
00524     ls = (LDSYM *)(ldbuf + LDHDRSZ);
00525     for (i=lhp->l_nsyms ; i!=0 ; i--,ls++) {
00526         char *symname;
00527         char tmpsym[SYMNMLEN+1];
00528 
00529         if (!LDR_EXPORT(*ls)) {
00530             continue;
00531         }
00532 
00533         if (ls->l_zeroes == 0) {
00534             symname = ls->l_offset + lhp->l_stoff + ldbuf;
00535         } else {
00536             /*
00537              * The l_name member is not zero terminated, we must copy the
00538              * first SYMNMLEN chars and make sure we have a zero byte at the
00539              * end.
00540              */
00541 
00542             strncpy(tmpsym, ls->l_name, SYMNMLEN);
00543             tmpsym[SYMNMLEN] = '\0';
00544             symname = tmpsym;
00545         }
00546         ep->name = malloc((unsigned) (strlen(symname) + 1));
00547         strcpy(ep->name, symname);
00548         ep->addr = (void *)((unsigned long)
00549                 mp->entry + ls->l_value - shdata.s_vaddr);
00550         ep++;
00551     }
00552     free(ldbuf);
00553     while (ldclose(ldp) == FAILURE) {
00554         /* Empty body */
00555     }
00556     return 0;
00557 
00558     /*
00559      * This is a factoring out of the error-handling code to make the rest of
00560      * the function much simpler to read.
00561      */
00562 
00563   error:
00564     errvalid++;
00565     strcpy(errbuf, "readExports: ");
00566     strcat(errbuf, errMsg);
00567 
00568     if (ldp != NULL) {
00569         while (ldclose(ldp) == FAILURE) {
00570             /* Empty body */
00571         }
00572     }
00573     return -1;
00574 }
00575 
00576 /*
00577  * Find the main modules entry point. This is used as export pointer for
00578  * loadbind() to be able to resolve references to the main part.
00579  */
00580 
00581 static void *
00582 findMain(void)
00583 {
00584     struct ld_info *lp;
00585     char *buf;
00586     int size = 4*1024;
00587     int i;
00588     void *ret;
00589 
00590     buf = malloc(size);
00591     if (buf == NULL) {
00592         goto error;
00593     }
00594 
00595     while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
00596         free(buf);
00597         size += 4*1024;
00598         buf = malloc(size);
00599         if (buf == NULL) {
00600             goto error;
00601         }
00602     }
00603 
00604     if (i == -1) {
00605         free(buf);
00606         goto error;
00607     }
00608 
00609     /*
00610      * The first entry is the main module. The entry point returned by load()
00611      * does actually point to the data segment origin.
00612      */
00613 
00614     lp = (struct ld_info *) buf;
00615     ret = lp->ldinfo_dataorg;
00616     free(buf);
00617     return ret;
00618 
00619   error:
00620     errvalid++;
00621     strcpy(errbuf, "findMain: ");
00622     strcat(errbuf, strerror(errno));
00623     return NULL;
00624 }
00625 
00626 /*
00627  * Local Variables:
00628  * mode: c
00629  * c-basic-offset: 4
00630  * fill-column: 78
00631  * End:
00632  */



Generated on Wed Mar 12 12:18:25 2008 by  doxygen 1.5.1