text: /* Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /*
   *  dso.c -- DSO system function emulation for AIX
   *
   *  This is *only* intended for AIX < 4.3.
   */
  
  /*
   *  Based on libdl (dlfcn.c/dlfcn.h) which is
   *  Copyright (c) 1992,1993,1995,1996,1997,1988
   *  Jens-Uwe Mager, Helios Software GmbH, Hannover, Germany.
   *
   *  Not derived from licensed software.
   *
   *  Permission is granted to freely use, copy, modify, and redistribute
   *  this software, provided that the author is not construed to be liable
   *  for any results of using the software, alterations are clearly marked
   *  as such, and this notice is not modified.
   *
   *  Changes marked with `--jwe' were made on April 7 1996 by
   *  John W. Eaton <jwe@bevo.che.wisc.edu> to support g++ 
   *
   *  Bundled, stripped and adjusted on April 1998 as one single source file 
   *  for inclusion into the Apache HTTP server by 
   *  Ralf S. Engelschall <rse@apache.org>
   *
   *  Added to APR by David Reid April 2000
   */
  
  #include <stdio.h>
  #include <errno.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/ldr.h>
  #include <a.out.h>
  #include "apr_arch_dso.h"
  #include "apr_portable.h"
  
  #if APR_HAS_DSO
  
  #undef FREAD
  #undef FWRITE
  #include <ldfcn.h>
  
  /*
   * AIX 4.3 does remove some useful definitions from ldfcn.h. Define
   * these here to compensate for that lossage.
   */
  #ifndef BEGINNING
  #define BEGINNING SEEK_SET
  #endif
  #ifndef FSEEK
  #define FSEEK(ldptr,o,p)   fseek(IOPTR(ldptr),(p==BEGINNING)?(OFFSET(ldptr) +o):o,p)
  #endif
  #ifndef FREAD
  #define FREAD(p,s,n,ldptr) fread(p,s,n,IOPTR(ldptr))
  #endif
  
  /*
   * Mode flags for the dlopen routine.
   */
  #undef  RTLD_LAZY
  #define RTLD_LAZY    1    /* lazy function call binding */
  #undef  RTLD_NOW
  #define RTLD_NOW    2    /* immediate function call binding */
  #undef  RTLD_GLOBAL
  #define RTLD_GLOBAL    0x100    /* allow symbols to be global */
  
  /*
   * To be able to initialize, a library may provide a dl_info structure
   * that contains functions to be called to initialize and terminate.
   */
  struct dl_info {
      void (*init) (void);
      void (*fini) (void);
  };
  
  /* APR functions...
   *
   * As the AIX functions have been declared in the header file we just
   * add the basic "wrappers" here.
   */
  
  APR_DECLARE(apr_status_t) apr_os_dso_handle_put(apr_dso_handle_t **aprdso,
                                                  apr_os_dso_handle_t osdso,
                                                  apr_pool_t *pool)
  {
      *aprdso = apr_pcalloc(pool, sizeof **aprdso);
      (*aprdso)->handle = osdso;
      (*aprdso)->pool = pool;
      return APR_SUCCESS;
  }
  
  APR_DECLARE(apr_status_t) apr_os_dso_handle_get(apr_os_dso_handle_t *osdso,
                                                  apr_dso_handle_t *aprdso)
  {
      *osdso = aprdso->handle;
      return APR_SUCCESS;
  }
  
  static apr_status_t dso_cleanup(void *thedso)
  {
      apr_dso_handle_t *dso = thedso;
  
      if (dso->handle != NULL && dlclose(dso->handle) != 0)
          return APR_EINIT;
      dso->handle = NULL;
  
      return APR_SUCCESS;
  }
  
  APR_DECLARE(apr_status_t) apr_dso_load(apr_dso_handle_t **res_handle, 
                                         const char *path, apr_pool_t *ctx)
  {
      void *os_handle = dlopen((char *)path, RTLD_NOW | RTLD_GLOBAL);
  
      *res_handle = apr_pcalloc(ctx, sizeof(*res_handle));
  
      if(os_handle == NULL) {
          (*res_handle)->errormsg = dlerror();       
          return APR_EDSOOPEN;
      }
  
      (*res_handle)->handle = (void*)os_handle;
      (*res_handle)->pool = ctx;
      (*res_handle)->errormsg = NULL;
  
      apr_pool_cleanup_register(ctx, *res_handle, dso_cleanup, apr_pool_cleanup_null);
  
      return APR_SUCCESS;
  }
  
  APR_DECLARE(apr_status_t) apr_dso_unload(apr_dso_handle_t *handle)
  {
      return apr_pool_cleanup_run(handle->pool, handle, dso_cleanup);
  }
  
  APR_DECLARE(apr_status_t) apr_dso_sym(apr_dso_handle_sym_t *ressym, 
                                        apr_dso_handle_t *handle, 
                                        const char *symname)
  {
      void *retval = dlsym(handle->handle, symname);
  
      if (retval == NULL) {
          handle->errormsg = dlerror();
          return APR_ESYMNOTFOUND;
      }
  
      *ressym = retval;
      return APR_SUCCESS;
  }
  
  APR_DECLARE(const char *) apr_dso_error(apr_dso_handle_t *dso, char *buffer, apr_size_t buflen)
  {
      if (dso->errormsg) {
          apr_cpystrn(buffer, dso->errormsg, buflen);
          return dso->errormsg;
      }
      return "No Error";
  }
  
  
  
  /*
   * We simulate dlopen() et al. through a call to load. Because AIX has
   * no call to find an exported symbol we read the loader section of the
   * loaded module and build a list of exported symbols and their virtual
   * address.
   */
  
  typedef struct {
      char *name;            /* the symbols's name */
      void *addr;            /* its relocated virtual address */
  } Export, *ExportPtr;
  
  /*
   * xlC uses the following structure to list its constructors and
   * destructors. This is gleaned from the output of munch.
   */
  typedef struct {
      void (*init) (void);    /* call static constructors */
      void (*term) (void);    /* call static destructors */
  } Cdtor, *CdtorPtr;
  
  typedef void (*GccCDtorPtr) (void);
  
  /*
   * The void * handle returned from dlopen is actually a ModulePtr.
   */
  typedef struct Module {
      struct Module *next;
      char *name;            /* module name for refcounting */
      int refCnt;            /* the number of references */
      void *entry;        /* entry point from load */
      struct dl_info *info;    /* optional init/terminate functions */
      CdtorPtr cdtors;        /* optional C++ constructors */
      GccCDtorPtr gcc_ctor;    /* g++ constructors  --jwe */
      GccCDtorPtr gcc_dtor;    /* g++ destructors  --jwe */
      int nExports;        /* the number of exports found */
      ExportPtr exports;        /* the array of exports */
  } Module, *ModulePtr;
  
  /*
   * We keep a list of all loaded modules to be able to call the fini
   * handlers and destructors at atexit() time.
   */
  static ModulePtr modList;
  
  /*
   * The last error from one of the dl* routines is kept in static
   * variables here. Each error is returned only once to the caller.
   */
  static char errbuf[BUFSIZ];
  static int errvalid;
  
  /*
   * The `fixed' gcc header files on AIX 3.2.5 provide a prototype for
   * strdup().  --jwe
   */
  extern char *strdup(const char *);
  static void caterr(char *);
  static int readExports(ModulePtr);
  static void terminate(void);
  static void *findMain(void);
  
  void *dlopen(const char *path, int mode)
  {
      register ModulePtr mp;
      static void *mainModule;
  
      /*
       * Upon the first call register a terminate handler that will
       * close all libraries. Also get a reference to the main module
       * for use with loadbind.
       */
      if (!mainModule) {
      if ((mainModule = findMain()) == NULL)
          return NULL;
      atexit(terminate);
      }
      /*
       * Scan the list of modules if we have the module already loaded.
       */
      for (mp = modList; mp; mp = mp->next)
      if (strcmp(mp->name, path) == 0) {
          mp->refCnt++;
          return mp;
      }
      if ((mp = (ModulePtr) calloc(1, sizeof(*mp))) == NULL) {
      errvalid++;
      strcpy(errbuf, "calloc: ");
      strcat(errbuf, strerror(errno));
      return NULL;
      }
      if ((mp->name = strdup(path)) == NULL) {
      errvalid++;
      strcpy(errbuf, "strdup: ");
      strcat(errbuf, strerror(errno));
      free(mp);
      return NULL;
      }
      /*
       * load should be declared load(const char *...). Thus we
       * cast the path to a normal char *. Ugly.
       */
      if ((mp->entry = (void *) loadAndInit((char *) path, L_NOAUTODEFER, NULL)) == NULL) {
      free(mp->name);
      free(mp);
      errvalid++;
      strcpy(errbuf, "dlopen: ");
      strcat(errbuf, path);
      strcat(errbuf, ": ");
      /*
       * If AIX says the file is not executable, the error
       * can be further described by querying the loader about
       * the last error.
       */
      if (errno == ENOEXEC) {
          char *tmp[BUFSIZ / sizeof(char *)];
          if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
          strcpy(errbuf, strerror(errno));
          else {
          char **p;
          for (p = tmp; *p; p++)
              caterr(*p);
          }
      }
      else
          strcat(errbuf, strerror(errno));
      return NULL;
      }
      mp->refCnt = 1;
      mp->next = modList;
      modList = mp;
      if (loadbind(0, mainModule, mp->entry) == -1) {
      dlclose(mp);
      errvalid++;
      strcpy(errbuf, "loadbind: ");
      strcat(errbuf, strerror(errno));
      return NULL;
      }
      /*
       * If the user wants global binding, loadbind against all other
       * loaded modules.
       */
      if (mode & RTLD_GLOBAL) {
      register ModulePtr mp1;
      for (mp1 = mp->next; mp1; mp1 = mp1->next)
          if (loadbind(0, mp1->entry, mp->entry) == -1) {
          dlclose(mp);
          errvalid++;
          strcpy(errbuf, "loadbind: ");
          strcat(errbuf, strerror(errno));
          return NULL;
          }
      }
      if (readExports(mp) == -1) {
      dlclose(mp);
      return NULL;
      }
      /*
       * If there is a dl_info structure, call the init function.
       */
      if (mp->info = (struct dl_info *) dlsym(mp, "dl_info")) {
      if (mp->info->init)
          (*mp->info->init) ();
      }
      else
      errvalid = 0;
      /*
       * If the shared object was compiled using xlC we will need
       * to call static constructors (and later on dlclose destructors).
       */
      if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) {
      CdtorPtr cp = mp->cdtors;
      while (cp->init || cp->term) {
          if (cp->init && cp->init != (void (*)(void)) 0xffffffff)
          (*cp->init) ();
          cp++;
      }
      /*
       * If the shared object was compiled using g++, we will need
       * to call global constructors using the _GLOBAL__DI function,
       * and later, global destructors using the _GLOBAL_DD
       * funciton.  --jwe
       */
      }
      else if (mp->gcc_ctor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DI")) {
      (*mp->gcc_ctor) ();
      mp->gcc_dtor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DD");
      }
      else
      errvalid = 0;
      return mp;
  }
  
  /*
   * Attempt to decipher an AIX loader error message and append it
   * to our static error message buffer.
   */
  static void caterr(char *s)
  {
      register char *p = s;
  
      while (*p >= '0' && *p <= '9')
      p++;
      switch (atoi(s)) {
      case L_ERROR_TOOMANY:
      strcat(errbuf, "to many errors");
      break;
      case L_ERROR_NOLIB:
      strcat(errbuf, "can't load library");
      strcat(errbuf, p);
      break;
      case L_ERROR_UNDEF:
      strcat(errbuf, "can't find symbol");
      strcat(errbuf, p);
      break;
      case L_ERROR_RLDBAD:
      strcat(errbuf, "bad RLD");
      strcat(errbuf, p);
      break;
      case L_ERROR_FORMAT:
      strcat(errbuf, "bad exec format in");
      strcat(errbuf, p);
      break;
      case L_ERROR_ERRNO:
      strcat(errbuf, strerror(atoi(++p)));
      break;
      default:
      strcat(errbuf, s);
      break;
      }
  }
  
  void *dlsym(void *handle, const char *symbol)
  {
      register ModulePtr mp = (ModulePtr) handle;
      register ExportPtr ep;
      register int i;
  
      /*
       * Could speed up the search, but I assume that one assigns
       * the result to function pointers anyways.
       */
      for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
      if (strcmp(ep->name, symbol) == 0)
          return ep->addr;
      errvalid++;
      strcpy(errbuf, "dlsym: undefined symbol ");
      strcat(errbuf, symbol);
      return NULL;
  }
  
  const char *dlerror(void)
  {
      if (errvalid) {
      errvalid = 0;
      return errbuf;
      }
      return NULL;
  }
  
  int dlclose(void *handle)
  {
      register ModulePtr mp = (ModulePtr) handle;
      int result;
      register ModulePtr mp1;
  
      if (--mp->refCnt > 0)
      return 0;
      if (mp->info && mp->info->fini)
      (*mp->info->fini) ();
      if (mp->cdtors) {
      CdtorPtr cp = mp->cdtors;
      while (cp->init || cp->term) {
          if (cp->term && cp->init != (void (*)(void)) 0xffffffff)
          (*cp->term) ();
          cp++;
      }
      /*
       * If the function to handle global destructors for g++
       * exists, call it.  --jwe
       */
      }
      else if (mp->gcc_dtor) {
      (*mp->gcc_dtor) ();
      }
      result = unload(mp->entry);
      if (result == -1) {
      errvalid++;
      strcpy(errbuf, strerror(errno));
      }
      if (mp->exports) {
      register ExportPtr ep;
      register int i;
      for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
          if (ep->name)
          free(ep->name);
      free(mp->exports);
      }
      if (mp == modList)
      modList = mp->next;
      else {
      for (mp1 = modList; mp1; mp1 = mp1->next)
          if (mp1->next == mp) {
          mp1->next = mp->next;
          break;
          }
      }
      free(mp->name);
      free(mp);
      return result;
  }
  
  static void terminate(void)
  {
      while (modList)
      dlclose(modList);
  }
  
  /*
   * Build the export table from the XCOFF .loader section.
   */
  static int readExports(ModulePtr mp)
  {
      LDFILE *ldp = NULL;
      SCNHDR sh, shdata;
      LDHDR *lhp;
      char *ldbuf;
      LDSYM *ls;
      int i;
      ExportPtr ep;
      struct ld_info *lp;
      char *buf;
      int size = 4 * 1024;
      void *dataorg;
  
      /*
       * The module might be loaded due to the LIBPATH
       * environment variable. Search for the loaded
       * module using L_GETINFO.
       */
      if ((buf = malloc(size)) == NULL) {
      errvalid++;
      strcpy(errbuf, "readExports: ");
      strcat(errbuf, strerror(errno));
      return -1;
      }
      while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
      free(buf);
      size += 4 * 1024;
      if ((buf = malloc(size)) == NULL) {
          errvalid++;
          strcpy(errbuf, "readExports: ");
          strcat(errbuf, strerror(errno));
          return -1;
      }
      }
      if (i == -1) {
      errvalid++;
      strcpy(errbuf, "readExports: ");
      strcat(errbuf, strerror(errno));
      free(buf);
      return -1;
      }
      /*
       * Traverse the list of loaded modules. The entry point
       * returned by load() does actually point to the TOC
       * entry contained in the data segment.
       */
      lp = (struct ld_info *) buf;
      while (lp) {
      if ((unsigned long) mp->entry >= (unsigned long) lp->ldinfo_dataorg &&
          (unsigned long) mp->entry < (unsigned long) lp->ldinfo_dataorg +
          lp->ldinfo_datasize) {
          dataorg = lp->ldinfo_dataorg;
          ldp = ldopen(lp->ldinfo_filename, ldp);
          break;
      }
      if (lp->ldinfo_next == 0)
          lp = NULL;
      else
          lp = (struct ld_info *) ((char *) lp + lp->ldinfo_next);
      }
      free(buf);
      if (!ldp) {
      errvalid++;
      strcpy(errbuf, "readExports: ");
      strcat(errbuf, strerror(errno));
      return -1;
      }
      if (TYPE(ldp) != U802TOCMAGIC) {
      errvalid++;
      strcpy(errbuf, "readExports: bad magic");
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      /*
       * Get the padding for the data section. This is needed for
       * AIX 4.1 compilers. This is used when building the final
       * function pointer to the exported symbol.
       */
      if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
      errvalid++;
      strcpy(errbuf, "readExports: cannot read data section header");
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
      errvalid++;
      strcpy(errbuf, "readExports: cannot read loader section header");
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      /*
       * We read the complete loader section in one chunk, this makes
       * finding long symbol names residing in the string table easier.
       */
      if ((ldbuf = (char *) malloc(sh.s_size)) == NULL) {
      errvalid++;
      strcpy(errbuf, "readExports: ");
      strcat(errbuf, strerror(errno));
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
      errvalid++;
      strcpy(errbuf, "readExports: cannot seek to loader section");
      free(ldbuf);
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
      errvalid++;
      strcpy(errbuf, "readExports: cannot read loader section");
      free(ldbuf);
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      lhp = (LDHDR *) ldbuf;
      ls = (LDSYM *) (ldbuf + LDHDRSZ);
      /*
       * Count the number of exports to include in our export table.
       */
      for (i = lhp->l_nsyms; i; i--, ls++) {
      if (!LDR_EXPORT(*ls))
          continue;
      mp->nExports++;
      }
      if ((mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
      errvalid++;
      strcpy(errbuf, "readExports: ");
      strcat(errbuf, strerror(errno));
      free(ldbuf);
      while (ldclose(ldp) == FAILURE);
      return -1;
      }
      /*
       * Fill in the export table. All entries are relative to
       * the beginning of the data origin.
       */
      ep = mp->exports;
      ls = (LDSYM *) (ldbuf + LDHDRSZ);
      for (i = lhp->l_nsyms; i; i--, ls++) {
      char *symname;
      char tmpsym[SYMNMLEN + 1];
      if (!LDR_EXPORT(*ls))
          continue;
      if (ls->l_zeroes == 0)
          symname = ls->l_offset + lhp->l_stoff + ldbuf;
      else {
          /*
           * The l_name member is not zero terminated, we
           * must copy the first SYMNMLEN chars and make
           * sure we have a zero byte at the end.
           */
          strncpy(tmpsym, ls->l_name, SYMNMLEN);
          tmpsym[SYMNMLEN] = '\0';
          symname = tmpsym;
      }
      ep->name = strdup(symname);
      ep->addr = (void *) ((unsigned long) dataorg +
                   ls->l_value - shdata.s_vaddr);
      ep++;
      }
      free(ldbuf);
      while (ldclose(ldp) == FAILURE);
      return 0;
  }
  
  /*
   * Find the main modules data origin. This is used as export pointer
   * for loadbind() to be able to resolve references to the main part.
   */
  static void *findMain(void)
  {
      struct ld_info *lp;
      char *buf;
      int size = 4 * 1024;
      int i;
      void *ret;
  
      if ((buf = malloc(size)) == NULL) {
      errvalid++;
      strcpy(errbuf, "findMain: ");
      strcat(errbuf, strerror(errno));
      return NULL;
      }
      while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
      free(buf);
      size += 4 * 1024;
      if ((buf = malloc(size)) == NULL) {
          errvalid++;
          strcpy(errbuf, "findMain: ");
          strcat(errbuf, strerror(errno));
          return NULL;
      }
      }
      if (i == -1) {
      errvalid++;
      strcpy(errbuf, "findMain: ");
      strcat(errbuf, strerror(errno));
      free(buf);
      return NULL;
      }
      /*
       * The first entry is the main module. The data segment
       * starts with the TOC entries for all exports, so the
       * data segment origin works as argument for loadbind.
       */
      lp = (struct ld_info *) buf;
      ret = lp->ldinfo_dataorg;
      free(buf);
      return ret;
  }
  
  #endif
  