diff options
author | Mark Wielaard <mark@klomp.org> | 2018-07-04 00:31:46 +0200 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2018-07-04 00:31:46 +0200 |
commit | 94c975eefdfbfa9319b7bab69600c76fe6be7910 (patch) | |
tree | e29e208499440fdbaac2a867bcd7029f12e89a7a /src/readelf.c | |
parent | 766df1e15c1e8f8bc9890e7538eb2ef15a6516dc (diff) | |
parent | aa36de0335e3ce12898954985a208f6336731289 (diff) | |
download | elfutils-94c975eefdfbfa9319b7bab69600c76fe6be7910.tar.gz |
Merge tag 'elfutils-0.173' into mjw/RH-DTSdts-0.173
elfutils 0.173 release
Removed riscv backend for now.
Removed new arm32 tests.
Added various new files to libdw_static_pic.a to support split dwarf.
Diffstat (limited to 'src/readelf.c')
-rw-r--r-- | src/readelf.c | 3956 |
1 files changed, 3313 insertions, 643 deletions
diff --git a/src/readelf.c b/src/readelf.c index 73be474b..faed61a6 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -1,7 +1,6 @@ /* Print information from ELF file in human-readable form. - Copyright (C) 1999-2016 Red Hat, Inc. + Copyright (C) 1999-2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <drepper@redhat.com>, 1999. This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +35,8 @@ #include <locale.h> #include <stdarg.h> #include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> #include <stdlib.h> #include <string.h> #include <strings.h> @@ -57,6 +58,20 @@ #include "../libdw/known-dwarf.h" +#ifdef __linux__ +#define CORE_SIGILL SIGILL +#define CORE_SIGBUS SIGBUS +#define CORE_SIGFPE SIGFPE +#define CORE_SIGSEGV SIGSEGV +#define CORE_SI_USER SI_USER +#else +/* We want the linux version of those as that is what shows up in the core files. */ +#define CORE_SIGILL 4 /* Illegal instruction (ANSI). */ +#define CORE_SIGBUS 7 /* BUS error (4.2 BSD). */ +#define CORE_SIGFPE 8 /* Floating-point exception (ANSI). */ +#define CORE_SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define CORE_SI_USER 0 /* Sent by kill, sigsend. */ +#endif /* Name and version of program. */ ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; @@ -67,6 +82,13 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; /* argp key value for --elf-section, non-ascii. */ #define ELF_INPUT_SECTION 256 +/* argp key value for --dwarf-skeleton, non-ascii. */ +#define DWARF_SKELETON 257 + +/* Terrible hack for hooking unrelated skeleton/split compile units, + see __libdw_link_skel_split in print_debug. */ +static bool do_not_close_dwfl = false; + /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { @@ -74,6 +96,9 @@ static const struct argp_option options[] = { "elf-section", ELF_INPUT_SECTION, "SECTION", OPTION_ARG_OPTIONAL, N_("Use the named SECTION (default .gnu_debugdata) as (compressed) ELF " "input data"), 0 }, + { "dwarf-skeleton", DWARF_SKELETON, "FILE", 0, + N_("Used with -w to find the skeleton Compile Units in FILE associated " + "with the Split Compile units in a .dwo input file"), 0 }, { NULL, 0, NULL, 0, N_("ELF output selection:"), 0 }, { "all", 'a', NULL, 0, N_("All these plus -p .strtab -p .dynstr -p .comment"), 0 }, @@ -84,6 +109,7 @@ static const struct argp_option options[] = { "program-headers", 'l', NULL, 0, N_("Display the program headers"), 0 }, { "segments", 'l', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, { "relocs", 'r', NULL, 0, N_("Display relocations"), 0 }, + { "section-groups", 'g', NULL, 0, N_("Display the section groups"), 0 }, { "section-headers", 'S', NULL, 0, N_("Display the sections' headers"), 0 }, { "sections", 'S', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, { "symbols", 's', "SECTION", OPTION_ARG_OPTIONAL, @@ -97,8 +123,8 @@ static const struct argp_option options[] = { NULL, 0, NULL, 0, N_("Additional output selection:"), 0 }, { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL, - N_("Display DWARF section content. SECTION can be one of abbrev, " - "aranges, decodedaranges, frame, gdb_index, info, loc, line, " + N_("Display DWARF section content. SECTION can be one of abbrev, addr, " + "aranges, decodedaranges, frame, gdb_index, info, info+, loc, line, " "decodedline, ranges, pubnames, str, macinfo, macro or exception"), 0 }, { "hex-dump", 'x', "SECTION", 0, N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 }, @@ -139,6 +165,9 @@ static struct argp argp = /* If non-null, the section from which we should read to (compressed) ELF. */ static const char *elf_input_section = NULL; +/* If non-null, the file that contains the skeleton CUs. */ +static const char *dwarf_skeleton = NULL; + /* Flags set by the option controlling the output. */ /* True if dynamic segment should be printed. */ @@ -201,14 +230,16 @@ static bool decodedline = false; /* True if we want to show more information about compressed sections. */ static bool print_decompress = false; +/* True if we want to show split compile units for debug_info skeletons. */ +static bool show_split_units = false; + /* Select printing of debugging sections. */ static enum section_e { section_abbrev = 1, /* .debug_abbrev */ section_aranges = 2, /* .debug_aranges */ section_frame = 4, /* .debug_frame or .eh_frame & al. */ - section_info = 8, /* .debug_info, .debug_types */ - section_types = section_info, + section_info = 8, /* .debug_info, (implies .debug_types) */ section_line = 16, /* .debug_line */ section_loc = 32, /* .debug_loc */ section_pubnames = 64, /* .debug_pubnames */ @@ -218,11 +249,13 @@ static enum section_e section_exception = 1024, /* .eh_frame & al. */ section_gdb_index = 2048, /* .gdb_index */ section_macro = 4096, /* .debug_macro */ + section_addr = 8192, /* .debug_addr */ + section_types = 16384, /* .debug_types (implied by .debug_info) */ section_all = (section_abbrev | section_aranges | section_frame | section_info | section_line | section_loc | section_pubnames | section_str | section_macinfo | section_ranges | section_exception | section_gdb_index - | section_macro) + | section_macro | section_addr | section_types) } print_debug_sections, implicit_debug_sections; /* Select hex dumping of sections. */ @@ -276,15 +309,26 @@ static void print_strings (Ebl *ebl); static void dump_archive_index (Elf *, const char *); +/* Looked up once with gettext in main. */ +static char *yes_str; +static char *no_str; + int main (int argc, char *argv[]) { + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + /* Set locale. */ setlocale (LC_ALL, ""); /* Initialize the message catalog. */ textdomain (PACKAGE_TARNAME); + /* Look up once. */ + yes_str = gettext ("yes"); + no_str = gettext ("no"); + /* Parse and process arguments. */ int remaining; argp_parse (&argp, argc, argv, 0, &remaining, NULL); @@ -405,9 +449,18 @@ parse_opt (int key, char *arg, break; case 'w': if (arg == NULL) - print_debug_sections = section_all; + { + print_debug_sections = section_all; + implicit_debug_sections = section_info; + show_split_units = true; + } else if (strcmp (arg, "abbrev") == 0) print_debug_sections |= section_abbrev; + else if (strcmp (arg, "addr") == 0) + { + print_debug_sections |= section_addr; + implicit_debug_sections |= section_info; + } else if (strcmp (arg, "aranges") == 0) print_debug_sections |= section_aranges; else if (strcmp (arg, "decodedaranges") == 0) @@ -423,7 +476,16 @@ parse_opt (int key, char *arg, else if (strcmp (arg, "frame") == 0 || strcmp (arg, "frames") == 0) print_debug_sections |= section_frame; else if (strcmp (arg, "info") == 0) - print_debug_sections |= section_info; + { + print_debug_sections |= section_info; + print_debug_sections |= section_types; + } + else if (strcmp (arg, "info+") == 0) + { + print_debug_sections |= section_info; + print_debug_sections |= section_types; + show_split_units = true; + } else if (strcmp (arg, "loc") == 0) { print_debug_sections |= section_loc; @@ -439,7 +501,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) @@ -465,7 +531,7 @@ parse_opt (int key, char *arg, print_string_sections = true; break; } - /* Fall through. */ + FALLTHROUGH; case 'x': add_dump_section (arg, false); any_control_option = true; @@ -500,6 +566,9 @@ parse_opt (int key, char *arg, else elf_input_section = arg; break; + case DWARF_SKELETON: + dwarf_skeleton = arg; + break; default: return ARGP_ERR_UNKNOWN; } @@ -722,31 +791,9 @@ find_no_debuginfo (Dwfl_Module *mod, debuglink_crc, debuginfo_file_name); } -/* Process one input file. */ -static void -process_file (int fd, const char *fname, bool only_one) +static Dwfl * +create_dwfl (int fd, const char *fname) { - if (print_archive_index) - check_archive_index (fd, fname, only_one); - - if (!any_control_option) - return; - - if (elf_input_section != NULL) - { - /* Replace fname and fd with section content. */ - char *fnname = alloca (strlen (fname) + strlen (elf_input_section) + 2); - sprintf (fnname, "%s:%s", fname, elf_input_section); - fd = open_input_section (fd); - if (fd == -1) - { - error (0, 0, gettext ("No such section '%s' in '%s'"), - elf_input_section, fname); - return; - } - fname = fnname; - } - /* Duplicate an fd for dwfl_report_offline to swallow. */ int dwfl_fd = dup (fd); if (unlikely (dwfl_fd < 0)) @@ -774,11 +821,42 @@ process_file (int fd, const char *fname, bool only_one) error (0, 0, gettext ("failed reading '%s': %s"), fname, dwfl_errmsg (-1)); close (dwfl_fd); /* Consumed on success, not on failure. */ + dwfl = NULL; } else + dwfl_report_end (dwfl, NULL, NULL); + + return dwfl; +} + +/* Process one input file. */ +static void +process_file (int fd, const char *fname, bool only_one) +{ + if (print_archive_index) + check_archive_index (fd, fname, only_one); + + if (!any_control_option) + return; + + if (elf_input_section != NULL) { - dwfl_report_end (dwfl, NULL, NULL); + /* Replace fname and fd with section content. */ + char *fnname = alloca (strlen (fname) + strlen (elf_input_section) + 2); + sprintf (fnname, "%s:%s", fname, elf_input_section); + fd = open_input_section (fd); + if (fd == -1) + { + error (0, 0, gettext ("No such section '%s' in '%s'"), + elf_input_section, fname); + return; + } + fname = fnname; + } + Dwfl *dwfl = create_dwfl (fd, fname); + if (dwfl != NULL) + { if (only_one) { /* Clear ONLY_ONE if we have multiple modules, from an archive. */ @@ -790,7 +868,10 @@ process_file (int fd, const char *fname, bool only_one) struct process_dwflmod_args a = { .fd = fd, .only_one = only_one }; dwfl_getmodules (dwfl, &process_dwflmod, &a, 0); } - dwfl_end (dwfl); + /* Terrible hack for hooking unrelated skeleton/split compile units, + see __libdw_link_skel_split in print_debug. */ + if (! do_not_close_dwfl) + dwfl_end (dwfl); /* Need to close the replaced fd if we created it. Caller takes care of original. */ @@ -3126,9 +3207,18 @@ handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) { Elf32_Word inner = bucket[cnt]; + Elf32_Word chain_len = 0; while (inner > 0 && inner < nchain) { ++nsyms; + ++chain_len; + if (chain_len > nchain) + { + error (0, 0, gettext ("invalid chain in sysv.hash section %d"), + (int) elf_ndxscn (scn)); + free (lengths); + return; + } if (maxlength < ++lengths[cnt]) ++maxlength; @@ -3183,9 +3273,18 @@ handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt) { Elf64_Xword inner = bucket[cnt]; + Elf64_Xword chain_len = 0; while (inner > 0 && inner < nchain) { ++nsyms; + ++chain_len; + if (chain_len > nchain) + { + error (0, 0, gettext ("invalid chain in sysv.hash64 section %d"), + (int) elf_ndxscn (scn)); + free (lengths); + return; + } if (maxlength < ++lengths[cnt]) ++maxlength; @@ -3606,9 +3705,9 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) } -static char * -format_dwarf_addr (Dwfl_Module *dwflmod, - int address_size, Dwarf_Addr address, Dwarf_Addr raw) +void +print_dwarf_addr (Dwfl_Module *dwflmod, + int address_size, Dwarf_Addr address, Dwarf_Addr raw) { /* See if there is a name we can give for this address. */ GElf_Sym sym; @@ -3634,61 +3733,41 @@ format_dwarf_addr (Dwfl_Module *dwflmod, : dwfl_module_relocation_info (dwflmod, i, NULL)); } - char *result; if ((name != NULL ? (off != 0 ? (scn != NULL ? (address_size == 0 - ? asprintf (&result, - gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"), - scn, address, name, off) - : asprintf (&result, - gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"), - scn, 2 + address_size * 2, address, - name, off)) + ? printf ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">", + scn, address, name, off) + : printf ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">", + scn, 2 + address_size * 2, address, + name, off)) : (address_size == 0 - ? asprintf (&result, - gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"), - address, name, off) - : asprintf (&result, - gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"), - 2 + address_size * 2, address, - name, off))) + ? printf ("%#" PRIx64 " <%s+%#" PRIx64 ">", + address, name, off) + : printf ("%#0*" PRIx64 " <%s+%#" PRIx64 ">", + 2 + address_size * 2, address, + name, off))) : (scn != NULL ? (address_size == 0 - ? asprintf (&result, - gettext ("%s+%#" PRIx64 " <%s>"), - scn, address, name) - : asprintf (&result, - gettext ("%s+%#0*" PRIx64 " <%s>"), - scn, 2 + address_size * 2, address, name)) + ? printf ("%s+%#" PRIx64 " <%s>", scn, address, name) + : printf ("%s+%#0*" PRIx64 " <%s>", + scn, 2 + address_size * 2, address, name)) : (address_size == 0 - ? asprintf (&result, - gettext ("%#" PRIx64 " <%s>"), - address, name) - : asprintf (&result, - gettext ("%#0*" PRIx64 " <%s>"), - 2 + address_size * 2, address, name)))) + ? printf ("%#" PRIx64 " <%s>", address, name) + : printf ("%#0*" PRIx64 " <%s>", + 2 + address_size * 2, address, name)))) : (scn != NULL ? (address_size == 0 - ? asprintf (&result, - gettext ("%s+%#" PRIx64), - scn, address) - : asprintf (&result, - gettext ("%s+%#0*" PRIx64), - scn, 2 + address_size * 2, address)) + ? printf ("%s+%#" PRIx64, scn, address) + : printf ("%s+%#0*" PRIx64, scn, 2 + address_size * 2, address)) : (address_size == 0 - ? asprintf (&result, - "%#" PRIx64, - address) - : asprintf (&result, - "%#0*" PRIx64, - 2 + address_size * 2, address)))) < 0) - error (EXIT_FAILURE, 0, _("memory exhausted")); - - return result; + ? printf ("%#" PRIx64, address) + : printf ("%#0*" PRIx64, 2 + address_size * 2, address)))) < 0) + error (EXIT_FAILURE, 0, _("sprintf failure")); } + static const char * dwarf_tag_string (unsigned int tag) { @@ -3935,6 +4014,62 @@ dwarf_locexpr_opcode_string (unsigned int code) } +static const char * +dwarf_unit_string (unsigned int type) +{ + switch (type) + { +#define DWARF_ONE_KNOWN_DW_UT(NAME, CODE) case CODE: return #NAME; + DWARF_ALL_KNOWN_DW_UT +#undef DWARF_ONE_KNOWN_DW_UT + default: + return NULL; + } +} + + +static const char * +dwarf_range_list_encoding_string (unsigned int kind) +{ + switch (kind) + { +#define DWARF_ONE_KNOWN_DW_RLE(NAME, CODE) case CODE: return #NAME; + DWARF_ALL_KNOWN_DW_RLE +#undef DWARF_ONE_KNOWN_DW_RLE + default: + return NULL; + } +} + + +static const char * +dwarf_loc_list_encoding_string (unsigned int kind) +{ + switch (kind) + { +#define DWARF_ONE_KNOWN_DW_LLE(NAME, CODE) case CODE: return #NAME; + DWARF_ALL_KNOWN_DW_LLE +#undef DWARF_ONE_KNOWN_DW_LLE + default: + return NULL; + } +} + + +static const char * +dwarf_line_content_description_string (unsigned int kind) +{ + switch (kind) + { +#define DWARF_ONE_KNOWN_DW_LNCT(NAME, CODE) case CODE: return #NAME; + DWARF_ALL_KNOWN_DW_LNCT +#undef DWARF_ONE_KNOWN_DW_LNCT + default: + return NULL; + } +} + + /* Used by all dwarf_foo_name functions. */ static const char * string_or_unknown (const char *known, unsigned int code, @@ -4074,6 +4209,39 @@ dwarf_discr_list_name (unsigned int code) } +static const char * +dwarf_unit_name (unsigned int type) +{ + const char *ret = dwarf_unit_string (type); + return string_or_unknown (ret, type, DW_UT_lo_user, DW_UT_hi_user, true); +} + + +static const char * +dwarf_range_list_encoding_name (unsigned int kind) +{ + const char *ret = dwarf_range_list_encoding_string (kind); + return string_or_unknown (ret, kind, 0, 0, false); +} + + +static const char * +dwarf_loc_list_encoding_name (unsigned int kind) +{ + const char *ret = dwarf_loc_list_encoding_string (kind); + return string_or_unknown (ret, kind, 0, 0, false); +} + + +static const char * +dwarf_line_content_description_name (unsigned int kind) +{ + const char *ret = dwarf_line_content_description_string (kind); + return string_or_unknown (ret, kind, DW_LNCT_lo_user, DW_LNCT_hi_user, + false); +} + + static void print_block (size_t n, const void *block) { @@ -4091,6 +4259,43 @@ print_block (size_t n, const void *block) } static void +print_bytes (size_t n, const unsigned char *bytes) +{ + while (n-- > 0) + { + printf ("%02x", *bytes++); + if (n > 0) + printf (" "); + } +} + +static int +get_indexed_addr (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr) +{ + if (cu == NULL) + return -1; + + Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr]; + if (debug_addr == NULL) + return -1; + + Dwarf_Off base = __libdw_cu_addr_base (cu); + Dwarf_Word off = idx * cu->address_size; + if (base > debug_addr->d_size + || off > debug_addr->d_size - base + || cu->address_size > debug_addr->d_size - base - off) + return -1; + + const unsigned char *addrp = debug_addr->d_buf + base + off; + if (cu->address_size == 4) + *addr = read_4ubyte_unaligned (cu->dbg, addrp); + else + *addr = read_8ubyte_unaligned (cu->dbg, addrp); + + return 0; +} + +static void print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, unsigned int vers, unsigned int addrsize, unsigned int offset_size, struct Dwarf_CU *cu, Dwarf_Word len, const unsigned char *data) @@ -4137,15 +4342,16 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, data += addrsize; CONSUME (addrsize); - char *a = format_dwarf_addr (dwflmod, 0, addr, addr); - printf ("%*s[%4" PRIuMAX "] %s %s\n", - indent, "", (uintmax_t) offset, op_name, a); - free (a); + printf ("%*s[%2" PRIuMAX "] %s ", + indent, "", (uintmax_t) offset, op_name); + print_dwarf_addr (dwflmod, 0, addr, addr); + printf ("\n"); offset += 1 + addrsize; break; case DW_OP_call_ref: + case DW_OP_GNU_variable_value: /* Offset operand. */ if (ref_size != 4 && ref_size != 8) goto invalid; /* Cannot be used in CFA. */ @@ -4156,8 +4362,8 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, addr = read_8ubyte_unaligned (dbg, data); data += ref_size; CONSUME (ref_size); - - printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + /* addr is a DIE offset, so format it as one. */ + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "]\n", indent, "", (uintmax_t) offset, op_name, (uintmax_t) addr); offset += 1 + ref_size; @@ -4169,7 +4375,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const1u: // XXX value might be modified by relocation NEED (1); - printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu8 "\n", indent, "", (uintmax_t) offset, op_name, *((uint8_t *) data)); ++data; @@ -4180,7 +4386,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const2u: NEED (2); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu16 "\n", indent, "", (uintmax_t) offset, op_name, read_2ubyte_unaligned (dbg, data)); CONSUME (2); @@ -4191,7 +4397,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const4u: NEED (4); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu32 "\n", indent, "", (uintmax_t) offset, op_name, read_4ubyte_unaligned (dbg, data)); CONSUME (4); @@ -4202,7 +4408,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const8u: NEED (8); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 "\n", indent, "", (uintmax_t) offset, op_name, (uint64_t) read_8ubyte_unaligned (dbg, data)); CONSUME (8); @@ -4213,7 +4419,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const1s: NEED (1); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRId8 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRId8 "\n", indent, "", (uintmax_t) offset, op_name, *((int8_t *) data)); ++data; @@ -4224,7 +4430,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const2s: NEED (2); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRId16 "\n", indent, "", (uintmax_t) offset, op_name, read_2sbyte_unaligned (dbg, data)); CONSUME (2); @@ -4235,7 +4441,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const4s: NEED (4); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRId32 "\n", indent, "", (uintmax_t) offset, op_name, read_4sbyte_unaligned (dbg, data)); CONSUME (4); @@ -4246,7 +4452,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_const8s: NEED (8); // XXX value might be modified by relocation - printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRId64 "\n", indent, "", (uintmax_t) offset, op_name, read_8sbyte_unaligned (dbg, data)); CONSUME (8); @@ -4262,12 +4468,32 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, uint64_t uleb; NEED (1); get_uleb128 (uleb, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 "\n", indent, "", (uintmax_t) offset, op_name, uleb); CONSUME (data - start); offset += 1 + (data - start); break; + case DW_OP_addrx: + case DW_OP_GNU_addr_index: + case DW_OP_constx: + case DW_OP_GNU_const_index:; + start = data; + NEED (1); + get_uleb128 (uleb, data, data + len); + printf ("%*s[%2" PRIuMAX "] %s [%" PRIu64 "] ", + indent, "", (uintmax_t) offset, op_name, uleb); + CONSUME (data - start); + offset += 1 + (data - start); + if (get_indexed_addr (cu, uleb, &addr) != 0) + printf ("???\n"); + else + { + print_dwarf_addr (dwflmod, 0, addr, addr); + printf ("\n"); + } + break; + case DW_OP_bit_piece: start = data; uint64_t uleb2; @@ -4275,7 +4501,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, get_uleb128 (uleb, data, data + len); NEED (1); get_uleb128 (uleb2, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n", indent, "", (uintmax_t) offset, op_name, uleb, uleb2); CONSUME (data - start); offset += 1 + (data - start); @@ -4288,7 +4514,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, int64_t sleb; NEED (1); get_sleb128 (sleb, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRId64 "\n", indent, "", (uintmax_t) offset, op_name, sleb); CONSUME (data - start); offset += 1 + (data - start); @@ -4300,7 +4526,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, get_uleb128 (uleb, data, data + len); NEED (1); get_sleb128 (sleb, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n", indent, "", (uintmax_t) offset, op_name, uleb, sleb); CONSUME (data - start); offset += 1 + (data - start); @@ -4308,26 +4534,28 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_call2: NEED (2); - printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIx16 "]\n", indent, "", (uintmax_t) offset, op_name, read_2ubyte_unaligned (dbg, data)); CONSUME (2); + data += 2; offset += 3; break; case DW_OP_call4: NEED (4); - printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIx32 "]\n", indent, "", (uintmax_t) offset, op_name, read_4ubyte_unaligned (dbg, data)); CONSUME (4); + data += 4; offset += 5; break; case DW_OP_skip: case DW_OP_bra: NEED (2); - printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIuMAX "\n", indent, "", (uintmax_t) offset, op_name, (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data) + 3)); CONSUME (2); @@ -4339,7 +4567,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, start = data; NEED (1); get_uleb128 (uleb, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s: ", + printf ("%*s[%2" PRIuMAX "] %s: ", indent, "", (uintmax_t) offset, op_name); NEED (uleb); print_block (uleb, data); @@ -4348,6 +4576,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, offset += 1 + (data - start); break; + case DW_OP_implicit_pointer: case DW_OP_GNU_implicit_pointer: /* DIE offset operand. */ start = data; @@ -4363,28 +4592,30 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, NEED (1); get_sleb128 (sleb, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] %+" PRId64 "\n", + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "] %+" PRId64 "\n", indent, "", (intmax_t) offset, op_name, (uintmax_t) addr, sleb); CONSUME (data - start); offset += 1 + (data - start); break; + case DW_OP_entry_value: case DW_OP_GNU_entry_value: /* Size plus expression block. */ start = data; NEED (1); get_uleb128 (uleb, data, data + len); - printf ("%*s[%4" PRIuMAX "] %s:\n", + printf ("%*s[%2" PRIuMAX "] %s:\n", indent, "", (uintmax_t) offset, op_name); NEED (uleb); - print_ops (dwflmod, dbg, indent + 6, indent + 6, vers, + print_ops (dwflmod, dbg, indent + 5, indent + 5, vers, addrsize, offset_size, cu, uleb, data); data += uleb; CONSUME (data - start); offset += 1 + (data - start); break; + case DW_OP_const_type: case DW_OP_GNU_const_type: /* uleb128 CU relative DW_TAG_base_type DIE offset, 1-byte unsigned size plus block. */ @@ -4396,7 +4627,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, NEED (1); uint8_t usize = *(uint8_t *) data++; NEED (usize); - printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] ", + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "] ", indent, "", (uintmax_t) offset, op_name, uleb); print_block (usize, data); data += usize; @@ -4404,6 +4635,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, offset += 1 + (data - start); break; + case DW_OP_regval_type: case DW_OP_GNU_regval_type: /* uleb128 register number, uleb128 CU relative DW_TAG_base_type DIE offset. */ @@ -4414,12 +4646,13 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, get_uleb128 (uleb2, data, data + len); if (! print_unresolved_addresses && cu != NULL) uleb2 += cu->start; - printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " [%6" PRIx64 "]\n", + printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 " [%6" PRIx64 "]\n", indent, "", (uintmax_t) offset, op_name, uleb, uleb2); CONSUME (data - start); offset += 1 + (data - start); break; + case DW_OP_deref_type: case DW_OP_GNU_deref_type: /* 1-byte unsigned size of value, uleb128 CU relative DW_TAG_base_type DIE offset. */ @@ -4430,6 +4663,20 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, get_uleb128 (uleb, data, data + len); if (! print_unresolved_addresses && cu != NULL) uleb += cu->start; + printf ("%*s[%2" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n", + indent, "", (uintmax_t) offset, + op_name, usize, uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_xderef_type: + /* 1-byte unsigned size of value, uleb128 base_type DIE offset. */ + start = data; + NEED (1); + usize = *(uint8_t *) data++; + NEED (1); + get_uleb128 (uleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n", indent, "", (uintmax_t) offset, op_name, usize, uleb); @@ -4437,7 +4684,9 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, offset += 1 + (data - start); break; + case DW_OP_convert: case DW_OP_GNU_convert: + case DW_OP_reinterpret: case DW_OP_GNU_reinterpret: /* uleb128 CU relative offset to DW_TAG_base_type, or zero for conversion to untyped. */ @@ -4446,7 +4695,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, get_uleb128 (uleb, data, data + len); if (uleb != 0 && ! print_unresolved_addresses && cu != NULL) uleb += cu->start; - printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n", + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "]\n", indent, "", (uintmax_t) offset, op_name, uleb); CONSUME (data - start); offset += 1 + (data - start); @@ -4459,7 +4708,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, uintmax_t param_off = (uintmax_t) read_4ubyte_unaligned (dbg, data); if (! print_unresolved_addresses && cu != NULL) param_off += cu->start; - printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n", + printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "]\n", indent, "", (uintmax_t) offset, op_name, param_off); CONSUME (4); data += 4; @@ -4468,7 +4717,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, default: /* No Operand. */ - printf ("%*s[%4" PRIuMAX "] %s\n", + printf ("%*s[%2" PRIuMAX "] %s\n", indent, "", (uintmax_t) offset, op_name); ++offset; break; @@ -4478,7 +4727,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, continue; invalid: - printf (gettext ("%*s[%4" PRIuMAX "] %s <TRUNCATED>\n"), + printf (gettext ("%*s[%2" PRIuMAX "] %s <TRUNCATED>\n"), indent, "", (uintmax_t) offset, op_name); break; } @@ -4492,31 +4741,38 @@ struct listptr bool dwarf64:1; bool warned:1; struct Dwarf_CU *cu; + unsigned int attr; }; #define listptr_offset_size(p) ((p)->dwarf64 ? 8 : 4) #define listptr_address_size(p) ((p)->addr64 ? 8 : 4) static Dwarf_Addr -listptr_base (struct listptr *p) +cudie_base (Dwarf_Die *cudie) { Dwarf_Addr base; - Dwarf_Die cu = CUDIE (p->cu); /* Find the base address of the compilation unit. It will normally be specified by DW_AT_low_pc. In DWARF-3 draft 4, the base address could be overridden by DW_AT_entry_pc. It's been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc for compilation units with discontinuous ranges. */ - if (unlikely (dwarf_lowpc (&cu, &base) != 0)) + if (unlikely (dwarf_lowpc (cudie, &base) != 0)) { Dwarf_Attribute attr_mem; - if (dwarf_formaddr (dwarf_attr (&cu, DW_AT_entry_pc, &attr_mem), + if (dwarf_formaddr (dwarf_attr (cudie, DW_AT_entry_pc, &attr_mem), &base) != 0) base = 0; } return base; } +static Dwarf_Addr +listptr_base (struct listptr *p) +{ + Dwarf_Die cu = CUDIE (p->cu); + return cudie_base (&cu); +} + static int compare_listptr (const void *a, const void *b, void *arg) { @@ -4552,6 +4808,15 @@ compare_listptr (const void *a, const void *b, void *arg) gettext ("%s %#" PRIx64 " used with different base addresses"), name, (uint64_t) p1->offset); } + if (p1->attr != p2 ->attr) + { + p1->warned = p2->warned = true; + error (0, 0, + gettext ("%s %#" PRIx64 + " used with different attribute %s and %s"), + name, (uint64_t) p1->offset, dwarf_attr_name (p2->attr), + dwarf_attr_name (p2->attr)); + } } return 0; @@ -4564,8 +4829,12 @@ struct listptr_table struct listptr *table; }; -static struct listptr_table known_loclistptr; +static struct listptr_table known_locsptr; +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) @@ -4579,7 +4848,7 @@ reset_listptr (struct listptr_table *table) static bool notice_listptr (enum section_e section, struct listptr_table *table, uint_fast8_t address_size, uint_fast8_t offset_size, - struct Dwarf_CU *cu, Dwarf_Off offset) + struct Dwarf_CU *cu, Dwarf_Off offset, unsigned int attr) { if (print_debug_sections & section) { @@ -4600,7 +4869,8 @@ notice_listptr (enum section_e section, struct listptr_table *table, .addr64 = address_size == 8, .dwarf64 = offset_size == 8, .offset = offset, - .cu = cu + .cu = cu, + .attr = attr }; if (p->offset != offset) @@ -4624,7 +4894,8 @@ static bool skip_listptr_hole (struct listptr_table *table, size_t *idxp, uint_fast8_t *address_sizep, uint_fast8_t *offset_sizep, Dwarf_Addr *base, struct Dwarf_CU **cu, ptrdiff_t offset, - unsigned char **readp, unsigned char *endp) + unsigned char **readp, unsigned char *endp, + unsigned int *attr) { if (table->n == 0) return false; @@ -4659,10 +4930,62 @@ skip_listptr_hole (struct listptr_table *table, size_t *idxp, *base = listptr_base (p); if (cu != NULL) *cu = p->cu; + if (attr != NULL) + *attr = p->attr; return false; } +static Dwarf_Off +next_listptr_offset (struct listptr_table *table, size_t idx) +{ + /* Note that multiple attributes could in theory point to the same loclist + offset, so make sure we pick one that is bigger than the current one. + The table is sorted on offset. */ + Dwarf_Off offset = table->table[idx].offset; + while (++idx < table->n) + { + Dwarf_Off next = table->table[idx].offset; + if (next > offset) + return next; + } + return 0; +} + +/* Returns the listptr associated with the given index, or NULL. */ +static struct listptr * +get_listptr (struct listptr_table *table, size_t idx) +{ + if (idx >= table->n) + return NULL; + return &table->table[idx]; +} + +/* Returns the next index, base address and CU associated with the + list unit offsets. If there is none false is returned, otherwise + true. Assumes the table has been sorted. */ +static bool +listptr_cu (struct listptr_table *table, size_t *idxp, + Dwarf_Off start, Dwarf_Off end, + Dwarf_Addr *base, struct Dwarf_CU **cu) +{ + while (*idxp < table->n + && table->table[*idxp].offset < start) + ++*idxp; + + if (*idxp < table->n + && table->table[*idxp].offset >= start + && table->table[*idxp].offset < end) + { + struct listptr *p = &table->table[*idxp]; + *base = listptr_base (p); + *cu = p->cu; + ++*idxp; + return true; + } + + return false; +} static void print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), @@ -4712,20 +5035,22 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext (" [%5u] offset: %" PRId64 ", children: %s, tag: %s\n"), code, (int64_t) offset, - has_children ? gettext ("yes") : gettext ("no"), + has_children ? yes_str : no_str, dwarf_tag_name (tag)); size_t cnt = 0; unsigned int name; unsigned int form; + Dwarf_Sword data; Dwarf_Off enoffset; - while (dwarf_getabbrevattr (&abbrev, cnt, - &name, &form, &enoffset) == 0) + while (dwarf_getabbrevattr_data (&abbrev, cnt, &name, &form, + &data, &enoffset) == 0) { - printf (" attr: %s, form: %s, offset: %#" PRIx64 "\n", - dwarf_attr_name (name), dwarf_form_name (form), - (uint64_t) enoffset); - + printf (" attr: %s, form: %s", + dwarf_attr_name (name), dwarf_form_name (form)); + if (form == DW_FORM_implicit_const) + printf (" (%" PRId64 ")", data); + printf (", offset: %#" PRIx64 "\n", (uint64_t) enoffset); ++cnt; } @@ -4735,6 +5060,230 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), } +static void +print_debug_addr_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_addr] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_addr section data: %s"), + elf_errmsg (-1)); + return; + } + + size_t idx = 0; + sort_listptr (&known_addrbases, "addr_base"); + + 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) + { + /* We cannot really know whether or not there is an header. The + DebugFission extension to DWARF4 doesn't add one. The DWARF5 + .debug_addr variant does. Whether or not we have an header, + DW_AT_[GNU_]addr_base points at "index 0". So if the current + offset equals the CU addr_base then we can just start + printing addresses. If there is no CU with an exact match + then we'll try to parse the header first. */ + Dwarf_Off off = (Dwarf_Off) (readp + - (const unsigned char *) data->d_buf); + + printf ("Table at offset %" PRIx64 " ", off); + + struct listptr *listptr = get_listptr (&known_addrbases, idx++); + const unsigned char *next_unitp; + + uint64_t unit_length; + uint16_t version; + uint8_t address_size; + uint8_t segment_size; + if (listptr == NULL) + { + error (0, 0, "Warning: No CU references .debug_addr after %" PRIx64, + off); + + /* We will have to assume it is just addresses to the end... */ + address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + next_unitp = readendp; + printf ("Unknown CU:\n"); + } + else + { + 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 (listptr->offset == off) + { + address_size = listptr_address_size (listptr); + segment_size = 0; + version = 4; + + /* The addresses start here, but where do they end? */ + listptr = get_listptr (&known_addrbases, idx); + if (listptr == NULL) + next_unitp = readendp; + else if (listptr->cu->version < 5) + { + next_unitp = start + listptr->offset; + if (listptr->offset < off || listptr->offset > data->d_size) + { + error (0, 0, + "Warning: Bad address base for next unit at %" + PRIx64, off); + next_unitp = readendp; + } + } + else + { + /* Tricky, we don't have a header for this unit, but + there is one for the next. We will have to + "guess" how big it is and subtract it from the + offset (because that points after the header). */ + unsigned int offset_size = listptr_offset_size (listptr); + Dwarf_Off next_off = (listptr->offset + - (offset_size == 4 ? 4 : 12) /* len */ + - 2 /* version */ + - 1 /* address size */ + - 1); /* segment selector size */ + next_unitp = start + next_off; + if (next_off < off || next_off > data->d_size) + { + error (0, 0, + "Warning: Couldn't calculate .debug_addr " + " unit lenght at %" PRIx64, off); + next_unitp = readendp; + } + } + unit_length = (uint64_t) (next_unitp - readp); + + /* Pretend we have a header. */ + printf ("\n"); + printf (gettext (" Length: %8" PRIu64 "\n"), + unit_length); + printf (gettext (" DWARF version: %8" PRIu16 "\n"), version); + printf (gettext (" Address size: %8" PRIu64 "\n"), + (uint64_t) address_size); + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + printf ("\n"); + } + else + { + /* OK, we have to parse an header first. */ + 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); + } + printf ("\n"); + printf (gettext (" Length: %8" PRIu64 "\n"), + unit_length); + + /* We need at least 2-bytes (version) + 1-byte + (addr_size) + 1-byte (segment_size) = 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; + } + + address_size = *readp++; + printf (gettext (" Address size: %8" PRIu64 "\n"), + (uint64_t) address_size); + + if (address_size != 4 && address_size != 8) + { + error (0, 0, gettext ("unsupported address size")); + goto next_unit; + } + + segment_size = *readp++; + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + printf ("\n"); + + if (segment_size != 0) + { + error (0, 0, gettext ("unsupported segment size")); + goto next_unit; + } + + if (listptr->offset != (Dwarf_Off) (readp - start)) + { + error (0, 0, "Address index doesn't start after header"); + goto next_unit; + } + } + } + + int digits = 1; + size_t addresses = (next_unitp - readp) / address_size; + while (addresses >= 10) + { + ++digits; + addresses /= 10; + } + + unsigned int index = 0; + size_t index_offset = readp - (const unsigned char *) data->d_buf; + printf (" Addresses start at offset 0x%zx:\n", index_offset); + while (readp <= next_unitp - address_size) + { + Dwarf_Addr addr = read_addr_unaligned_inc (address_size, dbg, + readp); + printf (" [%*u] ", digits, index++); + print_dwarf_addr (dwflmod, address_size, addr, addr); + printf ("\n"); + } + 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 content of DWARF .debug_aranges section. We fortunately do not have to know a bit about the structure of the section, libdwarf takes care of it. */ @@ -4815,7 +5364,8 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), return; } - Elf_Data *data = dbg->sectiondata[IDX_debug_aranges]; + Elf_Data *data = (dbg->sectiondata[IDX_debug_aranges] + ?: elf_rawdata (scn, NULL)); if (unlikely (data == NULL)) { @@ -4937,18 +5487,17 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), if (range_address == 0 && range_length == 0 && segment == 0) break; - char *b = format_dwarf_addr (dwflmod, address_size, range_address, - range_address); - char *e = format_dwarf_addr (dwflmod, address_size, - range_address + range_length - 1, - range_length); + printf (" "); + print_dwarf_addr (dwflmod, address_size, range_address, + range_address); + printf (".."); + print_dwarf_addr (dwflmod, address_size, + range_address + range_length - 1, + range_length); if (segment_size != 0) - printf (gettext (" %s..%s (%" PRIx64 ")\n"), b, e, - (uint64_t) segment); + printf (" (%" PRIx64 ")\n", (uint64_t) segment); else - printf (gettext (" %s..%s\n"), b, e); - free (b); - free (e); + printf ("\n"); } next_table: @@ -4962,6 +5511,402 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), } +static bool is_split_dwarf (Dwarf *dbg, uint64_t *id, Dwarf_CU **split_cu); + +/* Returns true and sets cu and cu_base if the given Dwarf is a split + DWARF (.dwo) file. */ +static bool +split_dwarf_cu_base (Dwarf *dbg, Dwarf_CU **cu, Dwarf_Addr *cu_base) +{ + uint64_t id; + if (is_split_dwarf (dbg, &id, cu)) + { + Dwarf_Die cudie; + if (dwarf_cu_info (*cu, NULL, NULL, &cudie, NULL, NULL, NULL, NULL) == 0) + { + *cu_base = cudie_base (&cudie); + return true; + } + } + return false; +} + +/* Print content of DWARF .debug_rnglists section. */ +static void +print_debug_rnglists_section (Dwfl_Module *dwflmod, + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, + Dwarf *dbg __attribute__((unused))) +{ + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + Elf_Data *data =(dbg->sectiondata[IDX_debug_rnglists] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_rnglists content: %s"), + elf_errmsg (-1)); + return; + } + + /* For the listptr to get the base address/CU. */ + sort_listptr (&known_rnglistptr, "rnglistptr"); + size_t listptr_idx = 0; + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = ((unsigned char *) data->d_buf + + data->d_size); + while (readp < dataend) + { + if (unlikely (readp > dataend - 4)) + { + invalid_data: + error (0, 0, gettext ("invalid data in section [%zu] '%s'"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + return; + } + + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + printf (gettext ("Table at Offset 0x%" PRIx64 ":\n\n"), + (uint64_t) offset); + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto invalid_data; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + printf (gettext (" Length: %8" PRIu64 "\n"), unit_length); + + /* We need at least 2-bytes + 1-byte + 1-byte + 4-bytes = 8 + bytes to complete the header. And this unit cannot go beyond + the section data. */ + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto invalid_data; + + const unsigned char *nexthdr = readp + unit_length; + + uint16_t 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_table; + } + + uint8_t address_size = *readp++; + printf (gettext (" Address size: %8" PRIu64 "\n"), + (uint64_t) address_size); + + if (address_size != 4 && address_size != 8) + { + error (0, 0, gettext ("unsupported address size")); + goto next_table; + } + + uint8_t segment_size = *readp++; + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + + if (segment_size != 0 && segment_size != 4 && segment_size != 8) + { + error (0, 0, gettext ("unsupported segment size")); + goto next_table; + } + + uint32_t offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + printf (gettext (" Offset entries: %8" PRIu64 "\n"), + (uint64_t) offset_entry_count); + + /* We need the CU that uses this unit to get the initial base address. */ + Dwarf_Addr cu_base = 0; + struct Dwarf_CU *cu = NULL; + if (listptr_cu (&known_rnglistptr, &listptr_idx, + (Dwarf_Off) offset, + (Dwarf_Off) (nexthdr - (unsigned char *) data->d_buf), + &cu_base, &cu) + || split_dwarf_cu_base (dbg, &cu, &cu_base)) + { + Dwarf_Die cudie; + if (dwarf_cu_die (cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf (gettext (" Unknown CU base: ")); + else + printf (gettext (" CU [%6" PRIx64 "] base: "), + dwarf_dieoffset (&cudie)); + print_dwarf_addr (dwflmod, address_size, cu_base, cu_base); + printf ("\n"); + } + else + printf (gettext (" Not associated with a CU.\n")); + + printf ("\n"); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count > 0) + { + uint64_t max_entries = (unit_length - 8) / offset_size; + if (offset_entry_count > max_entries) + { + error (0, 0, + gettext ("too many offset entries for unit length")); + offset_entry_count = max_entries; + } + + printf (gettext (" Offsets starting at 0x%" PRIx64 ":\n"), + (uint64_t) (offset_array_start + - (unsigned char *) data->d_buf)); + for (uint32_t idx = 0; idx < offset_entry_count; idx++) + { + printf (" [%6" PRIu32 "] ", idx); + if (offset_size == 4) + { + uint32_t off = read_4ubyte_unaligned_inc (dbg, readp); + printf ("0x%" PRIx32 "\n", off); + } + else + { + uint64_t off = read_8ubyte_unaligned_inc (dbg, readp); + printf ("0x%" PRIx64 "\n", off); + } + } + printf ("\n"); + } + + Dwarf_Addr base = cu_base; + bool start_of_list = true; + while (readp < nexthdr) + { + uint8_t kind = *readp++; + uint64_t op1, op2; + + /* Skip padding. */ + if (start_of_list && kind == DW_RLE_end_of_list) + continue; + + if (start_of_list) + { + base = cu_base; + printf (" Offset: %" PRIx64 ", Index: %" PRIx64 "\n", + (uint64_t) (readp - (unsigned char *) data->d_buf - 1), + (uint64_t) (readp - offset_array_start - 1)); + start_of_list = false; + } + + printf (" %s", dwarf_range_list_encoding_name (kind)); + switch (kind) + { + case DW_RLE_end_of_list: + start_of_list = true; + printf ("\n\n"); + break; + + case DW_RLE_base_addressx: + if ((uint64_t) (nexthdr - readp) < 1) + { + invalid_range: + error (0, 0, gettext ("invalid range list data")); + goto next_table; + } + get_uleb128 (op1, readp, nexthdr); + printf (" %" PRIx64 "\n", op1); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr; + if (get_indexed_addr (cu, op1, &addr) != 0) + printf (" ???\n"); + else + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, addr, addr); + printf ("\n"); + } + } + break; + + case DW_RLE_startx_endx: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr1; + Dwarf_Addr addr2; + if (get_indexed_addr (cu, op1, &addr1) != 0 + || get_indexed_addr (cu, op2, &addr2) != 0) + { + printf (" ???..\n"); + printf (" ???\n"); + } + else + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, addr1, addr1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, + addr2 - 1, addr2); + printf ("\n"); + } + } + break; + + case DW_RLE_startx_length: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr1; + Dwarf_Addr addr2; + if (get_indexed_addr (cu, op1, &addr1) != 0) + { + printf (" ???..\n"); + printf (" ???\n"); + } + else + { + addr2 = addr1 + op2; + printf (" "); + print_dwarf_addr (dwflmod, address_size, addr1, addr1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, + addr2 - 1, addr2); + printf ("\n"); + } + } + break; + + case DW_RLE_offset_pair: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + op1 += base; + op2 += base; + printf (" "); + print_dwarf_addr (dwflmod, address_size, op1, op1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, op2 - 1, op2); + printf ("\n"); + } + break; + + case DW_RLE_base_address: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 4) + goto invalid_range; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_range; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + } + base = op1; + printf (" 0x%" PRIx64 "\n", base); + if (! print_unresolved_addresses) + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, base, base); + printf ("\n"); + } + break; + + case DW_RLE_start_end: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_range; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + op2 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 16) + goto invalid_range; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + op2 = read_8ubyte_unaligned_inc (dbg, readp); + } + printf (" 0x%" PRIx64 "..0x%" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, op1, op1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, op2 - 1, op2); + printf ("\n"); + } + break; + + case DW_RLE_start_length: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 4) + goto invalid_range; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_range; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" 0x%" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + op2 = op1 + op2; + printf (" "); + print_dwarf_addr (dwflmod, address_size, op1, op1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, op2 - 1, op2); + printf ("\n"); + } + break; + + default: + goto invalid_range; + } + } + + next_table: + if (readp != nexthdr) + { + size_t padding = nexthdr - readp; + printf (gettext (" %zu padding bytes\n\n"), padding); + readp = nexthdr; + } + } +} + /* Print content of DWARF .debug_ranges section. */ static void print_debug_ranges_section (Dwfl_Module *dwflmod, @@ -4969,8 +5914,8 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { - Elf_Data *data = dbg->sectiondata[IDX_debug_ranges]; - + Elf_Data *data = (dbg->sectiondata[IDX_debug_ranges] + ?: elf_rawdata (scn, NULL)); if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get .debug_ranges content: %s"), @@ -4992,15 +5937,32 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, Dwarf_Addr base = 0; unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size; unsigned char *readp = data->d_buf; + Dwarf_CU *last_cu = NULL; while (readp < endp) { ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + Dwarf_CU *cu = last_cu; if (first && skip_listptr_hole (&known_rangelistptr, &listptr_idx, - &address_size, NULL, &base, NULL, - offset, &readp, endp)) + &address_size, NULL, &base, &cu, + offset, &readp, endp, NULL)) continue; + if (last_cu != cu) + { + Dwarf_Die cudie; + if (dwarf_cu_die (cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf (gettext ("\n Unknown CU base: ")); + else + printf (gettext ("\n CU [%6" PRIx64 "] base: "), + dwarf_dieoffset (&cudie)); + print_dwarf_addr (dwflmod, address_size, base, base); + printf ("\n"); + } + last_cu = cu; + if (unlikely (data->d_size - offset < (size_t) address_size * 2)) { printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); @@ -5024,30 +5986,36 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ { - char *b = format_dwarf_addr (dwflmod, address_size, end, end); - printf (gettext (" [%6tx] base address %s\n"), offset, b); - free (b); + printf (gettext (" [%6tx] base address\n "), offset); + print_dwarf_addr (dwflmod, address_size, end, end); + printf ("\n"); base = end; } else if (begin == 0 && end == 0) /* End of list entry. */ { if (first) - printf (gettext (" [%6tx] empty list\n"), offset); + printf (gettext (" [%6tx] empty list\n"), offset); first = true; } else { - char *b = format_dwarf_addr (dwflmod, address_size, base + begin, - begin); - char *e = format_dwarf_addr (dwflmod, address_size, base + end, - end); /* We have an address range entry. */ if (first) /* First address range entry in a list. */ - printf (gettext (" [%6tx] %s..%s\n"), offset, b, e); + printf (" [%6tx] ", offset); else - printf (gettext (" %s..%s\n"), b, e); - free (b); - free (e); + printf (" "); + + printf ("range %" PRIx64 ", %" PRIx64 "\n", begin, end); + if (! print_unresolved_addresses) + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, base + begin, + base + begin); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, + base + end - 1, base + end); + printf ("\n"); + } first = false; } @@ -5541,7 +6509,8 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, bool is_eh_frame = strcmp (scnname, ".eh_frame") == 0; Elf_Data *data = (is_eh_frame ? elf_rawdata (scn, NULL) - : dbg->sectiondata[IDX_debug_frame]); + : (dbg->sectiondata[IDX_debug_frame] + ?: elf_rawdata (scn, NULL))); if (unlikely (data == NULL)) { @@ -5810,14 +6779,13 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, + (base - (const unsigned char *) data->d_buf) - bias); - char *a = format_dwarf_addr (dwflmod, cie->address_size, - pc_start, initial_location); printf ("\n [%6tx] FDE length=%" PRIu64 " cie=[%6tx]\n" " CIE_pointer: %" PRIu64 "\n" - " initial_location: %s", + " initial_location: ", offset, (uint64_t) unit_length, - cie->cie_offset, (uint64_t) cie_id, a); - free (a); + cie->cie_offset, (uint64_t) cie_id); + print_dwarf_addr (dwflmod, cie->address_size, + pc_start, initial_location); if ((fde_encoding & 0x70) == DW_EH_PE_pcrel) { vma_base = (((uint64_t) shdr->sh_offset @@ -5901,6 +6869,33 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, } +/* Returns the signedness (or false if it cannot be determined) and + the byte size (or zero if it cannot be gotten) of the given DIE + DW_AT_type attribute. Uses dwarf_peel_type and dwarf_aggregate_size. */ +static void +die_type_sign_bytes (Dwarf_Die *die, bool *is_signed, int *bytes) +{ + Dwarf_Attribute attr; + Dwarf_Die type; + + *bytes = 0; + *is_signed = false; + + if (dwarf_peel_type (dwarf_formref_die (dwarf_attr_integrate (die, + DW_AT_type, + &attr), &type), + &type) == 0) + { + Dwarf_Word val; + *is_signed = (dwarf_formudata (dwarf_attr (&type, DW_AT_encoding, + &attr), &val) == 0 + && (val == DW_ATE_signed || val == DW_ATE_signed_char)); + + if (dwarf_aggregate_size (&type, &val) == 0) + *bytes = val; + } +} + struct attrcb_args { Dwfl_Module *dwflmod; @@ -5908,6 +6903,7 @@ struct attrcb_args Dwarf_Die *die; int level; bool silent; + bool is_split; unsigned int version; unsigned int addrsize; unsigned int offset_size; @@ -5920,13 +6916,16 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) { struct attrcb_args *cbargs = (struct attrcb_args *) arg; const int level = cbargs->level; + Dwarf_Die *die = cbargs->die; + bool is_split = cbargs->is_split; unsigned int attr = dwarf_whatattr (attrp); if (unlikely (attr == 0)) { if (!cbargs->silent) - error (0, 0, gettext ("cannot get attribute code: %s"), - dwarf_errmsg (-1)); + error (0, 0, gettext ("DIE [%" PRIx64 "] " + "cannot get attribute code: %s"), + dwarf_dieoffset (die), dwarf_errmsg (-1)); return DWARF_CB_ABORT; } @@ -5934,14 +6933,21 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) if (unlikely (form == 0)) { if (!cbargs->silent) - error (0, 0, gettext ("cannot get attribute form: %s"), - dwarf_errmsg (-1)); + error (0, 0, gettext ("DIE [%" PRIx64 "] " + "cannot get attribute form: %s"), + dwarf_dieoffset (die), dwarf_errmsg (-1)); return DWARF_CB_ABORT; } switch (form) { case DW_FORM_addr: + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: + case DW_FORM_GNU_addr_index: if (!cbargs->silent) { Dwarf_Addr addr; @@ -5949,23 +6955,45 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) { attrval_out: if (!cbargs->silent) - error (0, 0, gettext ("cannot get attribute value: %s"), + error (0, 0, gettext ("DIE [%" PRIx64 "] " + "cannot get attribute '%s' (%s) value: " + "%s"), + dwarf_dieoffset (die), + dwarf_attr_name (attr), + dwarf_form_name (form), dwarf_errmsg (-1)); - return DWARF_CB_ABORT; + /* Don't ABORT, it might be other attributes can be resolved. */ + return DWARF_CB_OK; } - char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, - addr, addr); - printf (" %*s%-20s (%s) %s\n", - (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), a); - free (a); + if (form != DW_FORM_addr ) + { + Dwarf_Word index; + if (dwarf_formudata (attrp, &index) != 0) + goto attrval_out; + printf (" %*s%-20s (%s) [%" PRIx64 "] ", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), index); + } + else + printf (" %*s%-20s (%s) ", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form)); + print_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr, addr); + printf ("\n"); } break; case DW_FORM_indirect: case DW_FORM_strp: + case DW_FORM_line_strp: + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: case DW_FORM_string: case DW_FORM_GNU_strp_alt: + case DW_FORM_GNU_str_index: if (cbargs->silent) break; const char *str = dwarf_formstring (attrp); @@ -5983,15 +7011,21 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_FORM_ref2: case DW_FORM_ref1: case DW_FORM_GNU_ref_alt: + case DW_FORM_ref_sup4: + case DW_FORM_ref_sup8: if (cbargs->silent) break; Dwarf_Die ref; if (unlikely (dwarf_formref_die (attrp, &ref) == NULL)) goto attrval_out; - printf (" %*s%-20s (%s) [%6" PRIxMAX "]\n", + printf (" %*s%-20s (%s) ", (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), (uintmax_t) dwarf_dieoffset (&ref)); + dwarf_form_name (form)); + if (is_split) + printf ("{%6" PRIxMAX "}\n", (uintmax_t) dwarf_dieoffset (&ref)); + else + printf ("[%6" PRIxMAX "]\n", (uintmax_t) dwarf_dieoffset (&ref)); break; case DW_FORM_ref_sig8: @@ -6004,9 +7038,12 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) break; case DW_FORM_sec_offset: + case DW_FORM_rnglistx: + case DW_FORM_loclistx: + case DW_FORM_implicit_const: case DW_FORM_udata: case DW_FORM_sdata: - case DW_FORM_data8: + case DW_FORM_data8: /* Note no data16 here, we see that as block. */ case DW_FORM_data4: case DW_FORM_data2: case DW_FORM_data1:; @@ -6015,6 +7052,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) goto attrval_out; const char *valuestr = NULL; + bool as_hex_id = false; switch (attr) { /* This case can take either a constant or a loclistptr. */ @@ -6029,9 +7067,9 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) dwarf_form_name (form), (uintmax_t) num); return DWARF_CB_OK; } - /* else fallthrough */ + FALLTHROUGH; - /* These cases always take a loclistptr and no constant. */ + /* These cases always take a loclist[ptr] and no constant. */ case DW_AT_location: case DW_AT_data_location: case DW_AT_vtable_elem_location: @@ -6040,14 +7078,65 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_frame_base: case DW_AT_return_addr: case DW_AT_static_link: + case DW_AT_segment: case DW_AT_GNU_call_site_value: case DW_AT_GNU_call_site_data_value: case DW_AT_GNU_call_site_target: case DW_AT_GNU_call_site_target_clobbered: + case DW_AT_GNU_locviews: { - bool nlpt = notice_listptr (section_loc, &known_loclistptr, - cbargs->addrsize, cbargs->offset_size, - cbargs->cu, num); + bool nlpt; + if (cbargs->cu->version < 5) + { + if (! cbargs->is_split) + { + nlpt = notice_listptr (section_loc, &known_locsptr, + cbargs->addrsize, + cbargs->offset_size, + cbargs->cu, num, attr); + } + else + nlpt = true; + } + else + { + /* Only register for a real section offset. Otherwise + it is a DW_FORM_loclistx which is just an index + number and we should already have registered the + section offset for the index when we saw the + DW_AT_loclists_base CU attribute. */ + if (form == DW_FORM_sec_offset) + nlpt = notice_listptr (section_loc, &known_loclistsptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num, attr); + else + nlpt = true; + + } + + if (!cbargs->silent) + { + if (cbargs->cu->version < 5 || form == DW_FORM_sec_offset) + printf (" %*s%-20s (%s) location list [%6" + PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + nlpt ? "" : " <WARNING offset too big>"); + else + printf (" %*s%-20s (%s) location index [%6" + PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num); + } + } + return DWARF_CB_OK; + + case DW_AT_loclists_base: + { + bool nlpt = notice_listptr (section_loc, &known_loclistsptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num, attr); + if (!cbargs->silent) printf (" %*s%-20s (%s) location list [%6" PRIxMAX "]%s\n", (int) (level * 2), "", dwarf_attr_name (attr), @@ -6057,18 +7146,90 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) return DWARF_CB_OK; case DW_AT_ranges: + case DW_AT_start_scope: { - bool nlpt = notice_listptr (section_ranges, &known_rangelistptr, + bool nlpt; + if (cbargs->cu->version < 5) + nlpt = notice_listptr (section_ranges, &known_rangelistptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num, attr); + else + { + /* Only register for a real section offset. Otherwise + it is a DW_FORM_rangelistx which is just an index + number and we should already have registered the + section offset for the index when we saw the + DW_AT_rnglists_base CU attribute. */ + if (form == DW_FORM_sec_offset) + nlpt = notice_listptr (section_ranges, &known_rnglistptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num, attr); + else + nlpt = true; + } + + if (!cbargs->silent) + { + if (cbargs->cu->version < 5 || form == DW_FORM_sec_offset) + printf (" %*s%-20s (%s) range list [%6" + PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + nlpt ? "" : " <WARNING offset too big>"); + else + printf (" %*s%-20s (%s) range index [%6" + PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num); + } + } + return DWARF_CB_OK; + + case DW_AT_rnglists_base: + { + bool nlpt = notice_listptr (section_ranges, &known_rnglistptr, cbargs->addrsize, cbargs->offset_size, - cbargs->cu, num); + cbargs->cu, num, attr); if (!cbargs->silent) - printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]%s\n", + printf (" %*s%-20s (%s) range list [%6" + PRIxMAX "]%s\n", (int) (level * 2), "", dwarf_attr_name (attr), dwarf_form_name (form), (uintmax_t) num, nlpt ? "" : " <WARNING offset too big>"); } return DWARF_CB_OK; + case DW_AT_addr_base: + case DW_AT_GNU_addr_base: + { + bool addrbase = notice_listptr (section_addr, &known_addrbases, + cbargs->addrsize, + cbargs->offset_size, + cbargs->cu, num, attr); + if (!cbargs->silent) + printf (" %*s%-20s (%s) address base [%6" + PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + addrbase ? "" : " <WARNING offset too big>"); + } + 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; @@ -6102,6 +7263,49 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_discr_list: valuestr = dwarf_discr_list_name (num); break; + case DW_AT_decl_file: + case DW_AT_call_file: + { + if (cbargs->silent) + break; + + /* Try to get the actual file, the current interface only + gives us full paths, but we only want to show the file + name for now. */ + Dwarf_Die cudie; + if (dwarf_cu_die (cbargs->cu, &cudie, + NULL, NULL, NULL, NULL, NULL, NULL) != NULL) + { + Dwarf_Files *files; + size_t nfiles; + if (dwarf_getsrcfiles (&cudie, &files, &nfiles) == 0) + { + valuestr = dwarf_filesrc (files, num, NULL, NULL); + if (valuestr != NULL) + { + char *filename = strrchr (valuestr, '/'); + if (filename != NULL) + valuestr = filename + 1; + } + else + error (0, 0, gettext ("invalid file (%" PRId64 "): %s"), + num, dwarf_errmsg (-1)); + } + else + error (0, 0, gettext ("no srcfiles for CU [%" PRIx64 "]"), + dwarf_dieoffset (&cudie)); + } + else + error (0, 0, gettext ("couldn't get DWARF CU: %s"), + dwarf_errmsg (-1)); + if (valuestr == NULL) + valuestr = "???"; + } + break; + case DW_AT_GNU_dwo_id: + as_hex_id = true; + break; + default: /* Nothing. */ break; @@ -6115,39 +7319,97 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) Dwarf_Addr highpc; if (attr == DW_AT_high_pc && dwarf_highpc (cbargs->die, &highpc) == 0) { - char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, - highpc, highpc); - printf (" %*s%-20s (%s) %" PRIuMAX " (%s)\n", + printf (" %*s%-20s (%s) %" PRIuMAX " (", (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), (uintmax_t) num, a); - free (a); + dwarf_form_name (form), (uintmax_t) num); + print_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, highpc, highpc); + printf (")\n"); } else { - Dwarf_Sword snum = 0; - if (form == DW_FORM_sdata) - if (unlikely (dwarf_formsdata (attrp, &snum) != 0)) - goto attrval_out; - - if (valuestr == NULL) + if (as_hex_id) { - printf (" %*s%-20s (%s)", + printf (" %*s%-20s (%s) 0x%.16" PRIx64 "\n", (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form)); - if (form == DW_FORM_sdata) - printf (" %" PRIdMAX "\n", (intmax_t) snum); - else - printf (" %" PRIuMAX "\n", (uintmax_t) num); + dwarf_form_name (form), num); } else { - printf (" %*s%-20s (%s) %s", - (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), valuestr); - if (form == DW_FORM_sdata) - printf (" (%" PRIdMAX ")\n", (intmax_t) snum); + Dwarf_Sword snum = 0; + bool is_signed; + int bytes = 0; + if (attr == DW_AT_const_value) + die_type_sign_bytes (cbargs->die, &is_signed, &bytes); + else + is_signed = (form == DW_FORM_sdata + || form == DW_FORM_implicit_const); + + if (is_signed) + if (unlikely (dwarf_formsdata (attrp, &snum) != 0)) + goto attrval_out; + + if (valuestr == NULL) + { + printf (" %*s%-20s (%s) ", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form)); + } + else + { + printf (" %*s%-20s (%s) %s (", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), valuestr); + } + + switch (bytes) + { + case 1: + if (is_signed) + printf ("%" PRId8, (int8_t) snum); + else + printf ("%" PRIu8, (uint8_t) num); + break; + + case 2: + if (is_signed) + printf ("%" PRId16, (int16_t) snum); + else + printf ("%" PRIu16, (uint16_t) num); + break; + + case 4: + if (is_signed) + printf ("%" PRId32, (int32_t) snum); + else + printf ("%" PRIu32, (uint32_t) num); + break; + + case 8: + if (is_signed) + printf ("%" PRId64, (int64_t) snum); + else + printf ("%" PRIu64, (uint64_t) num); + break; + + default: + if (is_signed) + printf ("%" PRIdMAX, (intmax_t) snum); + else + printf ("%" PRIuMAX, (uintmax_t) num); + break; + } + + /* Make clear if we switched from a signed encoding to + an unsigned value. */ + if (attr == DW_AT_const_value + && (form == DW_FORM_sdata || form == DW_FORM_implicit_const) + && !is_signed) + printf (" (%" PRIdMAX ")", (intmax_t) num); + + if (valuestr == NULL) + printf ("\n"); else - printf (" (%" PRIuMAX ")\n", (uintmax_t) num); + printf (")\n"); } } break; @@ -6161,7 +7423,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) printf (" %*s%-20s (%s) %s\n", (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), flag ? gettext ("yes") : gettext ("no")); + dwarf_form_name (form), flag ? yes_str : no_str); break; case DW_FORM_flag_present: @@ -6169,7 +7431,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) break; printf (" %*s%-20s (%s) %s\n", (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), gettext ("yes")); + dwarf_form_name (form), yes_str); break; case DW_FORM_exprloc: @@ -6177,6 +7439,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_FORM_block2: case DW_FORM_block1: case DW_FORM_block: + case DW_FORM_data16: /* DWARF5 calls this a constant class. */ if (cbargs->silent) break; Dwarf_Block block; @@ -6195,7 +7458,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) print_block (block.length, block.data); break; } - /* Fall through. */ + FALLTHROUGH; case DW_AT_location: case DW_AT_data_location: @@ -6220,11 +7483,16 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_GNU_call_site_data_value: case DW_AT_GNU_call_site_target: case DW_AT_GNU_call_site_target_clobbered: - putchar ('\n'); - print_ops (cbargs->dwflmod, cbargs->dbg, - 12 + level * 2, 12 + level * 2, - cbargs->version, cbargs->addrsize, cbargs->offset_size, - attrp->cu, block.length, block.data); + if (form != DW_FORM_data16) + { + putchar ('\n'); + print_ops (cbargs->dwflmod, cbargs->dbg, + 12 + level * 2, 12 + level * 2, + cbargs->version, cbargs->addrsize, cbargs->offset_size, + attrp->cu, block.length, block.data); + } + else + print_block (block.length, block.data); break; } break; @@ -6232,9 +7500,9 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) default: if (cbargs->silent) break; - printf (" %*s%-20s (form: %#x) ???\n", + printf (" %*s%-20s (%s) ???\n", (int) (level * 2), "", dwarf_attr_name (attr), - (int) form); + dwarf_form_name (form)); break; } @@ -6247,7 +7515,7 @@ print_debug_units (Dwfl_Module *dwflmod, Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg, bool debug_types) { - const bool silent = !(print_debug_sections & section_info); + const bool silent = !(print_debug_sections & section_info) && !debug_types; const char *secname = section_name (ebl, ehdr, shdr); if (!silent) @@ -6262,73 +7530,140 @@ print_debug_units (Dwfl_Module *dwflmod, int maxdies = 20; Dwarf_Die *dies = (Dwarf_Die *) xmalloc (maxdies * sizeof (Dwarf_Die)); - Dwarf_Off offset = 0; - /* New compilation unit. */ - size_t cuhl; Dwarf_Half version; + + Dwarf_Die result; Dwarf_Off abbroffset; uint8_t addrsize; uint8_t offsize; - Dwarf_Off nextcu; - uint64_t typesig; - Dwarf_Off typeoff; + uint64_t unit_id; + Dwarf_Off subdie_off; + + int unit_res; + Dwarf_CU *cu; + Dwarf_CU cu_mem; + uint8_t unit_type; + Dwarf_Die cudie; + + /* We cheat a little because we want to see only the CUs from .debug_info + or .debug_types. We know the Dwarf_CU struct layout. Set it up at + the end of .debug_info if we want .debug_types only. Check the returned + Dwarf_CU is still in the expected section. */ + if (debug_types) + { + cu_mem.dbg = dbg; + cu_mem.end = dbg->sectiondata[IDX_debug_info]->d_size; + cu_mem.sec_idx = IDX_debug_info; + cu = &cu_mem; + } + else + cu = NULL; + next_cu: - if (dwarf_next_unit (dbg, offset, &nextcu, &cuhl, &version, - &abbroffset, &addrsize, &offsize, - debug_types ? &typesig : NULL, - debug_types ? &typeoff : NULL) != 0) + unit_res = dwarf_get_units (dbg, cu, &cu, &version, &unit_type, + &cudie, NULL); + if (unit_res == 1) goto do_return; + if (unit_res == -1) + { + if (!silent) + error (0, 0, gettext ("cannot get next unit: %s"), dwarf_errmsg (-1)); + goto do_return; + } + + if (cu->sec_idx != (size_t) (debug_types ? IDX_debug_types : IDX_debug_info)) + goto do_return; + + dwarf_cu_die (cu, &result, NULL, &abbroffset, &addrsize, &offsize, + &unit_id, &subdie_off); + if (!silent) { - if (debug_types) - printf (gettext (" Type unit at offset %" PRIu64 ":\n" - " Version: %" PRIu16 ", Abbreviation section offset: %" - PRIu64 ", Address size: %" PRIu8 - ", Offset size: %" PRIu8 - "\n Type signature: %#" PRIx64 - ", Type offset: %#" PRIx64 "\n"), - (uint64_t) offset, version, abbroffset, addrsize, offsize, - typesig, (uint64_t) typeoff); + Dwarf_Off offset = cu->start; + if (debug_types && version < 5) + { + Dwarf_Die typedie; + Dwarf_Off dieoffset; + dieoffset = dwarf_dieoffset (dwarf_offdie_types (dbg, subdie_off, + &typedie)); + printf (gettext (" Type unit at offset %" PRIu64 ":\n" + " Version: %" PRIu16 + ", Abbreviation section offset: %" PRIu64 + ", Address size: %" PRIu8 + ", Offset size: %" PRIu8 + "\n Type signature: %#" PRIx64 + ", Type offset: %#" PRIx64 " [%" PRIx64 "]\n"), + (uint64_t) offset, version, abbroffset, addrsize, offsize, + unit_id, (uint64_t) subdie_off, dieoffset); + } else - printf (gettext (" Compilation unit at offset %" PRIu64 ":\n" - " Version: %" PRIu16 ", Abbreviation section offset: %" - PRIu64 ", Address size: %" PRIu8 - ", Offset size: %" PRIu8 "\n"), - (uint64_t) offset, version, abbroffset, addrsize, offsize); + { + printf (gettext (" Compilation unit at offset %" PRIu64 ":\n" + " Version: %" PRIu16 + ", Abbreviation section offset: %" PRIu64 + ", Address size: %" PRIu8 + ", Offset size: %" PRIu8 "\n"), + (uint64_t) offset, version, abbroffset, addrsize, offsize); + + if (version >= 5 || (unit_type != DW_UT_compile + && unit_type != DW_UT_partial)) + { + printf (gettext (" Unit type: %s (%" PRIu8 ")"), + dwarf_unit_name (unit_type), unit_type); + if (unit_type == DW_UT_type + || unit_type == DW_UT_skeleton + || unit_type == DW_UT_split_compile + || unit_type == DW_UT_split_type) + printf (", Unit id: 0x%.16" PRIx64 "", unit_id); + if (unit_type == DW_UT_type + || unit_type == DW_UT_split_type) + { + Dwarf_Die typedie; + Dwarf_Off dieoffset; + dwarf_cu_info (cu, NULL, NULL, NULL, &typedie, + NULL, NULL, NULL); + dieoffset = dwarf_dieoffset (&typedie); + printf (", Unit DIE off: %#" PRIx64 " [%" PRIx64 "]", + subdie_off, dieoffset); + } + printf ("\n"); + } + } + } + + if (version < 2 || version > 5 + || unit_type < DW_UT_compile || unit_type > DW_UT_split_type) + { + if (!silent) + error (0, 0, gettext ("unknown version (%d) or unit type (%d)"), + version, unit_type); + goto next_cu; } struct attrcb_args args = { .dwflmod = dwflmod, - .dbg = dbg, .silent = silent, .version = version, .addrsize = addrsize, .offset_size = offsize }; - offset += cuhl; - + bool is_split = false; int level = 0; - - if (unlikely ((debug_types ? dwarf_offdie_types : dwarf_offdie) - (dbg, offset, &dies[level]) == NULL)) - { - if (!silent) - error (0, 0, gettext ("cannot get DIE at offset %" PRIu64 - " in section '%s': %s"), - (uint64_t) offset, secname, dwarf_errmsg (-1)); - goto do_return; - } - + dies[0] = cudie; args.cu = dies[0].cu; + args.dbg = dbg; + args.is_split = is_split; + /* We might return here again for the split CU subdie. */ + do_cu: do { - offset = dwarf_dieoffset (&dies[level]); - if (unlikely (offset == ~0ul)) + Dwarf_Off offset = dwarf_dieoffset (&dies[level]); + if (unlikely (offset == (Dwarf_Off) -1)) { if (!silent) error (0, 0, gettext ("cannot get DIE offset: %s"), @@ -6340,16 +7675,22 @@ print_debug_units (Dwfl_Module *dwflmod, if (unlikely (tag == DW_TAG_invalid)) { if (!silent) - error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIu64 - " in section '%s': %s"), + error (0, 0, gettext ("cannot get tag of DIE at offset [%" PRIx64 + "] in section '%s': %s"), (uint64_t) offset, secname, dwarf_errmsg (-1)); goto do_return; } if (!silent) - printf (" [%6" PRIx64 "] %*s%s\n", - (uint64_t) offset, (int) (level * 2), "", - dwarf_tag_name (tag)); + { + unsigned int code = dwarf_getabbrevcode (dies[level].abbrev); + if (is_split) + printf (" {%6" PRIx64 "} ", (uint64_t) offset); + else + printf (" [%6" PRIx64 "] ", (uint64_t) offset); + printf ("%*s%-20s abbrev: %u\n", (int) (level * 2), "", + dwarf_tag_name (tag), code); + } /* Print the attribute values. */ args.level = level; @@ -6389,9 +7730,68 @@ print_debug_units (Dwfl_Module *dwflmod, } while (level >= 0); - offset = nextcu; - if (offset != 0) - goto next_cu; + /* We might want to show the split compile unit if this was a skeleton. + We need to scan it if we are requesting printing .debug_ranges for + DWARF4 since GNU DebugFission uses "offsets" into the main ranges + section. */ + if (unit_type == DW_UT_skeleton + && ((!silent && show_split_units) + || (version < 5 && (print_debug_sections & section_ranges) != 0))) + { + Dwarf_Die subdie; + if (dwarf_cu_info (cu, NULL, NULL, NULL, &subdie, NULL, NULL, NULL) != 0 + || dwarf_tag (&subdie) == DW_TAG_invalid) + { + if (!silent) + { + Dwarf_Attribute dwo_at; + const char *dwo_name = + (dwarf_formstring (dwarf_attr (&cudie, DW_AT_dwo_name, + &dwo_at)) + ?: (dwarf_formstring (dwarf_attr (&cudie, DW_AT_GNU_dwo_name, + &dwo_at)) + ?: "<unknown>")); + fprintf (stderr, + "Could not find split unit '%s', id: %" PRIx64 "\n", + dwo_name, unit_id); + } + } + else + { + Dwarf_CU *split_cu = subdie.cu; + dwarf_cu_die (split_cu, &result, NULL, &abbroffset, + &addrsize, &offsize, &unit_id, &subdie_off); + Dwarf_Off offset = cu->start; + + if (!silent) + { + printf (gettext (" Split compilation unit at offset %" + PRIu64 ":\n" + " Version: %" PRIu16 + ", Abbreviation section offset: %" PRIu64 + ", Address size: %" PRIu8 + ", Offset size: %" PRIu8 "\n"), + (uint64_t) offset, version, abbroffset, + addrsize, offsize); + printf (gettext (" Unit type: %s (%" PRIu8 ")"), + dwarf_unit_name (unit_type), unit_type); + printf (", Unit id: 0x%.16" PRIx64 "", unit_id); + printf ("\n"); + } + + unit_type = DW_UT_split_compile; + is_split = true; + level = 0; + dies[0] = subdie; + args.cu = dies[0].cu; + args.dbg = split_cu->dbg; + args.is_split = is_split; + goto do_cu; + } + } + + /* And again... */ + goto next_cu; do_return: free (dies); @@ -6424,23 +7824,51 @@ print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, size_t address_size = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8; - Dwarf_Off cuoffset; - Dwarf_Off ncuoffset = 0; - size_t hsize; - while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize, - NULL, NULL, NULL) == 0) + Dwarf_Lines *lines; + size_t nlines; + Dwarf_Off off, next_off = 0; + Dwarf_CU *cu = NULL; + while (dwarf_next_lines (dbg, off = next_off, &next_off, &cu, NULL, NULL, + &lines, &nlines) == 0) { Dwarf_Die cudie; - if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL) - continue; + if (cu != NULL && dwarf_cu_info (cu, NULL, NULL, &cudie, + NULL, NULL, NULL, NULL) == 0) + printf (" CU [%" PRIx64 "] %s\n", + dwarf_dieoffset (&cudie), dwarf_diename (&cudie)); + else + { + /* DWARF5 lines can be independent of any CU, but they probably + are used by some CU. Determine the CU this block is for. */ + Dwarf_Off cuoffset; + Dwarf_Off ncuoffset = 0; + size_t hsize; + while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize, + NULL, NULL, NULL) == 0) + { + if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL) + continue; + Dwarf_Attribute stmt_list; + if (dwarf_attr (&cudie, DW_AT_stmt_list, &stmt_list) == NULL) + continue; + Dwarf_Word lineoff; + if (dwarf_formudata (&stmt_list, &lineoff) != 0) + continue; + if (lineoff == off) + { + /* Found the CU. */ + cu = cudie.cu; + break; + } + } - size_t nlines; - Dwarf_Lines *lines; - if (dwarf_getsrclines (&cudie, &lines, &nlines) != 0) - continue; + if (cu != NULL) + printf (" CU [%" PRIx64 "] %s\n", + dwarf_dieoffset (&cudie), dwarf_diename (&cudie)); + else + printf (" No CU\n"); + } - printf (" CU [%" PRIx64 "] %s\n", - dwarf_dieoffset (&cudie), dwarf_diename (&cudie)); printf (" line:col SBPE* disc isa op address" " (Statement Block Prologue Epilogue *End)\n"); const char *last_file = ""; @@ -6483,17 +7911,17 @@ print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, dwarf_linediscriminator (line, &disc); /* End sequence is special, it is one byte past. */ - char *a = format_dwarf_addr (dwflmod, address_size, - address - (endseq ? 1 : 0), address); - printf (" %4d:%-3d %c%c%c%c%c %4d %3d %2d %s\n", + printf (" %4d:%-3d %c%c%c%c%c %4d %3d %2d ", lineno, colno, (statement ? 'S' : ' '), (block ? 'B' : ' '), (prologue_end ? 'P' : ' '), (epilogue_begin ? 'E' : ' '), (endseq ? '*' : ' '), - disc, isa, lineop, a); - free (a); + disc, isa, lineop); + print_dwarf_addr (dwflmod, address_size, + address - (endseq ? 1 : 0), address); + printf ("\n"); if (endseq) printf("\n"); @@ -6502,6 +7930,231 @@ print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, } +/* Print the value of a form. + Returns new value of readp, or readendp on failure. */ +static const unsigned char * +print_form_data (Dwarf *dbg, int form, const unsigned char *readp, + const unsigned char *readendp, unsigned int offset_len, + Dwarf_Off str_offsets_base) +{ + Dwarf_Word val; + unsigned char *endp; + Elf_Data *data; + char *str; + switch (form) + { + case DW_FORM_data1: + if (readendp - readp < 1) + { + invalid_data: + error (0, 0, "invalid data"); + return readendp; + } + val = *readp++; + printf (" %" PRIx8, (unsigned int) val); + break; + + case DW_FORM_data2: + if (readendp - readp < 2) + goto invalid_data; + val = read_2ubyte_unaligned_inc (dbg, readp); + printf(" %" PRIx16, (unsigned int) val); + break; + + case DW_FORM_data4: + if (readendp - readp < 4) + goto invalid_data; + val = read_4ubyte_unaligned_inc (dbg, readp); + printf (" %" PRIx32, (unsigned int) val); + break; + + case DW_FORM_data8: + if (readendp - readp < 8) + goto invalid_data; + val = read_8ubyte_unaligned_inc (dbg, readp); + printf (" %" PRIx64, val); + break; + + case DW_FORM_sdata: + if (readendp - readp < 1) + goto invalid_data; + get_sleb128 (val, readp, readendp); + printf (" %" PRIx64, val); + break; + + case DW_FORM_udata: + if (readendp - readp < 1) + goto invalid_data; + get_uleb128 (val, readp, readendp); + printf (" %" PRIx64, val); + break; + + case DW_FORM_block: + if (readendp - readp < 1) + goto invalid_data; + get_uleb128 (val, readp, readendp); + if ((size_t) (readendp - readp) < val) + goto invalid_data; + print_bytes (val, readp); + readp += val; + break; + + case DW_FORM_block1: + if (readendp - readp < 1) + goto invalid_data; + val = *readp++; + if ((size_t) (readendp - readp) < val) + goto invalid_data; + print_bytes (val, readp); + readp += val; + break; + + case DW_FORM_block2: + if (readendp - readp < 2) + goto invalid_data; + val = read_2ubyte_unaligned_inc (dbg, readp); + if ((size_t) (readendp - readp) < val) + goto invalid_data; + print_bytes (val, readp); + readp += val; + break; + + case DW_FORM_block4: + if (readendp - readp < 4) + goto invalid_data; + val = read_4ubyte_unaligned_inc (dbg, readp); + if ((size_t) (readendp - readp) < val) + goto invalid_data; + print_bytes (val, readp); + readp += val; + break; + + case DW_FORM_data16: + if (readendp - readp < 16) + goto invalid_data; + print_bytes (16, readp); + readp += 16; + break; + + case DW_FORM_flag: + if (readendp - readp < 1) + goto invalid_data; + val = *readp++; + printf ("%s", val != 0 ? yes_str : no_str); + break; + + case DW_FORM_string: + endp = memchr (readp, '\0', readendp - readp); + if (endp == NULL) + goto invalid_data; + printf ("%s", readp); + readp = endp + 1; + break; + + case DW_FORM_strp: + case DW_FORM_line_strp: + case DW_FORM_strp_sup: + if ((size_t) (readendp - readp) < offset_len) + goto invalid_data; + if (offset_len == 8) + val = read_8ubyte_unaligned_inc (dbg, readp); + else + val = read_4ubyte_unaligned_inc (dbg, readp); + if (form == DW_FORM_strp) + data = dbg->sectiondata[IDX_debug_str]; + else if (form == DW_FORM_line_strp) + data = dbg->sectiondata[IDX_debug_line_str]; + else /* form == DW_FORM_strp_sup */ + { + Dwarf *alt = dwarf_getalt (dbg); + data = alt != NULL ? alt->sectiondata[IDX_debug_str] : NULL; + } + if (data == NULL || val >= data->d_size + || memchr (data->d_buf + val, '\0', data->d_size - val) == NULL) + str = "???"; + else + str = (char *) data->d_buf + val; + printf ("%s (%" PRIu64 ")", str, val); + break; + + case DW_FORM_sec_offset: + if ((size_t) (readendp - readp) < offset_len) + goto invalid_data; + if (offset_len == 8) + val = read_8ubyte_unaligned_inc (dbg, readp); + else + val = read_4ubyte_unaligned_inc (dbg, readp); + printf ("[%" PRIx64 "]", val); + break; + + case DW_FORM_strx: + case DW_FORM_GNU_str_index: + if (readendp - readp < 1) + goto invalid_data; + get_uleb128 (val, readp, readendp); + strx_val: + data = dbg->sectiondata[IDX_debug_str_offsets]; + if (data == NULL + || data->d_size - str_offsets_base < val) + str = "???"; + else + { + const unsigned char *strreadp = data->d_buf + str_offsets_base + val; + const unsigned char *strreadendp = data->d_buf + data->d_size; + if ((size_t) (strreadendp - strreadp) < offset_len) + str = "???"; + else + { + Dwarf_Off idx; + if (offset_len == 8) + idx = read_8ubyte_unaligned (dbg, strreadp); + else + idx = read_4ubyte_unaligned (dbg, strreadp); + + data = dbg->sectiondata[IDX_debug_str]; + if (data == NULL || idx >= data->d_size + || memchr (data->d_buf + idx, '\0', + data->d_size - idx) == NULL) + str = "???"; + else + str = (char *) data->d_buf + idx; + } + } + printf ("%s (%" PRIu64 ")", str, val); + break; + + case DW_FORM_strx1: + if (readendp - readp < 1) + goto invalid_data; + val = *readp++; + goto strx_val; + + case DW_FORM_strx2: + if (readendp - readp < 2) + goto invalid_data; + val = read_2ubyte_unaligned_inc (dbg, readp); + goto strx_val; + + case DW_FORM_strx3: + if (readendp - readp < 3) + goto invalid_data; + val = read_3ubyte_unaligned_inc (dbg, readp); + goto strx_val; + + case DW_FORM_strx4: + if (readendp - readp < 4) + goto invalid_data; + val = read_4ubyte_unaligned_inc (dbg, readp); + goto strx_val; + + default: + error (0, 0, gettext ("unknown form: %s"), dwarf_form_name (form)); + return readendp; + } + + return readp; +} + static void print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) @@ -6522,8 +8175,9 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, /* There is no functionality in libdw to read the information in the way it is represented here. Hardcode the decoder. */ - Elf_Data *data = dbg->sectiondata[IDX_debug_line]; - if (unlikely (data == NULL || data->d_buf == NULL)) + Elf_Data *data = (dbg->sectiondata[IDX_debug_line] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get line data section data: %s"), elf_errmsg (-1)); @@ -6558,35 +8212,67 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, } /* Check whether we have enough room in the section. */ - if (unlikely (unit_length > (size_t) (lineendp - linep) - || unit_length < 2 + length + 5 * 1)) + if (unlikely (unit_length > (size_t) (lineendp - linep))) goto invalid_data; lineendp = linep + unit_length; /* The next element of the header is the version identifier. */ + if ((size_t) (lineendp - linep) < 2) + goto invalid_data; uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); + size_t address_size + = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8; + unsigned char segment_selector_size = 0; + if (version > 4) + { + if ((size_t) (lineendp - linep) < 2) + goto invalid_data; + address_size = *linep++; + segment_selector_size = *linep++; + } + /* Next comes the header length. */ Dwarf_Word header_length; if (length == 4) - header_length = read_4ubyte_unaligned_inc (dbg, linep); + { + if ((size_t) (lineendp - linep) < 4) + goto invalid_data; + header_length = read_4ubyte_unaligned_inc (dbg, linep); + } else - header_length = read_8ubyte_unaligned_inc (dbg, linep); - //const unsigned char *header_start = linep; + { + if ((size_t) (lineendp - linep) < 8) + goto invalid_data; + header_length = read_8ubyte_unaligned_inc (dbg, linep); + } /* Next the minimum instruction length. */ + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; uint_fast8_t minimum_instr_len = *linep++; /* Next the maximum operations per instruction, in version 4 format. */ - uint_fast8_t max_ops_per_instr = version < 4 ? 1 : *linep++; + uint_fast8_t max_ops_per_instr; + if (version < 4) + max_ops_per_instr = 1; + else + { + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + max_ops_per_instr = *linep++; + } + + /* We need at least 4 more bytes. */ + if ((size_t) (lineendp - linep) < 4) + goto invalid_data; - /* Then the flag determining the default value of the is_stmt - register. */ + /* Then the flag determining the default value of the is_stmt + register. */ uint_fast8_t default_is_stmt = *linep++; /* Now the line base. */ - int_fast8_t line_base = *((const int_fast8_t *) linep); - ++linep; + int_fast8_t line_base = *linep++; /* And the line range. */ uint_fast8_t line_range = *linep++; @@ -6596,22 +8282,49 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, /* Print what we got so far. */ printf (gettext ("\n" - " Length: %" PRIu64 "\n" - " DWARF version: %" PRIuFAST16 "\n" - " Prologue length: %" PRIu64 "\n" - " Minimum instruction length: %" PRIuFAST8 "\n" - " Maximum operations per instruction: %" PRIuFAST8 "\n" - " Initial value if '%s': %" PRIuFAST8 "\n" - " Line base: %" PRIdFAST8 "\n" - " Line range: %" PRIuFAST8 "\n" - " Opcode base: %" PRIuFAST8 "\n" + " Length: %" PRIu64 "\n" + " DWARF version: %" PRIuFAST16 "\n" + " Prologue length: %" PRIu64 "\n" + " Address size: %zd\n" + " Segment selector size: %zd\n" + " Min instruction length: %" PRIuFAST8 "\n" + " Max operations per instruction: %" PRIuFAST8 "\n" + " Initial value if 'is_stmt': %" PRIuFAST8 "\n" + " Line base: %" PRIdFAST8 "\n" + " Line range: %" PRIuFAST8 "\n" + " Opcode base: %" PRIuFAST8 "\n" "\n" "Opcodes:\n"), (uint64_t) unit_length, version, (uint64_t) header_length, + address_size, (size_t) segment_selector_size, minimum_instr_len, max_ops_per_instr, - "is_stmt", default_is_stmt, line_base, + default_is_stmt, line_base, line_range, opcode_base); + if (version < 2 || version > 5) + { + error (0, 0, gettext ("cannot handle .debug_line version: %u\n"), + (unsigned int) version); + linep = lineendp; + continue; + } + + if (address_size != 4 && address_size != 8) + { + error (0, 0, gettext ("cannot handle address size: %u\n"), + (unsigned int) address_size); + linep = lineendp; + continue; + } + + if (segment_selector_size != 0) + { + error (0, 0, gettext ("cannot handle segment selector size: %u\n"), + (unsigned int) segment_selector_size); + linep = lineendp; + continue; + } + if (unlikely (linep + opcode_base - 1 >= lineendp)) { invalid_unit: @@ -6636,94 +8349,189 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, (int) linep[cnt - 1]), opcode_base_l10, cnt, linep[cnt - 1]); linep += opcode_base - 1; + if (unlikely (linep >= lineendp)) goto invalid_unit; + Dwarf_Off str_offsets_base = str_offsets_base_off (dbg, NULL); + puts (gettext ("\nDirectory table:")); - while (*linep != 0) + if (version > 4) { - unsigned char *endp = memchr (linep, '\0', lineendp - linep); - if (unlikely (endp == NULL)) - goto invalid_unit; + struct encpair { uint16_t desc; uint16_t form; }; + struct encpair enc[256]; - printf (" %s\n", (char *) linep); + printf (gettext (" [")); + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + unsigned char directory_entry_format_count = *linep++; + for (int i = 0; i < directory_entry_format_count; i++) + { + uint16_t desc, form; + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (desc, linep, lineendp); + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (form, linep, lineendp); + + enc[i].desc = desc; + enc[i].form = form; + + printf ("%s(%s)", + dwarf_line_content_description_name (desc), + dwarf_form_name (form)); + if (i + 1 < directory_entry_format_count) + printf (", "); + } + printf ("]\n"); + + uint64_t directories_count; + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (directories_count, linep, lineendp); + + if (directory_entry_format_count == 0 + && directories_count != 0) + goto invalid_data; - linep = endp + 1; + for (uint64_t i = 0; i < directories_count; i++) + { + printf (" %-5" PRIu64 " ", i); + for (int j = 0; j < directory_entry_format_count; j++) + { + linep = print_form_data (dbg, enc[j].form, + linep, lineendp, length, + str_offsets_base); + if (j + 1 < directory_entry_format_count) + printf (", "); + } + printf ("\n"); + if (linep >= lineendp) + goto invalid_unit; + } + } + else + { + while (*linep != 0) + { + unsigned char *endp = memchr (linep, '\0', lineendp - linep); + if (unlikely (endp == NULL)) + goto invalid_unit; + + printf (" %s\n", (char *) linep); + + linep = endp + 1; + } + /* Skip the final NUL byte. */ + ++linep; } - /* Skip the final NUL byte. */ - ++linep; if (unlikely (linep >= lineendp)) goto invalid_unit; - puts (gettext ("\nFile name table:\n" - " Entry Dir Time Size Name")); - for (unsigned int cnt = 1; *linep != 0; ++cnt) + + puts (gettext ("\nFile name table:")); + if (version > 4) { - /* First comes the file name. */ - char *fname = (char *) linep; - unsigned char *endp = memchr (fname, '\0', lineendp - linep); - if (unlikely (endp == NULL)) - goto invalid_unit; - linep = endp + 1; + struct encpair { uint16_t desc; uint16_t form; }; + struct encpair enc[256]; - /* Then the index. */ - unsigned int diridx; - if (lineendp - linep < 1) - goto invalid_unit; - get_uleb128 (diridx, linep, lineendp); + printf (gettext (" [")); + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + unsigned char file_name_format_count = *linep++; + for (int i = 0; i < file_name_format_count; i++) + { + uint64_t desc, form; + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (desc, linep, lineendp); + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (form, linep, lineendp); - /* Next comes the modification time. */ - unsigned int mtime; - if (lineendp - linep < 1) - goto invalid_unit; - get_uleb128 (mtime, linep, lineendp); + if (! libdw_valid_user_form (form)) + goto invalid_data; - /* Finally the length of the file. */ - unsigned int fsize; - if (lineendp - linep < 1) - goto invalid_unit; - get_uleb128 (fsize, linep, lineendp); + enc[i].desc = desc; + enc[i].form = form; - printf (" %-5u %-5u %-9u %-9u %s\n", - cnt, diridx, mtime, fsize, fname); - } - /* Skip the final NUL byte. */ - ++linep; + printf ("%s(%s)", + dwarf_line_content_description_name (desc), + dwarf_form_name (form)); + if (i + 1 < file_name_format_count) + printf (", "); + } + printf ("]\n"); - puts (gettext ("\nLine number statements:")); - Dwarf_Word address = 0; - unsigned int op_index = 0; - size_t line = 1; - uint_fast8_t is_stmt = default_is_stmt; + uint64_t file_name_count; + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (file_name_count, linep, lineendp); - /* Default address value, in case we do not find the CU. */ - size_t address_size - = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8; + if (file_name_format_count == 0 + && file_name_count != 0) + goto invalid_data; - /* Determine the CU this block is for. */ - Dwarf_Off cuoffset; - Dwarf_Off ncuoffset = 0; - size_t hsize; - while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize, - NULL, NULL, NULL) == 0) + for (uint64_t i = 0; i < file_name_count; i++) + { + printf (" %-5" PRIu64 " ", i); + for (int j = 0; j < file_name_format_count; j++) + { + linep = print_form_data (dbg, enc[j].form, + linep, lineendp, length, + str_offsets_base); + if (j + 1 < file_name_format_count) + printf (", "); + } + printf ("\n"); + if (linep >= lineendp) + goto invalid_unit; + } + } + else { - Dwarf_Die cudie; - if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL) - continue; - Dwarf_Attribute stmt_list; - if (dwarf_attr (&cudie, DW_AT_stmt_list, &stmt_list) == NULL) - continue; - Dwarf_Word lineoff; - if (dwarf_formudata (&stmt_list, &lineoff) != 0) - continue; - if (lineoff == start_offset) + puts (gettext (" Entry Dir Time Size Name")); + for (unsigned int cnt = 1; *linep != 0; ++cnt) { - /* Found the CU. */ - address_size = cudie.cu->address_size; - break; + /* First comes the file name. */ + char *fname = (char *) linep; + unsigned char *endp = memchr (fname, '\0', lineendp - linep); + if (unlikely (endp == NULL)) + goto invalid_unit; + linep = endp + 1; + + /* Then the index. */ + unsigned int diridx; + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (diridx, linep, lineendp); + + /* Next comes the modification time. */ + unsigned int mtime; + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (mtime, linep, lineendp); + + /* Finally the length of the file. */ + unsigned int fsize; + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (fsize, linep, lineendp); + + printf (" %-5u %-5u %-9u %-9u %s\n", + cnt, diridx, mtime, fsize, fname); } + /* Skip the final NUL byte. */ + ++linep; } + puts (gettext ("\nLine number statements:")); + Dwarf_Word address = 0; + unsigned int op_index = 0; + size_t line = 1; + uint_fast8_t is_stmt = default_is_stmt; + /* Apply the "operation advance" from a special opcode or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */ unsigned int op_addr_advance; @@ -6732,7 +8540,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, { op_addr_advance = minimum_instr_len * ((op_index + op_advance) / max_ops_per_instr); - address += op_advance; + address += op_addr_advance; show_op_index = (op_index > 0 || (op_index + op_advance) % max_ops_per_instr > 0); op_index = (op_index + op_advance) % max_ops_per_instr; @@ -6775,17 +8583,15 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, line += line_increment; advance_pc ((opcode - opcode_base) / line_range); - char *a = format_dwarf_addr (dwflmod, 0, address, address); + printf (gettext (" special opcode %u: address+%u = "), + opcode, op_addr_advance); + print_dwarf_addr (dwflmod, 0, address, address); if (show_op_index) - printf (gettext ("\ - special opcode %u: address+%u = %s, op_index = %u, line%+d = %zu\n"), - opcode, op_addr_advance, a, op_index, - line_increment, line); + printf (gettext (", op_index = %u, line%+d = %zu\n"), + op_index, line_increment, line); else - printf (gettext ("\ - special opcode %u: address+%u = %s, line%+d = %zu\n"), - opcode, op_addr_advance, a, line_increment, line); - free (a); + printf (gettext (", line%+d = %zu\n"), + line_increment, line); } else if (opcode == 0) { @@ -6825,9 +8631,9 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, else address = read_8ubyte_unaligned_inc (dbg, linep); { - char *a = format_dwarf_addr (dwflmod, 0, address, address); - printf (gettext (" set address to %s\n"), a); - free (a); + printf (gettext (" set address to ")); + print_dwarf_addr (dwflmod, 0, address, address); + printf ("\n"); } break; @@ -6892,15 +8698,12 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, get_uleb128 (u128, linep, lineendp); advance_pc (u128); { - char *a = format_dwarf_addr (dwflmod, 0, address, address); + printf (gettext (" advance address by %u to "), + op_addr_advance); + print_dwarf_addr (dwflmod, 0, address, address); if (show_op_index) - printf (gettext ("\ - advance address by %u to %s, op_index to %u\n"), - op_addr_advance, a, op_index); - else - printf (gettext (" advance address by %u to %s\n"), - op_addr_advance, a); - free (a); + printf (gettext (", op_index to %u"), op_index); + printf ("\n"); } break; @@ -6951,16 +8754,12 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, advance_pc ((255 - opcode_base) / line_range); { - char *a = format_dwarf_addr (dwflmod, 0, address, address); + printf (gettext (" advance address by constant %u to "), + op_addr_advance); + print_dwarf_addr (dwflmod, 0, address, address); if (show_op_index) - printf (gettext ("\ - advance address by constant %u to %s, op_index to %u\n"), - op_addr_advance, a, op_index); - else - printf (gettext ("\ - advance address by constant %u to %s\n"), - op_addr_advance, a); - free (a); + printf (gettext (", op_index to %u"), op_index); + printf ("\n"); } break; @@ -6974,11 +8773,11 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, address += u128; op_index = 0; { - char *a = format_dwarf_addr (dwflmod, 0, address, address); printf (gettext ("\ - advance address by fixed value %u to %s\n"), - u128, a); - free (a); + advance address by fixed value %u to \n"), + u128); + print_dwarf_addr (dwflmod, 0, address, address); + printf ("\n"); } break; @@ -7031,11 +8830,439 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, static void +print_debug_loclists_section (Dwfl_Module *dwflmod, + 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); + + Elf_Data *data = (dbg->sectiondata[IDX_debug_loclists] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_loclists content: %s"), + elf_errmsg (-1)); + return; + } + + /* For the listptr to get the base address/CU. */ + sort_listptr (&known_loclistsptr, "loclistsptr"); + size_t listptr_idx = 0; + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = ((unsigned char *) data->d_buf + + data->d_size); + while (readp < dataend) + { + if (unlikely (readp > dataend - 4)) + { + invalid_data: + error (0, 0, gettext ("invalid data in section [%zu] '%s'"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + return; + } + + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + printf (gettext ("Table at Offset 0x%" PRIx64 ":\n\n"), + (uint64_t) offset); + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto invalid_data; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + printf (gettext (" Length: %8" PRIu64 "\n"), unit_length); + + /* We need at least 2-bytes + 1-byte + 1-byte + 4-bytes = 8 + bytes to complete the header. And this unit cannot go beyond + the section data. */ + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto invalid_data; + + const unsigned char *nexthdr = readp + unit_length; + + uint16_t 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_table; + } + + uint8_t address_size = *readp++; + printf (gettext (" Address size: %8" PRIu64 "\n"), + (uint64_t) address_size); + + if (address_size != 4 && address_size != 8) + { + error (0, 0, gettext ("unsupported address size")); + goto next_table; + } + + uint8_t segment_size = *readp++; + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + + if (segment_size != 0) + { + error (0, 0, gettext ("unsupported segment size")); + goto next_table; + } + + uint32_t offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + printf (gettext (" Offset entries: %8" PRIu64 "\n"), + (uint64_t) offset_entry_count); + + /* We need the CU that uses this unit to get the initial base address. */ + Dwarf_Addr cu_base = 0; + struct Dwarf_CU *cu = NULL; + if (listptr_cu (&known_loclistsptr, &listptr_idx, + (Dwarf_Off) offset, + (Dwarf_Off) (nexthdr - (unsigned char *) data->d_buf), + &cu_base, &cu) + || split_dwarf_cu_base (dbg, &cu, &cu_base)) + { + Dwarf_Die cudie; + if (dwarf_cu_die (cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf (gettext (" Unknown CU base: ")); + else + printf (gettext (" CU [%6" PRIx64 "] base: "), + dwarf_dieoffset (&cudie)); + print_dwarf_addr (dwflmod, address_size, cu_base, cu_base); + printf ("\n"); + } + else + printf (gettext (" Not associated with a CU.\n")); + + printf ("\n"); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count > 0) + { + uint64_t max_entries = (unit_length - 8) / offset_size; + if (offset_entry_count > max_entries) + { + error (0, 0, + gettext ("too many offset entries for unit length")); + offset_entry_count = max_entries; + } + + printf (gettext (" Offsets starting at 0x%" PRIx64 ":\n"), + (uint64_t) (offset_array_start + - (unsigned char *) data->d_buf)); + for (uint32_t idx = 0; idx < offset_entry_count; idx++) + { + printf (" [%6" PRIu32 "] ", idx); + if (offset_size == 4) + { + uint32_t off = read_4ubyte_unaligned_inc (dbg, readp); + printf ("0x%" PRIx32 "\n", off); + } + else + { + uint64_t off = read_8ubyte_unaligned_inc (dbg, readp); + printf ("0x%" PRIx64 "\n", off); + } + } + printf ("\n"); + } + + Dwarf_Addr base = cu_base; + bool start_of_list = true; + while (readp < nexthdr) + { + uint8_t kind = *readp++; + uint64_t op1, op2, len; + + /* Skip padding. */ + if (start_of_list && kind == DW_LLE_end_of_list) + continue; + + if (start_of_list) + { + base = cu_base; + printf (" Offset: %" PRIx64 ", Index: %" PRIx64 "\n", + (uint64_t) (readp - (unsigned char *) data->d_buf - 1), + (uint64_t) (readp - offset_array_start - 1)); + start_of_list = false; + } + + printf (" %s", dwarf_loc_list_encoding_name (kind)); + switch (kind) + { + case DW_LLE_end_of_list: + start_of_list = true; + printf ("\n\n"); + break; + + case DW_LLE_base_addressx: + if ((uint64_t) (nexthdr - readp) < 1) + { + invalid_entry: + error (0, 0, gettext ("invalid loclists data")); + goto next_table; + } + get_uleb128 (op1, readp, nexthdr); + printf (" %" PRIx64 "\n", op1); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr; + if (get_indexed_addr (cu, op1, &addr) != 0) + printf (" ???\n"); + else + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, addr, addr); + printf ("\n"); + } + } + break; + + case DW_LLE_startx_endx: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr1; + Dwarf_Addr addr2; + if (get_indexed_addr (cu, op1, &addr1) != 0 + || get_indexed_addr (cu, op2, &addr2) != 0) + { + printf (" ???..\n"); + printf (" ???\n"); + } + else + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, addr1, addr1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, + addr2 - 1, addr2); + printf ("\n"); + } + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (len, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < len) + goto invalid_entry; + print_ops (dwflmod, dbg, 8, 8, version, + address_size, offset_size, cu, len, readp); + readp += len; + break; + + case DW_LLE_startx_length: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr1; + Dwarf_Addr addr2; + if (get_indexed_addr (cu, op1, &addr1) != 0) + { + printf (" ???..\n"); + printf (" ???\n"); + } + else + { + addr2 = addr1 + op2; + printf (" "); + print_dwarf_addr (dwflmod, address_size, addr1, addr1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, + addr2 - 1, addr2); + printf ("\n"); + } + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (len, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < len) + goto invalid_entry; + print_ops (dwflmod, dbg, 8, 8, version, + address_size, offset_size, cu, len, readp); + readp += len; + break; + + case DW_LLE_offset_pair: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + op1 += base; + op2 += base; + printf (" "); + print_dwarf_addr (dwflmod, address_size, op1, op1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, op2 - 1, op2); + printf ("\n"); + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (len, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < len) + goto invalid_entry; + print_ops (dwflmod, dbg, 8, 8, version, + address_size, offset_size, cu, len, readp); + readp += len; + break; + + case DW_LLE_default_location: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (len, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < len) + goto invalid_entry; + print_ops (dwflmod, dbg, 8, 8, version, + address_size, offset_size, cu, len, readp); + readp += len; + break; + + case DW_LLE_base_address: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 4) + goto invalid_entry; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_entry; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + } + base = op1; + printf (" 0x%" PRIx64 "\n", base); + if (! print_unresolved_addresses) + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, base, base); + printf ("\n"); + } + break; + + case DW_LLE_start_end: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_entry; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + op2 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 16) + goto invalid_entry; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + op2 = read_8ubyte_unaligned_inc (dbg, readp); + } + printf (" 0x%" PRIx64 "..0x%" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + printf (" "); + print_dwarf_addr (dwflmod, address_size, op1, op1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, op2 - 1, op2); + printf ("\n"); + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (len, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < len) + goto invalid_entry; + print_ops (dwflmod, dbg, 8, 8, version, + address_size, offset_size, cu, len, readp); + readp += len; + break; + + case DW_LLE_start_length: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 4) + goto invalid_entry; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_entry; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (op2, readp, nexthdr); + printf (" 0x%" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + op2 = op1 + op2; + printf (" "); + print_dwarf_addr (dwflmod, address_size, op1, op1); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, op2 - 1, op2); + printf ("\n"); + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_entry; + get_uleb128 (len, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < len) + goto invalid_entry; + print_ops (dwflmod, dbg, 8, 8, version, + address_size, offset_size, cu, len, readp); + readp += len; + break; + + default: + goto invalid_entry; + } + } + + next_table: + if (readp != nexthdr) + { + size_t padding = nexthdr - readp; + printf (gettext (" %zu padding bytes\n\n"), padding); + readp = nexthdr; + } + } +} + + +static void print_debug_loc_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { - Elf_Data *data = dbg->sectiondata[IDX_debug_loc]; + Elf_Data *data = (dbg->sectiondata[IDX_debug_loc] + ?: elf_rawdata (scn, NULL)); if (unlikely (data == NULL)) { @@ -7049,35 +9276,154 @@ print_debug_loc_section (Dwfl_Module *dwflmod, elf_ndxscn (scn), section_name (ebl, ehdr, shdr), (uint64_t) shdr->sh_offset); - sort_listptr (&known_loclistptr, "loclistptr"); + sort_listptr (&known_locsptr, "loclistptr"); size_t listptr_idx = 0; uint_fast8_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; uint_fast8_t offset_size = 4; bool first = true; - struct Dwarf_CU *cu = NULL; Dwarf_Addr base = 0; unsigned char *readp = data->d_buf; unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size; + Dwarf_CU *last_cu = NULL; while (readp < endp) { ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + Dwarf_CU *cu = last_cu; + unsigned int attr = 0; - if (first && skip_listptr_hole (&known_loclistptr, &listptr_idx, + if (first && skip_listptr_hole (&known_locsptr, &listptr_idx, &address_size, &offset_size, &base, - &cu, offset, &readp, endp)) + &cu, offset, &readp, endp, &attr)) continue; - if (unlikely (data->d_size - offset < (size_t) address_size * 2)) - { + if (last_cu != cu) + { + Dwarf_Die cudie; + if (dwarf_cu_die (cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf (gettext ("\n Unknown CU base: ")); + else + printf (gettext ("\n CU [%6" PRIx64 "] base: "), + dwarf_dieoffset (&cudie)); + print_dwarf_addr (dwflmod, address_size, base, base); + printf ("\n"); + } + last_cu = cu; + + if (attr == DW_AT_GNU_locviews) + { + Dwarf_Off next_off = next_listptr_offset (&known_locsptr, + listptr_idx); + const unsigned char *locp = readp; + const unsigned char *locendp; + if (next_off == 0 + || next_off > (size_t) (endp + - (const unsigned char *) data->d_buf)) + locendp = endp; + else + locendp = (const unsigned char *) data->d_buf + next_off; + + while (locp < locendp) + { + uint64_t v1, v2; + get_uleb128 (v1, locp, locendp); + if (locp >= locendp) + { + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); + break; + } + get_uleb128 (v2, locp, locendp); + if (first) /* First view pair in a list. */ + printf (" [%6tx] ", offset); + else + printf (" "); + printf ("view pair %" PRId64 ", %" PRId64 "\n", v1, v2); + first = false; + } + + first = true; + readp = (unsigned char *) locendp; + continue; + } + + /* GNU DebugFission encoded addresses as addrx. */ + bool is_debugfission = ((cu != NULL + || split_dwarf_cu_base (dbg, &cu, &base)) + && (cu->version < 5 + && cu->unit_type == DW_UT_split_compile)); + if (!is_debugfission + && unlikely (data->d_size - offset < (size_t) address_size * 2)) + { + invalid_data: printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); break; } Dwarf_Addr begin; Dwarf_Addr end; - if (address_size == 8) + bool use_base = true; + if (is_debugfission) + { + const unsigned char *locp = readp; + const unsigned char *locendp = readp + data->d_size; + if (locp >= locendp) + goto invalid_data; + + Dwarf_Word idx; + unsigned char code = *locp++; + switch (code) + { + case DW_LLE_GNU_end_of_list_entry: + begin = 0; + end = 0; + break; + + case DW_LLE_GNU_base_address_selection_entry: + if (locp >= locendp) + goto invalid_data; + begin = (Dwarf_Addr) -1; + get_uleb128 (idx, locp, locendp); + if (get_indexed_addr (cu, idx, &end) != 0) + end = idx; /* ... */ + break; + + case DW_LLE_GNU_start_end_entry: + if (locp >= locendp) + goto invalid_data; + get_uleb128 (idx, locp, locendp); + if (get_indexed_addr (cu, idx, &begin) != 0) + begin = idx; /* ... */ + if (locp >= locendp) + goto invalid_data; + get_uleb128 (idx, locp, locendp); + if (get_indexed_addr (cu, idx, &end) != 0) + end = idx; /* ... */ + use_base = false; + break; + + case DW_LLE_GNU_start_length_entry: + if (locp >= locendp) + goto invalid_data; + get_uleb128 (idx, locp, locendp); + if (get_indexed_addr (cu, idx, &begin) != 0) + begin = idx; /* ... */ + if (locendp - locp < 4) + goto invalid_data; + end = read_4ubyte_unaligned_inc (dbg, locp); + end += begin; + use_base = false; + break; + + default: + goto invalid_data; + } + + readp = (unsigned char *) locp; + } + else if (address_size == 8) { begin = read_8ubyte_unaligned_inc (dbg, readp); end = read_8ubyte_unaligned_inc (dbg, readp); @@ -7092,15 +9438,15 @@ print_debug_loc_section (Dwfl_Module *dwflmod, if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ { - char *b = format_dwarf_addr (dwflmod, address_size, end, end); - printf (gettext (" [%6tx] base address %s\n"), offset, b); - free (b); + printf (gettext (" [%6tx] base address\n "), offset); + print_dwarf_addr (dwflmod, address_size, end, end); + printf ("\n"); base = end; } else if (begin == 0 && end == 0) /* End of list entry. */ { if (first) - printf (gettext (" [%6tx] empty list\n"), offset); + printf (gettext (" [%6tx] empty list\n"), offset); first = true; } else @@ -7108,18 +9454,22 @@ print_debug_loc_section (Dwfl_Module *dwflmod, /* We have a location expression entry. */ uint_fast16_t len = read_2ubyte_unaligned_inc (dbg, readp); - char *b = format_dwarf_addr (dwflmod, address_size, base + begin, - begin); - char *e = format_dwarf_addr (dwflmod, address_size, base + end, - end); - if (first) /* First entry in a list. */ - printf (gettext (" [%6tx] %s..%s"), offset, b, e); + printf (" [%6tx] ", offset); else - printf (gettext (" %s..%s"), b, e); + printf (" "); - free (b); - free (e); + printf ("range %" PRIx64 ", %" PRIx64 "\n", begin, end); + if (! print_unresolved_addresses) + { + Dwarf_Addr dab = use_base ? base + begin : begin; + Dwarf_Addr dae = use_base ? base + end : end; + printf (" "); + print_dwarf_addr (dwflmod, address_size, dab, dab); + printf ("..\n "); + print_dwarf_addr (dwflmod, address_size, dae - 1, dae); + printf ("\n"); + } if (endp - readp <= (ptrdiff_t) len) { @@ -7127,8 +9477,9 @@ print_debug_loc_section (Dwfl_Module *dwflmod, break; } - print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), - 3 /*XXX*/, address_size, offset_size, cu, len, readp); + print_ops (dwflmod, dbg, 11, 11, + cu != NULL ? cu->version : 3, + address_size, offset_size, cu, len, readp); first = false; readp += len; @@ -7172,8 +9523,9 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), /* There is no function in libdw to iterate over the raw content of the section but it is easy enough to do. */ - Elf_Data *data = dbg->sectiondata[IDX_debug_macinfo]; - if (unlikely (data == NULL || data->d_buf == NULL)) + Elf_Data *data = (dbg->sectiondata[IDX_debug_macinfo] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get macro information section data: %s"), elf_errmsg (-1)); @@ -7334,8 +9686,9 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), (uint64_t) shdr->sh_offset); putc_unlocked ('\n', stdout); - Elf_Data *data = dbg->sectiondata[IDX_debug_macro]; - if (unlikely (data == NULL || data->d_buf == NULL)) + Elf_Data *data = (dbg->sectiondata[IDX_debug_macro] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get macro information section data: %s"), elf_errmsg (-1)); @@ -7418,7 +9771,19 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), line_offset); } - const unsigned char *vendor[DW_MACRO_hi_user - DW_MACRO_lo_user]; + struct mac_culist *cu = NULL; + if (line_offset != (Dwarf_Off) -1) + { + cu = culist; + while (cu != NULL && line_offset != cu->offset) + cu = cu->next; + } + + Dwarf_Off str_offsets_base = str_offsets_base_off (dbg, (cu != NULL + ? cu->die.cu + : NULL)); + + const unsigned char *vendor[DW_MACRO_hi_user - DW_MACRO_lo_user + 1]; memset (vendor, 0, sizeof vendor); if (flag & 0x04) { @@ -7452,21 +9817,8 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), if (readp + 1 > readendp) goto invalid_data; unsigned int form = *readp++; - printf (" %s", dwarf_form_string (form)); - if (form != DW_FORM_data1 - && form != DW_FORM_data2 - && form != DW_FORM_data4 - && form != DW_FORM_data8 - && form != DW_FORM_sdata - && form != DW_FORM_udata - && form != DW_FORM_block - && form != DW_FORM_block1 - && form != DW_FORM_block2 - && form != DW_FORM_block4 - && form != DW_FORM_flag - && form != DW_FORM_string - && form != DW_FORM_strp - && form != DW_FORM_sec_offset) + printf (" %s", dwarf_form_name (form)); + if (! libdw_valid_user_form (form)) goto invalid_data; args--; if (args > 0) @@ -7501,22 +9853,16 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), /* Find the CU DIE that matches this line offset. */ const char *fname = "???"; - if (line_offset != (Dwarf_Off) -1) + if (cu != NULL) { - struct mac_culist *cu = culist; - while (cu != NULL && line_offset != cu->offset) - cu = cu->next; - if (cu != NULL) - { - if (cu->files == NULL - && dwarf_getsrcfiles (&cu->die, &cu->files, - NULL) != 0) - cu->files = (Dwarf_Files *) -1l; - - if (cu->files != (Dwarf_Files *) -1l) - fname = (dwarf_filesrc (cu->files, u128_2, - NULL, NULL) ?: "???"); - } + if (cu->files == NULL + && dwarf_getsrcfiles (&cu->die, &cu->files, + NULL) != 0) + cu->files = (Dwarf_Files *) -1l; + + if (cu->files != (Dwarf_Files *) -1l) + fname = (dwarf_filesrc (cu->files, u128_2, + NULL, NULL) ?: "???"); } printf ("%*sstart_file %u, [%u] %s\n", level, "", u128, u128_2, fname); @@ -7587,26 +9933,22 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), get_uleb128 (u128, readp, readendp); if (readp + offset_len > readendp) goto invalid_data; - if (offset_len == 8) - off = read_8ubyte_unaligned_inc (dbg, readp); - else - off = read_4ubyte_unaligned_inc (dbg, readp); - // Needs support for reading from supplementary object file. - printf ("%*s#define <str-at-0x%" PRIx64 ">, line %u (sup)\n", - level, "", off, u128); + printf ("%*s#define ", level, ""); + readp = print_form_data (dbg, DW_FORM_strp_sup, + readp, readendp, offset_len, + str_offsets_base); + printf (", line %u (sup)\n", u128); break; case DW_MACRO_undef_sup: get_uleb128 (u128, readp, readendp); if (readp + offset_len > readendp) goto invalid_data; - if (offset_len == 8) - off = read_8ubyte_unaligned_inc (dbg, readp); - else - off = read_4ubyte_unaligned_inc (dbg, readp); - // Needs support for reading from supplementary object file. - printf ("%*s#undef <str-at-0x%" PRIx64 ">, line %u (sup)\n", - level, "", off, u128); + printf ("%*s#undef ", level, ""); + readp = print_form_data (dbg, DW_FORM_strp_sup, + readp, readendp, offset_len, + str_offsets_base); + printf (", line %u (sup)\n", u128); break; case DW_MACRO_import_sup: @@ -7616,6 +9958,7 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), off = read_8ubyte_unaligned_inc (dbg, readp); else off = read_4ubyte_unaligned_inc (dbg, readp); + // XXX Needs support for reading from supplementary object file. printf ("%*s#include offset 0x%" PRIx64 " (sup)\n", level, "", off); break; @@ -7624,26 +9967,22 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), get_uleb128 (u128, readp, readendp); if (readp + offset_len > readendp) goto invalid_data; - if (offset_len == 8) - off = read_8ubyte_unaligned_inc (dbg, readp); - else - off = read_4ubyte_unaligned_inc (dbg, readp); - // Needs support for reading indirect string offset table - printf ("%*s#define <str-at-0x%" PRIx64 ">, line %u (strx)\n", - level, "", off, u128); + printf ("%*s#define ", level, ""); + readp = print_form_data (dbg, DW_FORM_strx, + readp, readendp, offset_len, + str_offsets_base); + printf (", line %u (strx)\n", u128); break; case DW_MACRO_undef_strx: get_uleb128 (u128, readp, readendp); if (readp + offset_len > readendp) goto invalid_data; - if (offset_len == 8) - off = read_8ubyte_unaligned_inc (dbg, readp); - else - off = read_4ubyte_unaligned_inc (dbg, readp); - // Needs support for reading indirect string offset table. - printf ("%*s#undef <str-at-0x%" PRIx64 ">, line %u (strx)\n", - level, "", off, u128); + printf ("%*s#undef ", level, ""); + readp = print_form_data (dbg, DW_FORM_strx, + readp, readendp, offset_len, + str_offsets_base); + printf (", line %u (strx)\n", u128); break; default: @@ -7659,128 +9998,14 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), // Just skip the arguments, we cannot really interpret them, // but print as much as we can. unsigned int args = *op_desc++; - while (args > 0) + while (args > 0 && readp < readendp) { unsigned int form = *op_desc++; - Dwarf_Word val; - switch (form) - { - case DW_FORM_data1: - if (readp + 1 > readendp) - goto invalid_data; - val = *readp++; - printf (" %" PRIx8, (unsigned int) val); - break; - - case DW_FORM_data2: - if (readp + 2 > readendp) - goto invalid_data; - val = read_2ubyte_unaligned_inc (dbg, readp); - printf(" %" PRIx16, (unsigned int) val); - break; - - case DW_FORM_data4: - if (readp + 4 > readendp) - goto invalid_data; - val = read_4ubyte_unaligned_inc (dbg, readp); - printf (" %" PRIx32, (unsigned int) val); - break; - - case DW_FORM_data8: - if (readp + 8 > readendp) - goto invalid_data; - val = read_8ubyte_unaligned_inc (dbg, readp); - printf (" %" PRIx64, val); - break; - - case DW_FORM_sdata: - get_sleb128 (val, readp, readendp); - printf (" %" PRIx64, val); - break; - - case DW_FORM_udata: - get_uleb128 (val, readp, readendp); - printf (" %" PRIx64, val); - break; - - case DW_FORM_block: - get_uleb128 (val, readp, readendp); - printf (" block[%" PRIu64 "]", val); - if (readp + val > readendp) - goto invalid_data; - readp += val; - break; - - case DW_FORM_block1: - if (readp + 1 > readendp) - goto invalid_data; - val = *readp++; - printf (" block[%" PRIu64 "]", val); - if (readp + val > readendp) - goto invalid_data; - break; - - case DW_FORM_block2: - if (readp + 2 > readendp) - goto invalid_data; - val = read_2ubyte_unaligned_inc (dbg, readp); - printf (" block[%" PRIu64 "]", val); - if (readp + val > readendp) - goto invalid_data; - break; - - case DW_FORM_block4: - if (readp + 2 > readendp) - goto invalid_data; - val =read_4ubyte_unaligned_inc (dbg, readp); - printf (" block[%" PRIu64 "]", val); - if (readp + val > readendp) - goto invalid_data; - break; - - case DW_FORM_flag: - if (readp + 1 > readendp) - goto invalid_data; - val = *readp++; - printf (" %s", val != 0 ? gettext ("yes") : gettext ("no")); - break; - - case DW_FORM_string: - endp = memchr (readp, '\0', readendp - readp); - if (endp == NULL) - goto invalid_data; - printf (" %s", readp); - readp = endp + 1; - break; - - case DW_FORM_strp: - if (readp + offset_len > readendp) - goto invalid_data; - if (offset_len == 8) - val = read_8ubyte_unaligned_inc (dbg, readp); - else - val = read_4ubyte_unaligned_inc (dbg, readp); - printf (" %s", dwarf_getstring (dbg, val, NULL)); - break; - - case DW_FORM_sec_offset: - if (readp + offset_len > readendp) - goto invalid_data; - if (offset_len == 8) - val = read_8ubyte_unaligned_inc (dbg, readp); - else - val = read_4ubyte_unaligned_inc (dbg, readp); - printf (" %" PRIx64, val); - break; - - default: - error (0, 0, gettext ("vendor opcode not verified?")); - return; - } - + readp = print_form_data (dbg, form, readp, readendp, + offset_len, str_offsets_base); args--; if (args > 0) - putchar_unlocked (','); + printf (", "); } putchar_unlocked ('\n'); } @@ -7828,10 +10053,11 @@ print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), static void print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl, GElf_Ehdr *ehdr, - Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) + Elf_Scn *scn, GElf_Shdr *shdr, + Dwarf *dbg __attribute__ ((unused))) { - const size_t sh_size = (dbg->sectiondata[IDX_debug_str] ? - dbg->sectiondata[IDX_debug_str]->d_size : 0); + Elf_Data *data = elf_rawdata (scn, NULL); + const size_t sh_size = data ? data->d_size : 0; /* Compute floor(log16(shdr->sh_size)). */ GElf_Addr tmp = sh_size; @@ -7854,20 +10080,207 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), while (offset < sh_size) { size_t len; - const char *str = dwarf_getstring (dbg, offset, &len); - if (unlikely (str == NULL)) + const char *str = (const char *) data->d_buf + offset; + const char *endp = memchr (str, '\0', sh_size - offset); + if (unlikely (endp == NULL)) { - printf (gettext (" *** error while reading strings: %s\n"), - dwarf_errmsg (-1)); + printf (gettext (" *** error, missing string terminator\n")); break; } printf (" [%*" PRIx64 "] \"%s\"\n", digits, (uint64_t) offset, str); - + len = endp - str; offset += len + 1; } } +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'. */ @@ -8314,12 +10727,11 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, uint32_t idx = read_4ubyte_unaligned (dbg, readp); readp += 4; - char *l = format_dwarf_addr (dwflmod, 8, low, low); - char *h = format_dwarf_addr (dwflmod, 8, high - 1, high); - printf (" [%4zu] %s..%s, CU index: %5" PRId32 "\n", - n, l, h, idx); - free (l); - free (h); + printf (" [%4zu] ", n); + print_dwarf_addr (dwflmod, 8, low, low); + printf (".."); + print_dwarf_addr (dwflmod, 8, high - 1, high); + printf (", CU index: %5" PRId32 "\n", idx); n++; } @@ -8404,9 +10816,60 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, } } +/* Returns true and sets split DWARF CU id if there is a split compile + unit in the given Dwarf, and no non-split units are found (before it). */ +static bool +is_split_dwarf (Dwarf *dbg, uint64_t *id, Dwarf_CU **split_cu) +{ + Dwarf_CU *cu = NULL; + while (dwarf_get_units (dbg, cu, &cu, NULL, NULL, NULL, NULL) == 0) + { + uint8_t unit_type; + if (dwarf_cu_info (cu, NULL, &unit_type, NULL, NULL, + id, NULL, NULL) != 0) + return false; + + if (unit_type != DW_UT_split_compile && unit_type != DW_UT_split_type) + return false; + + /* We really only care about the split compile unit, the types + should be fine and self sufficient. Also they don't have an + id that we can match with a skeleton unit. */ + if (unit_type == DW_UT_split_compile) + { + *split_cu = cu; + return true; + } + } + + return false; +} + +/* Check that there is one and only one Dwfl_Module, return in arg. */ +static int +getone_dwflmod (Dwfl_Module *dwflmod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + Dwfl_Module **m = (Dwfl_Module **) arg; + if (*m != NULL) + return DWARF_CB_ABORT; + *m = dwflmod; + return DWARF_CB_OK; +} + static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) { + /* Used for skeleton file, if necessary for split DWARF. */ + Dwfl *skel_dwfl = NULL; + Dwfl_Module *skel_mod = NULL; + char *skel_name = NULL; + Dwarf *split_dbg = NULL; + Dwarf_CU *split_cu = NULL; + /* Before we start the real work get a debug context descriptor. */ Dwarf_Addr dwbias; Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); @@ -8422,6 +10885,141 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) dwfl_errmsg (-1)); dbg = &dummy_dbg; } + else + { + /* If we are asked about a split dwarf (.dwo) file, use the user + provided, or find the corresponding skeleton file. If we got + a skeleton file, replace the given dwflmod and dbg, with one + derived from the skeleton file to provide enough context. */ + uint64_t split_id; + if (is_split_dwarf (dbg, &split_id, &split_cu)) + { + if (dwarf_skeleton != NULL) + skel_name = strdup (dwarf_skeleton); + else + { + /* Replace file.dwo with file.o and see if that matches. */ + const char *fname; + dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, + &fname, NULL); + if (fname != NULL) + { + size_t flen = strlen (fname); + if (flen > 4 && strcmp (".dwo", fname + flen - 4) == 0) + { + skel_name = strdup (fname); + if (skel_name != NULL) + { + skel_name[flen - 3] = 'o'; + skel_name[flen - 2] = '\0'; + } + } + } + } + + if (skel_name != NULL) + { + int skel_fd = open (skel_name, O_RDONLY); + if (skel_fd == -1) + fprintf (stderr, "Warning: Couldn't open DWARF skeleton file" + " '%s'\n", skel_name); + else + skel_dwfl = create_dwfl (skel_fd, skel_name); + + if (skel_dwfl != NULL) + { + if (dwfl_getmodules (skel_dwfl, &getone_dwflmod, + &skel_mod, 0) != 0) + { + fprintf (stderr, "Warning: Bad DWARF skeleton," + " multiple modules '%s'\n", skel_name); + dwfl_end (skel_dwfl); + skel_mod = NULL; + } + } + else if (skel_fd != -1) + fprintf (stderr, "Warning: Couldn't create skeleton dwfl for" + " '%s': %s\n", skel_name, dwfl_errmsg (-1)); + + if (skel_mod != NULL) + { + Dwarf *skel_dbg = dwfl_module_getdwarf (skel_mod, &dwbias); + if (skel_dbg != NULL) + { + /* First check the skeleton CU DIE, only fetch + the split DIE if we know the id matches to + not unnecessary search for any split DIEs we + don't need. */ + Dwarf_CU *cu = NULL; + while (dwarf_get_units (skel_dbg, cu, &cu, + NULL, NULL, NULL, NULL) == 0) + { + uint8_t unit_type; + uint64_t skel_id; + if (dwarf_cu_info (cu, NULL, &unit_type, NULL, NULL, + &skel_id, NULL, NULL) == 0 + && unit_type == DW_UT_skeleton + && split_id == skel_id) + { + Dwarf_Die subdie; + if (dwarf_cu_info (cu, NULL, NULL, NULL, + &subdie, + NULL, NULL, NULL) == 0 + && dwarf_tag (&subdie) != DW_TAG_invalid) + { + split_dbg = dwarf_cu_getdwarf (subdie.cu); + if (split_dbg == NULL) + fprintf (stderr, + "Warning: Couldn't get split_dbg:" + " %s\n", dwarf_errmsg (-1)); + break; + } + else + { + /* Everything matches up, but not + according to libdw. Which means + the user knew better. So... + Terrible hack... We can never + destroy the underlying dwfl + because it would free the wrong + Dwarfs... So we leak memory...*/ + if (cu->split == NULL + && dwarf_skeleton != NULL) + { + do_not_close_dwfl = true; + __libdw_link_skel_split (cu, split_cu); + split_dbg = dwarf_cu_getdwarf (split_cu); + break; + } + else + fprintf (stderr, "Warning: Couldn't get" + " skeleton subdie: %s\n", + dwarf_errmsg (-1)); + } + } + } + if (split_dbg == NULL) + fprintf (stderr, "Warning: '%s' didn't contain a skeleton for split id %" PRIx64 "\n", skel_name, split_id); + } + else + fprintf (stderr, "Warning: Couldn't get skeleton DWARF:" + " %s\n", dwfl_errmsg (-1)); + } + } + + if (split_dbg != NULL) + { + dbg = split_dbg; + dwflmod = skel_mod; + } + else if (skel_name == NULL) + fprintf (stderr, + "Warning: split DWARF file, but no skeleton found.\n"); + } + else if (dwarf_skeleton != NULL) + fprintf (stderr, "Warning: DWARF skeleton given," + " but not a split DWARF file\n"); + } /* Get the section header string table index. */ size_t shstrndx; @@ -8429,6 +11027,42 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + /* If the .debug_info section is listed as implicitly required then + we must make sure to handle it before handling any other debug + section. Various other sections depend on the CU DIEs being + scanned (silently) first. */ + bool implicit_info = (implicit_debug_sections & section_info) != 0; + bool explicit_info = (print_debug_sections & section_info) != 0; + if (implicit_info) + { + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == SHT_PROGBITS) + { + const char *name = elf_strptr (ebl->elf, shstrndx, + shdr->sh_name); + if (name == NULL) + continue; + + if (strcmp (name, ".debug_info") == 0 + || strcmp (name, ".debug_info.dwo") == 0 + || strcmp (name, ".zdebug_info") == 0 + || strcmp (name, ".zdebug_info.dwo") == 0) + { + print_debug_info_section (dwflmod, ebl, ehdr, + scn, shdr, dbg); + break; + } + } + } + print_debug_sections &= ~section_info; + implicit_debug_sections &= ~section_info; + } + /* Look through all the sections for the debugging sections to print. */ Elf_Scn *scn = NULL; while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) @@ -8449,17 +11083,30 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) #define NEW_SECTION(name) \ { ".debug_" #name, section_##name, print_debug_##name##_section } NEW_SECTION (abbrev), + NEW_SECTION (addr), NEW_SECTION (aranges), NEW_SECTION (frame), NEW_SECTION (info), NEW_SECTION (types), NEW_SECTION (line), NEW_SECTION (loc), + /* loclists is loc for DWARF5. */ + { ".debug_loclists", section_loc, + print_debug_loclists_section }, NEW_SECTION (pubnames), NEW_SECTION (str), + /* 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), + /* rnglists is ranges for DWARF5. */ + { ".debug_rnglists", section_ranges, + print_debug_rnglists_section }, { ".eh_frame", section_frame | section_exception, print_debug_frame_section }, { ".eh_frame_hdr", section_frame | section_exception, @@ -8477,22 +11124,45 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) int n; for (n = 0; n < ndebug_sections; ++n) - if (strcmp (name, debug_sections[n].name) == 0 - || (name[0] == '.' && name[1] == 'z' - && debug_sections[n].name[1] == 'd' - && strcmp (&name[2], &debug_sections[n].name[1]) == 0) - ) - { - if ((print_debug_sections | implicit_debug_sections) - & debug_sections[n].bitmask) - debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); - break; - } + { + size_t dbglen = strlen (debug_sections[n].name); + size_t scnlen = strlen (name); + if ((strncmp (name, debug_sections[n].name, dbglen) == 0 + && (dbglen == scnlen + || (scnlen == dbglen + 4 + && strstr (name, ".dwo") == name + dbglen))) + || (name[0] == '.' && name[1] == 'z' + && debug_sections[n].name[1] == 'd' + && strncmp (&name[2], &debug_sections[n].name[1], + dbglen - 1) == 0 + && (scnlen == dbglen + 1 + || (scnlen == dbglen + 5 + && strstr (name, ".dwo") == name + dbglen + 1)))) + { + if ((print_debug_sections | implicit_debug_sections) + & debug_sections[n].bitmask) + debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); + break; + } + } } } - reset_listptr (&known_loclistptr); + dwfl_end (skel_dwfl); + free (skel_name); + + /* Turn implicit and/or explicit back on in case we go over another file. */ + if (implicit_info) + implicit_debug_sections |= section_info; + if (explicit_info) + print_debug_sections |= section_info; + + reset_listptr (&known_locsptr); + reset_listptr (&known_loclistsptr); reset_listptr (&known_rangelistptr); + reset_listptr (&known_rnglistptr); + reset_listptr (&known_addrbases); + reset_listptr (&known_stroffbases); } @@ -9226,7 +11896,7 @@ handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos) printf (" %s\n", name); break; } - /* Fall through */ + FALLTHROUGH; case 'x': /* hex */ case 'p': /* address */ case 's': /* address of string */ @@ -9335,10 +12005,10 @@ handle_siginfo_note (Elf *core, GElf_Word descsz, GElf_Off desc_pos) if (si_code > 0) switch (si_signo) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: + case CORE_SIGILL: + case CORE_SIGFPE: + case CORE_SIGSEGV: + case CORE_SIGBUS: { uint64_t addr; if (! buf_read_ulong (core, &ptr, end, &addr)) @@ -9349,7 +12019,7 @@ handle_siginfo_note (Elf *core, GElf_Word descsz, GElf_Off desc_pos) default: ; } - else if (si_code == SI_USER) + else if (si_code == CORE_SI_USER) { int pid, uid; if (! buf_read_int (core, &ptr, end, &pid) @@ -9511,9 +12181,9 @@ handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr, return; bad_note: - error (EXIT_FAILURE, 0, - gettext ("cannot get content of note section: %s"), - elf_errmsg (-1)); + error (0, 0, + gettext ("cannot get content of note: %s"), + data != NULL ? "garbage data" : elf_errmsg (-1)); } static void |