Exemplo n.º 1
0
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
}
Exemplo n.º 2
0
/*
 * 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;
}
Exemplo n.º 3
0
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();
    }
}
Exemplo n.º 4
0
/*
 * 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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
/*
 * 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;
}
Exemplo n.º 7
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 */
		}
	 }
	 
}
Exemplo n.º 8
0
static int fill_dll_info(void) {
    return loadquery(L_GETINFO, dladdr_buffer, sizeof(dladdr_buffer));
}
Exemplo n.º 9
0
/* 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;
}
Exemplo n.º 10
0
// 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()