| From 201a71ce18734b1cebc337225f345fd754a6414f Mon Sep 17 00:00:00 2001 |
| Message-Id: <201a71ce18734b1cebc337225f345fd754a6414f.1573552234.git.pmatilai@redhat.com> |
| In-Reply-To: <ce6e8556a8f93327d6de0446f21ac5e549861d82.1573552234.git.pmatilai@redhat.com> |
| References: <ce6e8556a8f93327d6de0446f21ac5e549861d82.1573552234.git.pmatilai@redhat.com> |
| From: Mark Wielaard <mark@klomp.org> |
| Date: Mon, 17 Jun 2019 11:23:25 +0200 |
| Subject: [PATCH 2/3] Handle .debug_macro in debugedit. |
| |
| When compiling with -g3 gcc will generate a .debug_macro section |
| which has pointers to the .debug_str section. Since we might rewrite |
| the .debug_str section, we also need to update any .debug_macro |
| pointers. |
| |
| Updated the debugedit.at testcase by building everything with -g |
| and add various checks to see the .debug_macro section looks OK |
| after running debugedit. Added a new rpmbuild.at testcase to check |
| handing of .debug_macro in the whole rpmbuild debuginfo pipeline |
| to double check the separate .debug file also contains the macros. |
| |
| Original patch by Michael Schroeder <mls@suse.de>. Extended by |
| Mark Wielaard <mark@klomp.org> to deal with relocations and possible |
| multiple COMDAT .debug_macro sections. |
| |
| tests/Makefile.am | 1 + |
| tests/data/SPECS/hello-g3.spec | 60 ++++++++++ |
| tests/debugedit.at | 79 ++++++++++++- |
| tests/rpmbuild.at | 33 ++++++ |
| tools/debugedit.c | 196 +++++++++++++++++++++++++++++++-- |
| 5 files changed, 356 insertions(+), 13 deletions(-) |
| create mode 100644 tests/data/SPECS/hello-g3.spec |
| |
| [ test-suite part edited out, too painful to backport ] |
| |
| diff --git a/tools/debugedit.c b/tools/debugedit.c |
| index cf9cc3ca9..84483ef5e 100644 |
| |
| |
| @@ -41,6 +41,7 @@ |
| #include <gelf.h> |
| #include <dwarf.h> |
| |
| + |
| /* Unfortunately strtab manipulation functions were only officially added |
| to elfutils libdw in 0.167. Before that there were internal unsupported |
| ebl variants. While libebl.h isn't supported we'll try to use it anyway |
| @@ -432,6 +433,7 @@ typedef struct debug_section |
| int sec, relsec; |
| REL *relbuf; |
| REL *relend; |
| + struct debug_section *next; /* Only happens for COMDAT .debug_macro. */ |
| } debug_section; |
| |
| static debug_section debug_sections[] = |
| @@ -1989,11 +1991,35 @@ edit_dwarf2 (DSO *dso) |
| for (j = 0; debug_sections[j].name; ++j) |
| if (strcmp (name, debug_sections[j].name) == 0) |
| { |
| + struct debug_section *debug_sec = &debug_sections[j]; |
| if (debug_sections[j].data) |
| { |
| - error (0, 0, "%s: Found two copies of %s section", |
| - dso->filename, name); |
| - return 1; |
| + if (j != DEBUG_MACRO) |
| + { |
| + error (0, 0, "%s: Found two copies of %s section", |
| + dso->filename, name); |
| + return 1; |
| + } |
| + else |
| + { |
| + /* In relocatable files .debug_macro might |
| + appear multiple times as COMDAT |
| + section. */ |
| + struct debug_section *sec; |
| + sec = calloc (sizeof (struct debug_section), 1); |
| + if (sec == NULL) |
| + error (1, errno, |
| + "%s: Could not allocate more macro sections", |
| + dso->filename); |
| + sec->name = ".debug_macro"; |
| + |
| + struct debug_section *macro_sec = debug_sec; |
| + while (macro_sec->next != NULL) |
| + macro_sec = macro_sec->next; |
| + |
| + macro_sec->next = sec; |
| + debug_sec = sec; |
| + } |
| } |
| |
| scn = dso->scn[i]; |
| @@ -2002,10 +2028,10 @@ edit_dwarf2 (DSO *dso) |
| assert (elf_getdata (scn, data) == NULL); |
| assert (data->d_off == 0); |
| assert (data->d_size == dso->shdr[i].sh_size); |
| - debug_sections[j].data = data->d_buf; |
| - debug_sections[j].elf_data = data; |
| - debug_sections[j].size = data->d_size; |
| - debug_sections[j].sec = i; |
| + debug_sec->data = data->d_buf; |
| + debug_sec->elf_data = data; |
| + debug_sec->size = data->d_size; |
| + debug_sec->sec = i; |
| break; |
| } |
| |
| @@ -2028,7 +2054,26 @@ edit_dwarf2 (DSO *dso) |
| + (dso->shdr[i].sh_type == SHT_RELA), |
| debug_sections[j].name) == 0) |
| { |
| - debug_sections[j].relsec = i; |
| + if (j == DEBUG_MACRO) |
| + { |
| + /* Pick the correct one. */ |
| + int rel_target = dso->shdr[i].sh_info; |
| + struct debug_section *macro_sec = &debug_sections[j]; |
| + while (macro_sec != NULL) |
| + { |
| + if (macro_sec->sec == rel_target) |
| + { |
| + macro_sec->relsec = i; |
| + break; |
| + } |
| + macro_sec = macro_sec->next; |
| + } |
| + if (macro_sec == NULL) |
| + error (0, 1, "No .debug_macro reloc section: %s", |
| + dso->filename); |
| + } |
| + else |
| + debug_sections[j].relsec = i; |
| break; |
| } |
| } |
| @@ -2062,6 +2107,7 @@ edit_dwarf2 (DSO *dso) |
| struct abbrev_tag tag, *t; |
| int phase; |
| bool info_rel_updated = false; |
| + bool macro_rel_updated = false; |
| |
| for (phase = 0; phase < 2; phase++) |
| { |
| @@ -2279,6 +2325,113 @@ edit_dwarf2 (DSO *dso) |
| } |
| } |
| |
| + /* The .debug_macro section also contains offsets into the |
| + .debug_str section and references to the .debug_line |
| + tables, so we need to update those as well if we update |
| + the strings or the stmts. */ |
| + if ((need_strp_update || need_stmt_update) |
| + && debug_sections[DEBUG_MACRO].data) |
| + { |
| + /* There might be multiple (COMDAT) .debug_macro sections. */ |
| + struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; |
| + while (macro_sec != NULL) |
| + { |
| + setup_relbuf(dso, macro_sec, &reltype); |
| + rel_updated = false; |
| + |
| + ptr = macro_sec->data; |
| + endsec = ptr + macro_sec->size; |
| + int op = 0, macro_version, macro_flags; |
| + int offset_len = 4, line_offset = 0; |
| + |
| + while (ptr < endsec) |
| + { |
| + if (!op) |
| + { |
| + macro_version = read_16 (ptr); |
| + macro_flags = read_8 (ptr); |
| + if (macro_version < 4 || macro_version > 5) |
| + error (1, 0, "unhandled .debug_macro version: %d", |
| + macro_version); |
| + if ((macro_flags & ~2) != 0) |
| + error (1, 0, "unhandled .debug_macro flags: 0x%x", |
| + macro_flags); |
| + |
| + offset_len = (macro_flags & 0x01) ? 8 : 4; |
| + line_offset = (macro_flags & 0x02) ? 1 : 0; |
| + |
| + if (offset_len != 4) |
| + error (0, 1, |
| + "Cannot handle 8 byte macro offsets: %s", |
| + dso->filename); |
| + |
| + /* Update the line_offset if it is there. */ |
| + if (line_offset) |
| + { |
| + if (phase == 0) |
| + ptr += offset_len; |
| + else |
| + { |
| + size_t idx, new_idx; |
| + idx = do_read_32_relocated (ptr); |
| + new_idx = find_new_list_offs (&dso->lines, |
| + idx); |
| + write_32_relocated (ptr, new_idx); |
| + } |
| + } |
| + } |
| + |
| + op = read_8 (ptr); |
| + if (!op) |
| + continue; |
| + switch(op) |
| + { |
| + case DW_MACRO_GNU_define: |
| + case DW_MACRO_GNU_undef: |
| + read_uleb128 (ptr); |
| + ptr = ((unsigned char *) strchr ((char *) ptr, '\0') |
| + + 1); |
| + break; |
| + case DW_MACRO_GNU_start_file: |
| + read_uleb128 (ptr); |
| + read_uleb128 (ptr); |
| + break; |
| + case DW_MACRO_GNU_end_file: |
| + break; |
| + case DW_MACRO_GNU_define_indirect: |
| + case DW_MACRO_GNU_undef_indirect: |
| + read_uleb128 (ptr); |
| + if (phase == 0) |
| + { |
| + size_t idx = read_32_relocated (ptr); |
| + record_existing_string_entry_idx (&dso->strings, |
| + idx); |
| + } |
| + else |
| + { |
| + struct stridxentry *entry; |
| + size_t idx, new_idx; |
| + idx = do_read_32_relocated (ptr); |
| + entry = string_find_entry (&dso->strings, idx); |
| + new_idx = strent_offset (entry->entry); |
| + write_32_relocated (ptr, new_idx); |
| + } |
| + break; |
| + case DW_MACRO_GNU_transparent_include: |
| + ptr += offset_len; |
| + break; |
| + default: |
| + error (1, 0, "Unhandled DW_MACRO op 0x%x", op); |
| + break; |
| + } |
| + } |
| + |
| + if (rel_updated) |
| + macro_rel_updated = true; |
| + macro_sec = macro_sec->next; |
| + } |
| + } |
| + |
| /* Same for the debug_str section. Make sure everything is |
| in place for phase 1 updating of debug_info |
| references. */ |
| @@ -2308,10 +2461,24 @@ edit_dwarf2 (DSO *dso) |
| new strp, strings and/or linep offsets. */ |
| if (need_strp_update || need_string_replacement || need_stmt_update) |
| dirty_section (DEBUG_INFO); |
| + if (need_strp_update || need_stmt_update) |
| + dirty_section (DEBUG_MACRO); |
| + if (need_stmt_update) |
| + dirty_section (DEBUG_LINE); |
| |
| - /* Update any debug_info relocations addends we might have touched. */ |
| + /* Update any relocations addends we might have touched. */ |
| if (info_rel_updated) |
| update_rela_data (dso, &debug_sections[DEBUG_INFO]); |
| + |
| + if (macro_rel_updated) |
| + { |
| + struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; |
| + while (macro_sec != NULL) |
| + { |
| + update_rela_data (dso, macro_sec); |
| + macro_sec = macro_sec->next; |
| + } |
| + } |
| } |
| |
| return 0; |
| @@ -2843,6 +3010,17 @@ main (int argc, char *argv[]) |
| destroy_lines (&dso->lines); |
| free (dso); |
| |
| + /* In case there were multiple (COMDAT) .debug_macro sections, |
| + free them. */ |
| + struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; |
| + macro_sec = macro_sec->next; |
| + while (macro_sec != NULL) |
| + { |
| + struct debug_section *next = macro_sec->next; |
| + free (macro_sec); |
| + macro_sec = next; |
| + } |
| + |
| poptFreeContext (optCon); |
| |
| return 0; |
| -- |
| 2.23.0 |
| |