aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2020-02-29 20:02:11 -0800
committerRob Landley <rob@landley.net>2020-03-01 01:46:05 -0600
commit1a79bd562843eab3d2b630249719fdaffb06c641 (patch)
tree1ddf3a6180b4f11047f77a6e78fd018205bb11b4
parent04bec3ee366cad5bb4664ace7e3fdcd1dda8cfc8 (diff)
downloadtoybox-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-xtests/readelf.test19
-rw-r--r--toys/pending/readelf.c252
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");