/* elfls: Copyright (C) 1999,2011 by Brian Raiter * License GPLv2+: GNU GPL version 2 or later. * This is free software; you are free to change and redistribute it. * There is NO WARRANTY, to the extent permitted by law. */ #include #include #include #include #include #include #include #include #include "elfrw.h" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /* Memory allocation error message. */ #define nomem() (fputs("Out of memory!\n", stderr), exit(EXIT_FAILURE)) /* Structure used to organize strings to be displayed in a list. */ typedef struct textline { char *str; /* the string to display */ int size; /* the current length of the string */ int left; /* how much more the string can grow */ } textline; /* The online help text. */ static char const *yowzitch = "Usage: elfls [OPTIONS] FILE...\n" "Display information about the contents of ELF files.\n\n" " -c, --sources Display list of source files.\n" " -d, --dependencies Display list of dependencies.\n" " -P, --nophdr Omit the program header table.\n" " -S, --noshdr Omit the section header table.\n" " -i, --nostr Don't display some section contents.\n" " -p, --nopos Omit file position column.\n" " -w, --width=N Set maximum width of output.\n" " --help Display this help and exit.\n" " --version Display version information and exit.\n"; /* The version text. */ static char const *vourzhon = "elfls: version 1.1\n" "Copyright (C) 1999,2011 by Brian Raiter \n" "License GPLv2+: GNU GPL version 2 or later.\n" "This is free software; you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"; /* The global variables. */ static Elf64_Ehdr elffhdr; /* ELF header of current file */ static Elf64_Phdr *proghdr = NULL; /* program header table */ static Elf64_Shdr *secthdr = NULL; /* section header table */ static char *sectstr = NULL; /* sh string table */ static int proghdrs; /* FALSE if no ph table */ static int secthdrs; /* FALSE if no sh table */ static Elf64_Phdr *phentry = NULL; /* ph with the entry point */ static Elf64_Shdr *shshstr = NULL; /* sh with the sh string table */ static char const *programname; /* name of this program */ static char const *thefilename; /* name of current file */ static FILE *thefile; /* handle to current file */ static int phdrls = TRUE; /* TRUE = show ph table */ static int shdrls = TRUE; /* TRUE = show sh table */ static int srcfls = FALSE; /* TRUE = show source files */ static int ldepls = FALSE; /* TRUE = show libraries */ static int dostrs = TRUE; /* TRUE = show entry strings */ static int dooffs = TRUE; /* TRUE = show file offsets */ static char sizefmt[8]; /* num digits to show sizes */ static char addrfmt[8]; /* num digits to show addresses */ static char offsetfmt[8]; /* num digits to show offsets */ static int outwidth; /* maximum width of output */ /* The error-reporting function. */ static int err(char const *fmt, ...) { va_list args; fprintf(stderr, "%s: ", programname); if (fmt) { va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } else { fprintf(stderr, "%s: %s", thefilename, strerror(errno)); } fputc('\n', stderr); return 0; } /* * List output formatting functions. */ /* Allocate a textline array, including one initialized string for * each element. */ static textline *gettextlines(int count) { char *text; textline *lines; int width; int i; width = outwidth ? outwidth : 256; if (!(lines = malloc(count * (sizeof *lines + width)))) nomem(); text = (char*)(lines + count); for (i = 0 ; i < count ; ++i) { lines[i].str = text; lines[i].str[0] = '\0'; lines[i].size = 0; lines[i].left = width - 1; text += width; } return lines; } /* Concatenate formatted text to the given textline string. */ static int append(textline *line, char const *fmt, ...) { va_list args; int n; if (line->left <= 0) return 0; va_start(args, fmt); n = vsnprintf(line->str + line->size, line->left, fmt, args); va_end(args); if (n < 0) { line->size += line->left; line->left = 0; } else { line->size += n; line->left -= n; } return line->left; } /* Function to pass to qsort(). */ static int linesorter(const void *i1, const void *i2) { return strcmp(((textline*)i1)->str, ((textline*)i2)->str); } /* Output the given textline array strings to a stream, nicely * formatted in columns, somewhat in the style of ls. */ static void formatlist(FILE *fp, textline *lines, int count) { textline *line; int colx, dy; int i, j; colx = 1; for (i = 0, line = lines ; i < count ; ++i, ++line) { if (line->size > colx) colx = line->size; } if (outwidth) { i = (outwidth - 1) / colx; dy = i ? (count + i - 1) / i : count; colx = -(colx + 1); } else { dy = count; colx = 0; } for (j = 0 ; j < dy ; ++j) { for (i = j ; i < count - dy ; i += dy) fprintf(fp, "%*s", colx, lines[i].str); fprintf(fp, "%s\n", lines[i].str); } } /* Output the given list and its header text on one line. If it * won't fit on one line, then use formatlist() instead. */ static void outputlist(FILE *fp, textline *lines, int count, char const *header) { int maxwidth; int i, n; if (!count) return; fputs(header, fp); if (outwidth) { maxwidth = outwidth - strlen(header); for (i = n = 0 ; i < count ; ++i) { if ((n += lines[i].size + 1) >= maxwidth) { fputc('\n', fp); formatlist(fp, lines, count); return; } } } fputs(lines[0].str, fp); for (i = 1 ; i < count ; ++i) { fputc(' ', fp); fputs(lines[i].str, fp); } fputc('\n', fp); } /* * Generic file-reading functions. */ /* Read a piece of the current file into a freshly allocated buffer. */ static void *getarea(long offset, unsigned long size) { void *buf; if (fseek(thefile, offset, SEEK_SET)) return NULL; if (!(buf = malloc(size))) nomem(); if (fread(buf, size, 1, thefile) != 1) { free(buf); return NULL; } return buf; } /* Read a null-terminated string from the current file, up to a given * size, into a static buffer. If skip is nonzero, then skip over NULs * at the beginning of the string. */ static char const *getstring(unsigned long offset, unsigned long size, int skip) { static char *buf = NULL; static size_t buflen = 0; char *str; int n; if (!size) return ""; if (size > buflen) { if (!(buf = realloc(buf, size + 1))) nomem(); buflen = size; } if (fseek(thefile, offset, SEEK_SET)) return ""; if (skip) { if ((n = fread(buf, 1, size, thefile)) <= 0) return ""; for (str = buf ; !*str && n ; ++str, --n) ; } else { if (!fgets(buf, size, thefile)) return ""; n = size; str = buf; } if (!n) return ""; str[n] = '\0'; for (n = 0 ; str[n] ; ++n) if (str[n] < ' ' || str[n] > '~') str[n] = '.'; return str; } /* * Functions for examining ELF structures. */ /* Verify that the given ELF identifier is appropriate to our * expectations. */ static int checkelfident(unsigned char const id[EI_NIDENT]) { if (memcmp(id, ELFMAG, SELFMAG)) return err("%s: not an ELF file.", thefilename); if (id[EI_CLASS] != ELFCLASS32 && id[EI_CLASS] != ELFCLASS64) return err("%s: unrecognized ELF class: %d.", thefilename, (int)id[EI_CLASS]); if (id[EI_DATA] != ELFDATA2MSB && id[EI_DATA] != ELFDATA2LSB) return err("%s: unrecognized ELF data: %d.", thefilename, id[EI_DATA]); if (id[EI_VERSION] != EV_CURRENT) return err("%s: unrecognized ELF version: %d.", thefilename, (int)id[EI_VERSION]); return TRUE; } /* Read in the ELF header proper, and verify that its contents conform * to what the program can decipher. */ static int readelfhdr(void) { if (elfrw_read_Ehdr(thefile, &elffhdr) != 1) { if (ferror(thefile)) return err(NULL); else return err("%s: not an ELF file.", thefilename); } if (!checkelfident(elffhdr.e_ident)) return FALSE; switch (elffhdr.e_type) { case ET_REL: case ET_EXEC: case ET_DYN: case ET_CORE: break; default: return err("%s: unknown ELF file type (type = %u).", thefilename, elffhdr.e_type); } if (elffhdr.e_ehsize != sizeof(Elf32_Ehdr) && elffhdr.e_ehsize != sizeof(Elf64_Ehdr)) return err("%s: warning: unrecognized ELF header size: %d.", thefilename, elffhdr.e_ehsize); if (elffhdr.e_version != EV_CURRENT) return err("%s: unrecognized ELF header version: %u.", thefilename, (unsigned int)elffhdr.e_version); if (elffhdr.e_phoff != 0) { if (elffhdr.e_phentsize != sizeof(Elf32_Phdr) && elffhdr.e_phentsize != sizeof(Elf64_Phdr)) err("%s: unrecognized program header entry size: %u.", thefilename, elffhdr.e_phentsize); else proghdrs = 1; } if (elffhdr.e_shoff != 0) { if (elffhdr.e_shentsize != sizeof(Elf32_Shdr) && elffhdr.e_shentsize != sizeof(Elf64_Shdr)) err("%s: unrecognized section header entry size: %u.", thefilename, elffhdr.e_shentsize); else secthdrs = 1; } return TRUE; } /* Read in the program header table, if it is present. If the ELF * header lists an entry point, then compare the entry point with the * load address of the program header entries to determine which one * contains said point. */ static int readproghdrs(void) { int i, n; if (!proghdrs) return TRUE; n = elffhdr.e_phnum; if (!(proghdr = malloc(n * sizeof *proghdr))) nomem(); if (fseek(thefile, elffhdr.e_phoff, SEEK_SET) || elfrw_read_Phdrs(thefile, proghdr, n) != n) { err("%s: invalid program header table offset.", thefilename); proghdrs = FALSE; return TRUE; } if (elffhdr.e_entry) { for (i = 0 ; i < n ; ++i) { if (proghdr[i].p_type == PT_LOAD && elffhdr.e_entry >= proghdr[i].p_vaddr && elffhdr.e_entry < proghdr[i].p_vaddr + proghdr[i].p_memsz) { phentry = proghdr + i; break; } } } return TRUE; } /* Read in the section header table, if it is present. If a section * header string table is given in the ELF header, then also load the * string table contents. */ static int readsecthdrs(void) { int n; if (!secthdrs) return TRUE; n = elffhdr.e_shnum; if (!(secthdr = malloc(n * sizeof *secthdr))) nomem(); if (fseek(thefile, elffhdr.e_shoff, SEEK_SET) || elfrw_read_Shdrs(thefile, secthdr, n) != n) { err("%s: warning: invalid section header table offset.", thefilename); secthdrs = FALSE; return TRUE; } if (elffhdr.e_shstrndx != SHN_UNDEF) { shshstr = secthdr + elffhdr.e_shstrndx; if (!(sectstr = getarea(shshstr->sh_offset, shshstr->sh_size))) { err("%s: warning: invalid string table location.", thefilename); free(sectstr); sectstr = NULL; } } return TRUE; } /* Extract the list of source files, if present. The source files are * determined by loading the symbol table section (and its associated * string table) and looking up symbols of type STT_FILE. */ static int getsrcfiles(textline **plines) { Elf64_Sym *syms; char *nmstr; textline *lines; char *str; unsigned strtab, count, i; int j, n; if (!secthdrs) return 0; for (i = 0 ; i < elffhdr.e_shnum ; ++i) if (secthdr[i].sh_type == SHT_SYMTAB) break; if (i == elffhdr.e_shnum) return 0; count = secthdr[i].sh_size / secthdr[i].sh_entsize; strtab = secthdr[i].sh_link; if (!(syms = malloc(count * sizeof *syms))) nomem(); if (fseek(thefile, secthdr[i].sh_offset, SEEK_SET)) return err("%s: invalid symbol table offset.", thefilename); count = elfrw_read_Syms(thefile, syms, count); if (!count) return 0; nmstr = getarea(secthdr[strtab].sh_offset, secthdr[strtab].sh_size); if (!nmstr) { free(syms); return 0; } lines = gettextlines(count); n = 0; for (i = 0 ; i < count ; ++i) { if (ELF64_ST_TYPE(syms[i].st_info) != STT_FILE) continue; str = nmstr + syms[i].st_name; for (j = 0 ; j < n ; ++j) if (!strcmp(lines[j].str, str)) break; if (j == n) append(lines + n++, "%s", str); } free(syms); free(nmstr); if (n) *plines = lines; else free(lines); return n; } /* Extract the list of dependencies, if present. The program header * table entry of type PT_DYNAMIC contains the dynamic info, which * includes the dependent files, and the string table that contains * the file names. However, the string table is referenced by its * location in memory, as opposed to its location in the file, so * the program header table needs to be searched again to find the * part that will be loaded at that location. */ static int getlibraries(textline **plines) { Elf64_Dyn *dyns; char *nmstr; textline *lines; char *str; unsigned long strtab = 0, strsz = 0; unsigned count, i, j, n; if (!proghdrs) return 0; for (i = 0 ; i < elffhdr.e_phnum ; ++i) if (proghdr[i].p_type == PT_DYNAMIC) break; if (i == elffhdr.e_phnum) return 0; count = elfrw_count_Dyns(proghdr[i].p_filesz); if (!(dyns = malloc(count * sizeof *dyns))) nomem(); if (fseek(thefile, proghdr[i].p_offset, SEEK_SET)) return err("%s: invalid dynamic table offset.", thefilename); count = elfrw_read_Dyns(thefile, dyns, count); if (!count) return 0; n = 0; for (i = 0 ; i < count ; ++i) { if (dyns[i].d_tag == DT_STRTAB) strtab = dyns[i].d_un.d_ptr; else if (dyns[i].d_tag == DT_STRSZ) strsz = dyns[i].d_un.d_val; else if (dyns[i].d_tag == DT_NEEDED) ++n; } if (!strtab || !strsz) return 0; for (i = 0 ; i < elffhdr.e_phnum ; ++i) if (strtab >= proghdr[i].p_vaddr && strtab < proghdr[i].p_vaddr + proghdr[i].p_filesz) break; if (i == elffhdr.e_phnum) return 0; if (!(nmstr = getarea(proghdr[i].p_offset + (strtab - proghdr[i].p_vaddr), strsz))) return 0; lines = gettextlines(n); n = 0; for (i = 0 ; i < count ; ++i) { if (dyns[i].d_tag != DT_NEEDED) continue; str = nmstr + dyns[i].d_un.d_val; for (j = 0 ; j < n ; ++j) if (!strcmp(lines[j].str, str)) break; if (j == n) append(lines + n++, "%s", str); } free(dyns); free(nmstr); if (n) *plines = lines; else free(lines); return n; } /* * Output-generating functions. */ /* Determine how wide the size and offset columns need to be. */ static void makenumberfmts(void) { unsigned long maxsize, maxaddr, maxoffset; int i; maxsize = 0; maxoffset = 0; maxaddr = 0; if (proghdrs) { for (i = 0 ; i < elffhdr.e_phnum ; ++i) { if (maxsize < proghdr[i].p_filesz) maxsize = proghdr[i].p_filesz; if (maxoffset < proghdr[i].p_offset) maxoffset = proghdr[i].p_offset; if (maxaddr < proghdr[i].p_vaddr) maxaddr = proghdr[i].p_vaddr; } } if (secthdrs) { for (i = 0 ; i < elffhdr.e_shnum ; ++i) { if (maxsize < secthdr[i].sh_size) maxsize = secthdr[i].sh_size; if (maxoffset < secthdr[i].sh_offset) maxoffset = secthdr[i].sh_offset; } } maxsize >>= 20; for (i = 6 ; maxsize ; ++i, maxsize >>= 4) ; sprintf(sizefmt, "%%%dlX", i); maxoffset >>= 20; for (i = 6 ; maxoffset ; ++i, maxoffset >>= 4) ; sprintf(offsetfmt, "%%%dlX", i); maxaddr >>= 16; maxaddr >>= 16; for (i = 8 ; maxaddr ; ++i, maxaddr >>= 4) ; sprintf(addrfmt, "%%0%dlX", i); } /* Display a one-line description of the ELF file. */ static void describeehdr(FILE *fp) { fprintf(fp, "%s", thefilename); switch (elffhdr.e_type) { case ET_REL: break; case ET_EXEC: fputc('*', fp); break; case ET_DYN: fputc('&', fp); break; case ET_CORE: fputc('$', fp); break; default: fprintf(fp, "?(%u)", elffhdr.e_type); break; } switch (elffhdr.e_machine) { case EM_M32: fprintf(fp, " (AT&T M32)"); break; case EM_386: fprintf(fp, " (Intel 386)"); break; case EM_X86_64: fprintf(fp, " (Intel x86-64)"); break; case EM_860: fprintf(fp, " (Intel 860)"); break; case EM_MIPS: fprintf(fp, " (MIPS)"); break; case EM_68K: fprintf(fp, " (Motorola 68k)"); break; case EM_88K: fprintf(fp, " (Motorola 88k)"); break; case EM_SPARC: fprintf(fp, " (SPARC)"); break; case EM_S390: fprintf(fp, " (IBM S390)"); break; case EM_ALPHA: fprintf(fp, " (Digital Alpha)"); break; case EM_ARM: fprintf(fp, " (ARM)"); break; case EM_PARISC: fprintf(fp, " (HPPA)"); break; case EM_PPC: fprintf(fp, " (PowerPC)"); break; case EM_PPC64: fprintf(fp, " (64-bit PowerPC)"); break; default: fprintf(fp, " (?%u)", elffhdr.e_machine); break; } fputc('\n', fp); } /* Fill the given string with the terse description of the given * program header table entry. The description includes a character * that identifies the entry type, the segment permission flags, the * offset and size within the file, and the virtual address at which * to load the contents. */ static void describephdr(textline *line, Elf64_Phdr *phdr) { char const *str; int n; switch (phdr->p_type) { case PT_NULL: append(line, "(null)"); return; case PT_INTERP: append(line, "I "); break; case PT_NOTE: append(line, "N "); break; case PT_LOAD: append(line, "B "); break; case PT_PHDR: append(line, "P "); break; case PT_DYNAMIC: append(line, "D "); break; case PT_TLS: append(line, "T "); break; #ifdef PT_GNU_EH_FRAME case PT_GNU_EH_FRAME: append(line, "U "); break; #endif #ifdef PT_GNU_STACK case PT_GNU_STACK: append(line, ". "); break; #endif #ifdef PT_GNU_RELRO case PT_GNU_RELRO: append(line, "R "); break; #endif default: append(line, "? "); break; } if (dostrs) { switch (phdr->p_type) { case PT_INTERP: str = getstring(phdr->p_offset, phdr->p_filesz, FALSE); if (str && *str) { n = strlen(str); if (n > 30) append(line, "\"%.27s...\"", str); else append(line, "\"%s\"", str); return; } break; case PT_NOTE: if (phdr->p_filesz > 12) { str = getstring(phdr->p_offset + 12, phdr->p_filesz - 12, FALSE); if (str && *str) { n = strlen(str); if (n > 30) append(line, "\"%.27s...\"", str); else append(line, "\"%s\"", str); return; } } break; } } append(line, "%c%c%c", (phdr->p_flags & PF_R ? 'r' : '-'), (phdr->p_flags & PF_W ? 'w' : '-'), (phdr->p_flags & PF_X ? phdr == phentry ? 's' : 'x' : '-')); if (dooffs) append(line, sizefmt, phdr->p_offset); append(line, sizefmt, phdr->p_filesz); append(line, " "); append(line, addrfmt, phdr->p_vaddr); if (phdr->p_filesz != phdr->p_memsz) append(line, " +%lX", phdr->p_memsz - phdr->p_filesz); } /* Fill the given string with the terse description of the given * section header table entry. The description includes a character * that identifies the entry type, the flags, the offset and size * within the file, and the section name and the indices of any * related sections. */ static void describeshdr(textline *line, Elf64_Shdr *shdr) { char const *str; int n; switch (shdr->sh_type) { case SHT_NULL: append(line, "(null)"); return; case SHT_PROGBITS: if (sectstr) { str = sectstr + shdr->sh_name; if (!strcmp(str, ".comment")) { append(line, "C "); if (dostrs) { str = getstring(shdr->sh_offset, shdr->sh_size, TRUE); if (str && *str) { n = strlen(str); if (n > 30) append(line, "\"%.27s...\"", str); else append(line, "\"%s\"", str); return; } } break; } else if (!strcmp(str, ".interp")) { append(line, "I "); if (dostrs) { str = getstring(shdr->sh_offset, shdr->sh_size, FALSE); if (str && *str) { n = strlen(str); if (n > 30) append(line, "\"%.27s...\"", str); else append(line, "\"%s\"", str); return; } } break; } else if (!strcmp(str, ".plt") || !strcmp(str, ".got.plt")) { append(line, "P "); break; } else if (!strcmp(str, ".got")) { append(line, "O "); break; } else if (!memcmp(str, ".debug_", 7)) { append(line, "G "); break; } else if (!memcmp(str, ".eh_", 4)) { append(line, "U "); break; } } append(line, "B "); break; case SHT_NOTE: append(line, "N "); break; case SHT_STRTAB: append(line, "$ "); break; case SHT_SYMTAB: append(line, "S "); break; case SHT_DYNSYM: append(line, "S "); break; case SHT_DYNAMIC: append(line, "D "); break; case SHT_REL: append(line, "R "); break; case SHT_RELA: append(line, "R "); break; case SHT_HASH: append(line, "H "); break; case SHT_GROUP: append(line, "g "); break; case SHT_NOBITS: append(line, "0 "); break; #ifdef SHT_GNU_HASH case SHT_GNU_HASH: append(line, "H "); break; #endif #ifdef SHT_GNU_verdef case SHT_GNU_verdef: append(line, "V "); break; case SHT_GNU_verneed: append(line, "V "); break; case SHT_GNU_versym: append(line, "V "); break; #endif default: append(line, "? "); break; } if (dostrs) { if (shdr->sh_type == SHT_NOTE) { str = getstring(shdr->sh_offset + 12, shdr->sh_size - 12, FALSE); if (str && *str) { n = strlen(str); if (n > 30) append(line, "\"%.27s...\"", str); else append(line, "\"%s\"", str); return; } } } append(line, "%c%c%c", (shdr->sh_flags & SHF_ALLOC ? 'r' : '-'), (shdr->sh_flags & SHF_WRITE ? 'w' : '-'), (shdr->sh_flags & SHF_EXECINSTR ? 'x' : '-')); if (dooffs) append(line, offsetfmt, shdr->sh_offset); append(line, sizefmt, shdr->sh_size); append(line, " %s", sectstr ? sectstr + shdr->sh_name : "(n/a)"); if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) append(line, ":%lu", shdr->sh_info); if (shdr->sh_link) append(line, " [%lu]", shdr->sh_link); if (shdr->sh_type == SHT_STRTAB && shdr == shshstr) append(line, " [S]"); } /* * Top-level functions. */ /* Parse the command-line options. */ static void readoptions(int argc, char *argv[]) { static char const *optstring = "cdiPpSw:"; static struct option const options[] = { { "sources", no_argument, NULL, 'c' }, { "dependencies", no_argument, NULL, 'd' }, { "nophdr", no_argument, NULL, 'P' }, { "noshdr", no_argument, NULL, 'S' }, { "nostr", no_argument, NULL, 'i' }, { "nopos", no_argument, NULL, 'p' }, { "width", required_argument, NULL, 'w' }, { "help", no_argument, NULL, 'H' }, { "version", no_argument, NULL, 'V' }, { 0, 0, 0, 0 } }; char const *str; int n; programname = argv[0]; if (argc == 1) { fputs(yowzitch, stdout); exit(EXIT_SUCCESS); } if (!(str = getenv("COLUMNS")) || (outwidth = atoi(str)) <= 0) outwidth = 80; while ((n = getopt_long(argc, argv, optstring, options, NULL)) != EOF) { switch (n) { case 'c': srcfls = TRUE; break; case 'd': ldepls = TRUE; break; case 'P': phdrls = FALSE; break; case 'S': shdrls = FALSE; break; case 'i': dostrs = FALSE; break; case 'p': dooffs = FALSE; break; case 'w': outwidth = atoi(optarg); break; case 'H': fputs(yowzitch, stdout); exit(EXIT_SUCCESS); case 'V': fputs(vourzhon, stdout); exit(EXIT_SUCCESS); default: fprintf(stderr, "Try --help for more information.\n"); exit(EXIT_FAILURE); } } if (outwidth < 0) { err("invalid width parameter."); exit(EXIT_FAILURE); } } /* main(). */ int main(int argc, char *argv[]) { textline *lines = NULL; char **arg; int ret = 0; int count, i; readoptions(argc, argv); if (optind == argc) { err("nothing to do."); exit(EXIT_FAILURE); } for (arg = argv + optind ; (thefilename = *arg) != NULL ; ++arg) { if (!(thefile = fopen(thefilename, "rb"))) { perror(thefilename); ++ret; continue; } if (!readelfhdr() || !readproghdrs() || !readsecthdrs()) { fclose(thefile); ++ret; continue; } describeehdr(stdout); if (ldepls && proghdrs) { if ((count = getlibraries(&lines))) { outputlist(stdout, lines, count, "Dependencies: "); free(lines); } } if (srcfls && secthdrs) { if ((count = getsrcfiles(&lines))) { qsort(lines, count, sizeof *lines, linesorter); outputlist(stdout, lines, count, "Source files: "); free(lines); } } makenumberfmts(); if (phdrls && proghdrs) { printf("Program header table entries: %d", elffhdr.e_phnum); if (dooffs) printf(" (%lX - %lX)", (unsigned long)elffhdr.e_phoff, (unsigned long)elffhdr.e_phoff + elffhdr.e_phnum * elffhdr.e_phentsize); putchar('\n'); lines = gettextlines(elffhdr.e_phnum); for (i = 0 ; i < elffhdr.e_phnum ; ++i) { append(lines + i, "%2d ", i); describephdr(lines + i, proghdr + i); } formatlist(stdout, lines, elffhdr.e_phnum); free(lines); } if (shdrls && secthdrs) { printf("Section header table entries: %d", elffhdr.e_shnum); if (dooffs) printf(" (%lX - %lX)", (unsigned long)elffhdr.e_shoff, (unsigned long)elffhdr.e_shoff + elffhdr.e_shnum * elffhdr.e_shentsize); putchar('\n'); lines = gettextlines(elffhdr.e_shnum); for (i = 0 ; i < elffhdr.e_shnum ; ++i) { append(lines + i, "%2d ", i); describeshdr(lines + i, secthdr + i); } formatlist(stdout, lines, elffhdr.e_shnum); free(lines); } fclose(thefile); free(proghdr); free(secthdr); free(sectstr); } return ret; }