Changeset View
Standalone View
contrib/elftoolchain/readelf/readelf.c
Show All 40 Lines | |||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <stdarg.h> | #include <stdarg.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 109 Lines • ▼ Show 20 Lines | static struct option longopts[] = { | ||||
{"string-dump", required_argument, NULL, 'p'}, | {"string-dump", required_argument, NULL, 'p'}, | ||||
{"symbols", no_argument, NULL, 's'}, | {"symbols", no_argument, NULL, 's'}, | ||||
{"syms", no_argument, NULL, 's'}, | {"syms", no_argument, NULL, 's'}, | ||||
{"unwind", no_argument, NULL, 'u'}, | {"unwind", no_argument, NULL, 'u'}, | ||||
{"use-dynamic", no_argument, NULL, 'D'}, | {"use-dynamic", no_argument, NULL, 'D'}, | ||||
{"version-info", no_argument, 0, 'V'}, | {"version-info", no_argument, 0, 'V'}, | ||||
{"version", no_argument, 0, 'v'}, | {"version", no_argument, 0, 'v'}, | ||||
{"wide", no_argument, 0, 'W'}, | {"wide", no_argument, 0, 'W'}, | ||||
{"decompress", no_argument, 0, 'z'}, | |||||
emaste: in contrast to the comment above these were already sorted by long option name, so this one… | |||||
{NULL, 0, NULL, 0} | {NULL, 0, NULL, 0} | ||||
}; | }; | ||||
struct eflags_desc { | struct eflags_desc { | ||||
uint64_t flag; | uint64_t flag; | ||||
const char *desc; | const char *desc; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 6,605 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). | |||||
*/ | |||||
static int | |||||
decompress_section(struct section *s, unsigned char **buffer, uint64_t *sz) { | |||||
Done Inline Actions{ goes on next line readelf.c is trickier to deal with using clang-format because much of the existing code is non-compliant. Clang has a tool for formatting just a patch: http://clang.llvm.org/docs/ClangFormat.html#script-for-patch-reformatting emaste: `{` goes on next line
readelf.c is trickier to deal with using clang-format because much of… | |||||
GElf_Shdr sh; | |||||
if (gelf_getshdr(s->scn, &sh) == NULL) | |||||
errx(EXIT_FAILURE, "gelf_getshdr() failed: %s", | |||||
elf_errmsg(-1)); | |||||
Done Inline Actionsdoes not need wrapping emaste: does not need wrapping | |||||
if (sh.sh_flags & SHF_COMPRESSED) { | |||||
int ret; | |||||
GElf_Chdr chdr; | |||||
Elf64_Xword compressed_size; | |||||
unsigned char *compressed_data_buffer = NULL; | |||||
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)); | |||||
Done Inline Actionsindentation emaste: indentation | |||||
Done Inline ActionsWhy do we goto fail here but call errx() in the error case immediately above? markj: Why do we `goto fail` here but call `errx()` in the error case immediately above? | |||||
Done Inline ActionsNow that I looked at it, it makes more sense to warn the user that compression type is not supported. Would that be a reasonable way to address this? tig_freebsdfoundation.org: Now that I looked at it, it makes more sense to warn the user that compression type is not… | |||||
Done Inline ActionsSomething like "unknown compression type: %d"? Or perhaps I don't understand your question. markj: Something like "unknown compression type: %d"? Or perhaps I don't understand your question. | |||||
Done Inline ActionsThat makes sense. I'll use this format. tig_freebsdfoundation.org: That makes sense. I'll use this format. | |||||
if (chdr.ch_type != ELFCOMPRESS_ZLIB) | |||||
goto fail; | |||||
Done Inline Actionsindentation emaste: indentation | |||||
compressed_data_buffer = *buffer; | |||||
compressed_size = *sz; | |||||
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, | |||||
Done Inline Actionsextra EOL whitespace emaste: extra EOL whitespace | |||||
* 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) { | |||||
goto fail; | |||||
} | |||||
ret = inflateEnd(&strm); | |||||
if (ret != Z_OK) | |||||
Not Done Inline ActionsSo this frees the buffer passed by the caller, in one case it's d_buf from a libelf data descriptor. In other words, we are making assumptions about how libelf is allocating data buffers and that seems a bit fragile. I think it would be better for this function not to make any such assumptions and avoid replacing the input buffer with the output buffer. Just malloc() and return the output buffer (and its size) using separate parameters. markj: So this frees the buffer passed by the caller, in one case it's `d_buf` from a libelf data… | |||||
Done Inline ActionsOne question I have about this is if the decompression is unsuccessful, do we want to pass back the original buffer and size or just check the return value from the caller side? tig_freebsdfoundation.org: One question I have about this is if the decompression is unsuccessful, do we want to pass back… | |||||
Not Done Inline ActionsI think it's better to simply have the caller check the return value and decide which buffer to use. It's a bit more code, but not much, and is easier to follow. The caller has to know whether to free the returned buffer anyway. markj: I think it's better to simply have the caller check the return value and decide which buffer to… | |||||
goto fail; | |||||
free(*buffer); | |||||
*buffer = uncompressed_data_buffer; | |||||
*sz = uncompressed_size; | |||||
return (1); | |||||
fail: | |||||
Not Done Inline ActionsAll of the error paths that lead to the fail label do not result from a libelf error, so I believe elf_errmsg() will give you garbage here. markj: All of the error paths that lead to the `fail` label do not result from a libelf error, so I… | |||||
Not Done Inline ActionsI can see that now. Would it be good if I just warnx("decompress_section failed")? I'm not experienced in producing error messages would appreciate any suggestions. tig_freebsdfoundation.org: I can see that now. Would it be good if I just warnx("decompress_section failed")? I'm not… | |||||
Not Done Inline ActionsPer the zlib documentation, strm.msg will be set to an error string upon a failure, so you could print that. I would suggest checking it for NULL and just printing the error number in that case as a robustness measure. markj: Per the zlib documentation, `strm.msg` will be set to an error string upon a failure, so you… | |||||
Not Done Inline ActionsCould you specify what you mean by "checking it for NULL and just printing the error number"? I'm not sure which error number you are referring to tig_freebsdfoundation.org: Could you specify what you mean by "checking it for NULL and just printing the error number"? | |||||
Not Done Inline ActionsWhen reporting errors from zlib, you can print strm.msg as part of the error description. I'm not sure that it is guaranteed to be set for all errors (it would be worth reviewing the zlib docs), in which case you could print the error number that you had compared with Z_OK. markj: When reporting errors from zlib, you can print `strm.msg` as part of the error description. I'm… | |||||
Not Done Inline ActionsI looked at zlib manual and it seems like strm.msg handles most errors. In the rare case where there is an error but strm.msg is NULL printing the error number would be helpful. tig_freebsdfoundation.org: I looked at zlib manual and it seems like strm.msg handles most errors. In the rare case where… | |||||
free(uncompressed_data_buffer); | |||||
warnx("decompress_section failed: %s", elf_errmsg(-1)); | |||||
return (0); | |||||
Done Inline ActionsI think you want to call inflateEnd() as part of the cleanup here. Also the indentation looks wrong; goto labels should not have any indentation. markj: I think you want to call inflateEnd() as part of the cleanup here.
Also the indentation looks… | |||||
} | |||||
return (1); | |||||
} | |||||
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; | ||||
size_t sz, nbytes; | size_t sz, nbytes; | ||||
uint64_t addr; | uint64_t addr; | ||||
int elferr, i, j; | int elferr, i, j; | ||||
Done Inline Actionsextra blank line emaste: extra blank line | |||||
for (i = 1; (size_t) i < re->shnum; i++) { | for (i = 1; (size_t) i < re->shnum; i++) { | ||||
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; | ||||
} | } | ||||
addr = s->addr; | |||||
if (re->options & RE_Z) { | |||||
decompress_section(s, (unsigned char **) &d->d_buf, &d->d_size); | |||||
Done Inline ActionsWe are not checking the return value here. If it should be ignored, add a (void) cast of the return value - that's the conventional way to indicate that the return value is being ignored deliberately. markj: We are not checking the return value here. If it should be ignored, add a `(void)` cast of the… | |||||
} | |||||
buf = d->d_buf; | buf = d->d_buf; | ||||
sz = d->d_size; | sz = d->d_size; | ||||
addr = s->addr; | |||||
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 | ||||
Show All 38 Lines | if ((d = elf_getdata(s->scn, NULL)) == NULL && | ||||
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; | ||||
if (re->options & RE_Z) { | |||||
decompress_section(s, (unsigned char **) &d->d_buf, &d->d_size); | |||||
} | |||||
start = (unsigned char *) d->d_buf; | |||||
buf_end = start + d->d_size; | |||||
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)) | ||||
▲ Show 20 Lines • Show All 663 Lines • ▼ Show 20 Lines | main(int argc, char **argv) | ||||
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 |
in contrast to the comment above these were already sorted by long option name, so this one should be added in order