summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdw/ChangeLog8
-rw-r--r--libdw/dwarf.h11
-rw-r--r--libdw/dwarf_getsrclines.c437
-rw-r--r--libdw/libdwP.h38
-rw-r--r--tests/ChangeLog10
-rw-r--r--tests/Makefile.am2
-rwxr-xr-xtests/run-readelf-line.sh163
-rwxr-xr-xtests/testfile-dwarf-4.bz2bin0 -> 4304 bytes
-rw-r--r--tests/testfile-dwarf-45.source81
-rwxr-xr-xtests/testfile-dwarf-5.bz2bin0 -> 4318 bytes
10 files changed, 646 insertions, 104 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 9ec493fb..cc7eae9d 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,11 @@
+2018-05-05 Mark Wielaard <[email protected]>
+
+ * dwarf.h: Add DWARF line content descriptions.
+ * libdwP.h (libdw_valid_user_form): New static function.
+ * dwarf_getsrclines.c (read_srclines): Check and parse version 5
+ DWARF header, dir and file tables separately from older versions
+ where different.
+
2018-04-24 Mark Wielaard <[email protected]>
* dwarf_begin_elf.c (dwarf_scnnames): Add ".debug_line_str".
diff --git a/libdw/dwarf.h b/libdw/dwarf.h
index 99cc1128..fc9801bd 100644
--- a/libdw/dwarf.h
+++ b/libdw/dwarf.h
@@ -783,6 +783,17 @@ enum
DW_DEFAULTED_out_of_class = 2
};
+/* DWARF line content descriptions. */
+enum
+ {
+ DW_LNCT_path = 0x1,
+ DW_LNCT_directory_index = 0x2,
+ DW_LNCT_timestamp = 0x3,
+ DW_LNCT_size = 0x4,
+ DW_LNCT_MD5 = 0x5,
+ DW_LNCT_lo_user = 0x2000,
+ DW_LNCT_hi_user = 0x3fff
+ };
/* DWARF standard opcode encodings. */
enum
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
index d02c38db..2f966ab7 100644
--- a/libdw/dwarf_getsrclines.c
+++ b/libdw/dwarf_getsrclines.c
@@ -1,7 +1,6 @@
/* Return line number information of CU.
- Copyright (C) 2004-2010, 2013, 2014, 2015, 2016 Red Hat, Inc.
+ Copyright (C) 2004-2010, 2013, 2014, 2015, 2016, 2018 Red Hat, Inc.
This file is part of elfutils.
- Written by Ulrich Drepper <[email protected]>, 2004.
This file is free software; you can redistribute it and/or modify
it under the terms of either
@@ -157,18 +156,6 @@ read_srclines (Dwarf *dbg,
size_t nfilelist = 0;
unsigned int ndirlist = 0;
- struct filelist null_file =
- {
- .info =
- {
- .name = "???",
- .mtime = 0,
- .length = 0
- },
- .next = NULL
- };
- struct filelist *filelist = &null_file;
-
/* If there are a large number of lines, files or dirs don't blow up
the stack. Stack allocate some entries, only dynamically malloc
when more than MAX. */
@@ -177,16 +164,7 @@ read_srclines (Dwarf *dbg,
#define MAX_STACK_FILES (MAX_STACK_ALLOC / 4)
#define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16)
- struct dirlist
- {
- const char *dir;
- size_t len;
- };
- struct dirlist dirstack[MAX_STACK_DIRS];
- struct dirlist *dirarray = dirstack;
-
- /* We are about to process the statement program. Initialize the
- state machine registers (see 6.2.2 in the v2.1 specification). */
+ /* Initial statement program state (except for stmt_list, see below). */
struct line_state state =
{
.linelist = NULL,
@@ -222,25 +200,45 @@ read_srclines (Dwarf *dbg,
}
/* 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);
- if (unlikely (version < 2) || unlikely (version > 4))
+ if (unlikely (version < 2) || unlikely (version > 5))
{
__libdw_seterrno (DWARF_E_VERSION);
goto out;
}
+ /* DWARF5 explicitly lists address and segment_selector sizes. */
+ if (version >= 5)
+ {
+ if ((size_t) (lineendp - linep) < 2)
+ goto invalid_data;
+ size_t line_address_size = *linep++;
+ size_t segment_selector_size = *linep++;
+ if (line_address_size != address_size || segment_selector_size != 0)
+ goto invalid_data;
+ }
+
/* 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);
+ {
+ if ((size_t) (lineendp - linep) < 8)
+ goto invalid_data;
+ header_length = read_8ubyte_unaligned_inc (dbg, linep);
+ }
const unsigned char *header_start = linep;
/* Next the minimum instruction length. */
@@ -250,13 +248,17 @@ read_srclines (Dwarf *dbg,
uint_fast8_t max_ops_per_instr = 1;
if (version >= 4)
{
- if (unlikely (lineendp - linep < 5))
+ if (unlikely ((size_t) (lineendp - linep) < 1))
goto invalid_data;
max_ops_per_instr = *linep++;
if (unlikely (max_ops_per_instr == 0))
goto invalid_data;
}
+ /* 4 more bytes, is_stmt, line_base, line_range and opcode_base. */
+ if ((size_t) (lineendp - linep) < 4)
+ goto invalid_data;
+
/* Then the flag determining the default value of the is_stmt
register. */
uint_fast8_t default_is_stmt = *linep++;
@@ -277,29 +279,84 @@ read_srclines (Dwarf *dbg,
goto invalid_data;
linep += opcode_base - 1;
- /* First comes the list of directories. Add the compilation
- directory first since the index zero is used for it. */
- struct dirlist comp_dir_elem =
- {
- .dir = comp_dir,
- .len = comp_dir ? strlen (comp_dir) : 0,
- };
- ndirlist = 1;
+ /* To read DWARF5 dir and file lists we need to know the forms. For
+ now we skip everything, except the DW_LNCT_path and
+ DW_LNCT_directory_index. */
+ uint16_t forms[256];
+ unsigned char nforms = 0;
+ unsigned char form_path = -1; /* Which forms is DW_LNCT_path. */
+ unsigned char form_idx = -1; /* And which is DW_LNCT_directory_index. */
+
+ /* To read/skip form data. */
+ Dwarf_CU fake_cu = {
+ .dbg = dbg,
+ .sec_idx = IDX_debug_line,
+ .version = 5,
+ .offset_size = length,
+ .address_size = address_size,
+ .startp = (void *) linep,
+ .endp = (void *) lineendp,
+ };
/* First count the entries. */
- const unsigned char *dirp = linep;
unsigned int ndirs = 0;
- while (*dirp != 0)
+ if (version < 5)
{
- uint8_t *endp = memchr (dirp, '\0', lineendp - dirp);
- if (endp == NULL)
+ const unsigned char *dirp = linep;
+ while (*dirp != 0)
+ {
+ uint8_t *endp = memchr (dirp, '\0', lineendp - dirp);
+ if (endp == NULL)
+ goto invalid_data;
+ ++ndirs;
+ dirp = endp + 1;
+ }
+ ndirs = ndirs + 1; /* There is always the "unknown" dir. */
+ }
+ else
+ {
+ if ((size_t) (lineendp - linep) < 1)
+ goto invalid_data;
+ nforms = *linep++;
+ for (int i = 0; i < nforms; 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);
+
+ if (! libdw_valid_user_form (form))
+ goto invalid_data;
+
+ forms[i] = form;
+ if (desc == DW_LNCT_path)
+ form_path = i;
+ }
+
+ if (nforms > 0 && form_path == (unsigned char) -1)
+ goto invalid_data;
+
+ if ((size_t) (lineendp - linep) < 1)
+ goto invalid_data;
+ get_uleb128 (ndirs, linep, lineendp);
+
+ if (nforms == 0 && ndirs != 0)
goto invalid_data;
- ++ndirs;
- dirp = endp + 1;
}
- ndirlist += ndirs;
+
+ struct dirlist
+ {
+ const char *dir;
+ size_t len;
+ };
+ struct dirlist dirstack[MAX_STACK_DIRS];
+ struct dirlist *dirarray = dirstack;
/* Arrange the list in array form. */
+ ndirlist = ndirs;
if (ndirlist >= MAX_STACK_DIRS)
{
dirarray = (struct dirlist *) malloc (ndirlist * sizeof (*dirarray));
@@ -310,20 +367,82 @@ read_srclines (Dwarf *dbg,
goto out;
}
}
- dirarray[0] = comp_dir_elem;
- for (unsigned int n = 1; n < ndirlist; n++)
+
+ /* Entry zero is implicit for older versions, but explicit for 5+. */
+ struct dirlist comp_dir_elem;
+ if (version < 5)
{
- dirarray[n].dir = (char *) linep;
- uint8_t *endp = memchr (linep, '\0', lineendp - linep);
- assert (endp != NULL);
- dirarray[n].len = endp - linep;
- linep = endp + 1;
+ /* First comes the list of directories. Add the compilation
+ directory first since the index zero is used for it. */
+ comp_dir_elem.dir = comp_dir;
+ comp_dir_elem.len = comp_dir ? strlen (comp_dir) : 0,
+ dirarray[0] = comp_dir_elem;
+ for (unsigned int n = 1; n < ndirlist; n++)
+ {
+ dirarray[n].dir = (char *) linep;
+ uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+ assert (endp != NULL);
+ dirarray[n].len = endp - linep;
+ linep = endp + 1;
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
}
- /* Skip the final NUL byte. */
- ++linep;
+ else
+ {
+ Dwarf_Attribute attr;
+ attr.code = DW_AT_name;
+ attr.cu = &fake_cu;
+ for (unsigned int n = 0; n < ndirlist; n++)
+ {
+ const char *dir = NULL;
+ for (unsigned char m = 0; m < nforms; m++)
+ {
+ if (m == form_path)
+ {
+ attr.form = forms[m];
+ attr.valp = (void *) linep;
+ dir = dwarf_formstring (&attr);
+ }
+
+ size_t len = __libdw_form_val_len (&fake_cu, forms[m], linep);
+ if ((size_t) (lineendp - linep) < len)
+ goto invalid_data;
+
+ linep += len;
+ }
+
+ if (dir == NULL)
+ goto invalid_data;
+
+ dirarray[n].dir = dir;
+ dirarray[n].len = strlen (dir);
+ }
+ }
+
+ /* File index zero doesn't exist for DWARF < 5. Files are indexed
+ starting from 1. But for DWARF5 they are indexed starting from
+ zero, but the default index is still 1. In both cases the
+ "first" file is special and refers to the main compile unit file,
+ equal to the DW_AT_name of the DW_TAG_compile_unit. */
+ struct filelist null_file =
+ {
+ .info =
+ {
+ .name = "???",
+ .mtime = 0,
+ .length = 0
+ },
+ .next = NULL
+ };
+ struct filelist *filelist = &null_file;
+ nfilelist = 1;
/* Allocate memory for a new file. For the first MAX_STACK_FILES
- entries just return a slot in the preallocated stack array. */
+ entries just return a slot in the preallocated stack array.
+ This is slightly complicated because in DWARF < 5 new files could
+ be defined with DW_LNE_define_file after the normal file list was
+ read. */
struct filelist flstack[MAX_STACK_FILES];
#define NEW_FILE() ({ \
struct filelist *fl = (nfilelist < MAX_STACK_FILES \
@@ -337,69 +456,176 @@ read_srclines (Dwarf *dbg,
fl; })
/* Now read the files. */
- nfilelist = 1;
-
- if (unlikely (linep >= lineendp))
- goto invalid_data;
- while (*linep != 0)
+ if (version < 5)
{
- struct filelist *new_file = NEW_FILE ();
-
- /* First comes the file name. */
- char *fname = (char *) linep;
- uint8_t *endp = memchr (fname, '\0', lineendp - linep);
- if (endp == NULL)
- goto invalid_data;
- size_t fnamelen = endp - (uint8_t *) fname;
- linep = endp + 1;
-
- /* Then the index. */
- Dwarf_Word diridx;
if (unlikely (linep >= lineendp))
goto invalid_data;
- get_uleb128 (diridx, linep, lineendp);
- if (unlikely (diridx >= ndirlist))
+ while (*linep != 0)
{
- __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
- goto out;
- }
+ struct filelist *new_file = NEW_FILE ();
- if (*fname == '/')
- /* It's an absolute path. */
- new_file->info.name = fname;
- else
- {
- new_file->info.name = libdw_alloc (dbg, char, 1,
- dirarray[diridx].len + 1
- + fnamelen + 1);
- char *cp = new_file->info.name;
+ /* First comes the file name. */
+ char *fname = (char *) linep;
+ uint8_t *endp = memchr (fname, '\0', lineendp - linep);
+ if (endp == NULL)
+ goto invalid_data;
+ size_t fnamelen = endp - (uint8_t *) fname;
+ linep = endp + 1;
- if (dirarray[diridx].dir != NULL)
+ /* Then the index. */
+ Dwarf_Word diridx;
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (diridx, linep, lineendp);
+ if (unlikely (diridx >= ndirlist))
{
- /* This value could be NULL in case the DW_AT_comp_dir
- was not present. We cannot do much in this case.
- The easiest thing is to convert the path in an
- absolute path. */
- cp = stpcpy (cp, dirarray[diridx].dir);
+ __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
+ goto out;
}
- *cp++ = '/';
- strcpy (cp, fname);
- assert (strlen (new_file->info.name)
- < dirarray[diridx].len + 1 + fnamelen + 1);
+
+ if (*fname == '/')
+ /* It's an absolute path. */
+ new_file->info.name = fname;
+ else
+ {
+ new_file->info.name = libdw_alloc (dbg, char, 1,
+ dirarray[diridx].len + 1
+ + fnamelen + 1);
+ char *cp = new_file->info.name;
+
+ if (dirarray[diridx].dir != NULL)
+ {
+ /* This value could be NULL in case the DW_AT_comp_dir
+ was not present. We cannot do much in this case.
+ The easiest thing is to convert the path in an
+ absolute path. */
+ cp = stpcpy (cp, dirarray[diridx].dir);
+ }
+ *cp++ = '/';
+ strcpy (cp, fname);
+ assert (strlen (new_file->info.name)
+ < dirarray[diridx].len + 1 + fnamelen + 1);
+ }
+
+ /* Next comes the modification time. */
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (new_file->info.mtime, linep, lineendp);
+
+ /* Finally the length of the file. */
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (new_file->info.length, linep, lineendp);
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
+ }
+ else
+ {
+ if ((size_t) (lineendp - linep) < 1)
+ goto invalid_data;
+ nforms = *linep++;
+ form_path = form_idx = -1;
+ for (int i = 0; i < nforms; 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);
+
+ if (! libdw_valid_user_form (form))
+ goto invalid_data;
+
+ forms[i] = form;
+ if (desc == DW_LNCT_path)
+ form_path = i;
+ else if (desc == DW_LNCT_directory_index)
+ form_idx = i;
}
- /* Next comes the modification time. */
- if (unlikely (linep >= lineendp))
+ if (nforms > 0 && (form_path == (unsigned char) -1
+ || form_idx == (unsigned char) -1))
goto invalid_data;
- get_uleb128 (new_file->info.mtime, linep, lineendp);
- /* Finally the length of the file. */
- if (unlikely (linep >= lineendp))
+ size_t nfiles;
+ get_uleb128 (nfiles, linep, lineendp);
+
+ if (nforms == 0 && nfiles != 0)
goto invalid_data;
- get_uleb128 (new_file->info.length, linep, lineendp);
+
+ Dwarf_Attribute attr;
+ attr.cu = &fake_cu;
+ for (unsigned int n = 0; n < nfiles; n++)
+ {
+ const char *fname = NULL;
+ Dwarf_Word diridx = -1;
+ for (unsigned char m = 0; m < nforms; m++)
+ {
+ if (m == form_path)
+ {
+ attr.code = DW_AT_name;
+ attr.form = forms[m];
+ attr.valp = (void *) linep;
+ fname = dwarf_formstring (&attr);
+ }
+ else if (m == form_idx)
+ {
+ attr.code = DW_AT_decl_file; /* Close enough. */
+ attr.form = forms[m];
+ attr.valp = (void *) linep;
+ dwarf_formudata (&attr, &diridx);
+ }
+
+ size_t len = __libdw_form_val_len (&fake_cu, forms[m], linep);
+ if ((size_t) (lineendp - linep) < len)
+ goto invalid_data;
+
+ linep += len;
+ }
+
+ if (fname == NULL || diridx == (Dwarf_Word) -1)
+ goto invalid_data;
+
+ size_t fnamelen = strlen (fname);
+
+ if (unlikely (diridx >= ndirlist))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
+ goto out;
+ }
+
+ /* Yes, weird. Looks like an off-by-one in the spec. */
+ struct filelist *new_file = n == 0 ? &null_file : NEW_FILE ();
+
+ /* We follow the same rules as above for DWARF < 5, even
+ though the standard doesn't explicitly mention absolute
+ paths and ignoring the dir index. */
+ if (*fname == '/')
+ /* It's an absolute path. */
+ new_file->info.name = (char *) fname;
+ else
+ {
+ new_file->info.name = libdw_alloc (dbg, char, 1,
+ dirarray[diridx].len + 1
+ + fnamelen + 1);
+ char *cp = new_file->info.name;
+
+ /* In the DWARF >= 5 case, dir can never be NULL. */
+ cp = stpcpy (cp, dirarray[diridx].dir);
+ *cp++ = '/';
+ strcpy (cp, fname);
+ assert (strlen (new_file->info.name)
+ < dirarray[diridx].len + 1 + fnamelen + 1);
+ }
+
+ /* For now we just ignore the modification time and file length. */
+ new_file->info.mtime = 0;
+ new_file->info.length = 0;
+ }
}
- /* Skip the final NUL byte. */
- ++linep;
/* Consistency check. */
if (unlikely (linep != header_start + header_length))
@@ -408,6 +634,9 @@ read_srclines (Dwarf *dbg,
goto out;
}
+ /* We are about to process the statement program. Most state machine
+ registers have already been initialize above. Just add the is_stmt
+ default. See 6.2.2 in the v2.1 specification. */
state.is_stmt = default_is_stmt;
/* Apply the "operation advance" from a special opcode or
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 4de0f6c3..751206da 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -493,6 +493,44 @@ libdw_macro_nforms (Dwarf_Macro *macro)
return macro->table->table[macro->table->opcodes[macro->opcode - 1]].nforms;
}
+/* Returns true for any allowed FORM in the opcode_operands_table as
+ mentioned in the DWARF5 spec (6.3.1 Macro Information Header).
+ Or those mentioned in DWARF5 spec (6.2.4.2 Vendor-defined Content
+ Descriptions) for the directory/file table (plus DW_FORM_strp_sup). */
+static inline bool
+libdw_valid_user_form (int form)
+{
+ switch (form)
+ {
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_data16:
+ case DW_FORM_flag:
+ case DW_FORM_line_strp:
+ case DW_FORM_sdata:
+ case DW_FORM_sec_offset:
+ case DW_FORM_string:
+ case DW_FORM_strp:
+ case DW_FORM_strp_sup:
+ case DW_FORM_strx:
+ case DW_FORM_strx1:
+ case DW_FORM_strx2:
+ case DW_FORM_strx3:
+ case DW_FORM_strx4:
+ case DW_FORM_udata:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
/* We have to include the file at this point because the inline
functions access internals of the Dwarf structure. */
#include "memory-access.h"
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 14159d80..8a098b4c 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,13 @@
+2018-05-05 Mark Wielaard <[email protected]>
+
+ * testfile-dwarf-45.source: New file.
+ * testfile-dwarf-4.bz2: New test file.
+ * testfile-dwarf-5.bz2: Likewise.
+ * run-readelf-line.sh: Add testcases for testfile-dwarf-4 and
+ testfile-dwarf-5.
+ * Makefile (EXTRA_DIST): Add testfile-dwarf-45.source,
+ testfile-dwarf-4.bz2 and testfile-dwarf-5.bz2.
+
2018-04-19 Andreas Schwab <[email protected]>
* hello_riscv64.ko.bz2: New file.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6b1356e9..2f9ae237 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -231,6 +231,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
run-readelf-loc.sh testfileloc.bz2 \
run-readelf-aranges.sh run-readelf-line.sh testfilefoobarbaz.bz2 \
testfile-ppc64-min-instr.bz2 \
+ testfile-dwarf-45.source \
+ testfile-dwarf-4.bz2 testfile-dwarf-5.bz2 \
run-readelf-z.sh \
run-readelf-dwz-multi.sh libtestfile_multi_shared.so.bz2 \
testfile_multi.dwz.bz2 testfile_multi_main.bz2 \
diff --git a/tests/run-readelf-line.sh b/tests/run-readelf-line.sh
index f93bf47d..b08752ee 100755
--- a/tests/run-readelf-line.sh
+++ b/tests/run-readelf-line.sh
@@ -345,4 +345,167 @@ DWARF section [29] '.debug_line' at offset 0xdf6:
8:3 S * 0 0 0 0x00000000100005f7 <main+0x53>
EOF
+
+# Two tests for the same code but encoded using DWARF4 or DWARF5.
+# Output is identical except for the section offset and CU numbers.
+# See tests/testfile-dwarf-45.source.
+
+testfiles testfile-dwarf-4 testfile-dwarf-5
+
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=decodedline testfile-dwarf-4 << \EOF
+
+DWARF section [29] '.debug_line' at offset 0x1734:
+
+ CU [b] hello.c
+ line:col SBPE* disc isa op address (Statement Block Prologue Epilogue *End)
+ /var/tmp/hello/hello.c (mtime: 0, length: 0)
+ 21:0 S 0 0 0 0x0000000000400510 <foo>
+ 22:1 S 0 0 0 0x0000000000400510 <foo>
+ 22:3 0 0 0 0x0000000000400510 <foo>
+ 25:6 0 0 0 0x0000000000400514 <foo+0x4>
+ 25:34 S 0 0 0 0x000000000040051a <foo+0xa>
+ 25:3 0 0 0 0x000000000040051a <foo+0xa>
+ 26:34 0 0 0 0x000000000040051e <foo+0xe>
+ 25:1 1 0 0 0x0000000000400528 <foo+0x18>
+ /var/tmp/hello/hello.h (mtime: 0, length: 0)
+ 7:18 S 0 0 0 0x000000000040052b <foo+0x1b>
+ 9:3 S 0 0 0 0x000000000040052b <foo+0x1b>
+ 9:3 0 0 0 0x000000000040052b <foo+0x1b>
+ 10:6 S 0 0 0 0x000000000040052f <foo+0x1f>
+ 10:5 0 0 0 0x000000000040052f <foo+0x1f>
+ 12:7 S 0 0 0 0x0000000000400531 <foo+0x21>
+ /var/tmp/hello/hello.c (mtime: 0, length: 0)
+ 10:3 S 0 0 0 0x0000000000400531 <foo+0x21>
+ 12:3 S 0 0 0 0x0000000000400531 <foo+0x21>
+ 12:3 0 0 0 0x0000000000400531 <foo+0x21>
+ 13:6 S 0 0 0 0x0000000000400535 <foo+0x25>
+ 13:5 0 0 0 0x0000000000400535 <foo+0x25>
+ 15:7 S 0 0 0 0x0000000000400539 <foo+0x29>
+ 22:3 S 0 0 0 0x0000000000400539 <foo+0x29>
+ 22:3 0 0 0 0x0000000000400539 <foo+0x29>
+ 23:6 S 0 0 0 0x000000000040053d <foo+0x2d>
+ 23:5 0 0 0 0x000000000040053d <foo+0x2d>
+ 9:12 S 0 0 0 0x0000000000400550 <baz>
+ 10:1 S 0 0 0 0x0000000000400550 <baz>
+ 12:3 S 0 0 0 0x0000000000400550 <baz>
+ 12:3 0 0 0 0x0000000000400550 <baz>
+ 13:9 0 0 0 0x0000000000400556 <baz+0x6>
+ 15:7 S 0 0 0 0x000000000040055f <baz+0xf>
+ 15:3 0 0 0 0x000000000040055f <baz+0xf>
+ 15:7 * 0 0 0 0x0000000000400560 <baz+0x10>
+
+ CU [21c] world.c
+ line:col SBPE* disc isa op address (Statement Block Prologue Epilogue *End)
+ /var/tmp/hello/world.c (mtime: 0, length: 0)
+ 15:0 S 0 0 0 0x0000000000400410 <main>
+ 16:1 S 0 0 0 0x0000000000400410 <main>
+ 17:3 S 0 0 0 0x0000000000400410 <main>
+ 15:3 0 0 0 0x0000000000400410 <main>
+ 17:1 0 0 0 0x0000000000400419 <main+0x9>
+ 18:6 S 0 0 0 0x000000000040041e <main+0xe>
+ 18:5 0 0 0 0x000000000040041e <main+0xe>
+ 22:7 S 0 0 0 0x0000000000400421 <main+0x11>
+ 22:3 S * 0 0 0 0x000000000040042f <main+0x1f>
+
+ 6:0 S 0 0 0 0x0000000000400570 <calc>
+ 7:1 S 0 0 0 0x0000000000400570 <calc>
+ 7:3 0 0 0 0x0000000000400570 <calc>
+ 7:6 1 0 0 0x0000000000400575 <calc+0x5>
+ 7:24 0 0 0 0x0000000000400578 <calc+0x8>
+ 10:17 S 0 0 0 0x000000000040057d <calc+0xd>
+ 10:3 0 0 0 0x000000000040057d <calc+0xd>
+ /var/tmp/hello/hello.h (mtime: 0, length: 0)
+ 10:10 0 0 0 0x0000000000400583 <calc+0x13>
+ /var/tmp/hello/world.c (mtime: 0, length: 0)
+ 10:7 0 0 0 0x0000000000400585 <calc+0x15>
+ /var/tmp/hello/hello.h (mtime: 0, length: 0)
+ 7:10 S 0 0 0 0x0000000000400588 <calc+0x18>
+ 9:3 S 0 0 0 0x0000000000400588 <calc+0x18>
+ 10:3 0 0 0 0x0000000000400588 <calc+0x18>
+ 12:7 S 0 0 0 0x000000000040058f <calc+0x1f>
+ 12:3 0 0 0 0x000000000040058f <calc+0x1f>
+ /var/tmp/hello/world.c (mtime: 0, length: 0)
+ 11:10 0 0 0 0x0000000000400598 <calc+0x28>
+ 11:1 * 0 0 0 0x000000000040059a <calc+0x2a>
+
+EOF
+
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=decodedline testfile-dwarf-5 << \EOF
+
+DWARF section [29] '.debug_line' at offset 0x171f:
+
+ CU [c] hello.c
+ line:col SBPE* disc isa op address (Statement Block Prologue Epilogue *End)
+ /var/tmp/hello/hello.c (mtime: 0, length: 0)
+ 21:0 S 0 0 0 0x0000000000400510 <foo>
+ 22:1 S 0 0 0 0x0000000000400510 <foo>
+ 22:3 0 0 0 0x0000000000400510 <foo>
+ 25:6 0 0 0 0x0000000000400514 <foo+0x4>
+ 25:34 S 0 0 0 0x000000000040051a <foo+0xa>
+ 25:3 0 0 0 0x000000000040051a <foo+0xa>
+ 26:34 0 0 0 0x000000000040051e <foo+0xe>
+ 25:1 1 0 0 0x0000000000400528 <foo+0x18>
+ /var/tmp/hello/hello.h (mtime: 0, length: 0)
+ 7:18 S 0 0 0 0x000000000040052b <foo+0x1b>
+ 9:3 S 0 0 0 0x000000000040052b <foo+0x1b>
+ 9:3 0 0 0 0x000000000040052b <foo+0x1b>
+ 10:6 S 0 0 0 0x000000000040052f <foo+0x1f>
+ 10:5 0 0 0 0x000000000040052f <foo+0x1f>
+ 12:7 S 0 0 0 0x0000000000400531 <foo+0x21>
+ /var/tmp/hello/hello.c (mtime: 0, length: 0)
+ 10:3 S 0 0 0 0x0000000000400531 <foo+0x21>
+ 12:3 S 0 0 0 0x0000000000400531 <foo+0x21>
+ 12:3 0 0 0 0x0000000000400531 <foo+0x21>
+ 13:6 S 0 0 0 0x0000000000400535 <foo+0x25>
+ 13:5 0 0 0 0x0000000000400535 <foo+0x25>
+ 15:7 S 0 0 0 0x0000000000400539 <foo+0x29>
+ 22:3 S 0 0 0 0x0000000000400539 <foo+0x29>
+ 22:3 0 0 0 0x0000000000400539 <foo+0x29>
+ 23:6 S 0 0 0 0x000000000040053d <foo+0x2d>
+ 23:5 0 0 0 0x000000000040053d <foo+0x2d>
+ 9:12 S 0 0 0 0x0000000000400550 <baz>
+ 10:1 S 0 0 0 0x0000000000400550 <baz>
+ 12:3 S 0 0 0 0x0000000000400550 <baz>
+ 12:3 0 0 0 0x0000000000400550 <baz>
+ 13:9 0 0 0 0x0000000000400556 <baz+0x6>
+ 15:7 S 0 0 0 0x000000000040055f <baz+0xf>
+ 15:3 0 0 0 0x000000000040055f <baz+0xf>
+ 15:7 * 0 0 0 0x0000000000400560 <baz+0x10>
+
+ CU [218] world.c
+ line:col SBPE* disc isa op address (Statement Block Prologue Epilogue *End)
+ /var/tmp/hello/world.c (mtime: 0, length: 0)
+ 15:0 S 0 0 0 0x0000000000400410 <main>
+ 16:1 S 0 0 0 0x0000000000400410 <main>
+ 17:3 S 0 0 0 0x0000000000400410 <main>
+ 15:3 0 0 0 0x0000000000400410 <main>
+ 17:1 0 0 0 0x0000000000400419 <main+0x9>
+ 18:6 S 0 0 0 0x000000000040041e <main+0xe>
+ 18:5 0 0 0 0x000000000040041e <main+0xe>
+ 22:7 S 0 0 0 0x0000000000400421 <main+0x11>
+ 22:3 S * 0 0 0 0x000000000040042f <main+0x1f>
+
+ 6:0 S 0 0 0 0x0000000000400570 <calc>
+ 7:1 S 0 0 0 0x0000000000400570 <calc>
+ 7:3 0 0 0 0x0000000000400570 <calc>
+ 7:6 1 0 0 0x0000000000400575 <calc+0x5>
+ 7:24 0 0 0 0x0000000000400578 <calc+0x8>
+ 10:17 S 0 0 0 0x000000000040057d <calc+0xd>
+ 10:3 0 0 0 0x000000000040057d <calc+0xd>
+ /var/tmp/hello/hello.h (mtime: 0, length: 0)
+ 10:10 0 0 0 0x0000000000400583 <calc+0x13>
+ /var/tmp/hello/world.c (mtime: 0, length: 0)
+ 10:7 0 0 0 0x0000000000400585 <calc+0x15>
+ /var/tmp/hello/hello.h (mtime: 0, length: 0)
+ 7:10 S 0 0 0 0x0000000000400588 <calc+0x18>
+ 9:3 S 0 0 0 0x0000000000400588 <calc+0x18>
+ 10:3 0 0 0 0x0000000000400588 <calc+0x18>
+ 12:7 S 0 0 0 0x000000000040058f <calc+0x1f>
+ 12:3 0 0 0 0x000000000040058f <calc+0x1f>
+ /var/tmp/hello/world.c (mtime: 0, length: 0)
+ 11:10 0 0 0 0x0000000000400598 <calc+0x28>
+ 11:1 * 0 0 0 0x000000000040059a <calc+0x2a>
+
+EOF
+
exit 0
diff --git a/tests/testfile-dwarf-4.bz2 b/tests/testfile-dwarf-4.bz2
new file mode 100755
index 00000000..fd3aaea5
--- /dev/null
+++ b/tests/testfile-dwarf-4.bz2
Binary files differ
diff --git a/tests/testfile-dwarf-45.source b/tests/testfile-dwarf-45.source
new file mode 100644
index 00000000..c9c44463
--- /dev/null
+++ b/tests/testfile-dwarf-45.source
@@ -0,0 +1,81 @@
+# Nonsensical program used to generate an example DWARF4 and DWARF5 file.
+# The generated code is the same, but the DWARF representation is different.
+
+# = hello.h =
+
+extern int m;
+extern int baz (int x);
+
+static inline int
+frob (int a, int b)
+{
+ int c = a;
+
+ if (a > b)
+ c -= b;
+
+ return baz (c);
+}
+
+# = hello.c =
+
+#include <stddef.h>
+#include "hello.h"
+
+extern int main (int, char **);
+int m = 2;
+
+wchar_t foo (wchar_t);
+int baz (int x)
+{
+ int r = x;
+
+ if (x > m)
+ r -= m;
+
+ r = foo (r);
+ return r;
+}
+
+wchar_t
+foo (wchar_t f)
+{
+ if (f < 0)
+ return main (f, NULL);
+
+ return f > 0 ? frob (f - 1, m) : 0;
+}
+
+# = world.c =
+
+#include "hello.h"
+#include <stdlib.h>
+
+int
+calc (const char *word)
+{
+ if (word == 0 || word[0] == '\0')
+ return 0;
+
+ return frob (word[0], m + 42);
+}
+
+int
+main (int argc, const char **argv)
+{
+ const char *n;
+ if (argc > 1)
+ n = argv[0];
+ else
+ n = "world";
+
+ exit (calc (n));
+}
+
+$ gcc -gdwarf-4 -gno-as-loc-support -gno-variable-location-views -O2 -c world.c
+$ gcc -gdwarf-4 -gno-as-loc-support -gno-variable-location-views -O2 -c hello.c
+$ gcc -o testfile-dwarf-4 hello.o world.o
+
+$ gcc -gdwarf-5 -gno-as-loc-support -gno-variable-location-views -O2 -c world.c
+$ gcc -gdwarf-5 -gno-as-loc-support -gno-variable-location-views -O2 -c hello.c
+$ gcc -o testfile-dwarf-5 hello.o world.o
diff --git a/tests/testfile-dwarf-5.bz2 b/tests/testfile-dwarf-5.bz2
new file mode 100755
index 00000000..689fa4fc
--- /dev/null
+++ b/tests/testfile-dwarf-5.bz2
Binary files differ