static void diagnose_load_failure ( void ) { # define NBUF 1024 UChar buf[NBUF]; VG_(debugLog)(0, "initimg", "Diagnosing load failure\n"); if (sizeof(void*) == 8) { VG_(debugLog)(0, "initimg", "Can't safely do loadquery() " "in 64-bit mode. Sorry.\n"); /* because this requires dynamic linking to be working (IIRC) and it isn't; the tool file's dynamic linking was never done, because it was loaded by the bootstrap stub, which simply did sys_kload() but didn't make usla do the relevant relocations. */ } else { UChar** p; Int r = loadquery(L_GETMESSAGES, buf, NBUF); VG_(debugLog)(0, "initimg", "loadquery returned %d (0 = success)\n", r); p = (UChar**)(&buf[0]); for (; *p; p++) VG_(debugLog)(0, "initimg", "\"%s\"\n", *p); VG_(debugLog)(0, "initimg", "Use /usr/sbin/execerror to make " "sense of above string(s)\n"); VG_(debugLog)(0, "initimg", "See also comments at the bottom of\n"); VG_(debugLog)(0, "initimg", "coregrind/m_initimg/" "initimg-aix5.c (in Valgrind sources)\n"); } # undef NBUF }
/* * Find the main modules entry point. This is used as export pointer * for loadbind() to be able to resolve references to the main part. */ static void * findMain(void) { struct ld_info *lp; char *buf; int size = 4 * 1024; int i; void *ret; if ((buf = malloc(size)) == NULL) { errvalid++; strcpy(errbuf, "findMain: "); strcat(errbuf, strerror(errno)); return NULL; } while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { free(buf); size += 4 * 1024; if ((buf = malloc(size)) == NULL) { errvalid++; strcpy(errbuf, "findMain: "); strcat(errbuf, strerror(errno)); return NULL; } } if (i == -1) { errvalid++; strcpy(errbuf, "findMain: "); strcat(errbuf, strerror(errno)); free(buf); return NULL; } /* * The first entry is the main module. The entry point returned by load() * does actually point to the data segment origin. */ lp = (struct ld_info *) buf; ret = lp->ldinfo_dataorg; free(buf); return ret; }
ACP_EXPORT acp_rc_t acpSymTableInit(acp_sym_table_t *aSymTable) { acp_sint32_t sRet; sRet = loadquery(L_GETINFO, aSymTable->mObjInfoBuffer, (acp_uint32_t)sizeof(aSymTable->mObjInfoBuffer)); if (sRet != -1) { return ACP_RC_SUCCESS; } else { return ACP_RC_GET_OS_ERROR(); } }
/* * Find the main modules data origin. This is used as export pointer * for loadbind() to be able to resolve references to the main part. */ static void *findMain(void) { struct ld_info *lp; char *buf; int size = 4 * 1024; int i; void *ret; if ((buf = malloc(size)) == NULL) { errvalid++; strcpy(errbuf, "findMain: "); strcat(errbuf, strerror(errno)); return NULL; } while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { free(buf); size += 4 * 1024; if ((buf = malloc(size)) == NULL) { errvalid++; strcpy(errbuf, "findMain: "); strcat(errbuf, strerror(errno)); return NULL; } } if (i == -1) { errvalid++; strcpy(errbuf, "findMain: "); strcat(errbuf, strerror(errno)); free(buf); return NULL; } /* * The first entry is the main module. The data segment * starts with the TOC entries for all exports, so the * data segment origin works as argument for loadbind. */ lp = (struct ld_info *) buf; ret = lp->ldinfo_dataorg; free(buf); return ret; }
VOID *dlopen(const char *path, int mode) { register ModulePtr mp; static void *mainModule; /* * Upon the first call register a terminate handler that will * close all libraries. Also get a reference to the main module * for use with loadbind. */ if (!mainModule) { if ((mainModule = findMain()) == NULL) return NULL; atexit(terminate); } /* * Scan the list of modules if we have the module already loaded. */ for (mp = modList; mp; mp = mp->next) if (strcmp(mp->name, path) == 0) { mp->refCnt++; return (VOID *) mp; } if ((mp = (ModulePtr)calloc(1, sizeof(*mp))) == NULL) { errvalid++; strcpy(errbuf, "calloc: "); strcat(errbuf, strerror(errno)); return (VOID *) NULL; } mp->name = malloc((unsigned) (strlen(path) + 1)); strcpy(mp->name, path); /* * load should be declared load(const char *...). Thus we * cast the path to a normal char *. Ugly. */ if ((mp->entry = (void *)load((char *)path, L_NOAUTODEFER, NULL)) == NULL) { free(mp->name); free(mp); errvalid++; strcpy(errbuf, "dlopen: "); strcat(errbuf, path); strcat(errbuf, ": "); /* * If AIX says the file is not executable, the error * can be further described by querying the loader about * the last error. */ if (errno == ENOEXEC) { char *tmp[BUFSIZ/sizeof(char *)]; if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) strcpy(errbuf, strerror(errno)); else { char **p; for (p = tmp; *p; p++) caterr(*p); } } else strcat(errbuf, strerror(errno)); return (VOID *) NULL; } mp->refCnt = 1; mp->next = modList; modList = mp; if (loadbind(0, mainModule, mp->entry) == -1) { dlclose(mp); errvalid++; strcpy(errbuf, "loadbind: "); strcat(errbuf, strerror(errno)); return (VOID *) NULL; } /* * If the user wants global binding, loadbind against all other * loaded modules. */ if (mode & RTLD_GLOBAL) { register ModulePtr mp1; for (mp1 = mp->next; mp1; mp1 = mp1->next) if (loadbind(0, mp1->entry, mp->entry) == -1) { dlclose(mp); errvalid++; strcpy(errbuf, "loadbind: "); strcat(errbuf, strerror(errno)); return (VOID *) NULL; } } if (readExports(mp) == -1) { dlclose(mp); return (VOID *) NULL; } /* * If there is a dl_info structure, call the init function. */ if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) { if (mp->info->init) (*mp->info->init)(); } else errvalid = 0; /* * If the shared object was compiled using xlC we will need * to call static constructors (and later on dlclose destructors). */ if (mp->cdtors = (CdtorPtr)dlsym(mp, "__cdtors")) { while (mp->cdtors->init) { (*mp->cdtors->init)(); mp->cdtors++; } } else errvalid = 0; return (VOID *) mp; }
/* * Build the export table from the XCOFF .loader section. */ static int readExports(ModulePtr mp) { LDFILE *ldp = NULL; SCNHDR sh, shdata; LDHDR *lhp; char *ldbuf; LDSYM *ls; int i; ExportPtr ep; if ((ldp = ldopen(mp->name, ldp)) == NULL) { struct ld_info *lp; char *buf; int size = 4*1024; if (errno != ENOENT) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); return -1; } /* * The module might be loaded due to the LIBPATH * environment variable. Search for the loaded * module using L_GETINFO. */ if ((buf = malloc(size)) == NULL) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); return -1; } while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) { free(buf); size += 4*1024; if ((buf = malloc(size)) == NULL) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); return -1; } } if (i == -1) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); free(buf); return -1; } /* * Traverse the list of loaded modules. The entry point * returned by load() does actually point to the data * segment origin. */ lp = (struct ld_info *)buf; while (lp) { if (lp->ldinfo_dataorg == mp->entry) { ldp = ldopen(lp->ldinfo_filename, ldp); break; } if (lp->ldinfo_next == 0) lp = NULL; else lp = (struct ld_info *)((char *)lp + lp->ldinfo_next); } free(buf); if (!ldp) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); return -1; } } if (TYPE(ldp) != U802TOCMAGIC) { errvalid++; strcpy(errbuf, "readExports: bad magic"); while(ldclose(ldp) == FAILURE) ; return -1; } /* * Get the padding for the data section. This is needed for * AIX 4.1 compilers. This is used when building the final * function pointer to the exported symbol. */ if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) { errvalid++; strcpy(errbuf, "readExports: cannot read data section header"); while(ldclose(ldp) == FAILURE) ; return -1; } if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) { errvalid++; strcpy(errbuf, "readExports: cannot read loader section header"); while(ldclose(ldp) == FAILURE) ; return -1; } /* * We read the complete loader section in one chunk, this makes * finding long symbol names residing in the string table easier. */ if ((ldbuf = (char *)malloc(sh.s_size)) == NULL) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); while(ldclose(ldp) == FAILURE) ; return -1; } if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) { errvalid++; strcpy(errbuf, "readExports: cannot seek to loader section"); free(ldbuf); while(ldclose(ldp) == FAILURE) ; return -1; } if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) { errvalid++; strcpy(errbuf, "readExports: cannot read loader section"); free(ldbuf); while(ldclose(ldp) == FAILURE) ; return -1; } lhp = (LDHDR *)ldbuf; ls = (LDSYM *)(ldbuf+LDHDRSZ); /* * Count the number of exports to include in our export table. */ for (i = lhp->l_nsyms; i; i--, ls++) { if (!LDR_EXPORT(*ls)) continue; mp->nExports++; } if ((mp->exports = (ExportPtr)calloc(mp->nExports, sizeof(*mp->exports))) == NULL) { errvalid++; strcpy(errbuf, "readExports: "); strcat(errbuf, strerror(errno)); free(ldbuf); while(ldclose(ldp) == FAILURE) ; return -1; } /* * Fill in the export table. All entries are relative to * the entry point we got from load. */ ep = mp->exports; ls = (LDSYM *)(ldbuf+LDHDRSZ); for (i = lhp->l_nsyms; i; i--, ls++) { char *symname; char tmpsym[SYMNMLEN+1]; if (!LDR_EXPORT(*ls)) continue; if (ls->l_zeroes == 0) symname = ls->l_offset+lhp->l_stoff+ldbuf; else { /* * The l_name member is not zero terminated, we * must copy the first SYMNMLEN chars and make * sure we have a zero byte at the end. */ strncpy(tmpsym, ls->l_name, SYMNMLEN); tmpsym[SYMNMLEN] = '\0'; symname = tmpsym; } ep->name = malloc((unsigned) (strlen(symname) + 1)); strcpy(ep->name, symname); ep->addr = (void *)((unsigned long)mp->entry + ls->l_value - shdata.s_vaddr); ep++; } free(ldbuf); while(ldclose(ldp) == FAILURE) ; return 0; }
int pgin_all(my_region_t *Caller_Supplied_Regions, int lock_in_memory) { pid_t mypid=getpid(); /* For data (heap) section */ void *my_edata; /* For Loader / Shared lib section */ struct ld_xinfo *p_ld_xinfo; char *ar; char ldbuf[LOADQUERY_BUFFER]; /* For stats */ uint64_t pg_cnt; struct procentry64 p[6]; /* Initial stats checkpoint */ stats_checkpoint_initial(&p[0]); /***************************** * FAST PAGE-IN DIRECTIVE * ***************************** */ if (lock_in_memory) { /* Shortcut ! */ if (mlockall(MCL_CURRENT|MCL_FUTURE) >= 0) { /* Great. mlockall forces the page-ins across the address space. Page-outs will be resisted. OK for a small memory size. * For a large memory size relative to the host system, don't lock... use lock_in_memory=0. */ printf ("MLOCKALL\n"); stats_checkpoint(mypid, pg_cnt, &p[0], &p[1]); return 0; } else fprintf(stderr,"Page locking in memory was requested. mlockall returned %s, error code %d. Continuing...\n",strerror(errno),errno); /* Fall through into the soft handler */ } /***************************** * MANUAL PAGE-IN ALGORITHM * ***************************** */ /* Text region */ /* The Loader section will also cover text */ printf ("TEXT: pg_ref(_text=0x%p, _etext=0x%p, %d, %d)\n", &_text, &_etext, 0, PAGE_TOUCH_RDONLY); pg_cnt=pg_ref(&_text, &_etext, 0, PAGE_TOUCH_RDONLY); stats_checkpoint(mypid, pg_cnt, &p[0], &p[1]); /* Data region */ my_edata=sbrk(0); printf ("DATA: pg_ref(_data=0x%p, _edata=0x%p, %d, %d)\n", &_data, my_edata, 0, PAGE_TOUCH_RDONLY); pg_ref(&_data, my_edata, 0, PAGE_TOUCH_RDONLY); /* Multi-threaded: read-only- not thread-safe */ /*pg_cnt=pg_ref(&_data, my_edata, 0, PAGE_TOUCH_RDWR);*/ /* Single-threaded: read-write */ stats_checkpoint(mypid, pg_cnt, &p[1], &p[2]); /* Loader / Shared Library text & data regions */ if ((loadquery(L_GETXINFO, (void*)ldbuf, sizeof(ldbuf)))<0) { if (errno==ENOMEM) fprintf(stderr,"loadquery returned %s, error code %d. Increase the static buffers or recode to make it dynamic.\n",strerror(errno),errno); else fprintf(stderr,"loadquery returned %s, error code %d.\n",strerror(errno),errno); return EXIT_FAILURE; } for (p_ld_xinfo=(struct ld_xinfo*) ldbuf; p_ld_xinfo; p_ld_xinfo = (p_ld_xinfo->ldinfo_next ? (void*)p_ld_xinfo + p_ld_xinfo->ldinfo_next : NULL)) { ar = (char*)p_ld_xinfo + p_ld_xinfo->ldinfo_filename; printf ("%s: file=\"%s\" member=\"%s\" text=0x%015" PRIX64 " text_len=0x%016" PRIX64 ", data=0x%015" PRIX64 " data_len=0x%016" PRIX64 ", tdata=0x%015" PRIX64 " tdata_len=0x%016" PRIX64 ", tbss_len=0x%016" PRIX64 "\n", "LOAD+SHLIB", ar, ar + strlen(ar) + 1, (ptr64_t)p_ld_xinfo->ldinfo_textorg, (uint64_t)p_ld_xinfo->ldinfo_textsize, (ptr64_t)p_ld_xinfo->ldinfo_dataorg, (uint64_t)p_ld_xinfo->ldinfo_datasize, (ptr64_t)p_ld_xinfo->ldinfo_tdataorg, (uint64_t)p_ld_xinfo->ldinfo_tdatasize, (uint64_t)p_ld_xinfo->ldinfo_tbsssize); printf ("text\n"); pg_cnt=pg_ref((void*)(p_ld_xinfo->ldinfo_textorg), (void*)(p_ld_xinfo->ldinfo_textorg) + p_ld_xinfo->ldinfo_textsize, 0, PAGE_TOUCH_RDONLY); stats_checkpoint(mypid, pg_cnt, &p[2], &p[3]); printf ("data\n"); pg_cnt=pg_ref((void*)(p_ld_xinfo->ldinfo_dataorg), (void*)(p_ld_xinfo->ldinfo_dataorg) + p_ld_xinfo->ldinfo_datasize, 0, PAGE_TOUCH_RDONLY); stats_checkpoint(mypid, pg_cnt, &p[3], &p[4]); memcpy(&p[2],&p[4],sizeof(p[2])); /* Reinit p[2] for next loop */ } /* Stack */ /* Skipping this - low quantity / low value */ ; /* Caller Supplied Regions * Includes shared memory, i.e. shmget/shmat and mmap * There is no C API method to derive shared memory mappings using * public interfaces, up to the time of writing, July 2014 / AIX 7.1 TL3. * svmon -P <pid> & procmap -S <pid> derive this using the * private, undocumented kernel performance extensions, specifically * getvsidsandprocl_pid() and ptx_getsegstat(). * Until IBM supplies a public API or guidance on the private interfaces * these cannot be derived. No consideration is given to calling out * externally to svmon, because this call is way too slow. * * In the meantime, the caller can identify the shared segments to map. */ if (Caller_Supplied_Regions) { int i; for (i=0; Caller_Supplied_Regions[i].addr; i++) { printf("CALLER SUPPLIED REGION #%d: 0x%p - 0x%p\n", i, (void*)(Caller_Supplied_Regions[i].addr), (void*)(Caller_Supplied_Regions[i].addr) + Caller_Supplied_Regions[i].size); pg_cnt=pg_ref((void*)(Caller_Supplied_Regions[i].addr), (void*)(Caller_Supplied_Regions[i].addr) + Caller_Supplied_Regions[i].size, 0, PAGE_TOUCH_RDONLY); stats_checkpoint(mypid, pg_cnt, &p[4], &p[5]); memcpy(&p[4],&p[5],sizeof(p[4])); /* Reinit p[4] for next loop */ } } }
static int fill_dll_info(void) { return loadquery(L_GETINFO, dladdr_buffer, sizeof(dladdr_buffer)); }
/* ARGSUSED */ void *dlopen(const char *path, int mode) { register ModulePtr mp; static void *mainModule; /* * Upon the first call register a terminate handler that will * close all libraries. Also get a reference to the main module * for use with loadbind. */ if (!mainModule) { if ((mainModule = findMain()) == NULL) return NULL; atexit(terminate); } /* * Scan the list of modules if have the module already loaded. */ for (mp = modList; mp; mp = mp->next) if (strcmp(mp->name, path) == 0) { mp->refCnt++; return mp; } if ((mp = (ModulePtr)calloc(1, sizeof(*mp))) == NULL) { errvalid++; strcpy(errbuf, "calloc: "); strcat(errbuf, strerror(errno)); return NULL; } if ((mp->name = strdup(path)) == NULL) { errvalid++; strcpy(errbuf, "strdup: "); strcat(errbuf, strerror(errno)); free(mp); return NULL; } /* * load should be declared load(const char *...). Thus we * cast the path to a normal char *. Ugly. */ if ((mp->entry = (void *)load((char *)path, L_NOAUTODEFER, NULL)) == NULL) { free(mp->name); free(mp); errvalid++; strcpy(errbuf, "dlopen: "); strcat(errbuf, path); strcat(errbuf, ": "); /* * If AIX says the file is not executable, the error * can be further described by querying the loader about * the last error. */ if (errno == ENOEXEC) { char *tmp[BUFSIZ/sizeof(char *)]; if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1) strcpy(errbuf, strerror(errno)); else { char **p; for (p = tmp; *p; p++) caterr(*p); } } else strcat(errbuf, strerror(errno)); return NULL; } mp->refCnt = 1; mp->next = modList; modList = mp; if (loadbind(0, mainModule, mp->entry) == -1) { dlclose(mp); errvalid++; strcpy(errbuf, "loadbind: "); strcat(errbuf, strerror(errno)); return NULL; } if (readExports(mp) == -1) { dlclose(mp); return NULL; } /* * If there is a dl_info structure, call the init function. */ if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) { if (mp->info->init) (*mp->info->init)(); } else errvalid = 0; return mp; }
// Rebuild the internal module table. If an error occurs, old table remains // unchanged. static bool reload_table() { bool rc = false; trcVerbose("reload module table..."); entry_t* new_list = NULL; const struct ld_info* ldi = NULL; // Call loadquery(L_GETINFO..) to get a list of all loaded Dlls from AIX. loadquery // requires a large enough buffer. uint8_t* buffer = NULL; size_t buflen = 1024; for (;;) { buffer = (uint8_t*) ::realloc(buffer, buflen); if (loadquery(L_GETINFO, buffer, buflen) == -1) { if (errno == ENOMEM) { buflen *= 2; } else { trcVerbose("loadquery failed (%d)", errno); goto cleanup; } } else { break; } } trcVerbose("loadquery buffer size is %llu.", buflen); // Iterate over the loadquery result. For details see sys/ldr.h on AIX. ldi = (struct ld_info*) buffer; for (;;) { entry_t* e = (entry_t*) ::malloc(sizeof(entry_t)); if (!e) { trcVerbose("OOM."); goto cleanup; } memset(e, 0, sizeof(entry_t)); e->info.text = ldi->ldinfo_textorg; e->info.text_len = ldi->ldinfo_textsize; e->info.data = ldi->ldinfo_dataorg; e->info.data_len = ldi->ldinfo_datasize; e->info.path = g_stringlist.add(ldi->ldinfo_filename); if (!e->info.path) { trcVerbose("OOM."); goto cleanup; } // Extract short name { const char* p = strrchr(e->info.path, '/'); if (p) { p ++; e->info.shortname = p; } else { e->info.shortname = e->info.path; } } // Do we have a member name as well (see ldr.h)? const char* p_mbr_name = ldi->ldinfo_filename + strlen(ldi->ldinfo_filename) + 1; if (*p_mbr_name) { e->info.member = g_stringlist.add(p_mbr_name); if (!e->info.member) { trcVerbose("OOM."); goto cleanup; } } else { e->info.member = NULL; } if (strcmp(e->info.shortname, "libjvm.so") == 0) { // Note that this, theoretically, is fuzzy. We may accidentally contain // more than one libjvm.so. But that is improbable, so lets go with this // solution. e->info.is_in_vm = true; } trcVerbose("entry: %p %llu, %p %llu, %s %s %s, %d", e->info.text, e->info.text_len, e->info.data, e->info.data_len, e->info.path, e->info.shortname, (e->info.member ? e->info.member : "NULL"), e->info.is_in_vm ); // Add to list. add_entry_to_list(e, &new_list); // Next entry... if (ldi->ldinfo_next) { ldi = (struct ld_info*)(((char*)ldi) + ldi->ldinfo_next); } else { break; } } // We are done. All is well. Free old list and swap to new one. if (g_first) { free_entry_list(&g_first); } g_first = new_list; new_list = NULL; rc = true; cleanup: if (new_list) { free_entry_list(&new_list); } ::free(buffer); return rc; } // end LoadedLibraries::reload()