diff options
author | Mark Wielaard <mark@klomp.org> | 2018-04-30 02:54:07 +0200 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2018-05-30 12:14:10 +0200 |
commit | 4ac4a42376428248f7174c7bb713fff27bb4d4b0 (patch) | |
tree | 3853c641731266985a8b98a2076060bc44143bbe /src | |
parent | 50dfd98101dde72b32ea2d4df89166ce148a28f2 (diff) | |
download | elfutils-4ac4a42376428248f7174c7bb713fff27bb4d4b0.tar.gz |
readelf: Handle .debug_str_offsets.
The .debug_str_offsets tables are indirect string offsets into the
.debug_str section. For DWARF5 they can be in both the main, skeleton
and split dwarf (.dwo) files.
For DWARF4 with the GNU DebugFission extension the tables will not have
an header and they will only be in the split DWARF (.dwo) file, never in
the main (skeleton) file.
For DWARF5 the (non-split) unit DIE will have a DW_AT_str_offsets_base
attribute pointing at the actual index (after the header). The split
unit will never have this attribute (and use the table at offset zero).
Signed-off-by: Mark Wielaard <mark@klomp.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 9 | ||||
-rw-r--r-- | src/readelf.c | 213 |
2 files changed, 221 insertions, 1 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 01ecc610..545fb503 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2018-04-29 Mark Wielaard <mark@klomp.org> + + * readelf.c (parse_opt): Request implicit section_info for "str". + (known_stroffbases): New static variable. + (attr_callbackattr_callback): Handle DW_AT_str_offets_base. + (print_debug_str_offsets_section): New function. + (print_debug): Handle .debug_str_offsets as section_str. Reset + known_stroffbases. + 2018-04-27 Mark Wielaard <mark@klomp.org> * readelf.c (options): Add addr. diff --git a/src/readelf.c b/src/readelf.c index ab0223b8..be9fe88c 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -484,7 +484,11 @@ parse_opt (int key, char *arg, else if (strcmp (arg, "pubnames") == 0) print_debug_sections |= section_pubnames; else if (strcmp (arg, "str") == 0) - print_debug_sections |= section_str; + { + print_debug_sections |= section_str; + /* For mapping string offset tables to CUs. */ + implicit_debug_sections |= section_info; + } else if (strcmp (arg, "macinfo") == 0) print_debug_sections |= section_macinfo; else if (strcmp (arg, "macro") == 0) @@ -4824,6 +4828,7 @@ static struct listptr_table known_loclistsptr; static struct listptr_table known_rangelistptr; static struct listptr_table known_rnglistptr; static struct listptr_table known_addrbases; +static struct listptr_table known_stroffbases; static void reset_listptr (struct listptr_table *table) @@ -7192,6 +7197,21 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) } return DWARF_CB_OK; + case DW_AT_str_offsets_base: + { + bool stroffbase = notice_listptr (section_str, &known_stroffbases, + cbargs->addrsize, + cbargs->offset_size, + cbargs->cu, num, attr); + if (!cbargs->silent) + printf (" %*s%-20s (%s) str offsets base [%6" + PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + stroffbase ? "" : " <WARNING offset too big>"); + } + return DWARF_CB_OK; + case DW_AT_language: valuestr = dwarf_lang_name (num); break; @@ -9936,6 +9956,193 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), } } +static void +print_debug_str_offsets_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + if (shdr->sh_size == 0) + return; + + /* We like to get the section from libdw to make sure they are relocated. */ + Elf_Data *data = (dbg->sectiondata[IDX_debug_str_offsets] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_str_offsets section data: %s"), + elf_errmsg (-1)); + return; + } + + size_t idx = 0; + sort_listptr (&known_stroffbases, "str_offsets"); + + const unsigned char *start = (const unsigned char *) data->d_buf; + const unsigned char *readp = start; + const unsigned char *readendp = ((const unsigned char *) data->d_buf + + data->d_size); + + while (readp < readendp) + { + /* Most string offset tables will have a header. For split + dwarf unit GNU DebugFission didn't add one. But they were + also only defined for split units (main or skeleton units + didn't have indirect strings). So if we don't have a + DW_AT_str_offsets_base at all and this is offset zero, then + just start printing offsets immediately, if this is a .dwo + section. */ + Dwarf_Off off = (Dwarf_Off) (readp + - (const unsigned char *) data->d_buf); + + printf ("Table at offset %" PRIx64 " ", off); + + struct listptr *listptr = get_listptr (&known_stroffbases, idx++); + const unsigned char *next_unitp = readendp; + uint8_t offset_size; + bool has_header; + if (listptr == NULL) + { + /* This can happen for .dwo files. There is only an header + in the case this is a version 5 split DWARF file. */ + Dwarf_CU *cu; + uint8_t unit_type; + if (dwarf_get_units (dbg, NULL, &cu, NULL, &unit_type, + NULL, NULL) != 0) + { + error (0, 0, "Warning: Cannot find any DWARF unit."); + /* Just guess some values. */ + has_header = false; + offset_size = 4; + } + else if (off == 0 + && (unit_type == DW_UT_split_type + || unit_type == DW_UT_split_compile)) + { + has_header = cu->version > 4; + offset_size = cu->offset_size; + } + else + { + error (0, 0, + "Warning: No CU references .debug_str_offsets after %" + PRIx64, off); + has_header = cu->version > 4; + offset_size = cu->offset_size; + } + printf ("\n"); + } + else + { + /* This must be DWARF5, since GNU DebugFission didn't define + DW_AT_str_offsets_base. */ + has_header = true; + + Dwarf_Die cudie; + if (dwarf_cu_die (listptr->cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf ("Unknown CU (%s):\n", dwarf_errmsg (-1)); + else + printf ("for CU [%6" PRIx64 "]:\n", dwarf_dieoffset (&cudie)); + } + + if (has_header) + { + uint64_t unit_length; + uint16_t version; + uint16_t padding; + + unit_length = read_4ubyte_unaligned_inc (dbg, readp); + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > readendp - 8)) + { + invalid_data: + error (0, 0, "Invalid data"); + return; + } + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + else + offset_size = 4; + + printf ("\n"); + printf (gettext (" Length: %8" PRIu64 "\n"), + unit_length); + printf (gettext (" Offset size: %8" PRIu8 "\n"), + offset_size); + + /* We need at least 2-bytes (version) + 2-bytes (padding) = + 4 bytes to complete the header. And this unit cannot go + beyond the section data. */ + if (readp > readendp - 4 + || unit_length < 4 + || unit_length > (uint64_t) (readendp - readp)) + goto invalid_data; + + next_unitp = readp + unit_length; + + version = read_2ubyte_unaligned_inc (dbg, readp); + printf (gettext (" DWARF version: %8" PRIu16 "\n"), version); + + if (version != 5) + { + error (0, 0, gettext ("Unknown version")); + goto next_unit; + } + + padding = read_2ubyte_unaligned_inc (dbg, readp); + printf (gettext (" Padding: %8" PRIx16 "\n"), padding); + + if (listptr != NULL + && listptr->offset != (Dwarf_Off) (readp - start)) + { + error (0, 0, "String offsets index doesn't start after header"); + goto next_unit; + } + + printf ("\n"); + } + + int digits = 1; + size_t offsets = (next_unitp - readp) / offset_size; + while (offsets >= 10) + { + ++digits; + offsets /= 10; + } + + unsigned int index = 0; + size_t index_offset = readp - (const unsigned char *) data->d_buf; + printf (" Offsets start at 0x%zx:\n", index_offset); + while (readp <= next_unitp - offset_size) + { + Dwarf_Word offset; + if (offset_size == 4) + offset = read_4ubyte_unaligned_inc (dbg, readp); + else + offset = read_8ubyte_unaligned_inc (dbg, readp); + const char *str = dwarf_getstring (dbg, offset, NULL); + printf (" [%*u] [%*" PRIx64 "] \"%s\"\n", + digits, index++, (int) offset_size * 2, offset, str ?: "???"); + } + printf ("\n"); + + if (readp != next_unitp) + error (0, 0, "extra %zd bytes at end of unit", + (size_t) (next_unitp - readp)); + + next_unit: + readp = next_unitp; + } +} + /* Print the content of the call frame search table section '.eh_frame_hdr'. */ @@ -10752,6 +10959,9 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) /* A DWARF5 specialised debug string section. */ { ".debug_line_str", section_str, print_debug_str_section }, + /* DWARF5 string offsets table. */ + { ".debug_str_offsets", section_str, + print_debug_str_offsets_section }, NEW_SECTION (macinfo), NEW_SECTION (macro), NEW_SECTION (ranges), @@ -10807,6 +11017,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) reset_listptr (&known_rangelistptr); reset_listptr (&known_rnglistptr); reset_listptr (&known_addrbases); + reset_listptr (&known_stroffbases); } |