home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / unix / tclLoadAix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  12.9 KB  |  550 lines  |  [TEXT/CWIE]

  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.  * SCCS: @(#) tclLoadAix.c 1.11 96/10/07 10:41:24
  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)) {
  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)));
  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.