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 <jwe@bevo.che.wisc.edu> 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 <rse@apache.org> 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 100APR_DECLARE(apr_status_t) apr_os_dso_handle_put(apr_dso_handle_t **aprdso, 101 apr_os_dso_handle_t osdso, 102 apr_pool_t *pool) 103{ 104 *aprdso = apr_pcalloc(pool, sizeof **aprdso); 105 (*aprdso)->handle = osdso; 106 (*aprdso)->pool = pool; 107 return APR_SUCCESS; 108} 109 110APR_DECLARE(apr_status_t) apr_os_dso_handle_get(apr_os_dso_handle_t *osdso, 111 apr_dso_handle_t *aprdso) 112{ 113 *osdso = aprdso->handle; 114 return APR_SUCCESS; 115} 116 117static apr_status_t dso_cleanup(void *thedso) 118{ 119 apr_dso_handle_t *dso = thedso; 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 128APR_DECLARE(apr_status_t) apr_dso_load(apr_dso_handle_t **res_handle, 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 144 apr_pool_cleanup_register(ctx, *res_handle, dso_cleanup, apr_pool_cleanup_null); 145 146 return APR_SUCCESS; 147} 148 149APR_DECLARE(apr_status_t) apr_dso_unload(apr_dso_handle_t *handle) 150{ 151 return apr_pool_cleanup_run(handle->pool, handle, dso_cleanup); 152} 153 154APR_DECLARE(apr_status_t) apr_dso_sym(apr_dso_handle_sym_t *ressym, 155 apr_dso_handle_t *handle, 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 169APR_DECLARE(const char *) apr_dso_error(apr_dso_handle_t *dso, char *buffer, apr_size_t buflen) 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; 255 atexit(terminate); 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: "); 268 strcat(errbuf, strerror(errno)); 269 return NULL; 270 } 271 if ((mp->name = strdup(path)) == NULL) { 272 errvalid++; 273 strcpy(errbuf, "strdup: "); 274 strcat(errbuf, strerror(errno)); 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: "); 287 strcat(errbuf, path); 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) 297 strcpy(errbuf, strerror(errno)); 298 else { 299 char **p; 300 for (p = tmp; *p; p++) 301 caterr(*p); 302 } 303 } 304 else 305 strcat(errbuf, strerror(errno)); 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: "); 315 strcat(errbuf, strerror(errno)); 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: "); 329 strcat(errbuf, strerror(errno)); 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: 404 strcat(errbuf, strerror(atoi(++p))); 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++; 468 strcpy(errbuf, strerror(errno)); 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) 495 dlclose(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; 504 SCNHDR sh, shdata; 505 LDHDR *lhp; 506 char *ldbuf; 507 LDSYM *ls; 508 int i; 509 ExportPtr ep; 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: "); 523 strcat(errbuf, strerror(errno)); 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: "); 532 strcat(errbuf, strerror(errno)); 533 return -1; 534 } 535 } 536 if (i == -1) { 537 errvalid++; 538 strcpy(errbuf, "readExports: "); 539 strcat(errbuf, strerror(errno)); 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: "); 566 strcat(errbuf, strerror(errno)); 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: "); 599 strcat(errbuf, strerror(errno)); 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: "); 630 strcat(errbuf, strerror(errno)); 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: "); 683 strcat(errbuf, strerror(errno)); 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: "); 692 strcat(errbuf, strerror(errno)); 693 return NULL; 694 } 695 } 696 if (i == -1) { 697 errvalid++; 698 strcpy(errbuf, "findMain: "); 699 strcat(errbuf, strerror(errno)); 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 715