From c5e7ee27f606ed0bdb20d54e9de1a4adb56fc5d3 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Sat, 2 Jul 2016 12:53:40 -0700 Subject: Improve file(1)'s ELF support. Read any PT_NOTE sections to look for NT_GNU_BUILD_ID or Android API level notes. I deliberately didn't NT_GNU_ABI_TAG because it's noisy -- every Linux executable has one -- but not something most command-line users will have any use for. (And you can ask readelf(1) anyway.) Also read the section headers to implement "stripped"/"not stripped". This patch removes "uses %d libs" because it was actually just counting dynamic sections in the ELF file, and there are only 0 or 1 of those in a valid ELF flie. (If you really want this functionality, you have to *parse* the dynamic section looking for the DT_NEEDED entries. But that's more of a job for readelf(1) than file(1).) --- toys/pending/file.c | 125 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 43 deletions(-) diff --git a/toys/pending/file.c b/toys/pending/file.c index 18941227..b53bec0a 100644 --- a/toys/pending/file.c +++ b/toys/pending/file.c @@ -26,7 +26,7 @@ GLOBALS( // We don't trust elf.h to be there, and two codepaths for 32/64 is awkward // anyway, so calculate struct offsets manually. (It's a fixed ABI.) -static void do_elf_file(int fd) +static void do_elf_file(int fd, struct stat *sb) { int endian = toybuf[5], bits = toybuf[4], i, j; int64_t (*elf_int)(void *ptr, unsigned size) = peek_le; @@ -45,6 +45,11 @@ static void do_elf_file(int fd) {191, "tilegx"}, {3, "386"}, {6, "486"}, {62, "x86-64"}, {94, "xtensa"}, {0xabc7, "xtensa-old"} }; + int dynamic = 0; + int stripped = 1; + char *map; + off_t phoff, shoff; + int phsize, phnum, shsize, shnum; printf("ELF "); @@ -82,53 +87,87 @@ static void do_elf_file(int fd) else printf("(unknown arch %d)", j); bits--; - // If we know our bits and endianness and phentsize agrees show dynamic linker - if ((bits&1)==bits && endian && - (i = elf_int(toybuf+42+12*bits, 2)) == 32+24*bits) - { - char *map, *phdr; - int phsize = i, phnum = elf_int(toybuf+44+12*bits, 2), - psz = sysconf(_SC_PAGE_SIZE), lib = 0; - off_t phoff = elf_int(toybuf+28+4*bits, 4+4*bits), - mapoff = phoff^(phoff&(psz-1)); - - // map e_phentsize*e_phnum bytes at e_phoff - map = mmap(0, phsize*phnum, PROT_READ, MAP_SHARED, fd, mapoff); - if (map) { - // Find PT_INTERP entry. (Note: fields got reordered for 64 bit) - for (i = 0; isizeof(toybuf)-128 - || dlpos!=lseek(fd, dlpos, SEEK_SET) - || dllen!=readall(fd, toybuf+128, dllen)) break; - printf(", dynamic (%.*s", (int)dllen, toybuf+128); + // If what we've seen so far doesn't seem consistent, bail. + if (!((bits&1)==bits && endian && + (i = elf_int(toybuf+42+12*bits, 2)) == 32+24*bits)) { + printf(", corrupt?\n"); + return; + } + + // Stash what we need from the header; it's okay to reuse toybuf after this. + phsize = i; + phnum = elf_int(toybuf+44+12*bits, 2); + phoff = elf_int(toybuf+28+4*bits, 4+4*bits); + shsize = elf_int(toybuf+46+12*bits, 2); + shnum = elf_int(toybuf+48+12*bits, 2); + shoff = elf_int(toybuf+32+8*bits, 4+4*bits); + + map = mmap(0, sb->st_size, PROT_READ, MAP_SHARED, fd, 0); + if (!map) perror_exit("mmap"); + + // We need to read the phdrs for dynamic vs static and any notes. + // (Note: fields got reordered for 64 bit) + for (i = 0; i= 3*4) { // Don't try to read a truncated entry. + int n_namesz = elf_int(note, 4); + int n_descsz = elf_int(note+4, 4); + int n_type = elf_int(note+8, 4); + int notesz = 3*4 + ((n_namesz+3)&~3) + ((n_descsz+3)&~3); + + if (n_namesz==4 && !memcmp(note+12, "GNU", 4)) { + if (n_type == 3 /*NT_GNU_BUILD_ID*/) { + printf(", BuildID[%s]=", (n_descsz==20)?"sha1":"md5"); + for (j = 0; j < n_descsz; ++j) printf("%02x", note[16 + j]); + } + } else if (n_namesz==8 && !memcmp(note+12, "Android", 8)) { + if (n_type==1) printf(", for Android %d", (int)elf_int(note+20, 4)); + } + + note += notesz; + p_filesz -= notesz; } - if (!lib) printf(", static"); - else printf(" loads %d lib%s)", lib, lib>1 ? "s" : ""); - munmap(map, phsize*phnum); } } + if (!dynamic) printf(", static"); - // TODO: we'd need to actually parse the ELF file to report the rest... - // ", dynamically linked" - // " (uses shared libs)" - // ", for Linux 2.6.24" - // ", BuildID[sha1]=SHA" - // ", stripped" + // We need to read the shdrs for stripped/unstripped. + // (Note: fields got reordered for 64 bit) + for (i = 0; ist_size); } -static void do_regular_file(int fd, char *name) +static void do_regular_file(int fd, char *name, struct stat *sb) { char *s; int len = read(fd, s = toybuf, sizeof(toybuf)-256); @@ -136,7 +175,7 @@ static void do_regular_file(int fd, char *name) if (len<0) perror_msg("%s", name); - if (len>40 && strstart(&s, "\177ELF")) do_elf_file(fd); + if (len>40 && strstart(&s, "\177ELF")) do_elf_file(fd, sb); else if (len>28 && strstart(&s, "\x89PNG\x0d\x0a\x1a\x0a")) { // PNG is big-endian: https://www.w3.org/TR/PNG/#7Integers-and-byte-order int chunk_length = peek_be(s, 4); @@ -252,7 +291,7 @@ void file_main(void) if (fd!=-1) { if (!sb.st_size) what = "empty"; - else do_regular_file(fd, name); + else do_regular_file(fd, name, &sb); if (fd) close(fd); if (sb.st_size) continue; } -- cgit v1.2.3