tclLoadAix.cGo to the documentation of this file.00001 /* 00002 * tclLoadAix.c -- 00003 * 00004 * This file implements the dlopen and dlsym APIs under the AIX operating 00005 * system, to enable the Tcl "load" command to work. This code was 00006 * provided by Jens-Uwe Mager. 00007 * 00008 * This file is subject to the following copyright notice, which is 00009 * different from the notice used elsewhere in Tcl. The file has been 00010 * modified to incorporate the file dlfcn.h in-line. 00011 * 00012 * Copyright (c) 1992,1993,1995,1996, Jens-Uwe Mager, Helios Software GmbH 00013 * Not derived from licensed software. 00014 * 00015 * Permission is granted to freely use, copy, modify, and redistribute 00016 * this software, provided that the author is not construed to be liable 00017 * for any results of using the software, alterations are clearly marked 00018 * as such, and this notice is not modified. 00019 * 00020 * RCS: @(#) $Id: tclLoadAix.c,v 1.6 2007/04/16 13:36:36 dkf Exp $ 00021 * 00022 * Note: this file has been altered from the original in a few ways in order 00023 * to work properly with Tcl. 00024 */ 00025 00026 /* 00027 * @(#)dlfcn.c 1.7 revision of 95/08/14 19:08:38 00028 * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH 00029 * 30159 Hannover, Germany 00030 */ 00031 00032 #include <stdio.h> 00033 #include <errno.h> 00034 #include <string.h> 00035 #include <stdlib.h> 00036 #include <sys/types.h> 00037 #include <sys/ldr.h> 00038 #include <a.out.h> 00039 #include <ldfcn.h> 00040 #include "../compat/dlfcn.h" 00041 00042 /* 00043 * We simulate dlopen() et al. through a call to load. Because AIX has no call 00044 * to find an exported symbol we read the loader section of the loaded module 00045 * and build a list of exported symbols and their virtual address. 00046 */ 00047 00048 typedef struct { 00049 char *name; /* The symbols's name. */ 00050 void *addr; /* Its relocated virtual address. */ 00051 } Export, *ExportPtr; 00052 00053 /* 00054 * xlC uses the following structure to list its constructors and destructors. 00055 * This is gleaned from the output of munch. 00056 */ 00057 00058 typedef struct { 00059 void (*init)(void); /* call static constructors */ 00060 void (*term)(void); /* call static destructors */ 00061 } Cdtor, *CdtorPtr; 00062 00063 /* 00064 * The void * handle returned from dlopen is actually a ModulePtr. 00065 */ 00066 00067 typedef struct Module { 00068 struct Module *next; 00069 char *name; /* module name for refcounting */ 00070 int refCnt; /* the number of references */ 00071 void *entry; /* entry point from load */ 00072 struct dl_info *info; /* optional init/terminate functions */ 00073 CdtorPtr cdtors; /* optional C++ constructors */ 00074 int nExports; /* the number of exports found */ 00075 ExportPtr exports; /* the array of exports */ 00076 } Module, *ModulePtr; 00077 00078 /* 00079 * We keep a list of all loaded modules to be able to call the fini handlers 00080 * and destructors at atexit() time. 00081 */ 00082 00083 static ModulePtr modList; 00084 00085 /* 00086 * The last error from one of the dl* routines is kept in static variables 00087 * here. Each error is returned only once to the caller. 00088 */ 00089 00090 static char errbuf[BUFSIZ]; 00091 static int errvalid; 00092 00093 static void caterr(char *); 00094 static int readExports(ModulePtr); 00095 static void terminate(void); 00096 static void *findMain(void); 00097 00098 void * 00099 dlopen( 00100 const char *path, 00101 int mode) 00102 { 00103 register ModulePtr mp; 00104 static void *mainModule; 00105 00106 /* 00107 * Upon the first call register a terminate handler that will close all 00108 * libraries. Also get a reference to the main module for use with 00109 * loadbind. 00110 */ 00111 00112 if (!mainModule) { 00113 mainModule = findMain(); 00114 if (mainModule == NULL) { 00115 return NULL; 00116 } 00117 atexit(terminate); 00118 } 00119 00120 /* 00121 * Scan the list of modules if we have the module already loaded. 00122 */ 00123 00124 for (mp = modList; mp; mp = mp->next) { 00125 if (strcmp(mp->name, path) == 0) { 00126 mp->refCnt++; 00127 return (void *) mp; 00128 } 00129 } 00130 00131 mp = (ModulePtr) calloc(1, sizeof(*mp)); 00132 if (mp == NULL) { 00133 errvalid++; 00134 strcpy(errbuf, "calloc: "); 00135 strcat(errbuf, strerror(errno)); 00136 return NULL; 00137 } 00138 00139 mp->name = malloc((unsigned) (strlen(path) + 1)); 00140 strcpy(mp->name, path); 00141 00142 /* 00143 * load should be declared load(const char *...). Thus we cast the path to 00144 * a normal char *. Ugly. 00145 */ 00146 00147 mp->entry = (void *) load((char *)path, L_NOAUTODEFER, NULL); 00148 if (mp->entry == NULL) { 00149 free(mp->name); 00150 free(mp); 00151 errvalid++; 00152 strcpy(errbuf, "dlopen: "); 00153 strcat(errbuf, path); 00154 strcat(errbuf, ": "); 00155 00156 /* 00157 * If AIX says the file is not executable, the error can be further 00158 * described by querying the loader about the last error. 00159 */ 00160 00161 if (errno == ENOEXEC) { 00162 char *tmp[BUFSIZ/sizeof(char *)], **p; 00163 00164 if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) { 00165 strcpy(errbuf, strerror(errno)); 00166 } else { 00167 for (p=tmp ; *p ; p++) { 00168 caterr(*p); 00169 } 00170 } 00171 } else { 00172 strcat(errbuf, strerror(errno)); 00173 } 00174 return NULL; 00175 } 00176 00177 mp->refCnt = 1; 00178 mp->next = modList; 00179 modList = mp; 00180 00181 if (loadbind(0, mainModule, mp->entry) == -1) { 00182 loadbindFailure: 00183 dlclose(mp); 00184 errvalid++; 00185 strcpy(errbuf, "loadbind: "); 00186 strcat(errbuf, strerror(errno)); 00187 return NULL; 00188 } 00189 00190 /* 00191 * If the user wants global binding, loadbind against all other loaded 00192 * modules. 00193 */ 00194 00195 if (mode & RTLD_GLOBAL) { 00196 register ModulePtr mp1; 00197 00198 for (mp1 = mp->next; mp1; mp1 = mp1->next) { 00199 if (loadbind(0, mp1->entry, mp->entry) == -1) { 00200 goto loadbindFailure; 00201 } 00202 } 00203 } 00204 00205 if (readExports(mp) == -1) { 00206 dlclose(mp); 00207 return NULL; 00208 } 00209 00210 /* 00211 * If there is a dl_info structure, call the init function. 00212 */ 00213 00214 if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) { 00215 if (mp->info->init) { 00216 (*mp->info->init)(); 00217 } 00218 } else { 00219 errvalid = 0; 00220 } 00221 00222 /* 00223 * If the shared object was compiled using xlC we will need to call static 00224 * constructors (and later on dlclose destructors). 00225 */ 00226 00227 if (mp->cdtors = (CdtorPtr) dlsym(mp, "__cdtors")) { 00228 while (mp->cdtors->init) { 00229 (*mp->cdtors->init)(); 00230 mp->cdtors++; 00231 } 00232 } else { 00233 errvalid = 0; 00234 } 00235 00236 return (void *) mp; 00237 } 00238 00239 /* 00240 * Attempt to decipher an AIX loader error message and append it to our static 00241 * error message buffer. 00242 */ 00243 00244 static void 00245 caterr( 00246 char *s) 00247 { 00248 register char *p = s; 00249 00250 while (*p >= '0' && *p <= '9') { 00251 p++; 00252 } 00253 switch (atoi(s)) { /* INTL: "C", UTF safe. */ 00254 case L_ERROR_TOOMANY: 00255 strcat(errbuf, "to many errors"); 00256 break; 00257 case L_ERROR_NOLIB: 00258 strcat(errbuf, "can't load library"); 00259 strcat(errbuf, p); 00260 break; 00261 case L_ERROR_UNDEF: 00262 strcat(errbuf, "can't find symbol"); 00263 strcat(errbuf, p); 00264 break; 00265 case L_ERROR_RLDBAD: 00266 strcat(errbuf, "bad RLD"); 00267 strcat(errbuf, p); 00268 break; 00269 case L_ERROR_FORMAT: 00270 strcat(errbuf, "bad exec format in"); 00271 strcat(errbuf, p); 00272 break; 00273 case L_ERROR_ERRNO: 00274 strcat(errbuf, strerror(atoi(++p))); /* INTL: "C", UTF safe. */ 00275 break; 00276 default: 00277 strcat(errbuf, s); 00278 break; 00279 } 00280 } 00281 00282 void * 00283 dlsym( 00284 void *handle, 00285 const char *symbol) 00286 { 00287 register ModulePtr mp = (ModulePtr)handle; 00288 register ExportPtr ep; 00289 register int i; 00290 00291 /* 00292 * Could speed up the search, but I assume that one assigns the result to 00293 * function pointers anyways. 00294 */ 00295 00296 for (ep = mp->exports, i = mp->nExports; i; i--, ep++) { 00297 if (strcmp(ep->name, symbol) == 0) { 00298 return ep->addr; 00299 } 00300 } 00301 00302 errvalid++; 00303 strcpy(errbuf, "dlsym: undefined symbol "); 00304 strcat(errbuf, symbol); 00305 return NULL; 00306 } 00307 00308 char * 00309 dlerror(void) 00310 { 00311 if (errvalid) { 00312 errvalid = 0; 00313 return errbuf; 00314 } 00315 return NULL; 00316 } 00317 00318 int 00319 dlclose( 00320 void *handle) 00321 { 00322 register ModulePtr mp = (ModulePtr)handle; 00323 int result; 00324 register ModulePtr mp1; 00325 00326 if (--mp->refCnt > 0) { 00327 return 0; 00328 } 00329 00330 if (mp->info && mp->info->fini) { 00331 (*mp->info->fini)(); 00332 } 00333 00334 if (mp->cdtors) { 00335 while (mp->cdtors->term) { 00336 (*mp->cdtors->term)(); 00337 mp->cdtors++; 00338 } 00339 } 00340 00341 result = unload(mp->entry); 00342 if (result == -1) { 00343 errvalid++; 00344 strcpy(errbuf, strerror(errno)); 00345 } 00346 00347 if (mp->exports) { 00348 register ExportPtr ep; 00349 register int i; 00350 for (ep = mp->exports, i = mp->nExports; i; i--, ep++) { 00351 if (ep->name) { 00352 free(ep->name); 00353 } 00354 } 00355 free(mp->exports); 00356 } 00357 00358 if (mp == modList) { 00359 modList = mp->next; 00360 } else { 00361 for (mp1 = modList; mp1; mp1 = mp1->next) { 00362 if (mp1->next == mp) { 00363 mp1->next = mp->next; 00364 break; 00365 } 00366 } 00367 } 00368 00369 free(mp->name); 00370 free(mp); 00371 return result; 00372 } 00373 00374 static void 00375 terminate(void) 00376 { 00377 while (modList) { 00378 dlclose(modList); 00379 } 00380 } 00381 00382 /* 00383 * Build the export table from the XCOFF .loader section. 00384 */ 00385 00386 static int 00387 readExports( 00388 ModulePtr mp) 00389 { 00390 LDFILE *ldp = NULL; 00391 SCNHDR sh, shdata; 00392 LDHDR *lhp; 00393 char *ldbuf; 00394 LDSYM *ls; 00395 int i; 00396 ExportPtr ep; 00397 const char *errMsg; 00398 00399 #define Error(msg) do{errMsg=(msg);goto error;}while(0) 00400 #define SysErr() Error(strerror(errno)) 00401 00402 ldp = ldopen(mp->name, ldp); 00403 if (ldp == NULL) { 00404 struct ld_info *lp; 00405 char *buf; 00406 int size = 0; 00407 00408 if (errno != ENOENT) { 00409 SysErr(); 00410 } 00411 00412 /* 00413 * The module might be loaded due to the LIBPATH environment variable. 00414 * Search for the loaded module using L_GETINFO. 00415 */ 00416 00417 while (1) { 00418 size += 4 * 1024; 00419 buf = malloc(size); 00420 if (buf == NULL) { 00421 SysErr(); 00422 } 00423 00424 i = loadquery(L_GETINFO, buf, size); 00425 00426 if (i != -1) { 00427 break; 00428 } 00429 free(buf); 00430 if (errno != ENOMEM) { 00431 SysErr(); 00432 } 00433 } 00434 00435 /* 00436 * Traverse the list of loaded modules. The entry point returned by 00437 * load() does actually point to the data segment origin. 00438 */ 00439 00440 lp = (struct ld_info *) buf; 00441 while (lp) { 00442 if (lp->ldinfo_dataorg == mp->entry) { 00443 ldp = ldopen(lp->ldinfo_filename, ldp); 00444 break; 00445 } 00446 if (lp->ldinfo_next == 0) { 00447 lp = NULL; 00448 } else { 00449 lp = (struct ld_info *)((char *)lp + lp->ldinfo_next); 00450 } 00451 } 00452 00453 free(buf); 00454 00455 if (!ldp) { 00456 SysErr(); 00457 } 00458 } 00459 00460 if (TYPE(ldp) != U802TOCMAGIC) { 00461 Error("bad magic"); 00462 } 00463 00464 /* 00465 * Get the padding for the data section. This is needed for AIX 4.1 00466 * compilers. This is used when building the final function pointer to the 00467 * exported symbol. 00468 */ 00469 00470 if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) { 00471 Error("cannot read data section header"); 00472 } 00473 00474 if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) { 00475 Error("cannot read loader section header"); 00476 } 00477 00478 /* 00479 * We read the complete loader section in one chunk, this makes finding 00480 * long symbol names residing in the string table easier. 00481 */ 00482 00483 ldbuf = (char *) malloc(sh.s_size); 00484 if (ldbuf == NULL) { 00485 SysErr(); 00486 } 00487 00488 if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) { 00489 free(ldbuf); 00490 Error("cannot seek to loader section"); 00491 } 00492 00493 if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) { 00494 free(ldbuf); 00495 Error("cannot read loader section"); 00496 } 00497 00498 lhp = (LDHDR *) ldbuf; 00499 ls = (LDSYM *)(ldbuf + LDHDRSZ); 00500 00501 /* 00502 * Count the number of exports to include in our export table. 00503 */ 00504 00505 for (i = lhp->l_nsyms; i; i--, ls++) { 00506 if (!LDR_EXPORT(*ls)) { 00507 continue; 00508 } 00509 mp->nExports++; 00510 } 00511 00512 mp->exports = (ExportPtr) calloc(mp->nExports, sizeof(*mp->exports)); 00513 if (mp->exports == NULL) { 00514 free(ldbuf); 00515 SysErr(); 00516 } 00517 00518 /* 00519 * Fill in the export table. All entries are relative to the entry point 00520 * we got from load. 00521 */ 00522 00523 ep = mp->exports; 00524 ls = (LDSYM *)(ldbuf + LDHDRSZ); 00525 for (i=lhp->l_nsyms ; i!=0 ; i--,ls++) { 00526 char *symname; 00527 char tmpsym[SYMNMLEN+1]; 00528 00529 if (!LDR_EXPORT(*ls)) { 00530 continue; 00531 } 00532 00533 if (ls->l_zeroes == 0) { 00534 symname = ls->l_offset + lhp->l_stoff + ldbuf; 00535 } else { 00536 /* 00537 * The l_name member is not zero terminated, we must copy the 00538 * first SYMNMLEN chars and make sure we have a zero byte at the 00539 * end. 00540 */ 00541 00542 strncpy(tmpsym, ls->l_name, SYMNMLEN); 00543 tmpsym[SYMNMLEN] = '\0'; 00544 symname = tmpsym; 00545 } 00546 ep->name = malloc((unsigned) (strlen(symname) + 1)); 00547 strcpy(ep->name, symname); 00548 ep->addr = (void *)((unsigned long) 00549 mp->entry + ls->l_value - shdata.s_vaddr); 00550 ep++; 00551 } 00552 free(ldbuf); 00553 while (ldclose(ldp) == FAILURE) { 00554 /* Empty body */ 00555 } 00556 return 0; 00557 00558 /* 00559 * This is a factoring out of the error-handling code to make the rest of 00560 * the function much simpler to read. 00561 */ 00562 00563 error: 00564 errvalid++; 00565 strcpy(errbuf, "readExports: "); 00566 strcat(errbuf, errMsg); 00567 00568 if (ldp != NULL) { 00569 while (ldclose(ldp) == FAILURE) { 00570 /* Empty body */ 00571 } 00572 } 00573 return -1; 00574 } 00575 00576 /* 00577 * Find the main modules entry point. This is used as export pointer for 00578 * loadbind() to be able to resolve references to the main part. 00579 */ 00580 00581 static void * 00582 findMain(void) 00583 { 00584 struct ld_info *lp; 00585 char *buf; 00586 int size = 4*1024; 00587 int i; 00588 void *ret; 00589 00590 buf = malloc(size); 00591 if (buf == NULL) { 00592 goto error; 00593 } 00594 00595 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { 00596 free(buf); 00597 size += 4*1024; 00598 buf = malloc(size); 00599 if (buf == NULL) { 00600 goto error; 00601 } 00602 } 00603 00604 if (i == -1) { 00605 free(buf); 00606 goto error; 00607 } 00608 00609 /* 00610 * The first entry is the main module. The entry point returned by load() 00611 * does actually point to the data segment origin. 00612 */ 00613 00614 lp = (struct ld_info *) buf; 00615 ret = lp->ldinfo_dataorg; 00616 free(buf); 00617 return ret; 00618 00619 error: 00620 errvalid++; 00621 strcpy(errbuf, "findMain: "); 00622 strcat(errbuf, strerror(errno)); 00623 return NULL; 00624 } 00625 00626 /* 00627 * Local Variables: 00628 * mode: c 00629 * c-basic-offset: 4 00630 * fill-column: 78 00631 * End: 00632 */
Generated on Wed Mar 12 12:18:25 2008 by 1.5.1 |