diff options
author | Elliott Hughes <enh@google.com> | 2020-02-29 20:02:11 -0800 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2020-03-01 01:46:05 -0600 |
commit | 1a79bd562843eab3d2b630249719fdaffb06c641 (patch) | |
tree | 1ddf3a6180b4f11047f77a6e78fd018205bb11b4 | |
parent | 04bec3ee366cad5bb4664ace7e3fdcd1dda8cfc8 (diff) | |
download | toybox-1a79bd562843eab3d2b630249719fdaffb06c641.tar.gz |
readelf: various fixes.
Add -e, and stop documenting no-op -W.
Fix sign issues, and add a few extra sanity checks.
Redo the BE/LE 16/32/64 reading.
Remove the NOSPACE=1 from the -l test, and fix the -l code to match the
binutils output. Most usefully, this fixes the weird way the NULL
section's empty name would cause misalignment in the section to segment
mapping output.
Add a test for -s (symbol table).
-rwxr-xr-x | tests/readelf.test | 19 | ||||
-rw-r--r-- | toys/pending/readelf.c | 252 |
2 files changed, 160 insertions, 111 deletions
diff --git a/tests/readelf.test b/tests/readelf.test index 9c0e9567..a015e23a 100755 --- a/tests/readelf.test +++ b/tests/readelf.test @@ -70,7 +70,7 @@ Section Headers: [31] .strtab STRTAB 0000000000000000 001b18 0001f4 00 0 0 1 " "" "" -NOSPACE=1 testing "-l" "readelf -lW $elf-short" " +testing "-l" "readelf -lW $elf-short" " Elf file type is DYN (Shared object file) Entry point 0x1001 There are 10 program headers, starting at offset 52 @@ -200,3 +200,20 @@ Hex dump of section '.shstrtab': 0x000000f0 64656275 67646174 61000000 000000 debugdata...... " "" "" + +# TODO: remove the sed when we handle symbol versions +testing "-s" "readelf -s $elf-short | sed s/@.*//" " +Symbol table '.dynsym' contains 11 entries: + Num: Value Size Type Bind Vis Ndx Name + 0: 00000000 0 NOTYPE LOCAL DEFAULT UND + 1: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_init + 2: 00000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail + 3: 00000000 0 OBJECT GLOBAL DEFAULT UND __stack_chk_guard + 4: 00000000 0 FUNC GLOBAL DEFAULT UND memset + 5: 000010d8 12 FUNC GLOBAL DEFAULT 13 __aeabi_memclr + 6: 000010d8 12 FUNC GLOBAL DEFAULT 13 __aeabi_memclr4 + 7: 000010d8 12 FUNC GLOBAL DEFAULT 13 __aeabi_memclr8 + 8: 000010c8 16 FUNC GLOBAL DEFAULT 13 __aeabi_memset + 9: 000010c8 16 FUNC GLOBAL DEFAULT 13 __aeabi_memset4 + 10: 000010c8 16 FUNC GLOBAL DEFAULT 13 __aeabi_memset8 +" "" "" diff --git a/toys/pending/readelf.c b/toys/pending/readelf.c index acb1ed0f..ef888c07 100644 --- a/toys/pending/readelf.c +++ b/toys/pending/readelf.c @@ -4,25 +4,25 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/nm.html -USE_READELF(NEWTOY(readelf, "<1(dyn-syms)adhlnp:SsWx:", TOYFLAG_USR|TOYFLAG_BIN)) +USE_READELF(NEWTOY(readelf, "<1(dyn-syms)adehlnp:SsWx:", TOYFLAG_USR|TOYFLAG_BIN)) config READELF bool "readelf" default y help - usage: readelf [-adhlnSsW] [-p SECTION] [-x SECTION] [file...] + usage: readelf [-adehlnSs] [-p SECTION] [-x SECTION] [file...] Displays information about ELF files. -a Equivalent to -dhlnSs -d Show dynamic section + -e Headers (equivalent to -hlS) -h Show ELF header -l Show program headers -n Show notes -p S Dump strings found in named/numbered section -S Show section headers -s Show symbol tables (.dynsym and .symtab) - -W Don't truncate fields (default in toybox) -x S Hex dump of named/numbered section --dyn-syms Show just .dynsym symbol table @@ -35,48 +35,73 @@ GLOBALS( char *x, *p; char *elf, *shstrtab, *f; - long long shoff, phoff, size; - int bits, shnum, shentsize, phentsize; - int64_t (*elf_int)(void *ptr, unsigned size); + unsigned long long shoff, phoff, size; + int bits, endian, shnum, shentsize, phentsize; ) // Section header. struct sh { - int type, link, info; - long long flags, addr, offset, size, addralign, entsize; + unsigned type, link, info; + unsigned long long flags, addr, offset, size, addralign, entsize; char *name; }; // Program header. struct ph { - int type, flags; - long long offset, vaddr, paddr, filesz, memsz, align; + unsigned type, flags; + unsigned long long offset, vaddr, paddr, filesz, memsz, align; }; +static long long elf_get(char **p, int len) +{ + long long result = ((TT.endian == 2) ? peek_be : peek_le)(*p, len); + + *p += len; + return result; +} + +static unsigned long long elf_long(char **p) +{ + return elf_get(p, 4*(TT.bits+1)); +} + +static unsigned elf_int(char **p) +{ + return elf_get(p, 4); +} + +static unsigned short elf_short(char **p) +{ + return elf_get(p, 2); +} + static void get_sh(int i, struct sh *s) { char *shdr = TT.elf+TT.shoff+i*TT.shentsize; + int name_offset; if (i >= TT.shnum || shdr > TT.elf+TT.size-TT.shentsize) { - error_exit("%s: bad shdr %d",TT.f,i); + error_exit("%s: no shdr %d",TT.f,i); } - s->type = TT.elf_int(shdr+4, 4); - s->flags = TT.elf_int(shdr+8, 4*(TT.bits+1)); - s->addr = TT.elf_int(shdr+8+4*(TT.bits+1), 4*(TT.bits+1)); - s->offset = TT.elf_int(shdr+8+8*(TT.bits+1), 4*(TT.bits+1)); - s->size = TT.elf_int(shdr+8+12*(TT.bits+1), 4*(TT.bits+1)); - s->link = TT.elf_int(shdr+8+16*(TT.bits+1), 4); - s->info = TT.elf_int(shdr+12+16*(TT.bits+1), 4); - s->addralign = TT.elf_int(shdr+16+16*(TT.bits+1), 4*(TT.bits+1)); - s->entsize = TT.elf_int(shdr+16+20*(TT.bits+1), 4*(TT.bits+1)); + name_offset = elf_int(&shdr); + s->type = elf_int(&shdr); + s->flags = elf_long(&shdr); + s->addr = elf_long(&shdr); + s->offset = elf_long(&shdr); + s->size = elf_long(&shdr); + s->link = elf_int(&shdr); + s->info = elf_int(&shdr); + s->addralign = elf_long(&shdr); + s->entsize = elf_long(&shdr); if (!TT.shstrtab) s->name = "?"; else { - s->name = TT.shstrtab + TT.elf_int(shdr, 4); - if (s->name >= TT.elf+TT.size) error_exit("%s: bad shdr name %d",TT.f,i); + s->name = TT.shstrtab + name_offset; + if (s->name >= TT.elf+TT.size) error_exit("%s: shdr %d bad name", TT.f, i); if (s->offset >= TT.size-s->size && s->type != 8 /*SHT_NOBITS*/) - error_exit("%s: bad section %d",TT.f,i); + error_exit("%s: shdr %d has bad offset/size %llu/%llu", TT.f, i, + s->offset, s->size); } } @@ -107,27 +132,31 @@ static void get_ph(int i, struct ph *ph) { char *phdr = TT.elf+TT.phoff+i*TT.phentsize; - if (phdr > TT.elf+TT.size-TT.phentsize) error_exit("%s: bad phdr %d",TT.f,i); + if (phdr > TT.elf+TT.size-TT.phentsize) error_exit("%s: no phdr %d",TT.f,i); // Elf64_Phdr reordered fields. - ph->type = TT.elf_int(phdr, 4); + ph->type = elf_int(&phdr); if (TT.bits) { - ph->flags = TT.elf_int(phdr+=4, 4); - ph->offset = TT.elf_int(phdr+=4, 8); - ph->vaddr = TT.elf_int(phdr+=8, 8); - ph->paddr = TT.elf_int(phdr+=8, 8); - ph->filesz = TT.elf_int(phdr+=8, 8); - ph->memsz = TT.elf_int(phdr+=8, 8); - ph->align = TT.elf_int(phdr+=8, 8); + ph->flags = elf_int(&phdr); + ph->offset = elf_long(&phdr); + ph->vaddr = elf_long(&phdr); + ph->paddr = elf_long(&phdr); + ph->filesz = elf_long(&phdr); + ph->memsz = elf_long(&phdr); + ph->align = elf_long(&phdr); } else { - ph->offset = TT.elf_int(phdr+=4, 4); - ph->vaddr = TT.elf_int(phdr+=4, 4); - ph->paddr = TT.elf_int(phdr+=4, 4); - ph->filesz = TT.elf_int(phdr+=4, 4); - ph->memsz = TT.elf_int(phdr+=4, 4); - ph->flags = TT.elf_int(phdr+=4, 4); - ph->align = TT.elf_int(phdr+=4, 4); + ph->offset = elf_int(&phdr); + ph->vaddr = elf_int(&phdr); + ph->paddr = elf_int(&phdr); + ph->filesz = elf_int(&phdr); + ph->memsz = elf_int(&phdr); + ph->flags = elf_int(&phdr); + ph->align = elf_int(&phdr); } + + if (ph->offset >= TT.size-ph->filesz) + error_exit("%s: phdr %d has bad offset/size %llu/%llu", TT.f, i, + ph->offset, ph->filesz); } #define MAP(...) __VA_ARGS__ @@ -200,7 +229,7 @@ DECODER(stv_type, MAP({{0,"DEFAULT"},{1,"INTERNAL"},{2,"HIDDEN"}, static void show_symbols(struct sh *table, struct sh *strtab) { char *symtab = TT.elf+table->offset, *ndx; - int sym_size = (TT.bits ? 24 : 16), numsym = table->size/sym_size, i; + int numsym = table->size/(TT.bits ? 24 : 16), i; if (numsym == 0) return; @@ -209,24 +238,24 @@ static void show_symbols(struct sh *table, struct sh *strtab) " Num: %*s Size Type Bind Vis Ndx Name\n", table->name, numsym, 5+8*TT.bits, "Value"); for (i=0; i<numsym; i++) { - int st_name = TT.elf_int(symtab, 4), st_value, st_shndx; + int st_name = elf_int(&symtab), st_value, st_shndx; unsigned char st_info, st_other; long st_size; char *name; // The various fields were moved around for 64-bit. if (TT.bits) { - st_info = symtab[4]; - st_other = symtab[5]; - st_shndx = TT.elf_int(symtab+6, 2); - st_value = TT.elf_int(symtab+8, 8); - st_size = TT.elf_int(symtab+16, 8); + st_info = *symtab++; + st_other = *symtab++; + st_shndx = elf_short(&symtab); + st_value = elf_long(&symtab); + st_size = elf_long(&symtab); } else { - st_value = TT.elf_int(symtab+4, 4); - st_size = TT.elf_int(symtab+8, 4); - st_info = symtab[12]; - st_other = symtab[13]; - st_shndx = TT.elf_int(symtab+14, 2); + st_value = elf_int(&symtab); + st_size = elf_int(&symtab); + st_info = *symtab++; + st_other = *symtab++; + st_shndx = elf_short(&symtab); } name = TT.elf + strtab->offset + st_name; @@ -241,51 +270,53 @@ static void show_symbols(struct sh *table, struct sh *strtab) printf("%6d: %0*x %5ld %-7s %-6s %-9s%3s %s\n", i, 8*(TT.bits+1), st_value, st_size, stt_type(st_info & 0xf), stb_type(st_info >> 4), stv_type(st_other & 3), ndx, name); - symtab += sym_size; } } +static int notematch(int namesz, char **p, char *expected, int len) +{ + if (namesz != len || memcmp(*p, expected, namesz)) return 0; + *p += namesz; + return 1; +} + static void show_notes(long offset, long size) { char *note = TT.elf + offset; printf(" %-20s %10s\tDescription\n", "Owner", "Data size"); while (note < TT.elf+offset+size) { - int namesz = TT.elf_int(note, 4), descsz = TT.elf_int(note+4, 4), - type = TT.elf_int(note+8, 4), j = 0; - char *name = note+12; + char *p = note, *desc; + int namesz = elf_int(&p), descsz = elf_int(&p), type = elf_int(&p), j = 0; - printf(" %-20.*s 0x%08x\t", namesz, name, descsz); - if (!memcmp(name, "GNU", 4)) { + printf(" %-20.*s 0x%08x\t", namesz, p, descsz); + if (notematch(namesz, &p, "GNU", 4)) { if (type == 1) { - printf("NT_GNU_ABI_TAG\tOS: %s, ABI: %d.%d.%d", - !TT.elf_int(note+16, 4)?"Linux":"?", - (int)TT.elf_int(note+20, 4), (int)TT.elf_int(note+24, 4), - (int)TT.elf_int(note+28, 4)), j=1; + printf("NT_GNU_ABI_TAG\tOS: %s, ABI: %u.%u.%u", + !elf_int(&p)?"Linux":"?", elf_int(&p), elf_int(&p), elf_int(&p)), j=1; } else if (type == 3) { printf("NT_GNU_BUILD_ID\t"); - for (;j<descsz;j++) printf("%02x",note[16+j]); + for (;j<descsz;j++) printf("%02x", *p++); } else if (type == 4) { - printf("NT_GNU_GOLD_VERSION\t%.*s", descsz, note+16), j=1; - } - } else if (!memcmp(name, "Android", 8)) { + printf("NT_GNU_GOLD_VERSION\t%.*s", descsz, p), j=1; + } else p -= 4; + } else if (notematch(namesz, &p, "Android", 8)) { if (type == 1) { - printf("NT_VERSION\tAPI level %d", (int)TT.elf_int(note+20, 4)), j=1; - if (descsz>=132) printf(", NDK %.64s (%.64s)",note+24,note+24+64); - } - } else if (!memcmp(name, "CORE", 5) || !memcmp(name, "LINUX", 6)) { - char *desc = *name=='C' ? nt_type_core(type) : nt_type_linux(type); - - if (*desc != '0') printf("%s", desc), j=1; + printf("NT_VERSION\tAPI level %u", elf_int(&p)), j=1; + if (descsz>=132) printf(", NDK %.64s (%.64s)", p, p+64); + } else p -= 8; + } else if (notematch(namesz, &p, "CORE", 5)) { + if (*(desc = nt_type_core(type)) != '0') printf("%s", desc), j=1; + } else if (notematch(namesz, &p, "LINUX", 6)) { + if (*(desc = nt_type_linux(type)) != '0') printf("%s", desc), j=1; } // If we didn't do custom output above, show a hex dump. if (!j) { printf("0x%x\t", type); - for (;j<descsz;j++) printf("%c%02x",!j?'\t':' ',note[16+j]); + for (;j<descsz;j++) printf("%c%02x",!j?'\t':' ', *p++/*note[16+j]*/); } xputc('\n'); - note += 3*4 + ((namesz+3)&~3) + ((descsz+3)&~3); } } @@ -295,33 +326,35 @@ static void scan_elf() struct sh dynamic = {}, dynstr = {}, dynsym = {}, shstr = {}, strtab = {}, symtab = {}, s; struct ph ph; - int endian, version, elf_type, flags, entry, ehsize, phnum, shstrndx, i,j,w; + char *hdr = TT.elf; + int type, machine, version, flags, entry, ehsize, phnum, shstrndx, i, j, w; - if (TT.size < 45 || memcmp(TT.elf, "\177ELF", 4)) { + if (TT.size < 45 || memcmp(hdr, "\177ELF", 4)) { error_msg("%s: not ELF", TT.f); return; } - TT.bits = TT.elf[4] - 1; - endian = TT.elf[5]; - version = TT.elf[6]; - TT.elf_int = (endian==2) ? peek_be : peek_le; - if (TT.bits < 0 || TT.bits > 1 || endian < 1 || endian > 2 || version != 1) { + TT.bits = hdr[4] - 1; + TT.endian = hdr[5]; + if (TT.bits<0 || TT.bits>1 || TT.endian<1 || TT.endian>2 || hdr[6]!=1) { error_msg("%s: bad ELF", TT.f); return; } - elf_type = TT.elf_int(TT.elf+16, 2); - entry = TT.elf_int(TT.elf+24, 4+4*TT.bits); - TT.phoff = TT.elf_int(TT.elf+28+4*TT.bits, 4+4*TT.bits); - TT.shoff = TT.elf_int(TT.elf+32+8*TT.bits, 4+4*TT.bits); - flags = TT.elf_int(TT.elf+36+12*TT.bits, 4); - ehsize = TT.elf_int(TT.elf+40+12*TT.bits, 2); - TT.phentsize = TT.elf_int(TT.elf+42+12*TT.bits, 2); - phnum = TT.elf_int(TT.elf+44+12*TT.bits, 2); - TT.shentsize = TT.elf_int(TT.elf+46+12*TT.bits, 2); - TT.shnum = TT.elf_int(TT.elf+48+12*TT.bits, 2); - shstrndx = TT.elf_int(TT.elf+50+12*TT.bits, 2); + hdr += 16; // EI_NIDENT + type = elf_short(&hdr); + machine = elf_short(&hdr); + version = elf_int(&hdr); + entry = elf_long(&hdr); + TT.phoff = elf_long(&hdr); + TT.shoff = elf_long(&hdr); + flags = elf_int(&hdr); + ehsize = elf_short(&hdr); + TT.phentsize = elf_short(&hdr); + phnum = elf_short(&hdr); + TT.shentsize = elf_short(&hdr); + TT.shnum = elf_short(&hdr); + shstrndx = elf_short(&hdr); // Set up the section header string table so we can use section header names. // Core files have shstrndx == 0. @@ -343,15 +376,13 @@ static void scan_elf() for (i=0; i<16; i++) printf("%02x%c", TT.elf[i], i==15?'\n':' '); printf(" Class: ELF%d\n", TT.bits?64:32); printf(" Data: 2's complement, %s endian\n", - (endian==2)?"big":"little"); + (TT.endian==2)?"big":"little"); printf(" Version: 1 (current)\n"); printf(" OS/ABI: %s\n", os_abi(TT.elf[7])); printf(" ABI Version: %d\n", TT.elf[8]); - printf(" Type: %s\n", et_type(elf_type)); - printf(" Machine: %s\n", - elf_arch_name(TT.elf_int(TT.elf+18, 2))); - printf(" Version: 0x%x\n", - (int) TT.elf_int(TT.elf+20, 4)); + printf(" Type: %s\n", et_type(type)); + printf(" Machine: %s\n", elf_arch_name(machine)); + printf(" Version: 0x%x\n", version); printf(" Entry point address: 0x%x\n", entry); printf(" Start of program headers: %lld (bytes into file)\n", TT.phoff); @@ -416,7 +447,7 @@ static void scan_elf() "Entry point %#x\n" "There are %d program headers, starting at offset %lld\n" "\n", - et_type(elf_type), entry, phnum, TT.phoff); + et_type(type), entry, phnum, TT.phoff); } printf("Program Headers:\n" " %-14s %-8s %-*s %-*s %-7s %-7s Flg Align\n", "Type", @@ -427,7 +458,7 @@ static void scan_elf() ph_type(ph.type), ph.offset, w, ph.vaddr, w, ph.paddr, ph.filesz, ph.memsz, ph.flags&4?'R':' ', ph.flags&2?'W':' ', ph.flags&1?'E':' ', ph.align); - if (ph.type == 3 /*PH_INTERP*/) { + if (ph.type == 3 /*PH_INTERP*/ && ph.filesz && ph.filesz) { printf(" [Requesting program interpreter: %*s]\n", (int) ph.filesz-1, TT.elf+ph.offset); } @@ -438,11 +469,12 @@ static void scan_elf() " Segment Sections...\n"); for (i=0; i<phnum; i++) { get_ph(i, &ph); - printf(" %02d ", i); + printf(" %02d ", i); for (j=0; j<TT.shnum; j++) { get_sh(j, &s); + if (!*s.name) continue; if (s.offset >= ph.offset && s.offset+s.size <= ph.offset+ph.filesz) - printf(" %s", s.name); + printf("%s ", s.name); } xputc('\n'); } @@ -460,14 +492,13 @@ static void scan_elf() " %-*s %-20s %s\n", dynamic.offset, dynamic.size/dynamic.entsize, w+2, "Tag", "Type", "Name/Value"); - for (; dyn < end; dyn += dynamic.entsize) { - int es = 4*(TT.bits+1); - long tag = TT.elf_int(dyn, es), val = TT.elf_int(dyn+es, es); + while (dyn < end) { + unsigned long long tag = elf_long(&dyn), val = elf_long(&dyn); char *type = dt_type(tag); - printf(" 0x%0*lx %-20s ", w, tag, *type=='0' ? type : type+1); - if (*type == 'd') printf("%ld\n", val); - else if (*type == 'b') printf("%ld (bytes)\n", val); + printf(" 0x%0*llx %-20s ", w, tag, *type=='0' ? type : type+1); + if (*type == 'd') printf("%lld\n", val); + else if (*type == 'b') printf("%lld (bytes)\n", val); else if (*type == 's') printf("%s\n", TT.elf+dynstr.offset+val); else if (*type == 'f' || *type == 'F') { struct bitname { int bit; char *s; } @@ -484,7 +515,7 @@ static void scan_elf() printf("%s%s", names[j].s, (val &= ~mask) ? " " : ""); } } - if (val) printf("0x%lx", val); + if (val) printf("0x%llx", val); xputc('\n'); } else if (*type == 'N' || *type == 'R' || *type == 'S') { printf("%s: [%s]\n", *type=='N' ? "Shared library" : @@ -495,7 +526,7 @@ static void scan_elf() j = strlen(type); if (*type != '0') type += 2, j -= 3; printf("%*.*s\n", j, j, type); - } else printf("0x%lx\n", val); + } else printf("0x%llx\n", val); } } @@ -571,6 +602,7 @@ void readelf_main(void) int all = FLAG_d|FLAG_h|FLAG_l|FLAG_n|FLAG_S|FLAG_s|FLAG_dyn_syms; if (FLAG(a)) toys.optflags |= all; + if (FLAG(e)) toys.optflags |= FLAG_h|FLAG_l|FLAG_S; if (FLAG(s)) toys.optflags |= FLAG_dyn_syms; if (!(toys.optflags & (all|FLAG_p|FLAG_x))) help_exit("needs a flag"); |