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