diff --git a/ci/Dockerfile b/ci/Dockerfile index d8b0115bd..2e872be5f 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -45,6 +45,7 @@ RUN dnf -y install \ pkgconfig \ /usr/bin/gdb-add-index \ dwz \ + clang llvm \ && dnf clean all COPY . . diff --git a/tests/debugedit.at b/tests/debugedit.at index bcd86ac67..5fa2bf6a7 100644 --- a/tests/debugedit.at +++ b/tests/debugedit.at @@ -22,8 +22,9 @@ AT_BANNER([RPM debugedit]) AT_TESTED([debugedit]) # Helper to create some test binaries. -# Optional parameter can specify additional gcc parameters. -m4_define([RPM_DEBUGEDIT_SETUP],[[ +# Optional first parameter can specify alternative compiler than [gcc]. +# Optional second parameter can specify additional compiler parameters. +m4_define([RPM_DEBUGEDIT_SETUP],[ # Create some test binaries. Create and build them in different subdirs # to make sure they produce different relative/absolute paths. @@ -37,11 +38,11 @@ cp "${abs_srcdir}"/data/SOURCES/foobar.h subdir_headers cp "${abs_srcdir}"/data/SOURCES/baz.c . # First three object files (foo.o subdir_bar/bar.o and baz.o) -gcc -g3 -Isubdir_headers $1 -c subdir_foo/foo.c +m4_if($1,,gcc,$1) -g3 -Isubdir_headers -c $2 subdir_foo/foo.c cd subdir_bar -gcc -g3 -I../subdir_headers $1 -c bar.c +m4_if($1,,gcc,$1) -g3 -I../subdir_headers -c $2 bar.c cd .. -gcc -g3 -I$(pwd)/subdir_headers $1 -c $(pwd)/baz.c +m4_if($1,,gcc,$1) -g3 -I$(pwd)/subdir_headers -c $2 $(pwd)/baz.c # Then a partially linked object file (somewhat like a kernel module). # This will still have relocations between the debug sections. @@ -49,8 +50,8 @@ ld -r -o foobarbaz.part.o foo.o subdir_bar/bar.o baz.o # Create an executable. Relocations between debug sections will # have been resolved. -gcc -g3 -o foobarbaz.exe foo.o subdir_bar/bar.o baz.o -]]) +m4_if($1,,gcc,$1) -g3 -o foobarbaz.exe foo.o subdir_bar/bar.o baz.o +]) # === # Check debugedit --help doesn't crash and burn. @@ -254,7 +255,7 @@ AT_CLEANUP # Make sure -fdebug-types-section has updated strings in objects. # === m4_define([RPM_DEBUGEDIT_DEBUG_TYPES_OBJECTS],[ -RPM_DEBUGEDIT_SETUP($1) +RPM_DEBUGEDIT_SETUP($1,$2) AT_DATA([expout], [st1 @@ -274,21 +275,28 @@ stz AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ +AT_CHECK([ for i in ./foo.o ./subdir_bar/bar.o ./baz.o;do \ +m4_if($1,gcc,[[ \ readelf --debug-dump=info $i \ | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ | sort; +]],[[ \ + llvm-dwarfdump $i \ + | awk '/^0x[0-9a-f]*: [^ ]* Unit:/{p=0}/^0x[0-9a-f]*: Type Unit:/{p=1}{if(p)print}' \ + | sed -n 's/^ *DW_AT_name\t("\(stringp[^ ]*\|st.\)")$/\1/p' \ + | sort; +]]) \ done -]],[0],[expout]) +],[0],[expout]) ]) # === # Make sure -fdebug-types-section has updated strings in partial linked object. # === m4_define([RPM_DEBUGEDIT_DEBUG_TYPES_PARTIAL],[ -RPM_DEBUGEDIT_SETUP($1) +RPM_DEBUGEDIT_SETUP($1,$2) AT_DATA([expout], [st1 @@ -302,19 +310,26 @@ stz ]) AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ +AT_CHECK([ +m4_if($1,gcc,[[ readelf --debug-dump=info ./foobarbaz.part.o \ | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ | sort -]],[0],[expout]) +]],[[ + llvm-dwarfdump ./foobarbaz.part.o \ + | awk '/^0x[0-9a-f]*: [^ ]* Unit:/{p=0}/^0x[0-9a-f]*: Type Unit:/{p=1}{if(p)print}' \ + | sed -n 's/^ *DW_AT_name\t("\(stringp[^ ]*\|st.\)")$/\1/p' \ + | sort +]]) +],[0],[expout]) ]) # === # Make sure -fdebug-types-section has updated strings in executable. # === m4_define([RPM_DEBUGEDIT_DEBUG_TYPES_EXE],[ -RPM_DEBUGEDIT_SETUP($1) +RPM_DEBUGEDIT_SETUP($1,$2) AT_DATA([expout], [st1 @@ -328,42 +343,67 @@ stz ]) AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ +AT_CHECK([ +m4_if($1,gcc,[[ readelf --debug-dump=info ./foobarbaz.exe \ | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ | sort -]],[0],[expout]) +]],[[ + llvm-dwarfdump ./foobarbaz.exe \ + | awk '/^0x[0-9a-f]*: [^ ]* Unit:/{p=0}/^0x[0-9a-f]*: Type Unit:/{p=1}{if(p)print}' \ + | sed -n 's/^ *DW_AT_name\t("\(stringp[^ ]*\|st.\)")$/\1/p' \ + | sort +]]) +],[0],[expout]) ]) -AT_SETUP([debugedit DWARF-4 .debug_types objects]) +AT_SETUP([debugedit gcc DWARF-4 .debug_types objects]) +AT_KEYWORDS([debugtypes] [debugedit]) +RPM_DEBUGEDIT_DEBUG_TYPES_OBJECTS([gcc],[-gdwarf-4 -fdebug-types-section]) +AT_CLEANUP + +AT_SETUP([debugedit gcc DWARF-4 .debug_types partial]) AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_DEBUG_TYPES_OBJECTS([-gdwarf-4 -fdebug-types-section]) +RPM_DEBUGEDIT_DEBUG_TYPES_PARTIAL([gcc],[-gdwarf-4 -fdebug-types-section]) AT_CLEANUP -AT_SETUP([debugedit DWARF-4 .debug_types partial]) +AT_SETUP([debugedit gcc DWARF-4 .debug_types exe]) AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_DEBUG_TYPES_PARTIAL([-gdwarf-4 -fdebug-types-section]) +RPM_DEBUGEDIT_DEBUG_TYPES_EXE([gcc],[-gdwarf-4 -fdebug-types-section]) AT_CLEANUP -AT_SETUP([debugedit DWARF-4 .debug_types exe]) +AT_SETUP([debugedit gcc DWARF-5 .debug_types objects]) AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_DEBUG_TYPES_EXE([-gdwarf-4 -fdebug-types-section]) +RPM_DEBUGEDIT_DEBUG_TYPES_OBJECTS([gcc],[-gdwarf-5 -fdebug-types-section]) AT_CLEANUP -AT_SETUP([debugedit DWARF-5 .debug_types objects]) +AT_SETUP([debugedit gcc DWARF-5 .debug_types partial]) AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_DEBUG_TYPES_OBJECTS([-gdwarf-5 -fdebug-types-section]) +RPM_DEBUGEDIT_DEBUG_TYPES_PARTIAL([gcc],[-gdwarf-5 -fdebug-types-section]) AT_CLEANUP -AT_SETUP([debugedit DWARF-5 .debug_types partial]) +AT_SETUP([debugedit gcc DWARF-5 .debug_types exe]) AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_DEBUG_TYPES_PARTIAL([-gdwarf-5 -fdebug-types-section]) +RPM_DEBUGEDIT_DEBUG_TYPES_EXE([gcc],[-gdwarf-5 -fdebug-types-section]) AT_CLEANUP -AT_SETUP([debugedit DWARF-5 .debug_types exe]) +AT_SETUP([debugedit clang DWARF-5 .debug_types objects]) AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_DEBUG_TYPES_EXE([-gdwarf-5 -fdebug-types-section]) +# -x c++ as clang does ignores -fdebug-types-section for C. +RPM_DEBUGEDIT_DEBUG_TYPES_OBJECTS([clang],[-gdwarf-5 -fdebug-types-section -x c++]) +AT_CLEANUP + +AT_SETUP([debugedit clang DWARF-5 .debug_types partial]) +AT_KEYWORDS([debugtypes] [debugedit]) +# -x c++ as clang does ignores -fdebug-types-section for C. +RPM_DEBUGEDIT_DEBUG_TYPES_PARTIAL([clang],[-gdwarf-5 -fdebug-types-section -x c++]) +AT_CLEANUP + +AT_SETUP([debugedit clang DWARF-5 .debug_types exe]) +AT_KEYWORDS([debugtypes] [debugedit]) +# -x c++ as clang does ignores -fdebug-types-section for C. +RPM_DEBUGEDIT_DEBUG_TYPES_EXE([clang],[-gdwarf-5 -fdebug-types-section -x c++]) AT_CLEANUP # foo.o and bar.o are build with relative paths and so will use the @@ -373,9 +413,9 @@ AT_CLEANUP # === # Make sure .debug_line Directory Table entries are replaced -# in objects. +# in objects for DWARF-4. # === -AT_SETUP([debugedit .debug_line objects]) +AT_SETUP([debugedit gcc DWARF-4 .debug_line objects]) AT_KEYWORDS([debuginfo] [debugedit]) RPM_DEBUGEDIT_SETUP @@ -397,9 +437,9 @@ AT_CLEANUP # === # Make sure .debug_line Directory Table entries are replaced -# in partial linked object. +# in partial linked object for DWARF-4. # === -AT_SETUP([debugedit .debug_line partial]) +AT_SETUP([debugedit gcc DWARF-4 .debug_line partial]) AT_KEYWORDS([debuginfo] [debugedit]) RPM_DEBUGEDIT_SETUP @@ -419,9 +459,9 @@ AT_CLEANUP # === # Make sure .debug_line Directory Table entries are replaced -# in executable. +# in executable for DWARF-4. # === -AT_SETUP([debugedit .debug_line exe]) +AT_SETUP([debugedit gcc DWARF-4 .debug_line exe]) AT_KEYWORDS([debuginfo] [debugedit]) RPM_DEBUGEDIT_SETUP @@ -439,6 +479,107 @@ readelf --debug-dump=line ./foobarbaz.exe \ AT_CLEANUP +# === +# Make sure .debug_line Directory Table entries are replaced +# in objects for DWARF-5. +# === +AT_SETUP([debugedit clang DWARF-5 .debug_line objects]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP([clang],[-gdwarf-5]) + +AT_DATA([expout], +[../subdir_headers +/foo/bar/baz +/foo/bar/baz +/foo/bar/baz/baz.c +/foo/bar/baz/subdir_bar +bar.c +baz.c +foobar.h +foobar.h +foobar.h +subdir_foo/foo.c +subdir_headers +subdir_headers +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) +AT_CHECK([[ +readelf --debug-dump=line foo.o subdir_bar/bar.o baz.o \ + | sed -n '/^ The Directory Table/,/^ Line Number Statements:/p' | grep "^ [0-9]" \ + | sed 's/^.* //' | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_line Directory Table entries are replaced +# in partial linked object for DWARF-5. +# === +AT_SETUP([debugedit clang DWARF-5 .debug_line partial]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP([clang],[-gdwarf-5]) + +AT_DATA([expout], +[../subdir_headers +/foo/bar/baz +/foo/bar/baz +/foo/bar/baz/baz.c +/foo/bar/baz/subdir_bar +bar.c +baz.c +foobar.h +foobar.h +foobar.h +subdir_foo/foo.c +subdir_headers +subdir_headers +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) +AT_CHECK([[ +readelf --debug-dump=line ./foobarbaz.part.o \ + | sed -n '/^ The Directory Table/,/^ Line Number Statements:/p' | grep "^ [0-9]" \ + | sed 's/^.* //' | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_line Directory Table entries are replaced +# in executable for DWARF-5. +# === +AT_SETUP([debugedit clang DWARF-5 .debug_line exe]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP([clang],[-gdwarf-5]) + +AT_DATA([expout], +[../subdir_headers +/foo/bar/baz +/foo/bar/baz +/foo/bar/baz/baz.c +/foo/bar/baz/subdir_bar +bar.c +baz.c +foobar.h +foobar.h +foobar.h +subdir_foo/foo.c +subdir_headers +subdir_headers +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[ +readelf --debug-dump=line ./foobarbaz.exe \ + | sed -n '/^ The Directory Table/,/^ Line Number Statements:/p' | grep "^ [0-9]" \ + | sed 's/^.* //' | sort +]],[0],[expout]) + +AT_CLEANUP + # === # Make sure .debug_macro strings are still there # in objects. diff --git a/tools/debugedit.c b/tools/debugedit.c index 19f69e263..0d4b50df4 100644 --- a/tools/debugedit.c +++ b/tools/debugedit.c @@ -103,6 +103,8 @@ static bool need_string_replacement = false; /* Whether we need to do any updates of the string indexes (DW_FORM_strp) in debug_info for string indexes. */ static bool need_strp_update = false; +/* Likewise for DW_FORM_line_strp. */ +static bool need_line_strp_update = false; /* If the debug_line changes size we will need to update the DW_AT_stmt_list attributes indexes in the debug_info. */ static bool need_stmt_update = false; @@ -159,6 +161,7 @@ struct line_table ssize_t size_diff; /* Difference in (header) size. */ bool replace_dirs; /* Whether to replace any dir paths. */ bool replace_files; /* Whether to replace any file paths. */ + int phase_done; /* Which phase is already processed, initially -1. */ /* Header fields. */ uint32_t unit_length; @@ -192,7 +195,7 @@ typedef struct const char *filename; int lastscn; size_t phnum; - struct strings strings; + struct strings debug_str, debug_line_str; struct debug_lines lines; GElf_Shdr shdr[0]; } DSO; @@ -404,6 +407,9 @@ dwarf2_write_be32 (unsigned char *p, uint32_t v) static bool rel_updated; +/* Whether .debug_line needs update of its relocations. */ +static bool line_rel_updated; + #define do_write_32_relocated(ptr,val) ({ \ if (relptr && relptr < relend && relptr->ptr == ptr) \ { \ @@ -454,6 +460,11 @@ static debug_section debug_sections[] = #define DEBUG_TYPES 11 #define DEBUG_MACRO 12 #define DEBUG_GDB_SCRIPT 13 +#define DEBUG_RNGLISTS 14 +#define DEBUG_LINE_STR 15 +#define DEBUG_ADDR 16 +#define DEBUG_STR_OFFSETS 17 +#define DEBUG_LOCLISTS 18 { ".debug_info", NULL, NULL, 0, 0, 0 }, { ".debug_abbrev", NULL, NULL, 0, 0, 0 }, { ".debug_line", NULL, NULL, 0, 0, 0 }, @@ -468,6 +479,11 @@ static debug_section debug_sections[] = { ".debug_types", NULL, NULL, 0, 0, 0 }, { ".debug_macro", NULL, NULL, 0, 0, 0 }, { ".debug_gdb_scripts", NULL, NULL, 0, 0, 0 }, + { ".debug_rnglists", NULL, NULL, 0, 0, 0 }, + { ".debug_line_str", NULL, NULL, 0, 0, 0 }, + { ".debug_addr", NULL, NULL, 0, 0, 0 }, + { ".debug_str_offsets", NULL, NULL, 0, 0, 0 }, + { ".debug_loclists", NULL, NULL, 0, 0, 0 }, { NULL, NULL, NULL, 0, 0, 0 } }; @@ -544,9 +560,10 @@ setup_relbuf (DSO *dso, debug_section *sec, int *reltype) /* Relocations against section symbols are uninteresting in REL. */ if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0) continue; - /* Only consider relocations against .debug_str, .debug_line - and .debug_abbrev. */ + /* Only consider relocations against .debug_str, .debug_line_str, + .debug_line and .debug_abbrev. */ if (sym.st_shndx != debug_sections[DEBUG_STR].sec + && sym.st_shndx != debug_sections[DEBUG_LINE_STR].sec && sym.st_shndx != debug_sections[DEBUG_LINE].sec && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec) continue; @@ -757,7 +774,9 @@ no_memory: form = read_uleb128 (ptr); if (form == 2 || (form > DW_FORM_flag_present && form != DW_FORM_ref_sig8 - && form != DW_FORM_implicit_const)) + && form != DW_FORM_addrx && form != DW_FORM_implicit_const + && form != DW_FORM_rnglistx + && (form < DW_FORM_strx1 || form > DW_FORM_addrx4))) { error (0, 0, "%s: Unknown DWARF abbrev DW_FORM_0x%x", dso->filename, form); @@ -1031,17 +1050,22 @@ string_find_entry (struct strings *strings, size_t old_idx) a replacement file string has been recorded for it, otherwise returns false. */ static bool -record_file_string_entry_idx (struct strings *strings, size_t old_idx) +record_file_string_entry_idx (bool is_line_str, DSO *dso, size_t old_idx) { + struct strings *strings = is_line_str ? &dso->debug_line_str + : &dso->debug_str; bool ret = false; struct stridxentry *entry = string_find_new_entry (strings, old_idx); if (entry != NULL) { - if (old_idx >= debug_sections[DEBUG_STR].size) + debug_section *sec = &debug_sections[is_line_str ? DEBUG_LINE_STR + : DEBUG_STR]; + + if (old_idx >= sec->size) error (1, 0, "Bad string pointer index %zd", old_idx); Strent *strent; - const char *old_str = (char *)debug_sections[DEBUG_STR].data + old_idx; + const char *old_str = (char *)sec->data + old_idx; const char *file = skip_dir_prefix (old_str, base_dir); if (file == NULL) { @@ -1085,15 +1109,20 @@ record_file_string_entry_idx (struct strings *strings, size_t old_idx) base_dir with dest_dir, just records the existing string associated with the index. */ static void -record_existing_string_entry_idx (struct strings *strings, size_t old_idx) +record_existing_string_entry_idx (bool is_line_str, DSO *dso, size_t old_idx) { + struct strings *strings = is_line_str ? &dso->debug_line_str + : &dso->debug_str; struct stridxentry *entry = string_find_new_entry (strings, old_idx); if (entry != NULL) { - if (old_idx >= debug_sections[DEBUG_STR].size) + debug_section *sec = &debug_sections[is_line_str ? DEBUG_LINE_STR + : DEBUG_STR]; + + if (old_idx >= sec->size) error (1, 0, "Bad string pointer index %zd", old_idx); - const char *str = (char *)debug_sections[DEBUG_STR].data + old_idx; + const char *str = (char *)sec->data + old_idx; Strent *strent = strtab_add_len (strings->str_tab, str, strlen (str) + 1); if (strent == NULL) @@ -1190,6 +1219,7 @@ get_line_table (DSO *dso, size_t off, struct line_table **table) t->size_diff = 0; t->replace_dirs = false; t->replace_files = false; + t->phase_done = -1; unsigned char *ptr = debug_sections[DEBUG_LINE].data; unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; @@ -1226,13 +1256,28 @@ get_line_table (DSO *dso, size_t off, struct line_table **table) /* version */ t->version = read_16 (ptr); - if (t->version != 2 && t->version != 3 && t->version != 4) + if (t->version != 2 && t->version != 3 && t->version != 4 && t->version != 5) { error (0, 0, "%s: DWARF version %d unhandled", dso->filename, t->version); return false; } + if (t->version >= 5) + { + /* address_size */ + assert (ptr_size != 0); + if (ptr_size != read_8 (ptr)) + { + error (0, 0, "%s: .debug_line address size differs from .debug_info", + dso->filename); + return false; + } + + /* segment_selector_size */ + (void) read_8 (ptr); + } + /* header_length */ unsigned char *endprol = ptr + 4; t->header_length = read_32 (ptr); @@ -1465,11 +1510,14 @@ edit_dwarf2_line (DSO *dso) } } -/* Record or adjust (according to phase) DW_FORM_strp. */ +/* Record or adjust (according to phase) DW_FORM_strp or DW_FORM_line_strp. */ static void -edit_strp (DSO *dso, unsigned char *ptr, int phase, bool handled_strp) +edit_strp (DSO *dso, bool is_line_str, unsigned char *ptr, int phase, + bool handled_strp) { unsigned char *ptr_orig = ptr; + struct strings *strings = is_line_str ? &dso->debug_line_str + : &dso->debug_str; /* In the first pass we collect all strings, in the second we put the new references back (if there are @@ -1482,15 +1530,16 @@ edit_strp (DSO *dso, unsigned char *ptr, int phase, bool handled_strp) if (! handled_strp) { size_t idx = do_read_32_relocated (ptr); - record_existing_string_entry_idx (&dso->strings, idx); + record_existing_string_entry_idx (is_line_str, dso, idx); } } - else if (need_strp_update) /* && phase == 1 */ + else if (is_line_str ? need_line_strp_update : need_strp_update) + /* && phase == 1 */ { struct stridxentry *entry; size_t idx, new_idx; idx = do_read_32_relocated (ptr); - entry = string_find_entry (&dso->strings, idx); + entry = string_find_entry (strings, idx); new_idx = strent_offset (entry->entry); do_write_32_relocated (ptr, new_idx); } @@ -1521,14 +1570,24 @@ skip_form (DSO *dso, uint32_t *formp, unsigned char **ptrp) case DW_FORM_ref1: case DW_FORM_flag: case DW_FORM_data1: + case DW_FORM_strx1: + case DW_FORM_addrx1: ++*ptrp; break; case DW_FORM_ref2: case DW_FORM_data2: + case DW_FORM_strx2: + case DW_FORM_addrx2: *ptrp += 2; break; + case DW_FORM_strx3: + case DW_FORM_addrx3: + *ptrp += 3; + break; case DW_FORM_ref4: case DW_FORM_data4: + case DW_FORM_strx4: + case DW_FORM_addrx4: case DW_FORM_sec_offset: *ptrp += 4; break; @@ -1537,14 +1596,23 @@ skip_form (DSO *dso, uint32_t *formp, unsigned char **ptrp) case DW_FORM_ref_sig8: *ptrp += 8; break; + case DW_FORM_data16: + *ptrp += 16; + break; case DW_FORM_sdata: case DW_FORM_ref_udata: case DW_FORM_udata: + case DW_FORM_strx: + case DW_FORM_rnglistx: + case DW_FORM_addrx: read_uleb128 (*ptrp); break; case DW_FORM_strp: *ptrp += 4; break; + case DW_FORM_line_strp: + *ptrp += 4; + break; case DW_FORM_string: *ptrp = (unsigned char *) strchr ((char *)*ptrp, '\0') + 1; break; @@ -1569,7 +1637,7 @@ skip_form (DSO *dso, uint32_t *formp, unsigned char **ptrp) assert (len < UINT_MAX); break; default: - error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, *formp); + error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename, *formp); return FORM_ERROR; } @@ -1723,12 +1791,119 @@ read_dwarf4_line (DSO *dso, unsigned char *ptr, char *comp_dir, return true; } +/* Called by read_dwarf5_line first for directories and then file names as they + both have the same format. */ +static bool +read_dwarf5_line_entries (DSO *dso, unsigned char **ptrp, + struct line_table *table, int phase, + const char *entry_name) +{ + /* directory_entry_format_count */ + /* file_name_entry_format_count */ + unsigned format_count = read_8 (*ptrp); + + unsigned char *formats = *ptrp; + + /* directory_entry_format */ + /* file_name_entry_format */ + for (unsigned formati = 0; formati < format_count; ++formati) + { + read_uleb128 (*ptrp); + read_uleb128 (*ptrp); + } + + /* directories_count */ + /* file_names_count */ + unsigned entry_count = read_uleb128 (*ptrp); + + /* directories */ + /* file_names */ + for (unsigned entryi = 0; entryi < entry_count; ++entryi) + { + unsigned char *format_ptr = formats; + for (unsigned formati = 0; formati < format_count; ++formati) + { + unsigned lnct = read_uleb128 (format_ptr); + unsigned form = read_uleb128 (format_ptr); + bool handled_strp = false; + bool is_line_str = form == DW_FORM_line_strp; + if (lnct == DW_LNCT_path) + switch (form) + { + case DW_FORM_strp: + case DW_FORM_line_strp: + if (dest_dir && phase == 0) + { + size_t idx = do_read_32_relocated (*ptrp); + if (record_file_string_entry_idx (is_line_str, dso, idx)) + *(is_line_str ? &need_line_strp_update + : &need_strp_update) = true; + handled_strp = true; + } + break; + default: + error (0, 0, "%s: Unsupported " + ".debug_line %s %u DW_FORM_0x%x", + dso->filename, entry_name, entryi, form); + return false; + } + + switch (form) + { + case DW_FORM_strp: + case DW_FORM_line_strp: + edit_strp (dso, is_line_str, *ptrp, phase, handled_strp); + break; + } + + switch (skip_form (dso, &form, ptrp)) + { + case FORM_OK: + break; + case FORM_ERROR: + return false; + case FORM_INDIRECT: + error (0, 0, "%s: Unsupported " + ".debug_line %s %u DW_FORM_indirect", + dso->filename, entry_name, entryi); + return false; + } + } + } + + return true; +} + +/* Part of read_dwarf2_line processing DWARF-5. */ +static bool +read_dwarf5_line (DSO *dso, unsigned char *ptr, struct line_table *table, + int phase) +{ + REL *relptr_saved = relptr, *relend_saved = relend; + int reltype_saved = reltype; + bool rel_updated_saved = rel_updated; + rel_updated = false; + setup_relbuf(dso, &debug_sections[DEBUG_LINE], &reltype); + + bool retval = (read_dwarf5_line_entries (dso, &ptr, table, phase, "directory") + && read_dwarf5_line_entries (dso, &ptr, table, phase, + "file name")); + + relptr = relptr_saved; + relend = relend_saved; + reltype = reltype_saved; + line_rel_updated = rel_updated; + rel_updated = rel_updated_saved; + + return retval; +} + /* Called during phase zero for each debug_line table referenced from .debug_info. Outputs all source files seen and records any adjustments needed in the debug_list data structures. Returns true if line_table needs to be rewrite either the dir or file paths. */ static bool -read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) +read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase) { unsigned char *ptr; struct line_table *table; @@ -1742,6 +1917,9 @@ read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) ptr = debug_sections[DEBUG_LINE].data + off; ptr += (4 /* unit len */ + 2 /* version */ + + (table->version < 5 ? 0 : 0 + + 1 /* address_size */ + + 1 /* segment_selector*/) + 4 /* header len */ + 1 /* min instr len */ + (table->version >= 4) /* max op per instr, if version >= 4 */ @@ -1751,8 +1929,20 @@ read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) + 1 /* opcode base */ + table->opcode_base - 1); /* opcode len table */ - if (! read_dwarf4_line (dso, ptr, comp_dir, table)) - return false; + if (phase <= table->phase_done) + return true; + table->phase_done = phase; + + if (table->version >= 5) + { + if (! read_dwarf5_line (dso, ptr, table, phase)) + return false; + } + else if (phase == 0) + { + if (! read_dwarf4_line (dso, ptr, comp_dir, table)) + return false; + } dso->lines.debug_lines_len += 4 + table->unit_length + table->size_diff; need_stmt_update = table->replace_dirs || table->replace_files; @@ -1772,20 +1962,24 @@ find_new_list_offs (struct debug_lines *lines, size_t idx) return table->new_idx; } -/* Read DW_FORM_strp collecting compilation directory. */ +/* Read DW_FORM_strp or DW_FORM_line_strp collecting compilation directory. */ static void -edit_attributes_str_comp_dir (DSO *dso, unsigned char **ptrp, int phase, - char **comp_dirp, bool *handled_strpp) +edit_attributes_str_comp_dir (bool is_line_str, DSO *dso, unsigned char **ptrp, + int phase, char **comp_dirp, bool *handled_strpp) { + debug_section *sec = &debug_sections[is_line_str ? DEBUG_LINE_STR + : DEBUG_STR]; + if (sec->data == NULL) + return; const char *dir; size_t idx = do_read_32_relocated (*ptrp); /* In phase zero we collect the comp_dir. */ if (phase == 0) { - if (idx >= debug_sections[DEBUG_STR].size) + if (idx >= sec->size) error (1, 0, "%s: Bad string pointer index %zd for comp_dir", dso->filename, idx); - dir = (char *) debug_sections[DEBUG_STR].data + idx; + dir = (char *) sec->data + idx; free (*comp_dirp); *comp_dirp = strdup (dir); @@ -1793,8 +1987,8 @@ edit_attributes_str_comp_dir (DSO *dso, unsigned char **ptrp, int phase, if (dest_dir != NULL && phase == 0) { - if (record_file_string_entry_idx (&dso->strings, idx)) - need_strp_update = true; + if (record_file_string_entry_idx (is_line_str, dso, idx)) + *(is_line_str ? &need_line_strp_update : &need_strp_update) = true; *handled_strpp = true; } } @@ -1834,9 +2028,8 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) || form == DW_FORM_sec_offset) { list_offs = do_read_32_relocated (ptr); - if (phase == 0) - found_list_offs = 1; - else if (need_stmt_update) /* phase one */ + found_list_offs = 1; + if (phase == 1 && need_stmt_update) { size_t idx, new_idx; idx = do_read_32_relocated (ptr); @@ -1902,62 +2095,82 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) } } } - else if (form == DW_FORM_strp && - debug_sections[DEBUG_STR].data) - edit_attributes_str_comp_dir (dso, &ptr, phase, &comp_dir, + else if (form == DW_FORM_strp) + edit_attributes_str_comp_dir (false /* is_line_str */, dso, + &ptr, phase, &comp_dir, &handled_strp); + else if (form == DW_FORM_line_strp) + edit_attributes_str_comp_dir (true /* is_line_str */, dso, &ptr, + phase, &comp_dir, &handled_strp); } else if ((t->tag == DW_TAG_compile_unit || t->tag == DW_TAG_partial_unit) - && t->attr[i].attr == DW_AT_name - && form == DW_FORM_strp - && debug_sections[DEBUG_STR].data) + && t->attr[i].attr == DW_AT_name) { - /* DW_AT_name is the primary file for this compile - unit. If starting with / it is a full path name. - Note that we don't handle DW_FORM_string in this - case. */ - size_t idx = do_read_32_relocated (ptr); - - /* In phase zero we will look for a comp_dir to use. */ - if (phase == 0) + if (form == DW_FORM_strp && debug_sections[DEBUG_STR].data) { - if (idx >= debug_sections[DEBUG_STR].size) - error (1, 0, - "%s: Bad string pointer index %zd for unit name", - dso->filename, idx); - char *name = (char *) debug_sections[DEBUG_STR].data + idx; - if (*name == '/' && comp_dir == NULL) - { - char *enddir = strrchr (name, '/'); + /* DW_AT_name is the primary file for this compile + unit. If starting with / it is a full path name. + Note that we don't handle DW_FORM_string in this + case. */ + size_t idx = do_read_32_relocated (ptr); - if (enddir != name) + /* In phase zero we will look for a comp_dir to use. */ + if (phase == 0) + { + if (idx >= debug_sections[DEBUG_STR].size) + error (1, 0, + "%s: Bad string pointer index %zd for unit name", + dso->filename, idx); + char *name = ((char *) debug_sections[DEBUG_STR].data + + idx); + if (*name == '/' && comp_dir == NULL) { - comp_dir = malloc (enddir - name + 1); - memcpy (comp_dir, name, enddir - name); - comp_dir [enddir - name] = '\0'; + char *enddir = strrchr (name, '/'); + + if (enddir != name) + { + comp_dir = malloc (enddir - name + 1); + memcpy (comp_dir, name, enddir - name); + comp_dir [enddir - name] = '\0'; + } + else + comp_dir = strdup ("/"); } - else - comp_dir = strdup ("/"); } - } - /* First pass (0) records the new name to be - added to the debug string pool, the second - pass (1) stores it (the new index). */ - if (dest_dir && phase == 0) + /* First pass (0) records the new name to be + added to the debug string pool, the second + pass (1) stores it (the new index). */ + if (dest_dir && phase == 0) + { + if (record_file_string_entry_idx (false /* is_line_str */, + dso, idx)) + need_strp_update = true; + handled_strp = true; + } + } + else if (form == DW_FORM_line_strp) { - if (record_file_string_entry_idx (&dso->strings, idx)) - need_strp_update = true; - handled_strp = true; + error (0, 0, "%s: Unsupported " + "%s DW_AT_name DW_FORM_line_strp", + dso->filename, + t->tag == DW_TAG_compile_unit ? "DW_TAG_compile_unit" + : "DW_TAG_partial_unit"); + return NULL; } } switch (form) { case DW_FORM_strp: - edit_strp (dso, ptr, phase, handled_strp); + edit_strp (dso, false /* is_line_str */, ptr, phase, + handled_strp); break; + case DW_FORM_line_strp: + edit_strp (dso, true /* is_line_str */, ptr, phase, handled_strp); + break; + /* DW_FORM_strx* will be registered from .debug_str_offsets. */ } switch (skip_form (dso, &form, &ptr)) @@ -2001,7 +2214,7 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) that). Note that calculating the new size and offsets is done separately (at the end of phase zero after all CUs have been scanned in dwarf2_edit). */ - if (found_list_offs && ! read_dwarf2_line (dso, list_offs, comp_dir)) + if (found_list_offs && ! read_dwarf2_line (dso, list_offs, comp_dir, phase)) { free (comp_dir); return NULL; @@ -2026,6 +2239,73 @@ line_rel_cmp (const void *a, const void *b) return 0; } +/* Update .debug_str_offsets. */ +static bool +edit_dwarf5_str_offsets (DSO *dso, int phase) +{ + if (!need_strp_update) + return true; + + debug_section *sec = &debug_sections[DEBUG_STR_OFFSETS]; + unsigned char *ptr = sec->data; + if (ptr == NULL) + return true; + + setup_relbuf(dso, sec, &reltype); + unsigned char *endsec = ptr + sec->size; + while (ptr < endsec) + { + if (ptr + 4 + 2 + 2 > endsec) + { + error (0, 0, "%s: %s CU header too small", + dso->filename, sec->name); + return false; + } + + unsigned char *endunit = ptr + 4; + endunit += read_32 (ptr); + if (endunit == ptr + 0xffffffff) + { + error (0, 0, "%s: %s: 64-bit DWARF not supported", dso->filename, + sec->name); + return false; + } + + if ((endunit - ptr) % 4 != 0) + { + error (0, 0, "%s: %s: unit size %% 4 != 0", dso->filename, sec->name); + return false; + } + + if (endunit > endsec) + { + error (0, 0, "%s: %s too small", dso->filename, sec->name); + return false; + } + + int version = read_16 (ptr); + if (version != 5) + { + error (0, 0, "%s: %s: DWARF version %d unhandled", dso->filename, + sec->name, version); + return false; + } + + int padding = read_16 (ptr); + if (padding != 0) + { + error (0, 0, "%s: %s: Padding is not zero", dso->filename, sec->name); + return false; + } + + for (; ptr < endunit; ptr += 4) + edit_strp (dso, false /* is_line_str */, ptr, phase, + false /* handled_strp - unused */); + } + dirty_section (DEBUG_STR_OFFSETS); + return true; +} + static int edit_info (DSO *dso, int phase, struct debug_section *sec, bool is_types) { @@ -2175,13 +2455,13 @@ edit_info (DSO *dso, int phase, struct debug_section *sec, bool is_types) return 0; } -/* Rebuild .debug_str. */ +/* Rebuild .debug_str or .debug_line_str. */ static void -edit_dwarf2_any_str (DSO *dso) +edit_dwarf2_any_str (DSO *dso, struct strings *strings, debug_section *secp) { - Strtab *strtab = dso->strings.str_tab; - Elf_Data *strdata = debug_sections[DEBUG_STR].elf_data; - int strndx = debug_sections[DEBUG_STR].sec; + Strtab *strtab = strings->str_tab; + Elf_Data *strdata = secp->elf_data; + int strndx = secp->sec; Elf_Scn *strscn = dso->scn[strndx]; /* Out with the old. */ @@ -2193,8 +2473,8 @@ edit_dwarf2_any_str (DSO *dso) but the old ebl version will just abort on out of memory... */ strtab_finalize (strtab, strdata); - debug_sections[DEBUG_STR].size = strdata->d_size; - dso->strings.str_buf = strdata->d_buf; + secp->size = strdata->d_size; + strings->str_buf = strdata->d_buf; } static int @@ -2342,12 +2622,14 @@ edit_dwarf2 (DSO *dso) bool info_rel_updated = false; bool types_rel_updated = false; bool macro_rel_updated = false; + line_rel_updated = false; for (phase = 0; phase < 2; phase++) { /* If we don't need to update anyhing, skip phase 1. */ if (phase == 1 && !need_strp_update + && !need_line_strp_update && !need_string_replacement && !need_stmt_update) break; @@ -2570,15 +2852,15 @@ edit_dwarf2 (DSO *dso) if (phase == 0) { size_t idx = read_32_relocated (ptr); - record_existing_string_entry_idx (&dso->strings, - idx); + record_existing_string_entry_idx + (false /* is_line_str */, dso, idx); } else { struct stridxentry *entry; size_t idx, new_idx; idx = do_read_32_relocated (ptr); - entry = string_find_entry (&dso->strings, idx); + entry = string_find_entry (&dso->debug_str, idx); new_idx = strent_offset (entry->entry); write_32_relocated (ptr, new_idx); } @@ -2598,24 +2880,30 @@ edit_dwarf2 (DSO *dso) } } + if (! edit_dwarf5_str_offsets (dso, phase)) + return 1; + /* Same for the debug_str section. Make sure everything is in place for phase 1 updating of debug_info references. */ if (phase == 0 && need_strp_update) - edit_dwarf2_any_str (dso); - + edit_dwarf2_any_str (dso, &dso->debug_str, &debug_sections[DEBUG_STR]); + if (phase == 0 && need_line_strp_update) + edit_dwarf2_any_str (dso, &dso->debug_line_str, + &debug_sections[DEBUG_LINE_STR]); } /* After phase 1 we might have rewritten the debug_info with new strp, strings and/or linep offsets. */ - if (need_strp_update || need_string_replacement || need_stmt_update) { + if (need_strp_update || need_line_strp_update || need_string_replacement + || need_stmt_update) { dirty_section (DEBUG_INFO); if (debug_sections[DEBUG_TYPES].data != NULL) dirty_section (DEBUG_TYPES); } if (need_strp_update || need_stmt_update) dirty_section (DEBUG_MACRO); - if (need_stmt_update) + if (need_stmt_update || need_line_strp_update) dirty_section (DEBUG_LINE); /* Update any relocations addends we might have touched. */ @@ -2648,6 +2936,9 @@ edit_dwarf2 (DSO *dso) } } + if (line_rel_updated) + update_rela_data (dso, &debug_sections[DEBUG_LINE]); + return 0; } @@ -2740,7 +3031,8 @@ fdopen_dso (int fd, const char *name) } dso->filename = (const char *) strdup (name); - setup_strings (&dso->strings); + setup_strings (&dso->debug_str); + setup_strings (&dso->debug_line_str); setup_lines (&dso->lines); return dso; @@ -2748,7 +3040,8 @@ error_out: if (dso) { free ((char *) dso->filename); - destroy_strings (&dso->strings); + destroy_strings (&dso->debug_str); + destroy_strings (&dso->debug_line_str); destroy_lines (&dso->lines); free (dso); } @@ -3032,7 +3325,8 @@ main (int argc, char *argv[]) in elfutils before 0.169 we will have to update and write out all section data if any data has changed (when ELF_F_LAYOUT was set). https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */ - bool need_update = need_strp_update || need_stmt_update; + bool need_update = (need_strp_update || need_line_strp_update + || need_stmt_update); #if !_ELFUTILS_PREREQ (0, 169) /* string replacements or build_id updates don't change section size. */ @@ -3110,6 +3404,8 @@ main (int argc, char *argv[]) sec_size = debug_sections[DEBUG_STR].size; if (secnum == debug_sections[DEBUG_LINE].sec) sec_size = debug_sections[DEBUG_LINE].size; + if (secnum == debug_sections[DEBUG_LINE_STR].sec) + sec_size = debug_sections[DEBUG_LINE_STR].size; /* Zero means one. No alignment constraints. */ size_t addralign = shdr->sh_addralign ?: 1; @@ -3177,7 +3473,8 @@ main (int argc, char *argv[]) chmod (file, stat_buf.st_mode); free ((char *) dso->filename); - destroy_strings (&dso->strings); + destroy_strings (&dso->debug_str); + destroy_strings (&dso->debug_line_str); destroy_lines (&dso->lines); free (dso);
_______________________________________________ Rpm-maint mailing list Rpm-maint@lists.rpm.org http://lists.rpm.org/mailman/listinfo/rpm-maint