1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* 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;