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