Changeset View
Standalone View
contrib/elftoolchain/readelf/readelf.c
Show All 34 Lines | |||||
#include <err.h> | #include <err.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <gelf.h> | #include <gelf.h> | ||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <libdwarf.h> | #include <libdwarf.h> | ||||
#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> | ||||
#include "_elftc.h" | #include "_elftc.h" | ||||
ELFTC_VCSID("$Id: readelf.c 3769 2019-06-29 15:15:02Z emaste $"); | ELFTC_VCSID("$Id: readelf.c 3769 2019-06-29 15:15:02Z emaste $"); | ||||
Show All 25 Lines | |||||
#define RE_SS 0x00002000 | #define RE_SS 0x00002000 | ||||
#define RE_S 0x00004000 | #define RE_S 0x00004000 | ||||
#define RE_T 0x00008000 | #define RE_T 0x00008000 | ||||
#define RE_U 0x00010000 | #define RE_U 0x00010000 | ||||
#define RE_VV 0x00020000 | #define RE_VV 0x00020000 | ||||
#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. | ||||
*/ | */ | ||||
#define DW_A 0x00000001 | #define DW_A 0x00000001 | ||||
#define DW_FF 0x00000002 | #define DW_FF 0x00000002 | ||||
#define DW_F 0x00000004 | #define DW_F 0x00000004 | ||||
#define DW_I 0x00000008 | #define DW_I 0x00000008 | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | enum options | ||||
OPTION_DEBUG_DUMP | OPTION_DEBUG_DUMP | ||||
}; | }; | ||||
static struct option longopts[] = { | static struct option longopts[] = { | ||||
{"all", no_argument, NULL, 'a'}, | {"all", no_argument, NULL, 'a'}, | ||||
{"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'}, | ||||
{"headers", no_argument, NULL, 'e'}, | {"headers", no_argument, NULL, 'e'}, | ||||
{"help", no_argument, 0, 'H'}, | {"help", no_argument, 0, 'H'}, | ||||
{"hex-dump", required_argument, NULL, 'x'}, | {"hex-dump", required_argument, NULL, 'x'}, | ||||
{"histogram", no_argument, NULL, 'I'}, | {"histogram", no_argument, NULL, 'I'}, | ||||
{"notes", no_argument, NULL, 'n'}, | {"notes", no_argument, NULL, 'n'}, | ||||
▲ Show 20 Lines • Show All 6,628 Lines • ▼ Show 20 Lines | if ((data = elf_getdata(s->scn, NULL)) == NULL) { | ||||
return (0); | return (0); | ||||
} | } | ||||
if (gelf_getsym(data, i, &sym) != &sym) | if (gelf_getsym(data, i, &sym) != &sym) | ||||
return (0); | return (0); | ||||
return (sym.st_value); | return (sym.st_value); | ||||
} | } | ||||
/* | |||||
* Decompress a data section if needed (using ZLIB). | |||||
emaste: we can use `bool` and return `true` / `false` | |||||
* 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) | |||||
{ | |||||
Done Inline ActionsFreeBSD style(9) wants doesn't use this style; should be /* * Decompress a data section if needed (using ZLIB). * Returns 0 if sucessful, 1 otherwise. */ static int decompress_section(struct section *s, unsigned char *compressed_data_buffer, uint64_t compressed_size, unsigned char **ret_buf, uint64_t *ret_sz) { emaste: FreeBSD style(9) wants doesn't use this style; should be
```
/*
* Decompress a data section if… | |||||
GElf_Shdr sh; | |||||
Done Inline ActionsThe brace should be on a separate line. markj: The brace should be on a separate line. | |||||
if (gelf_getshdr(s->scn, &sh) == NULL) | |||||
errx(EXIT_FAILURE, "gelf_getshdr() failed: %s", elf_errmsg(-1)); | |||||
if (sh.sh_flags & SHF_COMPRESSED) { | |||||
Done Inline Actionsthese should be indented by 1/2 tabs emaste: these should be indented by 1/2 tabs | |||||
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. | |||||
*/ | |||||
markjUnsubmitted Done Inline ActionsThe indentation is wonky. markj: The indentation is wonky. | |||||
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); | |||||
Done Inline Actions{} are not required for single-statement bodies. They are permitted but need to be consistent throughout and they're not used elsewhere emaste: {} are not required for single-statement bodies. They are permitted but need to be consistent… | |||||
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: | |||||
markjUnsubmitted Done Inline ActionsPlease avoid indenting goto labels. markj: Please avoid indenting goto labels. | |||||
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; | ||||
(void) elf_errno(); | (void) elf_errno(); | ||||
if ((d = elf_getdata(s->scn, NULL)) == NULL && | if ((d = elf_getdata(s->scn, NULL)) == NULL && | ||||
(d = elf_rawdata(s->scn, NULL)) == NULL) { | (d = elf_rawdata(s->scn, NULL)) == NULL) { | ||||
elferr = elf_errno(); | elferr = elf_errno(); | ||||
if (elferr != 0) | if (elferr != 0) | ||||
warnx("elf_getdata failed: %s", | warnx("elf_getdata failed: %s", | ||||
elf_errmsg(elferr)); | elf_errmsg(elferr)); | ||||
continue; | continue; | ||||
} | } | ||||
(void) elf_errno(); | (void) elf_errno(); | ||||
if (d->d_size <= 0 || d->d_buf == NULL) { | if (d->d_size <= 0 || d->d_buf == NULL) { | ||||
printf("\nSection '%s' has no data to dump.\n", | printf("\nSection '%s' has no data to dump.\n", | ||||
s->name); | s->name); | ||||
continue; | continue; | ||||
} | } | ||||
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)) | |||||
Done Inline Actionsbuf needs to be freed after we're done with it, but only if it's not equal to d->d_buf. markj: `buf` needs to be freed after we're done with it, but only if it's not equal to `d->d_buf`. | |||||
Done Inline ActionsSince buf pointer keeps moving I added a pointer to newly allocated decompressed buffer. tig_freebsdfoundation.org: Since **buf** pointer keeps moving I added a pointer to newly allocated decompressed buffer. | |||||
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); | ||||
nbytes = sz > 16? 16 : sz; | nbytes = sz > 16? 16 : sz; | ||||
for (j = 0; j < 16; j++) { | for (j = 0; j < 16; j++) { | ||||
if ((size_t)j < nbytes) | if ((size_t)j < nbytes) | ||||
printf("%2.2x", buf[j]); | printf("%2.2x", buf[j]); | ||||
else | else | ||||
printf(" "); | printf(" "); | ||||
if ((j & 3) == 3) | if ((j & 3) == 3) | ||||
printf(" "); | printf(" "); | ||||
} | } | ||||
for (j = 0; (size_t)j < nbytes; j++) { | for (j = 0; (size_t)j < nbytes; j++) { | ||||
if (isprint(buf[j])) | if (isprint(buf[j])) | ||||
printf("%c", buf[j]); | printf("%c", buf[j]); | ||||
else | else | ||||
printf("."); | printf("."); | ||||
} | } | ||||
printf("\n"); | printf("\n"); | ||||
buf += nbytes; | buf += nbytes; | ||||
addr += nbytes; | addr += nbytes; | ||||
sz -= nbytes; | sz -= nbytes; | ||||
} | } | ||||
if (new_buf) { | |||||
free(new_buf); | |||||
} | } | ||||
markjUnsubmitted Done Inline Actionsfree(NULL) is fine, there's no need to check for NULL. markj: free(NULL) is fine, there's no need to check for NULL. | |||||
} | } | ||||
} | |||||
static void | static void | ||||
str_dump(struct readelf *re) | 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; | ||||
(void) elf_errno(); | (void) elf_errno(); | ||||
if ((d = elf_getdata(s->scn, NULL)) == NULL && | if ((d = elf_getdata(s->scn, NULL)) == NULL && | ||||
(d = elf_rawdata(s->scn, NULL)) == NULL) { | (d = elf_rawdata(s->scn, NULL)) == NULL) { | ||||
elferr = elf_errno(); | elferr = elf_errno(); | ||||
if (elferr != 0) | if (elferr != 0) | ||||
warnx("elf_getdata failed: %s", | warnx("elf_getdata failed: %s", | ||||
elf_errmsg(elferr)); | elf_errmsg(elferr)); | ||||
continue; | continue; | ||||
} | } | ||||
(void) elf_errno(); | (void) elf_errno(); | ||||
if (d->d_size <= 0 || d->d_buf == NULL) { | if (d->d_size <= 0 || d->d_buf == NULL) { | ||||
printf("\nSection '%s' has no data to dump.\n", | printf("\nSection '%s' has no data to dump.\n", | ||||
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)) | ||||
start++; | start++; | ||||
if (start >= buf_end) | if (start >= buf_end) | ||||
break; | break; | ||||
end = start + 1; | end = start + 1; | ||||
while (end < buf_end && isprint(*end)) | while (end < buf_end && isprint(*end)) | ||||
end++; | end++; | ||||
printf(" [%6lx] ", | printf(" [%6lx] ", | ||||
(long) (start - (unsigned char *) d->d_buf)); | (long) (start - (unsigned char *) d->d_buf)); | ||||
len = end - start; | len = end - start; | ||||
for (j = 0; (unsigned int) j < len; j++) | for (j = 0; (unsigned int) j < len; j++) | ||||
putchar(start[j]); | putchar(start[j]); | ||||
putchar('\n'); | putchar('\n'); | ||||
found = 1; | found = 1; | ||||
if (end >= buf_end) | if (end >= buf_end) | ||||
break; | break; | ||||
start = end + 1; | start = end + 1; | ||||
} | } | ||||
if (new_buf) { | |||||
Done Inline Actionsnew_buf is uninitialized if (re->options & RE_Z) == 0. markj: `new_buf` is uninitialized if `(re->options & RE_Z) == 0`. | |||||
Done Inline ActionsFixed by setting new_buf to NULL every loop. tig_freebsdfoundation.org: Fixed by setting new_buf to NULL every loop. | |||||
free(new_buf); | |||||
} | |||||
markjUnsubmitted Done Inline ActionsDitto. markj: Ditto. | |||||
if (!found) | if (!found) | ||||
printf(" No strings found in this section."); | printf(" No strings found in this section."); | ||||
putchar('\n'); | putchar('\n'); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
load_sections(struct readelf *re) | load_sections(struct readelf *re) | ||||
▲ Show 20 Lines • Show All 609 Lines • ▼ Show 20 Lines | Usage: %s [options] file...\n\ | ||||
-t | --section-details Print additional information about sections.\n\ | -t | --section-details Print additional information about sections.\n\ | ||||
-v | --version Print a version identifier and exit.\n\ | -v | --version Print a version identifier and exit.\n\ | ||||
-w[afilmoprsFLR] | --debug-dump={abbrev,aranges,decodedline,frames,\n\ | -w[afilmoprsFLR] | --debug-dump={abbrev,aranges,decodedline,frames,\n\ | ||||
frames-interp,info,loc,macro,pubnames,\n\ | frames-interp,info,loc,macro,pubnames,\n\ | ||||
ranges,Ranges,rawline,str}\n\ | ranges,Ranges,rawline,str}\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\ | ||||
-H | --help Print a help message.\n\ | -H | --help Print a help message.\n\ | ||||
-I | --histogram Print information on bucket list lengths for \n\ | -I | --histogram Print information on bucket list lengths for \n\ | ||||
hash sections.\n\ | hash sections.\n\ | ||||
-N | --full-section-name (accepted, but ignored)\n\ | -N | --full-section-name (accepted, but ignored)\n\ | ||||
-S | --sections | --section-headers\n\ | -S | --sections | --section-headers\n\ | ||||
Print information about section headers.\n\ | Print information about section headers.\n\ | ||||
-V | --version-info Print symbol versoning information.\n\ | -V | --version-info Print symbol versoning information.\n\ | ||||
-W | --wide Print information without wrapping long lines.\n" | -W | --wide Print information without wrapping long lines.\n" | ||||
Done Inline Actions-z needs to be documented in this usage string. markj: -z needs to be documented in this usage string. | |||||
static void | static void | ||||
readelf_usage(int status) | readelf_usage(int status) | ||||
{ | { | ||||
fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); | fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); | ||||
exit(status); | exit(status); | ||||
} | } | ||||
int | int | ||||
main(int argc, char **argv) | main(int argc, char **argv) | ||||
{ | { | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
fileargs_t *fa; | fileargs_t *fa; | ||||
struct readelf *re, re_storage; | struct readelf *re, re_storage; | ||||
unsigned long si; | unsigned long si; | ||||
int fd, opt, i; | int fd, opt, i; | ||||
char *ep; | char *ep; | ||||
re = &re_storage; | re = &re_storage; | ||||
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 '?': | ||||
readelf_usage(EXIT_SUCCESS); | readelf_usage(EXIT_SUCCESS); | ||||
break; | break; | ||||
case 'A': | case 'A': | ||||
re->options |= RE_AA; | re->options |= RE_AA; | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | case 'x': | ||||
re->options |= RE_X; | re->options |= RE_X; | ||||
si = strtoul(optarg, &ep, 10); | si = strtoul(optarg, &ep, 10); | ||||
if (*ep == '\0') | if (*ep == '\0') | ||||
add_dumpop(re, (size_t) si, NULL, HEX_DUMP, | add_dumpop(re, (size_t) si, NULL, HEX_DUMP, | ||||
DUMP_BY_INDEX); | DUMP_BY_INDEX); | ||||
else | else | ||||
add_dumpop(re, 0, optarg, HEX_DUMP, | add_dumpop(re, 0, optarg, HEX_DUMP, | ||||
DUMP_BY_NAME); | DUMP_BY_NAME); | ||||
break; | |||||
case 'z': | |||||
re->options |= RE_Z; | |||||
break; | 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); | ||||
} | } | ||||
} | } | ||||
argv += optind; | argv += optind; | ||||
Show All 40 Lines |
we can use bool and return true / false