1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * dso.c -- DSO system function emulation for AIX
20 * This is *only* intended for AIX < 4.3.
24 * Based on libdl (dlfcn.c/dlfcn.h) which is
25 * Copyright (c) 1992,1993,1995,1996,1997,1988
26 * Jens-Uwe Mager, Helios Software GmbH, Hannover, Germany.
28 * Not derived from licensed software.
30 * Permission is granted to freely use, copy, modify, and redistribute
31 * this software, provided that the author is not construed to be liable
32 * for any results of using the software, alterations are clearly marked
33 * as such, and this notice is not modified.
35 * Changes marked with `--jwe' were made on April 7 1996 by
36 * John W. Eaton <jwe@bevo.che.wisc.edu> to support g++
38 * Bundled, stripped and adjusted on April 1998 as one single source file
39 * for inclusion into the Apache HTTP server by
40 * Ralf S. Engelschall <rse@apache.org>
42 * Added to APR by David Reid April 2000
49 #include <sys/types.h>
52 #include "apr_arch_dso.h"
53 #include "apr_portable.h"
62 * AIX 4.3 does remove some useful definitions from ldfcn.h. Define
63 * these here to compensate for that lossage.
66 #define BEGINNING SEEK_SET
69 #define FSEEK(ldptr,o,p) fseek(IOPTR(ldptr),(p==BEGINNING)?(OFFSET(ldptr) +o):o,p)
72 #define FREAD(p,s,n,ldptr) fread(p,s,n,IOPTR(ldptr))
76 * Mode flags for the dlopen routine.
79 #define RTLD_LAZY 1 /* lazy function call binding */
81 #define RTLD_NOW 2 /* immediate function call binding */
83 #define RTLD_GLOBAL 0x100 /* allow symbols to be global */
86 * To be able to initialize, a library may provide a dl_info structure
87 * that contains functions to be called to initialize and terminate.
96 * As the AIX functions have been declared in the header file we just
97 * add the basic "wrappers" here.
100 APR_DECLARE(apr_status_t) apr_os_dso_handle_put(apr_dso_handle_t **aprdso,
101 apr_os_dso_handle_t osdso,
104 *aprdso = apr_pcalloc(pool, sizeof **aprdso);
105 (*aprdso)->handle = osdso;
106 (*aprdso)->pool = pool;
110 APR_DECLARE(apr_status_t) apr_os_dso_handle_get(apr_os_dso_handle_t *osdso,
111 apr_dso_handle_t *aprdso)
113 *osdso = aprdso->handle;
117 static apr_status_t dso_cleanup(void *thedso)
119 apr_dso_handle_t *dso = thedso;
121 if (dso->handle != NULL && dlclose(dso->handle) != 0)
128 APR_DECLARE(apr_status_t) apr_dso_load(apr_dso_handle_t **res_handle,
129 const char *path, apr_pool_t *ctx)
131 void *os_handle = dlopen((char *)path, RTLD_NOW | RTLD_GLOBAL);
133 *res_handle = apr_pcalloc(ctx, sizeof(*res_handle));
135 if(os_handle == NULL) {
136 (*res_handle)->errormsg = dlerror();
140 (*res_handle)->handle = (void*)os_handle;
141 (*res_handle)->pool = ctx;
142 (*res_handle)->errormsg = NULL;
144 apr_pool_cleanup_register(ctx, *res_handle, dso_cleanup, apr_pool_cleanup_null);
149 APR_DECLARE(apr_status_t) apr_dso_unload(apr_dso_handle_t *handle)
151 return apr_pool_cleanup_run(handle->pool, handle, dso_cleanup);
154 APR_DECLARE(apr_status_t) apr_dso_sym(apr_dso_handle_sym_t *ressym,
155 apr_dso_handle_t *handle,
158 void *retval = dlsym(handle->handle, symname);
160 if (retval == NULL) {
161 handle->errormsg = dlerror();
162 return APR_ESYMNOTFOUND;
169 APR_DECLARE(const char *) apr_dso_error(apr_dso_handle_t *dso, char *buffer, apr_size_t buflen)
172 apr_cpystrn(buffer, dso->errormsg, buflen);
173 return dso->errormsg;
181 * We simulate dlopen() et al. through a call to load. Because AIX has
182 * no call to find an exported symbol we read the loader section of the
183 * loaded module and build a list of exported symbols and their virtual
188 char *name; /* the symbols's name */
189 void *addr; /* its relocated virtual address */
190 } Export, *ExportPtr;
193 * xlC uses the following structure to list its constructors and
194 * destructors. This is gleaned from the output of munch.
197 void (*init) (void); /* call static constructors */
198 void (*term) (void); /* call static destructors */
201 typedef void (*GccCDtorPtr) (void);
204 * The void * handle returned from dlopen is actually a ModulePtr.
206 typedef struct Module {
208 char *name; /* module name for refcounting */
209 int refCnt; /* the number of references */
210 void *entry; /* entry point from load */
211 struct dl_info *info; /* optional init/terminate functions */
212 CdtorPtr cdtors; /* optional C++ constructors */
213 GccCDtorPtr gcc_ctor; /* g++ constructors --jwe */
214 GccCDtorPtr gcc_dtor; /* g++ destructors --jwe */
215 int nExports; /* the number of exports found */
216 ExportPtr exports; /* the array of exports */
217 } Module, *ModulePtr;
220 * We keep a list of all loaded modules to be able to call the fini
221 * handlers and destructors at atexit() time.
223 static ModulePtr modList;
226 * The last error from one of the dl* routines is kept in static
227 * variables here. Each error is returned only once to the caller.
229 static char errbuf[BUFSIZ];
233 * The `fixed' gcc header files on AIX 3.2.5 provide a prototype for
236 extern char *strdup(const char *);
237 static void caterr(char *);
238 static int readExports(ModulePtr);
239 static void terminate(void);
240 static void *findMain(void);
242 void *dlopen(const char *path, int mode)
244 register ModulePtr mp;
245 static void *mainModule;
248 * Upon the first call register a terminate handler that will
249 * close all libraries. Also get a reference to the main module
250 * for use with loadbind.
253 if ((mainModule = findMain()) == NULL)
258 * Scan the list of modules if we have the module already loaded.
260 for (mp = modList; mp; mp = mp->next)
261 if (strcmp(mp->name, path) == 0) {
265 if ((mp = (ModulePtr) calloc(1, sizeof(*mp))) == NULL) {
267 strcpy(errbuf, "calloc: ");
268 strcat(errbuf, strerror(errno));
271 if ((mp->name = strdup(path)) == NULL) {
273 strcpy(errbuf, "strdup: ");
274 strcat(errbuf, strerror(errno));
279 * load should be declared load(const char *...). Thus we
280 * cast the path to a normal char *. Ugly.
282 if ((mp->entry = (void *) loadAndInit((char *) path, L_NOAUTODEFER, NULL)) == NULL) {
286 strcpy(errbuf, "dlopen: ");
287 strcat(errbuf, path);
288 strcat(errbuf, ": ");
290 * If AIX says the file is not executable, the error
291 * can be further described by querying the loader about
294 if (errno == ENOEXEC) {
295 char *tmp[BUFSIZ / sizeof(char *)];
296 if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
297 strcpy(errbuf, strerror(errno));
300 for (p = tmp; *p; p++)
305 strcat(errbuf, strerror(errno));
311 if (loadbind(0, mainModule, mp->entry) == -1) {
314 strcpy(errbuf, "loadbind: ");
315 strcat(errbuf, strerror(errno));
319 * If the user wants global binding, loadbind against all other
322 if (mode & RTLD_GLOBAL) {
323 register ModulePtr mp1;
324 for (mp1 = mp->next; mp1; mp1 = mp1->next)
325 if (loadbind(0, mp1->entry, mp->entry) == -1) {
328 strcpy(errbuf, "loadbind: ");
329 strcat(errbuf, strerror(errno));
333 if (readExports(mp) == -1) {
338 * If there is a dl_info structure, call the init function.
340 if (mp->info = (struct dl_info *) dlsym(mp, "dl_info")) {
342 (*mp->info->init) ();
347 * If the shared object was compiled using xlC we will need
348 * to call static constructors (and later on dlclose destructors).
350 if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) {
351 CdtorPtr cp = mp->cdtors;
352 while (cp->init || cp->term) {
353 if (cp->init && cp->init != (void (*)(void)) 0xffffffff)
358 * If the shared object was compiled using g++, we will need
359 * to call global constructors using the _GLOBAL__DI function,
360 * and later, global destructors using the _GLOBAL_DD
364 else if (mp->gcc_ctor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DI")) {
366 mp->gcc_dtor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DD");
374 * Attempt to decipher an AIX loader error message and append it
375 * to our static error message buffer.
377 static void caterr(char *s)
379 register char *p = s;
381 while (*p >= '0' && *p <= '9')
384 case L_ERROR_TOOMANY:
385 strcat(errbuf, "to many errors");
388 strcat(errbuf, "can't load library");
392 strcat(errbuf, "can't find symbol");
396 strcat(errbuf, "bad RLD");
400 strcat(errbuf, "bad exec format in");
404 strcat(errbuf, strerror(atoi(++p)));
412 void *dlsym(void *handle, const char *symbol)
414 register ModulePtr mp = (ModulePtr) handle;
415 register ExportPtr ep;
419 * Could speed up the search, but I assume that one assigns
420 * the result to function pointers anyways.
422 for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
423 if (strcmp(ep->name, symbol) == 0)
426 strcpy(errbuf, "dlsym: undefined symbol ");
427 strcat(errbuf, symbol);
431 const char *dlerror(void)
440 int dlclose(void *handle)
442 register ModulePtr mp = (ModulePtr) handle;
444 register ModulePtr mp1;
446 if (--mp->refCnt > 0)
448 if (mp->info && mp->info->fini)
449 (*mp->info->fini) ();
451 CdtorPtr cp = mp->cdtors;
452 while (cp->init || cp->term) {
453 if (cp->term && cp->init != (void (*)(void)) 0xffffffff)
458 * If the function to handle global destructors for g++
459 * exists, call it. --jwe
462 else if (mp->gcc_dtor) {
465 result = unload(mp->entry);
468 strcpy(errbuf, strerror(errno));
471 register ExportPtr ep;
473 for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
481 for (mp1 = modList; mp1; mp1 = mp1->next)
482 if (mp1->next == mp) {
483 mp1->next = mp->next;
492 static void terminate(void)
499 * Build the export table from the XCOFF .loader section.
501 static int readExports(ModulePtr mp)
516 * The module might be loaded due to the LIBPATH
517 * environment variable. Search for the loaded
518 * module using L_GETINFO.
520 if ((buf = malloc(size)) == NULL) {
522 strcpy(errbuf, "readExports: ");
523 strcat(errbuf, strerror(errno));
526 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
529 if ((buf = malloc(size)) == NULL) {
531 strcpy(errbuf, "readExports: ");
532 strcat(errbuf, strerror(errno));
538 strcpy(errbuf, "readExports: ");
539 strcat(errbuf, strerror(errno));
544 * Traverse the list of loaded modules. The entry point
545 * returned by load() does actually point to the TOC
546 * entry contained in the data segment.
548 lp = (struct ld_info *) buf;
550 if ((unsigned long) mp->entry >= (unsigned long) lp->ldinfo_dataorg &&
551 (unsigned long) mp->entry < (unsigned long) lp->ldinfo_dataorg +
552 lp->ldinfo_datasize) {
553 dataorg = lp->ldinfo_dataorg;
554 ldp = ldopen(lp->ldinfo_filename, ldp);
557 if (lp->ldinfo_next == 0)
560 lp = (struct ld_info *) ((char *) lp + lp->ldinfo_next);
565 strcpy(errbuf, "readExports: ");
566 strcat(errbuf, strerror(errno));
569 if (TYPE(ldp) != U802TOCMAGIC) {
571 strcpy(errbuf, "readExports: bad magic");
572 while (ldclose(ldp) == FAILURE);
576 * Get the padding for the data section. This is needed for
577 * AIX 4.1 compilers. This is used when building the final
578 * function pointer to the exported symbol.
580 if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
582 strcpy(errbuf, "readExports: cannot read data section header");
583 while (ldclose(ldp) == FAILURE);
586 if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
588 strcpy(errbuf, "readExports: cannot read loader section header");
589 while (ldclose(ldp) == FAILURE);
593 * We read the complete loader section in one chunk, this makes
594 * finding long symbol names residing in the string table easier.
596 if ((ldbuf = (char *) malloc(sh.s_size)) == NULL) {
598 strcpy(errbuf, "readExports: ");
599 strcat(errbuf, strerror(errno));
600 while (ldclose(ldp) == FAILURE);
603 if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
605 strcpy(errbuf, "readExports: cannot seek to loader section");
607 while (ldclose(ldp) == FAILURE);
610 if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
612 strcpy(errbuf, "readExports: cannot read loader section");
614 while (ldclose(ldp) == FAILURE);
617 lhp = (LDHDR *) ldbuf;
618 ls = (LDSYM *) (ldbuf + LDHDRSZ);
620 * Count the number of exports to include in our export table.
622 for (i = lhp->l_nsyms; i; i--, ls++) {
623 if (!LDR_EXPORT(*ls))
627 if ((mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
629 strcpy(errbuf, "readExports: ");
630 strcat(errbuf, strerror(errno));
632 while (ldclose(ldp) == FAILURE);
636 * Fill in the export table. All entries are relative to
637 * the beginning of the data origin.
640 ls = (LDSYM *) (ldbuf + LDHDRSZ);
641 for (i = lhp->l_nsyms; i; i--, ls++) {
643 char tmpsym[SYMNMLEN + 1];
644 if (!LDR_EXPORT(*ls))
646 if (ls->l_zeroes == 0)
647 symname = ls->l_offset + lhp->l_stoff + ldbuf;
650 * The l_name member is not zero terminated, we
651 * must copy the first SYMNMLEN chars and make
652 * sure we have a zero byte at the end.
654 strncpy(tmpsym, ls->l_name, SYMNMLEN);
655 tmpsym[SYMNMLEN] = '\0';
658 ep->name = strdup(symname);
659 ep->addr = (void *) ((unsigned long) dataorg +
660 ls->l_value - shdata.s_vaddr);
664 while (ldclose(ldp) == FAILURE);
669 * Find the main modules data origin. This is used as export pointer
670 * for loadbind() to be able to resolve references to the main part.
672 static void *findMain(void)
680 if ((buf = malloc(size)) == NULL) {
682 strcpy(errbuf, "findMain: ");
683 strcat(errbuf, strerror(errno));
686 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
689 if ((buf = malloc(size)) == NULL) {
691 strcpy(errbuf, "findMain: ");
692 strcat(errbuf, strerror(errno));
698 strcpy(errbuf, "findMain: ");
699 strcat(errbuf, strerror(errno));
704 * The first entry is the main module. The data segment
705 * starts with the TOC entries for all exports, so the
706 * data segment origin works as argument for loadbind.
708 lp = (struct ld_info *) buf;
709 ret = lp->ldinfo_dataorg;