| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856 | /* * ldconfig - update shared library symlinks * * usage: ldconfig [-DvqnNX] [-f conf] [-C cache] [-r root] dir ... *        ldconfig -l [-Dv] lib ... *        ldconfig -p *        -D: debug mode, don't update links *        -v: verbose mode, print things as we go *        -q: quiet mode, don't print warnings *        -n: don't process standard directories *        -N: don't update the library cache *        -X: don't update the library links *        -l: library mode, manually link libraries *        -p: print the current library cache *        -f conf: use conf instead of /etc/ld.so.conf *        -C cache: use cache instead of /etc/ld.so.cache *        -r root: first, do a chroot to the indicated directory *        dir ...: directories to process *        lib ...: libraries to link * * Copyright 1994-2000 David Engel and Mitch D'Souza * * This program may be used for any purpose as long as this * copyright notice is kept. */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <ctype.h>#include <getopt.h>#include <dirent.h>#include <unistd.h>#include <link.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>#include <errno.h>#include "elf.h"#include "../config.h"#include "readsoname.h"struct exec{  unsigned long a_info;		/* Use macros N_MAGIC, etc for access */  unsigned a_text;		/* length of text, in bytes */  unsigned a_data;		/* length of data, in bytes */  unsigned a_bss;		/* length of uninitialized data area for file, in bytes */  unsigned a_syms;		/* length of symbol table data in file, in bytes */  unsigned a_entry;		/* start address */  unsigned a_trsize;		/* length of relocation info for text, in bytes */  unsigned a_drsize;		/* length of relocation info for data, in bytes */};#if !defined (N_MAGIC)#define N_MAGIC(exec) ((exec).a_info & 0xffff)#endif/* Code indicating object file or impure executable.  */#define OMAGIC 0407/* Code indicating pure executable.  */#define NMAGIC 0410/* Code indicating demand-paged executable.  */#define ZMAGIC 0413/* This indicates a demand-paged executable with the header in the text.    The first page is unmapped to help trap NULL pointer references */#define QMAGIC 0314/* Code indicating core file.  */#define CMAGIC 0421#ifdef __GNUC__void warn(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));void error(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));#endifchar *___strtok = NULL;/* For SunOS */#ifndef PATH_MAX#include <limits.h>#define PATH_MAX _POSIX_PATH_MAX#endif/* For SunOS */#ifndef N_MAGIC#define N_MAGIC(exec) ((exec).a_magic & 0xffff)#endif#define EXIT_OK    0#define EXIT_FATAL 128char *prog = NULL;int debug = 0;			/* debug mode */int verbose = 0;		/* verbose mode */int libmode = 0;		/* library mode */int nocache = 0;		/* don't build cache */int nolinks = 0;		/* don't update links */char *conffile = LDSO_CONF;	/* default conf file */char *cachefile = LDSO_CACHE;	/* default cache file */void cache_print(void);void cache_dolib(const char *dir, const char *so, int libtype);void cache_write(void);void warn(const char *fmt, ...){    va_list ap;    if (verbose < 0)        return;    fflush(stdout);    /* don't mix output and error messages */    fprintf(stderr, "%s: warning: ", prog);    va_start(ap, fmt);    vfprintf(stderr, fmt, ap);    va_end(ap);    fprintf(stderr, "\n");    return;}void error(const char *fmt, ...){    va_list ap;    fflush(stdout);    /* don't mix output and error messages */    fprintf(stderr, "%s: ", prog);    va_start(ap, fmt);    vfprintf(stderr, fmt, ap);    va_end(ap);    fprintf(stderr, "\n");    exit(EXIT_FATAL);}void *xmalloc(size_t size){    void *ptr;    if ((ptr = malloc(size)) == NULL)	error("out of memory");    return ptr;}char *xstrdup(const char *str){    char *ptr;    if ((ptr = strdup(str)) == NULL)	error("out of memory");    return ptr;}/* If shared library, return a malloced copy of the soname and set the   type, else return NULL.   expected_type should be either LIB_ANY or one of the following:-   LIB_DLL   LIB_ELF   LIB_ELF_LIBC5   LIB_ELF_LIBC6   If the lib is ELF and we can not deduce the type the type will   be set based on expected_type.   If the expected, actual/deduced types missmatch we display a warning   and use the actual/deduced type.*/char *is_shlib(const char *dir, const char *name, int *type,	int *islink, int expected_type){    char *good = NULL;    char *cp, *cp2;    FILE *file;    struct exec exec;    ElfW(Ehdr) *elf_hdr;    struct stat statbuf;    char buff[4096];    /* see if name is of the form libZ.so* */    if ((strncmp(name, "lib", 3) == 0 || strncmp(name, "ld-", 3) == 0) && \	name[strlen(name)-1] != '~' && (cp = strstr(name, ".so")))    {	/* find the start of the Vminor part, if any */	if (cp[3] == '.' && (cp2 = strchr(cp + 4, '.')))	    cp = cp2;	else	    cp = cp + strlen(cp);	/* construct the full path name */	sprintf(buff, "%s%s%s", dir, (*dir && strcmp(dir, "/")) ?		"/" : "", name);	/* first, make sure it's a regular file */	if (lstat(buff, &statbuf))	    warn("can't lstat %s (%s), skipping", buff, strerror(errno));	else if (!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode))	    warn("%s is not a regular file or symlink, skipping", buff);	else	{	    /* is it a regular file or a symlink */	    *islink = S_ISLNK(statbuf.st_mode);	    /* then try opening it */	    if (!(file = fopen(buff, "rb")))		warn("can't open %s (%s), skipping", buff, strerror(errno));	    else	    {		/* now make sure it's a shared library */		if (fread(&exec, sizeof exec, 1, file) < 1)		    warn("can't read header from %s, skipping", buff);		else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC)		{		    elf_hdr = (ElfW(Ehdr) *) &exec;		    if (elf_hdr->e_ident[0] != 0x7f ||			strncmp(&elf_hdr->e_ident[1], "ELF",3) != 0)		    {		        /* silently ignore linker scripts */		        if (strncmp((char *)&exec, "/* GNU ld", 9) != 0)			    warn("%s is not a shared library, skipping", buff);		    }		    else		    {		        /* always call readsoname to update type */			if(expected_type == LIB_DLL){			    warn("%s is not an a.out library, its ELF!\n", buff);			    expected_type=LIB_ANY;			}		        *type = LIB_ELF;		        good = readsoname(buff, file, expected_type, type, 					  elf_hdr->e_ident[EI_CLASS]);			if (good == NULL || *islink)			{			    if (good != NULL)			        free(good);			    good = xstrdup(name);			}			else			{			    /* if the soname does not match the filename,			       issue a warning, but only in debug mode. */			    int len = strlen(good);			    if (debug && (strncmp(good, name, len) != 0 ||				(name[len] != '\0' && name[len] != '.')))			        warn("%s has inconsistent soname (%s)",				     buff, good);			}		    }		}		else		{		    if (*islink)		        good = xstrdup(name);		    else		    {		        good = xmalloc(cp - name + 1);			strncpy(good, name, cp - name);			good[cp - name] = '\0';		    }		    if(expected_type != LIB_ANY && expected_type != LIB_DLL)		    {			warn("%s is not an ELF library, its an a.out DLL!", buff);			expected_type=LIB_ANY;		    }		    		    *type = LIB_DLL;		}		fclose(file);	    }	}    }    return good;}/* update the symlink to new library */void link_shlib(const char *dir, const char *file, const char *so){    int change = 1;    char libname[4096];    char linkname[4096];    struct stat libstat;    struct stat linkstat;    /* construct the full path names */    sprintf(libname, "%s/%s", dir, file);    sprintf(linkname, "%s/%s", dir, so);    /* see if a link already exists */    if (!stat(linkname, &linkstat))    {	/* now see if it's the one we want */	if (stat(libname, &libstat))	    warn("can't stat %s (%s)", libname, strerror(errno));	else if (libstat.st_dev == linkstat.st_dev &&	    libstat.st_ino == linkstat.st_ino)	    change = 0;    }    /* then update the link, if required */    if (change > 0 && !nolinks)    {	if (!lstat(linkname, &linkstat))	{	  if (!S_ISLNK(linkstat.st_mode))	  {	    warn("%s is not a symlink", linkname);	    change = -1;	  }	  else if (remove(linkname))	  {	    warn("can't unlink %s (%s)", linkname, strerror(errno));	    change = -1;	  }	}	if (change > 0)	{	  if (symlink(file, linkname))	  {	    warn("can't link %s to %s (%s)", linkname, file, strerror(errno));	    change = -1;	  }	}    }    /* some people like to know what we're doing */    if (verbose > 0)	printf("\t%s => %s%s\n", so, file,	       change < 0 ? " (SKIPPED)" :	       (change > 0 ? " (changed)" : ""));    return;}/* figure out which library is greater */int libcmp(char *p1, char *p2){    while (*p1)    {	if (isdigit(*p1) && isdigit(*p2))	{	    /* must compare this numerically */	    int v1, v2;	    v1 = strtoul(p1, &p1, 10);	    v2 = strtoul(p2, &p2, 10);	    if (v1 != v2)		return v1 - v2;	}	else if (isdigit(*p1) && !isdigit(*p2))	    return 1;	else if (!isdigit(*p1) && isdigit(*p2))	    return -1;	else if (*p1 != *p2)	    return *p1 - *p2;	else	    p1++, p2++;    }    return *p1 - *p2;}struct lib{    char *so;			/* soname of a library */    char *name;			/* name of a library */    int libtype;		/* type of a library */    int islink;			/* is it a symlink */    struct lib *next;		/* next library in list */};/* update all shared library links in a directory */void scan_dir(const char *name){    DIR *dir;    struct dirent *ent;    char *so;    struct lib *lp, *libs = NULL;    int libtype, islink;    int expected_type = LIB_ANY;    char *t;    /* Check for an embedded expected type */    t=strrchr(name, '=');    if( t )    {	*t++ = '\0'; /* Skip = char */	if(strcasecmp(t, "libc4") == 0)	{	    expected_type = LIB_DLL;	} 	else	{	    if(strcasecmp(t, "libc5") == 0)	    {		expected_type = LIB_ELF_LIBC5; 	    }	    else	    {		if(strcasecmp(t, "libc6") == 0)		{		    expected_type = LIB_ELF_LIBC6;		}		else		{		    warn("Unknown type field '%s' for dir '%s' - ignored\n", t, name);		    expected_type = LIB_ANY;		}	    }	}    }    /* let 'em know what's going on */    if (verbose > 0)	printf("%s:\n", name);    /* if we can't open it, we can't do anything */    if ((dir = opendir(name)) == NULL)    {	warn("can't open %s (%s), skipping", name, strerror(errno));	return;    }    /* yes, we have to look at every single file */    while ((ent = readdir(dir)) != NULL)    {	/* if it's not a shared library, don't bother */	if ((so = is_shlib(name, ent->d_name, &libtype, &islink, expected_type)) == NULL)	    continue;	/* have we already seen one with the same so name? */	for (lp = libs; lp; lp = lp->next)	{	    if (strcmp(so, lp->so) == 0)	    {	        /* we have, which one do we want to use? */	        if ((!islink && lp->islink) ||		    (islink == lp->islink && 		     libcmp(ent->d_name, lp->name) > 0))		{		    /* let's use the new one */		    free(lp->name);		    lp->name = xstrdup(ent->d_name);		    lp->libtype = libtype;		    lp->islink = islink;		} 		break;	    }	}	/* congratulations, you're the first one we've seen */	if (!lp)	{	    lp = xmalloc(sizeof *lp);	    lp->so = xstrdup(so);	    lp->name = xstrdup(ent->d_name);	    lp->libtype = libtype;	    lp->islink = islink;	    lp->next = libs;	    libs = lp;	}	free(so);    }    /* don't need this any more */    closedir(dir);    /* now we have all the latest libs, update the links */    for (lp = libs; lp; lp = lp->next)    {        if (!lp->islink)	    link_shlib(name, lp->name, lp->so);	if (!nocache)	    cache_dolib(name, lp->so, lp->libtype);    }    /* always try to clean up after ourselves */    while (libs)    {	lp = libs->next;	free(libs->so);	free(libs->name);	free(libs);	libs = lp;    }    return;}/* return the list of system-specific directories */char *get_extpath(void){    char *res = NULL, *cp;    FILE *file;    struct stat stat;    if ((file = fopen(conffile, "r")) != NULL)    {	fstat(fileno(file), &stat);	res = xmalloc(stat.st_size + 1);	fread(res, 1, stat.st_size, file);	fclose(file);	res[stat.st_size] = '\0';	/* convert comments fo spaces */	for (cp = res; *cp; /*nada*/) {	  if (*cp == '#') {	    do	      *cp++ = ' ';	    while (*cp && *cp != '\n');	  } else {	    cp++;	  }	}	      }    return res;}void usage(void){    fprintf(stderr,	    "ldconfig - update shared library symlinks\n"	    "\n"	    "usage: ldconfig [-DvqnNX] [-f conf] [-C cache] [-r root] dir ...\n"	    "       ldconfig -l [-Dv] lib ...\n"	    "       ldconfig -p\n"	    "\t-D:\t\tdebug mode, don't update links\n"	    "\t-v:\t\tverbose mode, print things as we go\n"	    "\t-q:\t\tquiet mode, don't print warnings\n"	    "\t-n:\t\tdon't process standard directories\n"	    "\t-N:\t\tdon't update the library cache\n"	    "\t-X:\t\tdon't update the library links\n"	    "\t-l:\t\tlibrary mode, manually link libraries\n"	    "\t-p:\t\tprint the current library cache\n"	    "\t-f conf :\tuse conf instead of %s\n"	    "\t-C cache:\tuse cache instead of %s\n"	    "\t-r root :\tfirst, do a chroot to the indicated directory\n"	    "\tdir ... :\tdirectories to process\n"	    "\tlib ... :\tlibraries to link\n"	    "\n"	    "Copyright 1994-2000 David Engel and Mitch D'Souza\n",	    LDSO_CONF, LDSO_CACHE	    );    exit(EXIT_FATAL);}int main(int argc, char **argv){    int i, c;    int nodefault = 0;    int printcache = 0;    char *cp, *dir, *so;    char *extpath;    int libtype, islink;    char *chroot_dir = NULL;    prog = argv[0];    opterr = 0;    while ((c = getopt(argc, argv, "DvqnNXlpf:C:r:")) != EOF)	switch (c)	{	case 'D':	    debug = 1;		/* debug mode */	    nocache = 1;	    nolinks = 1;	    verbose = 1;	    break;	case 'v':	    verbose = 1;	/* verbose mode */	    break;	case 'q':	    if (verbose <= 0)	        verbose = -1;	/* quiet mode */	    break;	case 'n':	    nodefault = 1;	/* no default dirs */	    nocache = 1;	    break;	case 'N':	    nocache = 1;	/* don't build cache */	    break;	case 'X':	    nolinks = 1;	/* don't update links */	    break;	case 'l':	    libmode = 1;	/* library mode */	    break;	case 'p':	    printcache = 1;	/* print cache */	    break;	case 'f':	    conffile = optarg;	/* alternate conf file */	    break;	case 'C':	    cachefile = optarg;	/* alternate cache file */	    break;	case 'r':	    chroot_dir = optarg;	    break;	default:	    usage();	    break;	/* THE REST OF THESE ARE UNDOCUMENTED AND MAY BE REMOVED	   IN FUTURE VERSIONS. */	}    if (chroot_dir && *chroot_dir) {	if (chroot(chroot_dir) < 0)	    error("couldn't chroot to %s (%s)", chroot_dir, strerror(errno));        if (chdir("/") < 0)	    error("couldn't chdir to / (%s)", strerror(errno));    }    /* allow me to introduce myself, hi, my name is ... */    if (verbose > 0)	printf("%s: uClibc version\n", argv[0]);    if (printcache)    {	/* print the cache -- don't you trust me? */	cache_print();	exit(EXIT_OK);    }    else if (libmode)    {	/* so you want to do things manually, eh? */	/* ok, if you're so smart, which libraries do we link? */	for (i = optind; i < argc; i++)	{	    /* split into directory and file parts */	    if (!(cp = strrchr(argv[i], '/')))	    {		dir = ".";	/* no dir, only a filename */		cp = argv[i];	    }	    else	    {		if (cp == argv[i])		    dir = "/";	/* file in root directory */		else		    dir = argv[i];		*cp++ = '\0';	/* neither of the above */	    }	    /* we'd better do a little bit of checking */	    if ((so = is_shlib(dir, cp, &libtype, &islink, LIB_ANY)) == NULL)		error("%s%s%s is not a shared library", dir,		      (*dir && strcmp(dir, "/")) ? "/" : "", cp);	    /* so far, so good, maybe he knows what he's doing */	    link_shlib(dir, cp, so);	}    }    else    {	/* the lazy bum want's us to do all the work for him */	/* don't cache dirs on the command line */	int nocache_save = nocache;	nocache = 1;	/* OK, which directories should we do? */	for (i = optind; i < argc; i++)	    scan_dir(argv[i]);	/* restore the desired caching state */	nocache = nocache_save;	/* look ma, no defaults */	if (!nodefault)	{	    /* I guess the defaults aren't good enough */	    if ((extpath = get_extpath()))	    {		for (cp = strtok(extpath, DIR_SEP); cp;		     cp = strtok(NULL, DIR_SEP))		    scan_dir(cp);		free(extpath);	    }	    scan_dir(UCLIBC_TARGET_PREFIX "/usr/lib");	    scan_dir(UCLIBC_TARGET_PREFIX "/lib");	}	if (!nocache)	    cache_write();    }    exit(EXIT_OK);}typedef struct liblist{    int flags;    int sooffset;    int liboffset;    char *soname;    char *libname;    struct liblist *next;} liblist_t;static header_t magic = { LDSO_CACHE_MAGIC, LDSO_CACHE_VER, 0 };static liblist_t *lib_head = NULL;static int liblistcomp(liblist_t *x, liblist_t *y){    int res;    if ((res = libcmp(x->soname, y->soname)) == 0)    {        res = libcmp(strrchr(x->libname, '/') + 1,		     strrchr(y->libname, '/') + 1);    }    return res;}void cache_dolib(const char *dir, const char *so, int libtype){    char fullpath[PATH_MAX];    liblist_t *new_lib, *cur_lib;    magic.nlibs++;    sprintf(fullpath, "%s/%s", dir, so);    new_lib = xmalloc(sizeof (liblist_t));    new_lib->flags = libtype;    new_lib->soname = xstrdup(so);    new_lib->libname = xstrdup(fullpath);    if (lib_head == NULL || liblistcomp(new_lib, lib_head) > 0)    {        new_lib->next = lib_head;	lib_head = new_lib;    }    else    {        for (cur_lib = lib_head; cur_lib->next != NULL &&	     liblistcomp(new_lib, cur_lib->next) <= 0;	     cur_lib = cur_lib->next)	    /* nothing */;	new_lib->next = cur_lib->next;	cur_lib->next = new_lib;    }}void cache_write(void){    int cachefd;    int stroffset = 0;    char tempfile[4096];    liblist_t *cur_lib;    if (!magic.nlibs)	return;    sprintf(tempfile, "%s~", cachefile);    if (unlink(tempfile) && errno != ENOENT)        error("can't unlink %s (%s)", tempfile, strerror(errno));    if ((cachefd = creat(tempfile, 0644)) < 0)	error("can't create %s (%s)", tempfile, strerror(errno));    if (write(cachefd, &magic, sizeof (header_t)) != sizeof (header_t))	error("can't write %s (%s)", tempfile, strerror(errno));    for (cur_lib = lib_head; cur_lib != NULL; cur_lib = cur_lib->next)    {        cur_lib->sooffset = stroffset;	stroffset += strlen(cur_lib->soname) + 1;	cur_lib->liboffset = stroffset;	stroffset += strlen(cur_lib->libname) + 1;	if (write(cachefd, cur_lib, sizeof (libentry_t)) !=	    sizeof (libentry_t))	    error("can't write %s (%s)", tempfile, strerror(errno));    }    for (cur_lib = lib_head; cur_lib != NULL; cur_lib = cur_lib->next)    {      if (write(cachefd, cur_lib->soname, strlen(cur_lib->soname) + 1)	  != strlen(cur_lib->soname) + 1)	  error("can't write %s (%s)", tempfile, strerror(errno));      if (write(cachefd, cur_lib->libname, strlen(cur_lib->libname) + 1)	  != strlen(cur_lib->libname) + 1)	  error("can't write %s (%s)", tempfile, strerror(errno));    }    if (close(cachefd))        error("can't close %s (%s)", tempfile, strerror(errno));    if (chmod(tempfile, 0644))	error("can't chmod %s (%s)", tempfile, strerror(errno));    if (rename(tempfile, cachefile))	error("can't rename %s (%s)", tempfile, strerror(errno));}void cache_print(void){    caddr_t c;    struct stat st;    int fd = 0;    char *strs;    header_t *header;    libentry_t *libent;    if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY))<0)	error("can't read %s (%s)", cachefile, strerror(errno));    if ((c = mmap(0,st.st_size, PROT_READ, MAP_SHARED ,fd, 0)) == (caddr_t)-1)	error("can't map %s (%s)", cachefile, strerror(errno));    close(fd);    if (memcmp(((header_t *)c)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))	error("%s cache corrupt", cachefile);    if (memcmp(((header_t *)c)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))	error("wrong cache version - expected %s", LDSO_CACHE_VER);    header = (header_t *)c;    libent = (libentry_t *)(c + sizeof (header_t));    strs = (char *)&libent[header->nlibs];    printf("%d libs found in cache `%s' (version %s)\n",	    header->nlibs, cachefile, LDSO_CACHE_VER);    for (fd = 0; fd < header->nlibs; fd++)    {	printf("\t%s ", strs + libent[fd].sooffset);	switch (libent[fd].flags & ~LIB_ELF64) 	{	  case LIB_DLL:	    printf("(libc4)");	    break;	  case LIB_ELF:	    printf("(ELF%s)", libent[fd].flags & LIB_ELF64 ? "/64" : "");	    break;	  case LIB_ELF_LIBC5:	  case LIB_ELF_LIBC6:	    printf("(libc%d%s)", (libent[fd].flags & ~LIB_ELF64) + 3,		   libent[fd].flags & LIB_ELF64 ? "/64" : "");	    break;	  default:	    printf("(unknown)");	    break;	}	printf(" => %s\n", strs + libent[fd].liboffset);    }    munmap (c,st.st_size);}
 |