Apache HTTPD
dso.c
Go to the documentation of this file.
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * dso.c -- DSO system function emulation for AIX
19 *
20 * This is *only* intended for AIX < 4.3.
21 */
22
23/*
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.
27 *
28 * Not derived from licensed software.
29 *
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.
34 *
35 * Changes marked with `--jwe' were made on April 7 1996 by
36 * John W. Eaton <[email protected]> to support g++
37 *
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 <[email protected]>
41 *
42 * Added to APR by David Reid April 2000
43 */
44
45#include <stdio.h>
46#include <errno.h>
47#include <string.h>
48#include <stdlib.h>
49#include <sys/types.h>
50#include <sys/ldr.h>
51#include <a.out.h>
52#include "apr_arch_dso.h"
53#include "apr_portable.h"
54
55#if APR_HAS_DSO
56
57#undef FREAD
58#undef FWRITE
59#include <ldfcn.h>
60
61/*
62 * AIX 4.3 does remove some useful definitions from ldfcn.h. Define
63 * these here to compensate for that lossage.
64 */
65#ifndef BEGINNING
66#define BEGINNING SEEK_SET
67#endif
68#ifndef FSEEK
69#define FSEEK(ldptr,o,p) fseek(IOPTR(ldptr),(p==BEGINNING)?(OFFSET(ldptr) +o):o,p)
70#endif
71#ifndef FREAD
72#define FREAD(p,s,n,ldptr) fread(p,s,n,IOPTR(ldptr))
73#endif
74
75/*
76 * Mode flags for the dlopen routine.
77 */
78#undef RTLD_LAZY
79#define RTLD_LAZY 1 /* lazy function call binding */
80#undef RTLD_NOW
81#define RTLD_NOW 2 /* immediate function call binding */
82#undef RTLD_GLOBAL
83#define RTLD_GLOBAL 0x100 /* allow symbols to be global */
84
85/*
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.
88 */
89struct dl_info {
90 void (*init) (void);
91 void (*fini) (void);
92};
93
94/* APR functions...
95 *
96 * As the AIX functions have been declared in the header file we just
97 * add the basic "wrappers" here.
98 */
99
103{
104 *aprdso = apr_pcalloc(pool, sizeof **aprdso);
105 (*aprdso)->handle = osdso;
106 (*aprdso)->pool = pool;
107 return APR_SUCCESS;
108}
109
112{
113 *osdso = aprdso->handle;
114 return APR_SUCCESS;
115}
116
117static apr_status_t dso_cleanup(void *thedso)
118{
120
121 if (dso->handle != NULL && dlclose(dso->handle) != 0)
122 return APR_EINIT;
123 dso->handle = NULL;
124
125 return APR_SUCCESS;
126}
127
129 const char *path, apr_pool_t *ctx)
130{
131 void *os_handle = dlopen((char *)path, RTLD_NOW | RTLD_GLOBAL);
132
133 *res_handle = apr_pcalloc(ctx, sizeof(*res_handle));
134
135 if(os_handle == NULL) {
136 (*res_handle)->errormsg = dlerror();
137 return APR_EDSOOPEN;
138 }
139
140 (*res_handle)->handle = (void*)os_handle;
141 (*res_handle)->pool = ctx;
142 (*res_handle)->errormsg = NULL;
143
145
146 return APR_SUCCESS;
147}
148
150{
152}
153
156 const char *symname)
157{
158 void *retval = dlsym(handle->handle, symname);
159
160 if (retval == NULL) {
161 handle->errormsg = dlerror();
162 return APR_ESYMNOTFOUND;
163 }
164
165 *ressym = retval;
166 return APR_SUCCESS;
167}
168
170{
171 if (dso->errormsg) {
172 apr_cpystrn(buffer, dso->errormsg, buflen);
173 return dso->errormsg;
174 }
175 return "No Error";
176}
177
178
179
180/*
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
184 * address.
185 */
186
187typedef struct {
188 char *name; /* the symbols's name */
189 void *addr; /* its relocated virtual address */
190} Export, *ExportPtr;
191
192/*
193 * xlC uses the following structure to list its constructors and
194 * destructors. This is gleaned from the output of munch.
195 */
196typedef struct {
197 void (*init) (void); /* call static constructors */
198 void (*term) (void); /* call static destructors */
199} Cdtor, *CdtorPtr;
200
201typedef void (*GccCDtorPtr) (void);
202
203/*
204 * The void * handle returned from dlopen is actually a ModulePtr.
205 */
206typedef struct Module {
207 struct Module *next;
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;
218
219/*
220 * We keep a list of all loaded modules to be able to call the fini
221 * handlers and destructors at atexit() time.
222 */
223static ModulePtr modList;
224
225/*
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.
228 */
229static char errbuf[BUFSIZ];
230static int errvalid;
231
232/*
233 * The `fixed' gcc header files on AIX 3.2.5 provide a prototype for
234 * strdup(). --jwe
235 */
236extern char *strdup(const char *);
237static void caterr(char *);
238static int readExports(ModulePtr);
239static void terminate(void);
240static void *findMain(void);
241
242void *dlopen(const char *path, int mode)
243{
244 register ModulePtr mp;
245 static void *mainModule;
246
247 /*
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.
251 */
252 if (!mainModule) {
253 if ((mainModule = findMain()) == NULL)
254 return NULL;
256 }
257 /*
258 * Scan the list of modules if we have the module already loaded.
259 */
260 for (mp = modList; mp; mp = mp->next)
261 if (strcmp(mp->name, path) == 0) {
262 mp->refCnt++;
263 return mp;
264 }
265 if ((mp = (ModulePtr) calloc(1, sizeof(*mp))) == NULL) {
266 errvalid++;
267 strcpy(errbuf, "calloc: ");
269 return NULL;
270 }
271 if ((mp->name = strdup(path)) == NULL) {
272 errvalid++;
273 strcpy(errbuf, "strdup: ");
275 free(mp);
276 return NULL;
277 }
278 /*
279 * load should be declared load(const char *...). Thus we
280 * cast the path to a normal char *. Ugly.
281 */
282 if ((mp->entry = (void *) loadAndInit((char *) path, L_NOAUTODEFER, NULL)) == NULL) {
283 free(mp->name);
284 free(mp);
285 errvalid++;
286 strcpy(errbuf, "dlopen: ");
288 strcat(errbuf, ": ");
289 /*
290 * If AIX says the file is not executable, the error
291 * can be further described by querying the loader about
292 * the last error.
293 */
294 if (errno == ENOEXEC) {
295 char *tmp[BUFSIZ / sizeof(char *)];
296 if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
298 else {
299 char **p;
300 for (p = tmp; *p; p++)
301 caterr(*p);
302 }
303 }
304 else
306 return NULL;
307 }
308 mp->refCnt = 1;
309 mp->next = modList;
310 modList = mp;
311 if (loadbind(0, mainModule, mp->entry) == -1) {
312 dlclose(mp);
313 errvalid++;
314 strcpy(errbuf, "loadbind: ");
316 return NULL;
317 }
318 /*
319 * If the user wants global binding, loadbind against all other
320 * loaded modules.
321 */
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) {
326 dlclose(mp);
327 errvalid++;
328 strcpy(errbuf, "loadbind: ");
330 return NULL;
331 }
332 }
333 if (readExports(mp) == -1) {
334 dlclose(mp);
335 return NULL;
336 }
337 /*
338 * If there is a dl_info structure, call the init function.
339 */
340 if (mp->info = (struct dl_info *) dlsym(mp, "dl_info")) {
341 if (mp->info->init)
342 (*mp->info->init) ();
343 }
344 else
345 errvalid = 0;
346 /*
347 * If the shared object was compiled using xlC we will need
348 * to call static constructors (and later on dlclose destructors).
349 */
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)
354 (*cp->init) ();
355 cp++;
356 }
357 /*
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
361 * funciton. --jwe
362 */
363 }
364 else if (mp->gcc_ctor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DI")) {
365 (*mp->gcc_ctor) ();
366 mp->gcc_dtor = (GccCDtorPtr) dlsym(mp, "_GLOBAL__DD");
367 }
368 else
369 errvalid = 0;
370 return mp;
371}
372
373/*
374 * Attempt to decipher an AIX loader error message and append it
375 * to our static error message buffer.
376 */
377static void caterr(char *s)
378{
379 register char *p = s;
380
381 while (*p >= '0' && *p <= '9')
382 p++;
383 switch (atoi(s)) {
384 case L_ERROR_TOOMANY:
385 strcat(errbuf, "to many errors");
386 break;
387 case L_ERROR_NOLIB:
388 strcat(errbuf, "can't load library");
389 strcat(errbuf, p);
390 break;
391 case L_ERROR_UNDEF:
392 strcat(errbuf, "can't find symbol");
393 strcat(errbuf, p);
394 break;
395 case L_ERROR_RLDBAD:
396 strcat(errbuf, "bad RLD");
397 strcat(errbuf, p);
398 break;
399 case L_ERROR_FORMAT:
400 strcat(errbuf, "bad exec format in");
401 strcat(errbuf, p);
402 break;
403 case L_ERROR_ERRNO:
405 break;
406 default:
407 strcat(errbuf, s);
408 break;
409 }
410}
411
412void *dlsym(void *handle, const char *symbol)
413{
414 register ModulePtr mp = (ModulePtr) handle;
415 register ExportPtr ep;
416 register int i;
417
418 /*
419 * Could speed up the search, but I assume that one assigns
420 * the result to function pointers anyways.
421 */
422 for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
423 if (strcmp(ep->name, symbol) == 0)
424 return ep->addr;
425 errvalid++;
426 strcpy(errbuf, "dlsym: undefined symbol ");
427 strcat(errbuf, symbol);
428 return NULL;
429}
430
431const char *dlerror(void)
432{
433 if (errvalid) {
434 errvalid = 0;
435 return errbuf;
436 }
437 return NULL;
438}
439
440int dlclose(void *handle)
441{
442 register ModulePtr mp = (ModulePtr) handle;
443 int result;
444 register ModulePtr mp1;
445
446 if (--mp->refCnt > 0)
447 return 0;
448 if (mp->info && mp->info->fini)
449 (*mp->info->fini) ();
450 if (mp->cdtors) {
451 CdtorPtr cp = mp->cdtors;
452 while (cp->init || cp->term) {
453 if (cp->term && cp->init != (void (*)(void)) 0xffffffff)
454 (*cp->term) ();
455 cp++;
456 }
457 /*
458 * If the function to handle global destructors for g++
459 * exists, call it. --jwe
460 */
461 }
462 else if (mp->gcc_dtor) {
463 (*mp->gcc_dtor) ();
464 }
465 result = unload(mp->entry);
466 if (result == -1) {
467 errvalid++;
469 }
470 if (mp->exports) {
471 register ExportPtr ep;
472 register int i;
473 for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
474 if (ep->name)
475 free(ep->name);
476 free(mp->exports);
477 }
478 if (mp == modList)
479 modList = mp->next;
480 else {
481 for (mp1 = modList; mp1; mp1 = mp1->next)
482 if (mp1->next == mp) {
483 mp1->next = mp->next;
484 break;
485 }
486 }
487 free(mp->name);
488 free(mp);
489 return result;
490}
491
492static void terminate(void)
493{
494 while (modList)
496}
497
498/*
499 * Build the export table from the XCOFF .loader section.
500 */
501static int readExports(ModulePtr mp)
502{
503 LDFILE *ldp = NULL;
505 LDHDR *lhp;
506 char *ldbuf;
507 LDSYM *ls;
508 int i;
510 struct ld_info *lp;
511 char *buf;
512 int size = 4 * 1024;
513 void *dataorg;
514
515 /*
516 * The module might be loaded due to the LIBPATH
517 * environment variable. Search for the loaded
518 * module using L_GETINFO.
519 */
520 if ((buf = malloc(size)) == NULL) {
521 errvalid++;
522 strcpy(errbuf, "readExports: ");
524 return -1;
525 }
526 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
527 free(buf);
528 size += 4 * 1024;
529 if ((buf = malloc(size)) == NULL) {
530 errvalid++;
531 strcpy(errbuf, "readExports: ");
533 return -1;
534 }
535 }
536 if (i == -1) {
537 errvalid++;
538 strcpy(errbuf, "readExports: ");
540 free(buf);
541 return -1;
542 }
543 /*
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.
547 */
548 lp = (struct ld_info *) buf;
549 while (lp) {
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);
555 break;
556 }
557 if (lp->ldinfo_next == 0)
558 lp = NULL;
559 else
560 lp = (struct ld_info *) ((char *) lp + lp->ldinfo_next);
561 }
562 free(buf);
563 if (!ldp) {
564 errvalid++;
565 strcpy(errbuf, "readExports: ");
567 return -1;
568 }
569 if (TYPE(ldp) != U802TOCMAGIC) {
570 errvalid++;
571 strcpy(errbuf, "readExports: bad magic");
572 while (ldclose(ldp) == FAILURE);
573 return -1;
574 }
575 /*
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.
579 */
580 if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
581 errvalid++;
582 strcpy(errbuf, "readExports: cannot read data section header");
583 while (ldclose(ldp) == FAILURE);
584 return -1;
585 }
586 if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
587 errvalid++;
588 strcpy(errbuf, "readExports: cannot read loader section header");
589 while (ldclose(ldp) == FAILURE);
590 return -1;
591 }
592 /*
593 * We read the complete loader section in one chunk, this makes
594 * finding long symbol names residing in the string table easier.
595 */
596 if ((ldbuf = (char *) malloc(sh.s_size)) == NULL) {
597 errvalid++;
598 strcpy(errbuf, "readExports: ");
600 while (ldclose(ldp) == FAILURE);
601 return -1;
602 }
603 if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
604 errvalid++;
605 strcpy(errbuf, "readExports: cannot seek to loader section");
606 free(ldbuf);
607 while (ldclose(ldp) == FAILURE);
608 return -1;
609 }
610 if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
611 errvalid++;
612 strcpy(errbuf, "readExports: cannot read loader section");
613 free(ldbuf);
614 while (ldclose(ldp) == FAILURE);
615 return -1;
616 }
617 lhp = (LDHDR *) ldbuf;
618 ls = (LDSYM *) (ldbuf + LDHDRSZ);
619 /*
620 * Count the number of exports to include in our export table.
621 */
622 for (i = lhp->l_nsyms; i; i--, ls++) {
623 if (!LDR_EXPORT(*ls))
624 continue;
625 mp->nExports++;
626 }
627 if ((mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
628 errvalid++;
629 strcpy(errbuf, "readExports: ");
631 free(ldbuf);
632 while (ldclose(ldp) == FAILURE);
633 return -1;
634 }
635 /*
636 * Fill in the export table. All entries are relative to
637 * the beginning of the data origin.
638 */
639 ep = mp->exports;
640 ls = (LDSYM *) (ldbuf + LDHDRSZ);
641 for (i = lhp->l_nsyms; i; i--, ls++) {
642 char *symname;
643 char tmpsym[SYMNMLEN + 1];
644 if (!LDR_EXPORT(*ls))
645 continue;
646 if (ls->l_zeroes == 0)
647 symname = ls->l_offset + lhp->l_stoff + ldbuf;
648 else {
649 /*
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.
653 */
654 strncpy(tmpsym, ls->l_name, SYMNMLEN);
655 tmpsym[SYMNMLEN] = '\0';
656 symname = tmpsym;
657 }
658 ep->name = strdup(symname);
659 ep->addr = (void *) ((unsigned long) dataorg +
660 ls->l_value - shdata.s_vaddr);
661 ep++;
662 }
663 free(ldbuf);
664 while (ldclose(ldp) == FAILURE);
665 return 0;
666}
667
668/*
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.
671 */
672static void *findMain(void)
673{
674 struct ld_info *lp;
675 char *buf;
676 int size = 4 * 1024;
677 int i;
678 void *ret;
679
680 if ((buf = malloc(size)) == NULL) {
681 errvalid++;
682 strcpy(errbuf, "findMain: ");
684 return NULL;
685 }
686 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
687 free(buf);
688 size += 4 * 1024;
689 if ((buf = malloc(size)) == NULL) {
690 errvalid++;
691 strcpy(errbuf, "findMain: ");
693 return NULL;
694 }
695 }
696 if (i == -1) {
697 errvalid++;
698 strcpy(errbuf, "findMain: ");
700 free(buf);
701 return NULL;
702 }
703 /*
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.
707 */
708 lp = (struct ld_info *) buf;
709 ret = lp->ldinfo_dataorg;
710 free(buf);
711 return ret;
712}
713
714#endif
const ap_regex_t char * errbuf
Definition ap_regex.h:198
char * strdup(const char *str)
APR Portability Routines.
request_rec int int apr_table_t const char * path
const unsigned char * buf
Definition util_md5.h:50
#define APR_ESYMNOTFOUND
Definition apr_errno.h:336
#define APR_EINIT
Definition apr_errno.h:474
#define APR_EDSOOPEN
Definition apr_errno.h:322
apr_brigade_flush void * ctx
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
apr_pool_t const char apr_dbd_t ** handle
Definition apr_dbd.h:142
const void apr_status_t(*) apr_status_t(* APR_DECLARE)(void) apr_pool_pre_cleanup_register(apr_pool_t *p
Definition apr_pools.h:646
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
char * buffer
apr_array_header_t ** result
apr_sockaddr_t * addr
apr_size_t buflen
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
void * apr_os_dso_handle_t
const char * s
Definition apr_strings.h:95
apr_pool_t * p
Definition md_event.c:32
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
static apr_status_t dso_cleanup(void *thedso)
Definition dso.c:41
char * name
INT info