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