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