readelf: Add -z decompression support

Compatible with GNU readelf, -z decompresses sections displayed by
-x or -p.

ELF Tool Chain ticket #555
https://sourceforge.net/p/elftoolchain/tickets/555/

Submitted by:	Tiger Gao <tig@FreeBSDFoundation.org>
Reviewed by:	markj
MFC after:	1 month
Sponsored by:	The FreeBSD Foundation
Differential Revision:    https://reviews.freebsd.org/D26909
This commit is contained in:
emaste 2020-10-31 15:27:45 +00:00
parent 3c56c838d5
commit e2ef6ccda5
3 changed files with 117 additions and 7 deletions

View File

@ -24,7 +24,7 @@
.\" .\"
.\" $Id: readelf.1 3753 2019-06-28 01:13:13Z emaste $ .\" $Id: readelf.1 3753 2019-06-28 01:13:13Z emaste $
.\" .\"
.Dd June 27, 2019 .Dd October 31, 2020
.Dt READELF 1 .Dt READELF 1
.Os .Os
.Sh NAME .Sh NAME
@ -49,6 +49,7 @@
.Fl -debug-dump Ns Op Ns = Ns Ar long-option-name , Ns ... .Fl -debug-dump Ns Op Ns = Ns Ar long-option-name , Ns ...
.Oc .Oc
.Op Fl x Ar section | Fl -hex-dump Ns = Ns Ar section .Op Fl x Ar section | Fl -hex-dump Ns = Ns Ar section
.Op Fl z | Fl -decompress
.Op Fl A | Fl -arch-specific .Op Fl A | Fl -arch-specific
.Op Fl D | Fl -use-dynamic .Op Fl D | Fl -use-dynamic
.Op Fl H | Fl -help .Op Fl H | Fl -help
@ -157,6 +158,13 @@ Display the contents of the specified section in hexadecimal.
The argument The argument
.Ar section .Ar section
should be the name of a section or a numeric section index. should be the name of a section or a numeric section index.
.It Fl z | Fl -decompress
Decompress contents of sections specified by
.Fl x
or
.Fl p
before displaying.
If the specified section is not compressed, it is displayed as is.
.It Fl A | Fl -arch-specific .It Fl A | Fl -arch-specific
This option is accepted but is currently unimplemented. This option is accepted but is currently unimplemented.
.It Fl D | Fl -use-dynamic .It Fl D | Fl -use-dynamic

View File

@ -40,12 +40,14 @@
#include <libelftc.h> #include <libelftc.h>
#include <libgen.h> #include <libgen.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <zlib.h>
#include <libcasper.h> #include <libcasper.h>
#include <casper/cap_fileargs.h> #include <casper/cap_fileargs.h>
@ -87,6 +89,7 @@ ELFTC_VCSID("$Id: readelf.c 3769 2019-06-29 15:15:02Z emaste $");
#define RE_WW 0x00040000 #define RE_WW 0x00040000
#define RE_W 0x00080000 #define RE_W 0x00080000
#define RE_X 0x00100000 #define RE_X 0x00100000
#define RE_Z 0x00200000
/* /*
* dwarf dump options. * dwarf dump options.
@ -189,6 +192,7 @@ static struct option longopts[] = {
{"arch-specific", no_argument, NULL, 'A'}, {"arch-specific", no_argument, NULL, 'A'},
{"archive-index", no_argument, NULL, 'c'}, {"archive-index", no_argument, NULL, 'c'},
{"debug-dump", optional_argument, NULL, OPTION_DEBUG_DUMP}, {"debug-dump", optional_argument, NULL, OPTION_DEBUG_DUMP},
{"decompress", no_argument, 0, 'z'},
{"dynamic", no_argument, NULL, 'd'}, {"dynamic", no_argument, NULL, 'd'},
{"file-header", no_argument, NULL, 'h'}, {"file-header", no_argument, NULL, 'h'},
{"full-section-name", no_argument, NULL, 'N'}, {"full-section-name", no_argument, NULL, 'N'},
@ -6900,17 +6904,96 @@ get_symbol_value(struct readelf *re, int symtab, int i)
return (sym.st_value); return (sym.st_value);
} }
/*
* Decompress a data section if needed (using ZLIB).
* Returns true if sucessful, false otherwise.
*/
static bool decompress_section(struct section *s,
unsigned char *compressed_data_buffer, uint64_t compressed_size,
unsigned char **ret_buf, uint64_t *ret_sz)
{
GElf_Shdr sh;
if (gelf_getshdr(s->scn, &sh) == NULL)
errx(EXIT_FAILURE, "gelf_getshdr() failed: %s", elf_errmsg(-1));
if (sh.sh_flags & SHF_COMPRESSED) {
int ret;
GElf_Chdr chdr;
Elf64_Xword inflated_size;
unsigned char *uncompressed_data_buffer = NULL;
Elf64_Xword uncompressed_size;
z_stream strm;
if (gelf_getchdr(s->scn, &chdr) == NULL)
errx(EXIT_FAILURE, "gelf_getchdr() failed: %s", elf_errmsg(-1));
if (chdr.ch_type != ELFCOMPRESS_ZLIB) {
warnx("unknown compression type: %d", chdr.ch_type);
return (false);
}
inflated_size = 0;
uncompressed_size = chdr.ch_size;
uncompressed_data_buffer = malloc(uncompressed_size);
compressed_data_buffer += sizeof(chdr);
compressed_size -= sizeof(chdr);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = compressed_size;
strm.avail_out = uncompressed_size;
ret = inflateInit(&strm);
if (ret != Z_OK)
goto fail;
/*
* The section can contain several compressed buffers,
* so decompress in a loop until all data is inflated.
*/
while (inflated_size < compressed_size) {
strm.next_in = compressed_data_buffer + inflated_size;
strm.next_out = uncompressed_data_buffer + inflated_size;
ret = inflate(&strm, Z_FINISH);
if (ret != Z_STREAM_END)
goto fail;
inflated_size = uncompressed_size - strm.avail_out;
ret = inflateReset(&strm);
if (ret != Z_OK)
goto fail;
}
if (strm.avail_out != 0)
warnx("Warning: wrong info in compression header.");
ret = inflateEnd(&strm);
if (ret != Z_OK)
goto fail;
*ret_buf = uncompressed_data_buffer;
*ret_sz = uncompressed_size;
return (true);
fail:
inflateEnd(&strm);
if (strm.msg)
warnx("%s", strm.msg);
else
warnx("ZLIB error: %d", ret);
free(uncompressed_data_buffer);
return (false);
}
return (false);
}
static void static void
hex_dump(struct readelf *re) hex_dump(struct readelf *re)
{ {
struct section *s; struct section *s;
Elf_Data *d; Elf_Data *d;
uint8_t *buf; uint8_t *buf, *new_buf;
size_t sz, nbytes; size_t sz, nbytes;
uint64_t addr; uint64_t addr;
int elferr, i, j; int elferr, i, j;
for (i = 1; (size_t) i < re->shnum; i++) { for (i = 1; (size_t) i < re->shnum; i++) {
new_buf = NULL;
s = &re->sl[i]; s = &re->sl[i];
if (find_dumpop(re, (size_t) i, s->name, HEX_DUMP, -1) == NULL) if (find_dumpop(re, (size_t) i, s->name, HEX_DUMP, -1) == NULL)
continue; continue;
@ -6932,6 +7015,11 @@ hex_dump(struct readelf *re)
buf = d->d_buf; buf = d->d_buf;
sz = d->d_size; sz = d->d_size;
addr = s->addr; addr = s->addr;
if (re->options & RE_Z) {
if (decompress_section(s, d->d_buf, d->d_size,
&new_buf, &sz))
buf = new_buf;
}
printf("\nHex dump of section '%s':\n", s->name); printf("\nHex dump of section '%s':\n", s->name);
while (sz > 0) { while (sz > 0) {
printf(" 0x%8.8jx ", (uintmax_t)addr); printf(" 0x%8.8jx ", (uintmax_t)addr);
@ -6955,6 +7043,7 @@ hex_dump(struct readelf *re)
addr += nbytes; addr += nbytes;
sz -= nbytes; sz -= nbytes;
} }
free(new_buf);
} }
} }
@ -6963,11 +7052,13 @@ str_dump(struct readelf *re)
{ {
struct section *s; struct section *s;
Elf_Data *d; Elf_Data *d;
unsigned char *start, *end, *buf_end; unsigned char *start, *end, *buf_end, *new_buf;
unsigned int len; unsigned int len;
size_t sz;
int i, j, elferr, found; int i, j, elferr, found;
for (i = 1; (size_t) i < re->shnum; i++) { for (i = 1; (size_t) i < re->shnum; i++) {
new_buf = NULL;
s = &re->sl[i]; s = &re->sl[i];
if (find_dumpop(re, (size_t) i, s->name, STR_DUMP, -1) == NULL) if (find_dumpop(re, (size_t) i, s->name, STR_DUMP, -1) == NULL)
continue; continue;
@ -6986,9 +7077,15 @@ str_dump(struct readelf *re)
s->name); s->name);
continue; continue;
} }
buf_end = (unsigned char *) d->d_buf + d->d_size;
start = (unsigned char *) d->d_buf;
found = 0; found = 0;
start = d->d_buf;
sz = d->d_size;
if (re->options & RE_Z) {
if (decompress_section(s, d->d_buf, d->d_size,
&new_buf, &sz))
start = new_buf;
}
buf_end = start + sz;
printf("\nString dump of section '%s':\n", s->name); printf("\nString dump of section '%s':\n", s->name);
for (;;) { for (;;) {
while (start < buf_end && !isprint(*start)) while (start < buf_end && !isprint(*start))
@ -7009,6 +7106,7 @@ str_dump(struct readelf *re)
break; break;
start = end + 1; start = end + 1;
} }
free(new_buf);
if (!found) if (!found)
printf(" No strings found in this section."); printf(" No strings found in this section.");
putchar('\n'); putchar('\n');
@ -7634,6 +7732,7 @@ Usage: %s [options] file...\n\
Display DWARF information.\n\ Display DWARF information.\n\
-x INDEX | --hex-dump=INDEX\n\ -x INDEX | --hex-dump=INDEX\n\
Display contents of a section as hexadecimal.\n\ Display contents of a section as hexadecimal.\n\
-z | --decompress Decompress the contents of a section before displaying it.\n\
-A | --arch-specific (accepted, but ignored)\n\ -A | --arch-specific (accepted, but ignored)\n\
-D | --use-dynamic Print the symbol table specified by the DT_SYMTAB\n\ -D | --use-dynamic Print the symbol table specified by the DT_SYMTAB\n\
entry in the \".dynamic\" section.\n\ entry in the \".dynamic\" section.\n\
@ -7668,7 +7767,7 @@ main(int argc, char **argv)
memset(re, 0, sizeof(*re)); memset(re, 0, sizeof(*re));
STAILQ_INIT(&re->v_dumpop); STAILQ_INIT(&re->v_dumpop);
while ((opt = getopt_long(argc, argv, "AacDdegHhIi:lNnp:rSstuVvWw::x:", while ((opt = getopt_long(argc, argv, "AacDdegHhIi:lNnp:rSstuVvWw::x:z",
longopts, NULL)) != -1) { longopts, NULL)) != -1) {
switch(opt) { switch(opt) {
case '?': case '?':
@ -7765,6 +7864,9 @@ main(int argc, char **argv)
add_dumpop(re, 0, optarg, HEX_DUMP, add_dumpop(re, 0, optarg, HEX_DUMP,
DUMP_BY_NAME); DUMP_BY_NAME);
break; break;
case 'z':
re->options |= RE_Z;
break;
case OPTION_DEBUG_DUMP: case OPTION_DEBUG_DUMP:
re->options |= RE_W; re->options |= RE_W;
parse_dwarf_op_long(re, optarg); parse_dwarf_op_long(re, optarg);

View File

@ -10,7 +10,7 @@ READELFDIR= ${ELFTCDIR}/readelf
PROG= readelf PROG= readelf
SRCS= readelf.c SRCS= readelf.c
LIBADD= dwarf elftc elf LIBADD= dwarf elftc elf z
.if ${MK_CASPER} != "no" .if ${MK_CASPER} != "no"
LIBADD+= casper LIBADD+= casper