1/* 2 * tclLoadAix.c -- 3 * 4 * This file implements the dlopen and dlsym APIs under the AIX operating 5 * system, to enable the Tcl "load" command to work. This code was 6 * provided by Jens-Uwe Mager. 7 * 8 * This file is subject to the following copyright notice, which is 9 * different from the notice used elsewhere in Tcl. The file has been 10 * modified to incorporate the file dlfcn.h in-line. 11 * 12 * Copyright (c) 1992,1993,1995,1996, Jens-Uwe Mager, Helios Software GmbH 13 * Not derived from licensed software. 14 * 15 * Permission is granted to freely use, copy, modify, and redistribute 16 * this software, provided that the author is not construed to be liable 17 * for any results of using the software, alterations are clearly marked 18 * as such, and this notice is not modified. 19 * 20 * RCS: @(#) $Id: tclLoadAix.c,v 1.6 2007/04/16 13:36:36 dkf Exp $ 21 * 22 * Note: this file has been altered from the original in a few ways in order 23 * to work properly with Tcl. 24 */ 25 26/* 27 * @(#)dlfcn.c 1.7 revision of 95/08/14 19:08:38 28 * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH 29 * 30159 Hannover, Germany 30 */ 31 32#include <stdio.h> 33#include <errno.h> 34#include <string.h> 35#include <stdlib.h> 36#include <sys/types.h> 37#include <sys/ldr.h> 38#include <a.out.h> 39#include <ldfcn.h> 40#include "../compat/dlfcn.h" 41 42/* 43 * We simulate dlopen() et al. through a call to load. Because AIX has no call 44 * to find an exported symbol we read the loader section of the loaded module 45 * and build a list of exported symbols and their virtual address. 46 */ 47 48typedef struct { 49 char *name; /* The symbols's name. */ 50 void *addr; /* Its relocated virtual address. */ 51} Export, *ExportPtr; 52 53/* 54 * xlC uses the following structure to list its constructors and destructors. 55 * This is gleaned from the output of munch. 56 */ 57 58typedef struct { 59 void (*init)(void); /* call static constructors */ 60 void (*term)(void); /* call static destructors */ 61} Cdtor, *CdtorPtr; 62 63/* 64 * The void * handle returned from dlopen is actually a ModulePtr. 65 */ 66 67typedef struct Module { 68 struct Module *next; 69 char *name; /* module name for refcounting */ 70 int refCnt; /* the number of references */ 71 void *entry; /* entry point from load */ 72 struct dl_info *info; /* optional init/terminate functions */ 73 CdtorPtr cdtors; /* optional C++ constructors */ 74 int nExports; /* the number of exports found */ 75 ExportPtr exports; /* the array of exports */ 76} Module, *ModulePtr; 77 78/* 79 * We keep a list of all loaded modules to be able to call the fini handlers 80 * and destructors at atexit() time. 81 */ 82 83static ModulePtr modList; 84 85/* 86 * The last error from one of the dl* routines is kept in static variables 87 * here. Each error is returned only once to the caller. 88 */ 89 90static char errbuf[BUFSIZ]; 91static int errvalid; 92 93static void caterr(char *); 94static int readExports(ModulePtr); 95static void terminate(void); 96static void *findMain(void); 97 98void * 99dlopen( 100 const char *path, 101 int mode) 102{ 103 register ModulePtr mp; 104 static void *mainModule; 105 106 /* 107 * Upon the first call register a terminate handler that will close all 108 * libraries. Also get a reference to the main module for use with 109 * loadbind. 110 */ 111 112 if (!mainModule) { 113 mainModule = findMain(); 114 if (mainModule == NULL) { 115 return NULL; 116 } 117 atexit(terminate); 118 } 119 120 /* 121 * Scan the list of modules if we have the module already loaded. 122 */ 123 124 for (mp = modList; mp; mp = mp->next) { 125 if (strcmp(mp->name, path) == 0) { 126 mp->refCnt++; 127 return (void *) mp; 128 } 129 } 130 131 mp = (ModulePtr) calloc(1, sizeof(*mp)); 132 if (mp == NULL) { 133 errvalid++; 134 strcpy(errbuf, "calloc: "); 135 strcat(errbuf, strerror(errno)); 136 return NULL; 137 } 138 139 mp->name = malloc((unsigned) (strlen(path) + 1)); 140 strcpy(mp->name, path); 141 142 /* 143 * load should be declared load(const char *...). Thus we cast the path to 144 * a normal char *. Ugly. 145 */ 146 147 mp->entry = (void *) load((char *)path, L_NOAUTODEFER, NULL); 148 if (mp->entry == NULL) { 149 free(mp->name); 150 free(mp); 151 errvalid++; 152 strcpy(errbuf, "dlopen: "); 153 strcat(errbuf, path); 154 strcat(errbuf, ": "); 155 156 /* 157 * If AIX says the file is not executable, the error can be further 158 * described by querying the loader about the last error. 159 */ 160 161 if (errno == ENOEXEC) { 162 char *tmp[BUFSIZ/sizeof(char *)], **p; 163 164 if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) { 165 strcpy(errbuf, strerror(errno)); 166 } else { 167 for (p=tmp ; *p ; p++) { 168 caterr(*p); 169 } 170 } 171 } else { 172 strcat(errbuf, strerror(errno)); 173 } 174 return NULL; 175 } 176 177 mp->refCnt = 1; 178 mp->next = modList; 179 modList = mp; 180 181 if (loadbind(0, mainModule, mp->entry) == -1) { 182 loadbindFailure: 183 dlclose(mp); 184 errvalid++; 185 strcpy(errbuf, "loadbind: "); 186 strcat(errbuf, strerror(errno)); 187 return NULL; 188 } 189 190 /* 191 * If the user wants global binding, loadbind against all other loaded 192 * modules. 193 */ 194 195 if (mode & RTLD_GLOBAL) { 196 register ModulePtr mp1; 197 198 for (mp1 = mp->next; mp1; mp1 = mp1->next) { 199 if (loadbind(0, mp1->entry, mp->entry) == -1) { 200 goto loadbindFailure; 201 } 202 } 203 } 204 205 if (readExports(mp) == -1) { 206 dlclose(mp); 207 return NULL; 208 } 209 210 /* 211 * If there is a dl_info structure, call the init function. 212 */ 213 214 if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) { 215 if (mp->info->init) { 216 (*mp->info->init)(); 217 } 218 } else { 219 errvalid = 0; 220 } 221 222 /* 223 * If the shared object was compiled using xlC we will need to call static 224 * constructors (and later on dlclose destructors). 225 */ 226 227 if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) { 228 while (mp->cdtors->init) { 229 (*mp->cdtors->init)(); 230 mp->cdtors++; 231 } 232 } else { 233 errvalid = 0; 234 } 235 236 return (void *) mp; 237} 238 239/* 240 * Attempt to decipher an AIX loader error message and append it to our static 241 * error message buffer. 242 */ 243 244static void 245caterr( 246 char *s) 247{ 248 register char *p = s; 249 250 while (*p >= '0' && *p <= '9') { 251 p++; 252 } 253 switch (atoi(s)) { /* INTL: "C", UTF safe. */ 254 case L_ERROR_TOOMANY: 255 strcat(errbuf, "to many errors"); 256 break; 257 case L_ERROR_NOLIB: 258 strcat(errbuf, "can't load library"); 259 strcat(errbuf, p); 260 break; 261 case L_ERROR_UNDEF: 262 strcat(errbuf, "can't find symbol"); 263 strcat(errbuf, p); 264 break; 265 case L_ERROR_RLDBAD: 266 strcat(errbuf, "bad RLD"); 267 strcat(errbuf, p); 268 break; 269 case L_ERROR_FORMAT: 270 strcat(errbuf, "bad exec format in"); 271 strcat(errbuf, p); 272 break; 273 case L_ERROR_ERRNO: 274 strcat(errbuf, strerror(atoi(++p))); /* INTL: "C", UTF safe. */ 275 break; 276 default: 277 strcat(errbuf, s); 278 break; 279 } 280} 281 282void * 283dlsym( 284 void *handle, 285 const char *symbol) 286{ 287 register ModulePtr mp = (ModulePtr)handle; 288 register ExportPtr ep; 289 register int i; 290 291 /* 292 * Could speed up the search, but I assume that one assigns the result to 293 * function pointers anyways. 294 */ 295 296 for (ep = mp->exports, i = mp->nExports; i; i--, ep++) { 297 if (strcmp(ep->name, symbol) == 0) { 298 return ep->addr; 299 } 300 } 301 302 errvalid++; 303 strcpy(errbuf, "dlsym: undefined symbol "); 304 strcat(errbuf, symbol); 305 return NULL; 306} 307 308char * 309dlerror(void) 310{ 311 if (errvalid) { 312 errvalid = 0; 313 return errbuf; 314 } 315 return NULL; 316} 317 318int 319dlclose( 320 void *handle) 321{ 322 register ModulePtr mp = (ModulePtr)handle; 323 int result; 324 register ModulePtr mp1; 325 326 if (--mp->refCnt > 0) { 327 return 0; 328 } 329 330 if (mp->info && mp->info->fini) { 331 (*mp->info->fini)(); 332 } 333 334 if (mp->cdtors) { 335 while (mp->cdtors->term) { 336 (*mp->cdtors->term)(); 337 mp->cdtors++; 338 } 339 } 340 341 result = unload(mp->entry); 342 if (result == -1) { 343 errvalid++; 344 strcpy(errbuf, strerror(errno)); 345 } 346 347 if (mp->exports) { 348 register ExportPtr ep; 349 register int i; 350 for (ep = mp->exports, i = mp->nExports; i; i--, ep++) { 351 if (ep->name) { 352 free(ep->name); 353 } 354 } 355 free(mp->exports); 356 } 357 358 if (mp == modList) { 359 modList = mp->next; 360 } else { 361 for (mp1 = modList; mp1; mp1 = mp1->next) { 362 if (mp1->next == mp) { 363 mp1->next = mp->next; 364 break; 365 } 366 } 367 } 368 369 free(mp->name); 370 free(mp); 371 return result; 372} 373 374static void 375terminate(void) 376{ 377 while (modList) { 378 dlclose(modList); 379 } 380} 381 382/* 383 * Build the export table from the XCOFF .loader section. 384 */ 385 386static int 387readExports( 388 ModulePtr mp) 389{ 390 LDFILE *ldp = NULL; 391 SCNHDR sh, shdata; 392 LDHDR *lhp; 393 char *ldbuf; 394 LDSYM *ls; 395 int i; 396 ExportPtr ep; 397 const char *errMsg; 398 399#define Error(msg) do{errMsg=(msg);goto error;}while(0) 400#define SysErr() Error(strerror(errno)) 401 402 ldp = ldopen(mp->name, ldp); 403 if (ldp == NULL) { 404 struct ld_info *lp; 405 char *buf; 406 int size = 0; 407 408 if (errno != ENOENT) { 409 SysErr(); 410 } 411 412 /* 413 * The module might be loaded due to the LIBPATH environment variable. 414 * Search for the loaded module using L_GETINFO. 415 */ 416 417 while (1) { 418 size += 4 * 1024; 419 buf = malloc(size); 420 if (buf == NULL) { 421 SysErr(); 422 } 423 424 i = loadquery(L_GETINFO, buf, size); 425 426 if (i != -1) { 427 break; 428 } 429 free(buf); 430 if (errno != ENOMEM) { 431 SysErr(); 432 } 433 } 434 435 /* 436 * Traverse the list of loaded modules. The entry point returned by 437 * load() does actually point to the data segment origin. 438 */ 439 440 lp = (struct ld_info *) buf; 441 while (lp) { 442 if (lp->ldinfo_dataorg == mp->entry) { 443 ldp = ldopen(lp->ldinfo_filename, ldp); 444 break; 445 } 446 if (lp->ldinfo_next == 0) { 447 lp = NULL; 448 } else { 449 lp = (struct ld_info *)((char *)lp + lp->ldinfo_next); 450 } 451 } 452 453 free(buf); 454 455 if (!ldp) { 456 SysErr(); 457 } 458 } 459 460 if (TYPE(ldp) != U802TOCMAGIC) { 461 Error("bad magic"); 462 } 463 464 /* 465 * Get the padding for the data section. This is needed for AIX 4.1 466 * compilers. This is used when building the final function pointer to the 467 * exported symbol. 468 */ 469 470 if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) { 471 Error("cannot read data section header"); 472 } 473 474 if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) { 475 Error("cannot read loader section header"); 476 } 477 478 /* 479 * We read the complete loader section in one chunk, this makes finding 480 * long symbol names residing in the string table easier. 481 */ 482 483 ldbuf = (char *) malloc(sh.s_size); 484 if (ldbuf == NULL) { 485 SysErr(); 486 } 487 488 if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) { 489 free(ldbuf); 490 Error("cannot seek to loader section"); 491 } 492 493 if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) { 494 free(ldbuf); 495 Error("cannot read loader section"); 496 } 497 498 lhp = (LDHDR *) ldbuf; 499 ls = (LDSYM *)(ldbuf + LDHDRSZ); 500 501 /* 502 * Count the number of exports to include in our export table. 503 */ 504 505 for (i = lhp->l_nsyms; i; i--, ls++) { 506 if (!LDR_EXPORT(*ls)) { 507 continue; 508 } 509 mp->nExports++; 510 } 511 512 mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports)); 513 if (mp->exports == NULL) { 514 free(ldbuf); 515 SysErr(); 516 } 517 518 /* 519 * Fill in the export table. All entries are relative to the entry point 520 * we got from load. 521 */ 522 523 ep = mp->exports; 524 ls = (LDSYM *)(ldbuf + LDHDRSZ); 525 for (i=lhp->l_nsyms ; i!=0 ; i--,ls++) { 526 char *symname; 527 char tmpsym[SYMNMLEN+1]; 528 529 if (!LDR_EXPORT(*ls)) { 530 continue; 531 } 532 533 if (ls->l_zeroes == 0) { 534 symname = ls->l_offset + lhp->l_stoff + ldbuf; 535 } else { 536 /* 537 * The l_name member is not zero terminated, we must copy the 538 * first SYMNMLEN chars and make sure we have a zero byte at the 539 * end. 540 */ 541 542 strncpy(tmpsym, ls->l_name, SYMNMLEN); 543 tmpsym[SYMNMLEN] = '\0'; 544 symname = tmpsym; 545 } 546 ep->name = malloc((unsigned) (strlen(symname) + 1)); 547 strcpy(ep->name, symname); 548 ep->addr = (void *)((unsigned long) 549 mp->entry + ls->l_value - shdata.s_vaddr); 550 ep++; 551 } 552 free(ldbuf); 553 while (ldclose(ldp) == FAILURE) { 554 /* Empty body */ 555 } 556 return 0; 557 558 /* 559 * This is a factoring out of the error-handling code to make the rest of 560 * the function much simpler to read. 561 */ 562 563 error: 564 errvalid++; 565 strcpy(errbuf, "readExports: "); 566 strcat(errbuf, errMsg); 567 568 if (ldp != NULL) { 569 while (ldclose(ldp) == FAILURE) { 570 /* Empty body */ 571 } 572 } 573 return -1; 574} 575 576/* 577 * Find the main modules entry point. This is used as export pointer for 578 * loadbind() to be able to resolve references to the main part. 579 */ 580 581static void * 582findMain(void) 583{ 584 struct ld_info *lp; 585 char *buf; 586 int size = 4*1024; 587 int i; 588 void *ret; 589 590 buf = malloc(size); 591 if (buf == NULL) { 592 goto error; 593 } 594 595 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { 596 free(buf); 597 size += 4*1024; 598 buf = malloc(size); 599 if (buf == NULL) { 600 goto error; 601 } 602 } 603 604 if (i == -1) { 605 free(buf); 606 goto error; 607 } 608 609 /* 610 * The first entry is the main module. The entry point returned by load() 611 * does actually point to the data segment origin. 612 */ 613 614 lp = (struct ld_info *) buf; 615 ret = lp->ldinfo_dataorg; 616 free(buf); 617 return ret; 618 619 error: 620 errvalid++; 621 strcpy(errbuf, "findMain: "); 622 strcat(errbuf, strerror(errno)); 623 return NULL; 624} 625 626/* 627 * Local Variables: 628 * mode: c 629 * c-basic-offset: 4 630 * fill-column: 78 631 * End: 632 */ 633