[Rpm-maint] [PATCH 5/5] debugedit: Implement DWARF-5.
Jan Kratochvil
jan.kratochvil at redhat.com
Wed Aug 12 22:38:10 UTC 2020
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);
More information about the Rpm-maint
mailing list