This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH][v3] Support .debug_macro


This is an updated .debug_macro patch with the following changes, as
discussed here and on IRC:

- .debug_macinfo prototype table is initialized in a ctor.

- dwarf_getmacros now serves .debug_macro as well, but in that case
  refuses to serve 0xff.

  In correlation with this change, I decided to drop dwarf_macro_version
  and dwarf_getmacros_die.  Currently this just is not necessary at all,
  because all existing opcodes that have the same value in DW_MACINFO_*
  and DW_MACRO_GNU_*, have also the same behavior, and 0xff is not
  allocated in DW_MACRO_GNU_* (and likely never will be, because the
  allocations will be done under Dwarf 5 standard).

  If Dwarf 5 comes out with possibly incompatible 0xff, we'll have to
  publish these interfaces, but it's easier to do that than to retract
  the new interfaces later if it turns out that they are not necessary.
  Adding these two interfaces should be a short-order matter, both are
  few-liners.

- Added a comment to dwarf_getmacros_die clarifying lifetime of
  Dwarf_Macro pointer passed to the callback.

- Dwarf_Macro_s simplified -- it now only carries table and attribute
  pointers and opcode.  Added accessor for determining number of forms,
  because traversing all the index tables correctly is a pain.

- Interface dwarf_macro_line_offset was replaced with
  dwarf_macro_getsrcfiles.  This prompted some changes in
  dwarf_getsrclines.c, which now exposes functions for loading line
  units by offset instead of by DIE.

  We also need somewhere to keep the DW_AT_stmt_list and DW_AT_comp_dir
  values, because both are needed for the debug line loader.  That
  somewhere is naturally the macro opcode table.  That means not all
  .debug_macinfo tables are the same anymore, and we end up treating
  them pretty much the same as .debug_macro ones--creating a table per
  unit, and caching it at Dwarf.

--8<------------------------------------------------------------------

- This code is based on the following proposal:
    http://www.dwarfstd.org/ShowIssue.php?issue=110722.1

- dwarf_getmacros serves either of .debug_macinfo or .debug_macro
  transparently, but if the latter uses opcode 0xff, it bails out with
  an error.  The reason is that in .debug_macro, 0xff is a custom code
  that can mean anything, while in .debug_macinfo there's fixed
  semantics associated with 0xff.

- dwarf_getmacros_off is a new interface used for requesting iteration
  through transparently included units.

- dwarf_macro_getparamcnt and dwarf_macro_param are new interfaces
  used for requesting number of parameters of an opcode and individual
  parameters.  dwarf_macro_getsrcfiles is a new interface used for
  requesting a file part of .debug_line unit associated with macro
  unit that the opcode comes from.

- The existing interfaces dwarf_macro_opcode, dwarf_macro_param1 and
  dwarf_macro_param2 remain operational for old- as well as new-style
  Dwarf macro sections, if applicable.

- dwarf_getsrclines was made into a light wrapper around a worker
  function that loads line unit given its offset.  The worker also
  caches loaded units in an offset-keyed search tree, so that we don't
  end up re-reading units even though they were read in a different
  domain (e.g. a macro unit request can prime cache for later CU
  lookup).  dwarf_macro_getsrcfiles calls the worker function under
  covers.

Signed-off-by: Petr Machata <pmachata@redhat.com>
---
 libdw/ChangeLog            |   39 ++
 libdw/Makefile.am          |    8 +-
 libdw/dwarf_end.c          |    6 +
 libdw/dwarf_error.c        |    3 +-
 libdw/dwarf_getmacros.c    |  545 ++++++++++++++++---
 libdw/dwarf_getsrclines.c  | 1275 +++++++++++++++++++++++---------------------
 libdw/dwarf_macro_param1.c |    8 +-
 libdw/dwarf_macro_param2.c |   18 +-
 libdw/libdw.h              |   76 ++-
 libdw/libdw.map            |   10 +-
 libdw/libdwP.h             |   87 ++-
 11 files changed, 1364 insertions(+), 711 deletions(-)

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 89b2735..37e8700 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,42 @@
+2014-09-10  Petr Machata  <pmachata@redhat.com>
+
+	* dwarf_macro_getparamcnt.c: New file.
+	* dwarf_macro_param.c: New file.
+	* dwarf_macro_getsrcfiles.c: New file.
+	* Makefile.am (libdw_a_SOURCES): Add the new files.
+	* libdwP.h (struct files_lines_s): New structure.
+	(DWARF_E_INVALID_OPCODE): New enumerator.
+	(struct Dwarf): New fields macro_ops, files_lines.
+	(Dwarf_Macro_Op_Proto, Dwarf_Macro_Op_Table): New structures for
+	keeping macro opcode prototypes in.
+	(Dwarf_Macro_s): Redefine from scratch.
+	(__libdw_getsrclines, __libdw_getcompdir, libdw_macro_nforms): New
+	internal interfaces.
+	* dwarf_error.c (errmsgs): Add a message for
+	DWARF_E_INVALID_OPCODE.
+	* dwarf_end.c (dwarf_end): Destroy struct Dwarf.macro_ops and
+	files_lines.
+	* libdw.h (dwarf_getmacros_off, dwarf_macro_getparamcnt)
+	(dwarf_macro_getsrcfiles, dwarf_macro_param): New public
+	interfaces.
+	* dwarf_getmacros.c (dwarf_getmacros_off): New function,
+	(get_offset_from, macro_op_compare, build_table)
+	(init_macinfo_table, get_macinfo_table, get_table_for_offset)
+	(cache_op_table, read_macros, gnu_macros_getmacros_off)
+	(macro_info_getmacros_off, do_dwarf_getmacros_die): New helper
+	functions.
+	(dwarf_getmacros): Adjust to dispatch to the new interfaces.
+	* dwarf_getsrclines.c (read_srclines): New function with guts
+	taken from dwarf_getsrclines.
+	(__libdw_getsrclines): Likewise.
+	(__libdw_getcompdir, files_lines_compare): New functions.
+	(dwarf_getsrclines): Make it dispatch to the new interfaces.
+	* dwarf_macro_param1.c (dwarf_macro_param1): Adjust to dispatch to
+	the new interfaces.
+	* dwarf_macro_param2.c (dwarf_macro_param2): Likewise.
+	* libdw.map (ELFUTILS_0.161): New. Add dwarf_getmacros_off,
+	dwarf_macro_getsrcfiles, dwarf_macro_getparamcnt, dwarf_macro_param.
+
 2014-10-15  Petr Machata  <pmachata@redhat.com>
 
 	* libdwP.h (struct Dwarf_Files_s.cu): Drop field.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 2e42a37..12e0fff 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -71,9 +71,11 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_getlocation.c dwarf_getstring.c dwarf_offabbrev.c \
 		  dwarf_getaranges.c dwarf_onearange.c dwarf_getarangeinfo.c \
 		  dwarf_getarange_addr.c dwarf_getattrs.c dwarf_formflag.c \
-		  dwarf_getmacros.c dwarf_macro_opcode.c dwarf_macro_param1.c \
-		  dwarf_macro_param2.c dwarf_addrdie.c \
-		  dwarf_getfuncs.c  \
+		  dwarf_getmacros.c dwarf_macro_getparamcnt.c	\
+		  dwarf_macro_opcode.c dwarf_macro_param.c	\
+		  dwarf_macro_param1.c dwarf_macro_param2.c	\
+		  dwarf_macro_getsrcfiles.c			\
+		  dwarf_addrdie.c dwarf_getfuncs.c \
 		  dwarf_decl_file.c dwarf_decl_line.c dwarf_decl_column.c \
 		  dwarf_func_inline.c dwarf_getsrc_file.c \
 		  libdw_findcu.c libdw_form.c libdw_alloc.c \
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 241a257..647a1b8 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -93,6 +93,12 @@ dwarf_end (dwarf)
       tdestroy (dwarf->cu_tree, cu_free);
       tdestroy (dwarf->tu_tree, cu_free);
 
+      /* Search tree for macro opcode tables.  */
+      tdestroy (dwarf->macro_ops, noop_free);
+
+      /* Search tree for decoded .debug_lines units.  */
+      tdestroy (dwarf->files_lines, noop_free);
+
       struct libdw_memblock *memp = dwarf->mem_tail;
       /* The first block is allocated together with the Dwarf object.  */
       while (memp->prev != NULL)
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 2292914..08b691a 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -1,5 +1,5 @@
 /* Retrieve ELF descriptor used for DWARF access.
-   Copyright (C) 2002, 2003, 2004, 2005, 2009 Red Hat, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2009, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -92,6 +92,7 @@ static const char *errmsgs[] =
     [DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"),
     [DWARF_E_INVALID_CFI] = N_("invalid CFI section"),
     [DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"),
+    [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"),
   };
 #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))
 
diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
index a3d2c81..7f889a9 100644
--- a/libdw/dwarf_getmacros.c
+++ b/libdw/dwarf_getmacros.c
@@ -1,5 +1,5 @@
 /* Get macro information.
-   Copyright (C) 2002-2009 Red Hat, Inc.
+   Copyright (C) 2002-2009, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -31,114 +31,495 @@
 # include <config.h>
 #endif
 
+#include <assert.h>
 #include <dwarf.h>
+#include <search.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <libdwP.h>
 
+static int
+get_offset_from (Dwarf_Die *die, int name, Dwarf_Word *retp)
+{
+  /* Get the appropriate attribute.  */
+  Dwarf_Attribute attr;
+  if (INTUSE(dwarf_attr) (die, name, &attr) == NULL)
+    return -1;
 
-ptrdiff_t
-dwarf_getmacros (die, callback, arg, offset)
-     Dwarf_Die *die;
-     int (*callback) (Dwarf_Macro *, void *);
-     void *arg;
-     ptrdiff_t offset;
+  /* Offset into the corresponding section.  */
+  return INTUSE(dwarf_formudata) (&attr, retp);
+}
+
+static int
+macro_op_compare (const void *p1, const void *p2)
 {
-  if (die == NULL)
+  const Dwarf_Macro_Op_Table *t1 = (const Dwarf_Macro_Op_Table *) p1;
+  const Dwarf_Macro_Op_Table *t2 = (const Dwarf_Macro_Op_Table *) p2;
+
+  if (t1->offset < t2->offset)
     return -1;
+  if (t1->offset > t2->offset)
+    return 1;
+
+  if (t1->sec_index < t2->sec_index)
+    return -1;
+  if (t1->sec_index > t2->sec_index)
+    return 1;
+
+  return 0;
+}
+
+static void
+build_table (Dwarf_Macro_Op_Table *table,
+	     Dwarf_Macro_Op_Proto op_protos[static 255])
+{
+  unsigned ct = 0;
+  for (unsigned i = 1; i < 256; ++i)
+    if (op_protos[i - 1].forms != NULL)
+      table->table[table->opcodes[i - 1] = ct++] = op_protos[i - 1];
+    else
+      table->opcodes[i - 1] = 0xff;
+}
+
+#define MACRO_PROTO(NAME, ...)					\
+  Dwarf_Macro_Op_Proto NAME = ({				\
+      static const uint8_t proto[] = {__VA_ARGS__};		\
+      (Dwarf_Macro_Op_Proto) {sizeof proto, proto};		\
+    })
+
+enum { macinfo_data_size = offsetof (Dwarf_Macro_Op_Table, table[5]) };
+static unsigned char macinfo_data[macinfo_data_size]
+	__attribute__ ((aligned (__alignof (Dwarf_Macro_Op_Table))));
+
+static __attribute__ ((constructor)) void
+init_macinfo_table (void)
+{
+  MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
+  MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
+  MACRO_PROTO (p_none);
+
+  Dwarf_Macro_Op_Proto op_protos[255] =
+    {
+      [DW_MACINFO_define - 1] = p_udata_str,
+      [DW_MACINFO_undef - 1] = p_udata_str,
+      [DW_MACINFO_vendor_ext - 1] = p_udata_str,
+      [DW_MACINFO_start_file - 1] = p_udata_udata,
+      [DW_MACINFO_end_file - 1] = p_none,
+      /* If you are adding more elements to this array, increase
+	 MACINFO_DATA_SIZE above.  */
+    };
+
+  Dwarf_Macro_Op_Table *macinfo_table = (void *) macinfo_data;
+  memset (macinfo_table, 0, sizeof macinfo_data);
+  build_table (macinfo_table, op_protos);
+  macinfo_table->sec_index = IDX_debug_macinfo;
+}
+
+static Dwarf_Macro_Op_Table *
+get_macinfo_table (Dwarf *dbg, Dwarf_Word macoff,
+		   __attribute__ ((unused)) const unsigned char *readp,
+		   __attribute__ ((unused)) const unsigned char *const endp,
+		   Dwarf_Die *cudie)
+{
+  assert (cudie != NULL);
+
+  Dwarf_Attribute attr_mem, *attr
+    = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &attr_mem);
+  Dwarf_Off line_offset = (Dwarf_Off) -1;
+  if (attr != NULL)
+    INTUSE(dwarf_formudata) (attr, &line_offset);
+
+  Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
+					     macinfo_data_size, 1);
+  memcpy (table, macinfo_data, macinfo_data_size);
+
+  table->offset = macoff;
+  table->sec_index = IDX_debug_macinfo;
+  table->line_offset = line_offset;
+  table->is_64bit = cudie->cu->address_size == 8;
+  table->comp_dir = __libdw_getcompdir (cudie);
+
+  return table;
+}
+
+static Dwarf_Macro_Op_Table *
+get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
+		      const unsigned char *readp,
+		      const unsigned char *const endp,
+		      Dwarf_Die *cudie)
+{
+  const unsigned char *startp = readp;
+
+  /* Request at least 3 bytes for header.  */
+  if (readp + 3 > endp)
+    {
+    invalid_dwarf:
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return NULL;
+    }
+
+  uint16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+  if (version != 4)
+    {
+      __libdw_seterrno (DWARF_E_INVALID_VERSION);
+      return NULL;
+    }
+
+  uint8_t flags = *readp++;
+  bool is_64bit = (flags & 0x1) != 0;
+
+  Dwarf_Off line_offset = (Dwarf_Off) -1;
+  if ((flags & 0x2) != 0)
+    {
+      line_offset = read_addr_unaligned_inc (is_64bit ? 8 : 4, dbg, readp);
+      if (readp > endp)
+	goto invalid_dwarf;
+    }
+  else if (cudie != NULL)
+    {
+      Dwarf_Attribute attr_mem, *attr
+	= INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &attr_mem);
+      if (attr != NULL)
+	INTUSE(dwarf_formudata) (attr, &line_offset);
+    }
+
+  /* """The macinfo entry types defined in this standard may, but
+     might not, be described in the table""".
+
+     I.e. these may be present.  It's tempting to simply skip them,
+     but it's probably more correct to tolerate that a producer tweaks
+     the way certain opcodes are encoded, for whatever reasons.  */
+
+  MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
+  MACRO_PROTO (p_udata_strp, DW_FORM_udata, DW_FORM_strp);
+  MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
+  MACRO_PROTO (p_secoffset, DW_FORM_sec_offset);
+  MACRO_PROTO (p_none);
+
+  Dwarf_Macro_Op_Proto op_protos[255] =
+    {
+      [DW_MACRO_GNU_define - 1] = p_udata_str,
+      [DW_MACRO_GNU_undef - 1] = p_udata_str,
+      [DW_MACRO_GNU_define_indirect - 1] = p_udata_strp,
+      [DW_MACRO_GNU_undef_indirect - 1] = p_udata_strp,
+      [DW_MACRO_GNU_start_file - 1] = p_udata_udata,
+      [DW_MACRO_GNU_end_file - 1] = p_none,
+      [DW_MACRO_GNU_transparent_include - 1] = p_secoffset,
+      /* N.B. DW_MACRO_undef_indirectx, DW_MACRO_define_indirectx
+	 should be added when 130313.1 is supported.  */
+    };
+
+  if ((flags & 0x4) != 0)
+    {
+      unsigned count = *readp++;
+      for (unsigned i = 0; i < count; ++i)
+	{
+	  unsigned opcode = *readp++;
+
+	  Dwarf_Macro_Op_Proto e;
+	  get_uleb128 (e.nforms, readp); // XXX checking
+	  e.forms = readp;
+	  op_protos[opcode - 1] = e;
+
+	  readp += e.nforms;
+	  if (readp > endp)
+	    {
+	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+	      return NULL;
+	    }
+	}
+    }
+
+  size_t ct = 0;
+  for (unsigned i = 1; i < 256; ++i)
+    if (op_protos[i - 1].forms != NULL)
+      ++ct;
+
+  /* We support at most 0xfe opcodes defined in the table, as 0xff is
+     a value that means that given opcode is not stored at all.  But
+     that should be fine, as opcode 0 is not allocated.  */
+  assert (ct < 0xff);
+
+  size_t macop_table_size = offsetof (Dwarf_Macro_Op_Table, table[ct]);
+
+  Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
+					     macop_table_size, 1);
+
+  *table = (Dwarf_Macro_Op_Table) {
+    .offset = macoff,
+    .sec_index = IDX_debug_macro,
+    .line_offset = line_offset,
+    .header_len = readp - startp,
+    .version = version,
+    .is_64bit = is_64bit,
+
+    /* NULL if CUDIE is NULL or DW_AT_comp_dir is absent.  */
+    .comp_dir = __libdw_getcompdir (cudie),
+  };
+  build_table (table, op_protos);
+
+  return table;
+}
+
+static Dwarf_Macro_Op_Table *
+cache_op_table (Dwarf *dbg, int sec_index, Dwarf_Off macoff,
+		const unsigned char *startp,
+		const unsigned char *const endp,
+		Dwarf_Die *cudie)
+{
+  Dwarf_Macro_Op_Table fake = { .offset = macoff, .sec_index = sec_index };
+  Dwarf_Macro_Op_Table **found = tfind (&fake, &dbg->macro_ops,
+					macro_op_compare);
+  if (found != NULL)
+    return *found;
+
+  Dwarf_Macro_Op_Table *table = sec_index == IDX_debug_macro
+    ? get_table_for_offset (dbg, macoff, startp, endp, cudie)
+    : get_macinfo_table (dbg, macoff, startp, endp, cudie);
+
+  if (table == NULL)
+    return NULL;
+
+  Dwarf_Macro_Op_Table **ret = tsearch (table, &dbg->macro_ops,
+					macro_op_compare);
+  if (unlikely (ret == NULL))
+    {
+      __libdw_seterrno (DWARF_E_NOMEM);
+      return NULL;
+    }
 
-  Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_macinfo];
-  if (unlikely (d == NULL) || unlikely (d->d_buf == NULL))
+  return *ret;
+}
+
+static ptrdiff_t
+read_macros (Dwarf *dbg, int sec_index,
+	     Dwarf_Off macoff, int (*callback) (Dwarf_Macro *, void *),
+	     void *arg, ptrdiff_t offset, bool accept_0xff,
+	     Dwarf_Die *cudie)
+{
+  Elf_Data *d = dbg->sectiondata[sec_index];
+  if (unlikely (d == NULL || d->d_buf == NULL))
     {
       __libdw_seterrno (DWARF_E_NO_ENTRY);
       return -1;
     }
 
-  if (offset == 0)
+  if (unlikely (macoff >= d->d_size))
     {
-      /* Get the appropriate attribute.  */
-      Dwarf_Attribute attr;
-      if (INTUSE(dwarf_attr) (die, DW_AT_macro_info, &attr) == NULL)
-	return -1;
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return -1;
+    }
 
-      /* Offset into the .debug_macinfo section.  */
-      Dwarf_Word macoff;
-      if (INTUSE(dwarf_formudata) (&attr, &macoff) != 0)
-	return -1;
+  const unsigned char *const startp = d->d_buf + macoff;
+  const unsigned char *const endp = d->d_buf + d->d_size;
 
-      offset = macoff;
-    }
-  if (unlikely (offset > (ptrdiff_t) d->d_size))
-    goto invalid;
+  Dwarf_Macro_Op_Table *table = cache_op_table (dbg, sec_index, macoff,
+						startp, endp, cudie);
+  if (table == NULL)
+    return -1;
 
-  const unsigned char *readp = d->d_buf + offset;
-  const unsigned char *readendp = d->d_buf + d->d_size;
+  if (offset == 0)
+    offset = table->header_len;
 
-  if (readp == readendp)
-    return 0;
+  assert (offset >= 0);
+  assert (offset < endp - startp);
+  const unsigned char *readp = startp + offset;
 
-  while (readp < readendp)
+  while (readp < endp)
     {
       unsigned int opcode = *readp++;
-      unsigned int u128;
-      unsigned int u128_2 = 0;
-      const char *str = NULL;
-      const unsigned char *endp;
+      if (opcode == 0)
+	/* Nothing more to do.  */
+	return 0;
+
+      if (unlikely (opcode == 0xff && ! accept_0xff))
+	{
+	  /* See comment below at dwarf_getmacros for explanation of
+	     why we are doing this.  */
+	  __libdw_seterrno (DWARF_E_INVALID_OPCODE);
+	  return -1;
+	}
+
+      unsigned int idx = table->opcodes[opcode - 1];
+      if (idx == 0xff)
+	{
+	  __libdw_seterrno (DWARF_E_INVALID_OPCODE);
+	  return -1;
+	}
+
+      Dwarf_Macro_Op_Proto *proto = &table->table[idx];
 
-      switch (opcode)
+      /* A fake CU with bare minimum data to fool dwarf_formX into
+	 doing the right thing with the attributes that we put out.
+	 We arbitrarily pretend it's version 4.  */
+      Dwarf_CU fake_cu = {
+	.dbg = dbg,
+	.version = 4,
+	.offset_size = table->is_64bit ? 8 : 4,
+      };
+
+      Dwarf_Attribute attributes[proto->nforms];
+      for (Dwarf_Word i = 0; i < proto->nforms; ++i)
 	{
-	case DW_MACINFO_define:
-	case DW_MACINFO_undef:
-	case DW_MACINFO_vendor_ext:
-	  /*  For the first two opcodes the parameters are
-	        line, string
-	      For the latter
-	        number, string.
-	      We can treat these cases together.  */
-	  get_uleb128 (u128, readp);
-
-	  endp = memchr (readp, '\0', readendp - readp);
-	  if (endp == NULL)
-	    goto invalid;
-
-	  str = (char *) readp;
-	  readp = endp + 1;
-	  break;
-
-	case DW_MACINFO_start_file:
-	  /* The two parameters are line and file index.  */
-	  get_uleb128 (u128, readp);
-	  get_uleb128 (u128_2, readp);
-	  break;
-
-	case DW_MACINFO_end_file:
-	  /* No parameters for this one.  */
-	  u128 = 0;
-	  break;
-
-	case 0:
-	  /* Nothing more to do.  */
-	  return 0;
-
-	default:
-	  goto invalid;
+	  /* We pretend this is a DW_AT_GNU_macros attribute so that
+	     DW_FORM_sec_offset forms get correctly interpreted as
+	     offset into .debug_macro.  */
+	  attributes[i].code = DW_AT_GNU_macros;
+	  attributes[i].form = proto->forms[i];
+	  attributes[i].valp = (void *) readp;
+	  attributes[i].cu = &fake_cu;
+
+	  readp += __libdw_form_val_len (dbg, &fake_cu,
+					 proto->forms[i], readp);
 	}
 
-      Dwarf_Macro mac;
-      mac.opcode = opcode;
-      mac.param1 = u128;
-      if (str == NULL)
-	mac.param2.u = u128_2;
-      else
-	mac.param2.s = str;
+      Dwarf_Macro macro = {
+	.table = table,
+	.opcode = opcode,
+	.attributes = attributes,
+      };
+
+      if (callback (&macro, arg) != DWARF_CB_OK)
+	return readp - startp;
+    }
+
+  return 0;
+}
+
+static ptrdiff_t
+gnu_macros_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+			  int (*callback) (Dwarf_Macro *, void *),
+			  void *arg, ptrdiff_t token, bool accept_0xff,
+			  Dwarf_Die *cudie)
+{
+  assert (token <= 0);
+
+  if (macoff >= dbg->sectiondata[IDX_debug_macro]->d_size)
+    {
+      __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+      return -1;
+    }
+
+  ptrdiff_t ret = read_macros (dbg, IDX_debug_macro, macoff,
+			       callback, arg, -token, accept_0xff, cudie);
+  if (ret == -1)
+    return -1;
+  else
+    return -ret;
+}
+
+static ptrdiff_t
+macro_info_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+			  int (*callback) (Dwarf_Macro *, void *),
+			  void *arg, ptrdiff_t token, Dwarf_Die *cudie)
+{
+  assert (token >= 0);
+
+  return read_macros (dbg, IDX_debug_macinfo, macoff,
+		      callback, arg, token, true, cudie);
+}
+
+ptrdiff_t
+dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+		     int (*callback) (Dwarf_Macro *, void *),
+		     void *arg, ptrdiff_t token)
+{
+  if (dbg == NULL)
+    {
+      __libdw_seterrno (DWARF_E_NO_DWARF);
+      return -1;
+    }
+
+  /* We use token values > 0 for iteration through .debug_macinfo and
+     values < 0 for iteration through .debug_macro.  Return value of
+     -1 also signifies an error, but that's fine, because .debug_macro
+     always contains at least three bytes of headers and after
+     iterating one opcode, we should never see anything above -4.  */
+
+  if (token > 0)
+    /* A continuation call from DW_AT_macro_info iteration.  */
+    return macro_info_getmacros_off (dbg, macoff, callback, arg, token, NULL);
+
+  /* Either a DW_AT_GNU_macros continuation, or a fresh start
+     thereof.  */
+  return gnu_macros_getmacros_off (dbg, macoff, callback, arg, token, true,
+				   NULL);
+}
+
+static ptrdiff_t
+do_dwarf_getmacros_die (Dwarf_Die *cudie, Dwarf_Off *macoffp,
+			int (*callback) (Dwarf_Macro *, void *),
+			void *arg, ptrdiff_t token, bool accept_0xff)
+{
+  if (cudie == NULL)
+    {
+      __libdw_seterrno (DWARF_E_NO_DWARF);
+      return -1;
+    }
+
+  if (token > 0 && macoffp != NULL)
+    /* A continuation call from DW_AT_macro_info iteration, meaning
+       *MACOFF contains previously-cached offset.  */
+    return macro_info_getmacros_off (cudie->cu->dbg, *macoffp,
+				     callback, arg, token, cudie);
 
-      if (callback (&mac, arg) != DWARF_CB_OK)
-	return readp - (const unsigned char *) d->d_buf;
+  /* A fresh start of DW_AT_macro_info iteration, or a continuation
+     thereof without a cache.  */
+  if (token > 0
+      || (token == 0 && dwarf_hasattr (cudie, DW_AT_macro_info)))
+    {
+      Dwarf_Word macoff;
+      if (macoffp == NULL)
+	macoffp = &macoff;
+      if (get_offset_from (cudie, DW_AT_macro_info, macoffp) != 0)
+	return -1;
+      return macro_info_getmacros_off (cudie->cu->dbg, *macoffp,
+				       callback, arg, token, cudie);
     }
 
-  /* If we come here the termination of the data for the CU is not
-     present.  */
- invalid:
-  __libdw_seterrno (DWARF_E_INVALID_DWARF);
-  return -1;
+  if (token < 0 && macoffp != NULL)
+    /* A continuation call from DW_AT_GNU_macros iteration.  */
+    return gnu_macros_getmacros_off (cudie->cu->dbg, *macoffp,
+				     callback, arg, token, accept_0xff,
+				     cudie);
+
+  /* Likewise without cache, or iteration start.  */
+  Dwarf_Word macoff;
+  if (macoffp == NULL)
+    macoffp = &macoff;
+  if (get_offset_from (cudie, DW_AT_GNU_macros, macoffp) != 0)
+    return -1;
+  return gnu_macros_getmacros_off (cudie->cu->dbg, *macoffp,
+				   callback, arg, token, accept_0xff,
+				   cudie);
+}
+
+ptrdiff_t
+dwarf_getmacros (die, callback, arg, offset)
+     Dwarf_Die *die;
+     int (*callback) (Dwarf_Macro *, void *);
+     void *arg;
+     ptrdiff_t offset;
+{
+  /* This function might be called from a code that expects to see
+     DW_MACINFO_* opcodes, not DW_MACRO_{GNU_,}* ones.  It is fine to
+     serve most DW_MACRO_{GNU_,}* opcodes to such code, because those
+     whose values are the same as DW_MACINFO_* ones also have the same
+     behavior.  It is not very likely that a .debug_macro section
+     would only use the part of opcode space that it shares with
+     .debug_macinfo, but it is possible.  Serving the opcodes that are
+     only valid in DW_MACRO_{GNU_,}* domain is OK as well, because
+     clients in general need to be ready that newer standards define
+     more opcodes, and have coping mechanisms for unfamiliar opcodes.
+
+     The one exception to the above rule is opcode 0xff, which has
+     concrete semantics in .debug_macinfo, but falls into vendor block
+     in .debug_macro, and can be assigned to do whatever.  There is
+     some small probability that the two opcodes would look
+     superficially similar enough that a client would be confused and
+     misbehave as a result.  For this reason, we refuse to serve
+     through this interface 0xff's originating from .debug_macro.  */
+
+  return do_dwarf_getmacros_die (die, NULL, callback, arg, offset, false);
 }
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
index 74d626c..1f460ec 100644
--- a/libdw/dwarf_getsrclines.c
+++ b/libdw/dwarf_getsrclines.c
@@ -34,6 +34,8 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
+#include <search.h>
+
 #include "dwarf.h"
 #include "libdwP.h"
 
@@ -65,14 +67,12 @@ compare_lines (const void *a, const void *b)
   return (*p1)->addr - (*p2)->addr;
 }
 
-int
-dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
+static int
+read_srclines (Dwarf *dbg,
+	       const unsigned char *linep, const unsigned char *lineendp,
+	       const char *comp_dir, unsigned address_size,
+	       Dwarf_Lines **linesp, Dwarf_Files **filesp)
 {
-  if (unlikely (cudie == NULL
-		|| (INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit
-		    && INTUSE(dwarf_tag) (cudie) != DW_TAG_partial_unit)))
-    return -1;
-
   int res = -1;
 
   struct linelist *linelist = NULL;
@@ -84,329 +84,428 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
 #define MAX_STACK_ALLOC 4096
   struct linelist *malloc_linelist = NULL;
 
-  /* Get the information if it is not already known.  */
-  struct Dwarf_CU *const cu = cudie->cu;
-  if (cu->lines == NULL)
+  if (unlikely (linep + 4 > lineendp))
     {
-      /* Failsafe mode: no data found.  */
-      cu->lines = (void *) -1l;
-      cu->files = (void *) -1l;
-
-      /* The die must have a statement list associated.  */
-      Dwarf_Attribute stmt_list_mem;
-      Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
-						       &stmt_list_mem);
-
-      /* Get the offset into the .debug_line section.  NB: this call
-	 also checks whether the previous dwarf_attr call failed.  */
-      const unsigned char *lineendp;
-      const unsigned char *linep
-	= __libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE,
-			   (unsigned char **) &lineendp, NULL);
-      if (linep == NULL)
-	goto out;
-
-      /* Get the compilation directory.  */
-      Dwarf_Attribute compdir_attr_mem;
-      Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
-							  DW_AT_comp_dir,
-							  &compdir_attr_mem);
-      const char *comp_dir = INTUSE(dwarf_formstring) (compdir_attr);
-
-      if (unlikely (linep + 4 > lineendp))
-	{
-	invalid_data:
-	  __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
-	  goto out;
-	}
-
-      Dwarf *dbg = cu->dbg;
-      Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
-      unsigned int length = 4;
-      if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
-	{
-	  if (unlikely (linep + 8 > lineendp))
-	    goto invalid_data;
-	  unit_length = read_8ubyte_unaligned_inc (dbg, linep);
-	  length = 8;
-	}
+    invalid_data:
+      __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
+      goto out;
+    }
 
-      /* Check whether we have enough room in the section.  */
-      if (unit_length < 2 + length + 5 * 1
-	  || unlikely (linep + unit_length > lineendp))
+  Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
+  unsigned int length = 4;
+  if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
+    {
+      if (unlikely (linep + 8 > lineendp))
 	goto invalid_data;
-      lineendp = linep + unit_length;
+      unit_length = read_8ubyte_unaligned_inc (dbg, linep);
+      length = 8;
+    }
 
-      /* The next element of the header is the version identifier.  */
-      uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
-      if (unlikely (version < 2) || unlikely (version > 4))
-	{
-	  __libdw_seterrno (DWARF_E_VERSION);
-	  goto out;
-	}
+  /* Check whether we have enough room in the section.  */
+  if (unit_length < 2 + length + 5 * 1
+      || unlikely (linep + unit_length > lineendp))
+    goto invalid_data;
+  lineendp = linep + unit_length;
 
-      /* Next comes the header length.  */
-      Dwarf_Word header_length;
-      if (length == 4)
-	header_length = read_4ubyte_unaligned_inc (dbg, linep);
-      else
-	header_length = read_8ubyte_unaligned_inc (dbg, linep);
-      const unsigned char *header_start = linep;
+  /* The next element of the header is the version identifier.  */
+  uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
+  if (unlikely (version < 2) || unlikely (version > 4))
+    {
+      __libdw_seterrno (DWARF_E_VERSION);
+      goto out;
+    }
 
-      /* Next the minimum instruction length.  */
-      uint_fast8_t minimum_instr_len = *linep++;
+  /* Next comes the header length.  */
+  Dwarf_Word header_length;
+  if (length == 4)
+    header_length = read_4ubyte_unaligned_inc (dbg, linep);
+  else
+    header_length = read_8ubyte_unaligned_inc (dbg, linep);
+  const unsigned char *header_start = linep;
 
-      /* Next the maximum operations per instruction, in version 4 format.  */
-      uint_fast8_t max_ops_per_instr = 1;
-      if (version >= 4)
-	{
-	  if (unlikely (lineendp - linep < 5))
-	    goto invalid_data;
-	  max_ops_per_instr = *linep++;
-	  if (unlikely (max_ops_per_instr == 0))
-	    goto invalid_data;
-	}
+  /* Next the minimum instruction length.  */
+  uint_fast8_t minimum_instr_len = *linep++;
 
-      /* Then the flag determining the default value of the is_stmt
-	 register.  */
-      uint_fast8_t default_is_stmt = *linep++;
+  /* Next the maximum operations per instruction, in version 4 format.  */
+  uint_fast8_t max_ops_per_instr = 1;
+  if (version >= 4)
+    {
+      if (unlikely (lineendp - linep < 5))
+	goto invalid_data;
+      max_ops_per_instr = *linep++;
+      if (unlikely (max_ops_per_instr == 0))
+	goto invalid_data;
+    }
 
-      /* Now the line base.  */
-      int_fast8_t line_base = (int8_t) *linep++;
+  /* Then the flag determining the default value of the is_stmt
+     register.  */
+  uint_fast8_t default_is_stmt = *linep++;
+
+  /* Now the line base.  */
+  int_fast8_t line_base = (int8_t) *linep++;
+
+  /* And the line range.  */
+  uint_fast8_t line_range = *linep++;
+
+  /* The opcode base.  */
+  uint_fast8_t opcode_base = *linep++;
+
+  /* Remember array with the standard opcode length (-1 to account for
+     the opcode with value zero not being mentioned).  */
+  const uint8_t *standard_opcode_lengths = linep - 1;
+  if (unlikely (lineendp - linep < opcode_base - 1))
+    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
+  {
+    const char *dir;
+    size_t len;
+    struct dirlist *next;
+  } comp_dir_elem =
+    {
+      .dir = comp_dir,
+      .len = comp_dir ? strlen (comp_dir) : 0,
+      .next = NULL
+    };
+  struct dirlist *dirlist = &comp_dir_elem;
+  unsigned int ndirlist = 1;
+
+  // XXX Directly construct array to conserve memory?
+  while (*linep != 0)
+    {
+      struct dirlist *new_dir =
+	(struct dirlist *) alloca (sizeof (*new_dir));
 
-      /* And the line range.  */
-      uint_fast8_t line_range = *linep++;
+      new_dir->dir = (char *) linep;
+      uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+      if (endp == NULL)
+	goto invalid_data;
+      new_dir->len = endp - linep;
+      new_dir->next = dirlist;
+      dirlist = new_dir;
+      ++ndirlist;
+      linep = endp + 1;
+    }
+  /* Skip the final NUL byte.  */
+  ++linep;
 
-      /* The opcode base.  */
-      uint_fast8_t opcode_base = *linep++;
+  /* Rearrange the list in array form.  */
+  struct dirlist **dirarray
+    = (struct dirlist **) alloca (ndirlist * sizeof (*dirarray));
+  for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next)
+    dirarray[n] = dirlist;
 
-      /* Remember array with the standard opcode length (-1 to account for
-	 the opcode with value zero not being mentioned).  */
-      const uint8_t *standard_opcode_lengths = linep - 1;
-      if (unlikely (lineendp - linep < opcode_base - 1))
+  /* Now read the files.  */
+  struct filelist null_file =
+    {
+      .info =
+      {
+	.name = "???",
+	.mtime = 0,
+	.length = 0
+      },
+      .next = NULL
+    };
+  struct filelist *filelist = &null_file;
+  unsigned int nfilelist = 1;
+
+  if (unlikely (linep >= lineendp))
+    goto invalid_data;
+  while (*linep != 0)
+    {
+      struct filelist *new_file =
+	(struct filelist *) alloca (sizeof (*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;
-      linep += opcode_base - 1;
+      size_t fnamelen = endp - (uint8_t *) fname;
+      linep = endp + 1;
 
-      /* First comes the list of directories.  Add the compilation
-	 directory first since the index zero is used for it.  */
-      struct dirlist
-      {
-	const char *dir;
-	size_t len;
-	struct dirlist *next;
-      } comp_dir_elem =
-	{
-	  .dir = comp_dir,
-	  .len = comp_dir ? strlen (comp_dir) : 0,
-	  .next = NULL
-	};
-      struct dirlist *dirlist = &comp_dir_elem;
-      unsigned int ndirlist = 1;
-
-      // XXX Directly construct array to conserve memory?
-      while (*linep != 0)
+      /* Then the index.  */
+      Dwarf_Word diridx;
+      get_uleb128 (diridx, linep);
+      if (unlikely (diridx >= ndirlist))
 	{
-	  struct dirlist *new_dir =
-	    (struct dirlist *) alloca (sizeof (*new_dir));
-
-	  new_dir->dir = (char *) linep;
-	  uint8_t *endp = memchr (linep, '\0', lineendp - linep);
-	  if (endp == NULL)
-	    goto invalid_data;
-	  new_dir->len = endp - linep;
-	  new_dir->next = dirlist;
-	  dirlist = new_dir;
-	  ++ndirlist;
-	  linep = endp + 1;
+	  __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
+	  goto out;
 	}
-      /* Skip the final NUL byte.  */
-      ++linep;
 
-      /* Rearrange the list in array form.  */
-      struct dirlist **dirarray
-	= (struct dirlist **) alloca (ndirlist * sizeof (*dirarray));
-      for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next)
-	dirarray[n] = dirlist;
-
-      /* Now read the files.  */
-      struct filelist null_file =
-	{
-	  .info =
-	  {
-	    .name = "???",
-	    .mtime = 0,
-	    .length = 0
-	  },
-	  .next = NULL
-	};
-      struct filelist *filelist = &null_file;
-      unsigned int nfilelist = 1;
-
-      if (unlikely (linep >= lineendp))
-	goto invalid_data;
-      while (*linep != 0)
+      if (*fname == '/')
+	/* It's an absolute path.  */
+	new_file->info.name = fname;
+      else
 	{
-	  struct filelist *new_file =
-	    (struct filelist *) alloca (sizeof (*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;
+	  new_file->info.name = libdw_alloc (dbg, char, 1,
+					     dirarray[diridx]->len + 1
+					     + fnamelen + 1);
+	  char *cp = new_file->info.name;
 
-	  /* Then the index.  */
-	  Dwarf_Word diridx;
-	  get_uleb128 (diridx, linep);
-	  if (unlikely (diridx >= ndirlist))
+	  if (dirarray[diridx]->dir != NULL)
 	    {
-	      __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
-	      goto out;
+	      /* 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);
 	    }
-
-	  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.  */
-	  get_uleb128 (new_file->info.mtime, linep);
-
-	  /* Finally the length of the file.  */
-	  get_uleb128 (new_file->info.length, linep);
-
-	  new_file->next = filelist;
-	  filelist = new_file;
-	  ++nfilelist;
+	  *cp++ = '/';
+	  strcpy (cp, fname);
+	  assert (strlen (new_file->info.name)
+		  < dirarray[diridx]->len + 1 + fnamelen + 1);
 	}
-      /* Skip the final NUL byte.  */
-      ++linep;
 
-      /* Consistency check.  */
-      if (unlikely (linep != header_start + header_length))
-	{
-	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
-	  goto out;
-	}
+      /* Next comes the modification time.  */
+      get_uleb128 (new_file->info.mtime, linep);
 
-        /* We are about to process the statement program.  Initialize the
-	   state machine registers (see 6.2.2 in the v2.1 specification).  */
-      Dwarf_Word addr = 0;
-      unsigned int op_index = 0;
-      unsigned int file = 1;
-      int line = 1;
-      unsigned int column = 0;
-      uint_fast8_t is_stmt = default_is_stmt;
-      bool basic_block = false;
-      bool prologue_end = false;
-      bool epilogue_begin = false;
-      unsigned int isa = 0;
-      unsigned int discriminator = 0;
-
-      /* Apply the "operation advance" from a special opcode
-	 or DW_LNS_advance_pc (as per DWARF4 6.2.5.1).  */
-      inline void advance_pc (unsigned int op_advance)
-      {
-	addr += minimum_instr_len * ((op_index + op_advance)
-				     / max_ops_per_instr);
-	op_index = (op_index + op_advance) % max_ops_per_instr;
-      }
+      /* Finally the length of the file.  */
+      get_uleb128 (new_file->info.length, linep);
 
-      /* Process the instructions.  */
+      new_file->next = filelist;
+      filelist = new_file;
+      ++nfilelist;
+    }
+  /* Skip the final NUL byte.  */
+  ++linep;
 
-      /* Adds a new line to the matrix.
-	 We cannot simply define a function because we want to use alloca.  */
+  /* Consistency check.  */
+  if (unlikely (linep != header_start + header_length))
+    {
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      goto out;
+    }
+
+  /* We are about to process the statement program.  Initialize the
+     state machine registers (see 6.2.2 in the v2.1 specification).  */
+  Dwarf_Word addr = 0;
+  unsigned int op_index = 0;
+  unsigned int file = 1;
+  int line = 1;
+  unsigned int column = 0;
+  uint_fast8_t is_stmt = default_is_stmt;
+  bool basic_block = false;
+  bool prologue_end = false;
+  bool epilogue_begin = false;
+  unsigned int isa = 0;
+  unsigned int discriminator = 0;
+
+  /* Apply the "operation advance" from a special opcode or
+     DW_LNS_advance_pc (as per DWARF4 6.2.5.1).  */
+  inline void advance_pc (unsigned int op_advance)
+  {
+    addr += minimum_instr_len * ((op_index + op_advance)
+				 / max_ops_per_instr);
+    op_index = (op_index + op_advance) % max_ops_per_instr;
+  }
+
+  /* Process the instructions.  */
+
+  /* Adds a new line to the matrix.
+     We cannot simply define a function because we want to use alloca.  */
 #define NEW_LINE(end_seq)						\
-      do {								\
-	struct linelist *ll = (nlinelist < MAX_STACK_ALLOC		\
-			       ? alloca (sizeof (struct linelist))	\
-			       : malloc (sizeof (struct linelist)));	\
-	if (nlinelist >= MAX_STACK_ALLOC)				\
-	  malloc_linelist = ll;						\
-	if (unlikely (add_new_line (ll, end_seq)))			\
-	  goto invalid_data;						\
-      } while (0)
-
-      inline bool add_new_line (struct linelist *new_line, bool end_sequence)
-      {
-	new_line->next = linelist;
-	linelist = new_line;
-	++nlinelist;
-
-	/* Set the line information.  For some fields we use bitfields,
-	   so we would lose information if the encoded values are too large.
-	   Check just for paranoia, and call the data "invalid" if it
-	   violates our assumptions on reasonable limits for the values.  */
+  do {								\
+    struct linelist *ll = (nlinelist < MAX_STACK_ALLOC		\
+			   ? alloca (sizeof (struct linelist))	\
+			   : malloc (sizeof (struct linelist)));	\
+    if (nlinelist >= MAX_STACK_ALLOC)				\
+      malloc_linelist = ll;						\
+    if (unlikely (add_new_line (ll, end_seq)))			\
+      goto invalid_data;						\
+  } while (0)
+
+  inline bool add_new_line (struct linelist *new_line, bool end_sequence)
+  {
+    new_line->next = linelist;
+    linelist = new_line;
+    ++nlinelist;
+
+    /* Set the line information.  For some fields we use bitfields,
+       so we would lose information if the encoded values are too large.
+       Check just for paranoia, and call the data "invalid" if it
+       violates our assumptions on reasonable limits for the values.  */
 #define SET(field)							      \
-	do {								      \
-	  new_line->line.field = field;					      \
-	  if (unlikely (new_line->line.field != field))			      \
-	    return true;						      \
-        } while (0)
-
-	SET (addr);
-	SET (op_index);
-	SET (file);
-	SET (line);
-	SET (column);
-	SET (is_stmt);
-	SET (basic_block);
-	SET (end_sequence);
-	SET (prologue_end);
-	SET (epilogue_begin);
-	SET (isa);
-	SET (discriminator);
+    do {								      \
+      new_line->line.field = field;					      \
+      if (unlikely (new_line->line.field != field))			      \
+	return true;						      \
+    } while (0)
+
+    SET (addr);
+    SET (op_index);
+    SET (file);
+    SET (line);
+    SET (column);
+    SET (is_stmt);
+    SET (basic_block);
+    SET (end_sequence);
+    SET (prologue_end);
+    SET (epilogue_begin);
+    SET (isa);
+    SET (discriminator);
 
 #undef SET
 
-	return false;
-      }
+    return false;
+  }
+
+  while (linep < lineendp)
+    {
+      unsigned int opcode;
+      unsigned int u128;
+      int s128;
+
+      /* Read the opcode.  */
+      opcode = *linep++;
 
-      while (linep < lineendp)
+      /* Is this a special opcode?  */
+      if (likely (opcode >= opcode_base))
+	{
+	  /* Yes.  Handling this is quite easy since the opcode value
+	     is computed with
+
+	     opcode = (desired line increment - line_base)
+		       + (line_range * address advance) + opcode_base
+	  */
+	  int line_increment = (line_base
+				+ (opcode - opcode_base) % line_range);
+
+	  /* Perform the increments.  */
+	  line += line_increment;
+	  advance_pc ((opcode - opcode_base) / line_range);
+
+	  /* Add a new line with the current state machine values.  */
+	  NEW_LINE (0);
+
+	  /* Reset the flags.  */
+	  basic_block = false;
+	  prologue_end = false;
+	  epilogue_begin = false;
+	  discriminator = 0;
+	}
+      else if (opcode == 0)
 	{
-	  unsigned int opcode;
-	  unsigned int u128;
-	  int s128;
+	  /* This an extended opcode.  */
+	  if (unlikely (lineendp - linep < 2))
+	    goto invalid_data;
+
+	  /* The length.  */
+	  uint_fast8_t len = *linep++;
+
+	  if (unlikely ((size_t) (lineendp - linep) < len))
+	    goto invalid_data;
 
-	  /* Read the opcode.  */
+	  /* The sub-opcode.  */
 	  opcode = *linep++;
 
-	  /* Is this a special opcode?  */
-	  if (likely (opcode >= opcode_base))
+	  switch (opcode)
 	    {
-	      /* Yes.  Handling this is quite easy since the opcode value
-		 is computed with
+	    case DW_LNE_end_sequence:
+	      /* Add a new line with the current state machine values.
+		 The is the end of the sequence.  */
+	      NEW_LINE (1);
+
+	      /* Reset the registers.  */
+	      addr = 0;
+	      op_index = 0;
+	      file = 1;
+	      line = 1;
+	      column = 0;
+	      is_stmt = default_is_stmt;
+	      basic_block = false;
+	      prologue_end = false;
+	      epilogue_begin = false;
+	      isa = 0;
+	      discriminator = 0;
+	      break;
+
+	    case DW_LNE_set_address:
+	      /* The value is an address.  The size is defined as
+		 apporiate for the target machine.  We use the
+		 address size field from the CU header.  */
+	      op_index = 0;
+	      if (unlikely (lineendp - linep < address_size))
+		goto invalid_data;
+	      if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
+					    address_size, &addr))
+		goto out;
+	      break;
+
+	    case DW_LNE_define_file:
+	      {
+		char *fname = (char *) linep;
+		uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+		if (endp == NULL)
+		  goto invalid_data;
+		size_t fnamelen = endp - linep;
+		linep = endp + 1;
+
+		unsigned int diridx;
+		get_uleb128 (diridx, linep);
+		Dwarf_Word mtime;
+		get_uleb128 (mtime, linep);
+		Dwarf_Word filelength;
+		get_uleb128 (filelength, linep);
+
+		struct filelist *new_file =
+		  (struct filelist *) alloca (sizeof (*new_file));
+		if (fname[0] == '/')
+		  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);
+		  }
 
-		 opcode = (desired line increment - line_base)
-		           + (line_range * address advance) + opcode_base
-	      */
-	      int line_increment = (line_base
-				    + (opcode - opcode_base) % line_range);
+		new_file->info.mtime = mtime;
+		new_file->info.length = filelength;
+		new_file->next = filelist;
+		filelist = new_file;
+		++nfilelist;
+	      }
+	      break;
+
+	    case DW_LNE_set_discriminator:
+	      /* Takes one ULEB128 parameter, the discriminator.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1))
+		goto invalid_data;
 
-	      /* Perform the increments.  */
-	      line += line_increment;
-	      advance_pc ((opcode - opcode_base) / line_range);
+	      get_uleb128 (discriminator, linep);
+	      break;
+
+	    default:
+	      /* Unknown, ignore it.  */
+	      if (unlikely ((size_t) (lineendp - (linep - 1)) < len))
+		goto invalid_data;
+	      linep += len - 1;
+	      break;
+	    }
+	}
+      else if (opcode <= DW_LNS_set_isa)
+	{
+	  /* This is a known standard opcode.  */
+	  switch (opcode)
+	    {
+	    case DW_LNS_copy:
+	      /* Takes no argument.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 0))
+		goto invalid_data;
 
 	      /* Add a new line with the current state machine values.  */
 	      NEW_LINE (0);
@@ -416,330 +515,195 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
 	      prologue_end = false;
 	      epilogue_begin = false;
 	      discriminator = 0;
-	    }
-	  else if (opcode == 0)
-	    {
-	      /* This an extended opcode.  */
-	      if (unlikely (lineendp - linep < 2))
+	      break;
+
+	    case DW_LNS_advance_pc:
+	      /* Takes one uleb128 parameter which is added to the
+		 address.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1))
 		goto invalid_data;
 
-	      /* The length.  */
-	      uint_fast8_t len = *linep++;
+	      get_uleb128 (u128, linep);
+	      advance_pc (u128);
+	      break;
 
-	      if (unlikely ((size_t) (lineendp - linep) < len))
+	    case DW_LNS_advance_line:
+	      /* Takes one sleb128 parameter which is added to the
+		 line.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1))
 		goto invalid_data;
 
-	      /* The sub-opcode.  */
-	      opcode = *linep++;
-
-	      switch (opcode)
-		{
-		case DW_LNE_end_sequence:
-		  /* Add a new line with the current state machine values.
-		     The is the end of the sequence.  */
-		  NEW_LINE (1);
-
-		  /* Reset the registers.  */
-		  addr = 0;
-		  op_index = 0;
-		  file = 1;
-		  line = 1;
-		  column = 0;
-		  is_stmt = default_is_stmt;
-		  basic_block = false;
-		  prologue_end = false;
-		  epilogue_begin = false;
-		  isa = 0;
-		  discriminator = 0;
-		  break;
-
-		case DW_LNE_set_address:
-		  /* The value is an address.  The size is defined as
-		     apporiate for the target machine.  We use the
-		     address size field from the CU header.  */
-		  op_index = 0;
-		  if (unlikely (lineendp - linep < cu->address_size))
-		    goto invalid_data;
-		  if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
-						cu->address_size, &addr))
-		    goto out;
-		  break;
-
-		case DW_LNE_define_file:
-		  {
-		    char *fname = (char *) linep;
-		    uint8_t *endp = memchr (linep, '\0', lineendp - linep);
-		    if (endp == NULL)
-		      goto invalid_data;
-		    size_t fnamelen = endp - linep;
-		    linep = endp + 1;
-
-		    unsigned int diridx;
-		    get_uleb128 (diridx, linep);
-		    Dwarf_Word mtime;
-		    get_uleb128 (mtime, linep);
-		    Dwarf_Word filelength;
-		    get_uleb128 (filelength, linep);
-
-		    struct filelist *new_file =
-		      (struct filelist *) alloca (sizeof (*new_file));
-		    if (fname[0] == '/')
-		      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);
-		      }
-
-		    new_file->info.mtime = mtime;
-		    new_file->info.length = filelength;
-		    new_file->next = filelist;
-		    filelist = new_file;
-		    ++nfilelist;
-		  }
-		  break;
-
-		case DW_LNE_set_discriminator:
-		  /* Takes one ULEB128 parameter, the discriminator.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1))
-		    goto invalid_data;
-
-		  get_uleb128 (discriminator, linep);
-		  break;
-
-		default:
-		  /* Unknown, ignore it.  */
-		  if (unlikely ((size_t) (lineendp - (linep - 1)) < len))
-		    goto invalid_data;
-		  linep += len - 1;
-		  break;
-		}
-	    }
-	  else if (opcode <= DW_LNS_set_isa)
-	    {
-	      /* This is a known standard opcode.  */
-	      switch (opcode)
-		{
-		case DW_LNS_copy:
-		  /* Takes no argument.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 0))
-		    goto invalid_data;
-
-		  /* Add a new line with the current state machine values.  */
-		  NEW_LINE (0);
-
-		  /* Reset the flags.  */
-		  basic_block = false;
-		  prologue_end = false;
-		  epilogue_begin = false;
-		  discriminator = 0;
-		  break;
-
-		case DW_LNS_advance_pc:
-		  /* Takes one uleb128 parameter which is added to the
-		     address.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1))
-		    goto invalid_data;
-
-		  get_uleb128 (u128, linep);
-		  advance_pc (u128);
-		  break;
-
-		case DW_LNS_advance_line:
-		  /* Takes one sleb128 parameter which is added to the
-		     line.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1))
-		    goto invalid_data;
-
-		  get_sleb128 (s128, linep);
-		  line += s128;
-		  break;
-
-		case DW_LNS_set_file:
-		  /* Takes one uleb128 parameter which is stored in file.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1))
-		    goto invalid_data;
-
-		  get_uleb128 (u128, linep);
-		  file = u128;
-		  break;
-
-		case DW_LNS_set_column:
-		  /* Takes one uleb128 parameter which is stored in column.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1))
-		    goto invalid_data;
-
-		  get_uleb128 (u128, linep);
-		  column = u128;
-		  break;
-
-		case DW_LNS_negate_stmt:
-		  /* Takes no argument.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 0))
-		    goto invalid_data;
-
-		  is_stmt = 1 - is_stmt;
-		  break;
-
-		case DW_LNS_set_basic_block:
-		  /* Takes no argument.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 0))
-		    goto invalid_data;
-
-		  basic_block = true;
-		  break;
-
-		case DW_LNS_const_add_pc:
-		  /* Takes no argument.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 0))
-		    goto invalid_data;
-
-		  advance_pc ((255 - opcode_base) / line_range);
-		  break;
-
-		case DW_LNS_fixed_advance_pc:
-		  /* Takes one 16 bit parameter which is added to the
-		     address.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1)
-		      || unlikely (lineendp - linep < 2))
-		    goto invalid_data;
-
-		  addr += read_2ubyte_unaligned_inc (dbg, linep);
-		  op_index = 0;
-		  break;
-
-		case DW_LNS_set_prologue_end:
-		  /* Takes no argument.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 0))
-		    goto invalid_data;
-
-		  prologue_end = true;
-		  break;
-
-		case DW_LNS_set_epilogue_begin:
-		  /* Takes no argument.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 0))
-		    goto invalid_data;
-
-		  epilogue_begin = true;
-		  break;
-
-		case DW_LNS_set_isa:
-		  /* Takes one uleb128 parameter which is stored in isa.  */
-		  if (unlikely (standard_opcode_lengths[opcode] != 1))
-		    goto invalid_data;
-
-		  get_uleb128 (isa, linep);
-		  break;
-		}
-	    }
-	  else
-	    {
-	      /* This is a new opcode the generator but not we know about.
-		 Read the parameters associated with it but then discard
-		 everything.  Read all the parameters for this opcode.  */
-	      for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
-		get_uleb128 (u128, linep);
-
-	      /* Next round, ignore this opcode.  */
-	      continue;
-	    }
-	}
+	      get_sleb128 (s128, linep);
+	      line += s128;
+	      break;
 
-      /* Put all the files in an array.  */
-      Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files,
-					sizeof (Dwarf_Files)
-					+ nfilelist * sizeof (Dwarf_Fileinfo)
-					+ (ndirlist + 1) * sizeof (char *),
-					1);
-      const char **dirs = (void *) &files->info[nfilelist];
+	    case DW_LNS_set_file:
+	      /* Takes one uleb128 parameter which is stored in file.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1))
+		goto invalid_data;
 
-      files->nfiles = nfilelist;
-      while (nfilelist-- > 0)
-	{
-	  files->info[nfilelist] = filelist->info;
-	  filelist = filelist->next;
-	}
-      assert (filelist == NULL);
-
-      /* Put all the directory strings in an array.  */
-      files->ndirs = ndirlist;
-      for (unsigned int i = 0; i < ndirlist; ++i)
-	dirs[i] = dirarray[i]->dir;
-      dirs[ndirlist] = NULL;
-
-      /* Make the file data structure available through the CU.  */
-      cu->files = files;
-
-      void *buf = libdw_alloc (dbg, Dwarf_Lines, (sizeof (Dwarf_Lines)
-						  + (sizeof (Dwarf_Line)
-						     * nlinelist)), 1);
-
-      /* First use the buffer for the pointers, and sort the entries.
-	 We'll write the pointers in the end of the buffer, and then
-	 copy into the buffer from the beginning so the overlap works.  */
-      assert (sizeof (Dwarf_Line) >= sizeof (Dwarf_Line *));
-      Dwarf_Line **sortlines = (buf + sizeof (Dwarf_Lines)
-				+ ((sizeof (Dwarf_Line)
-				    - sizeof (Dwarf_Line *)) * nlinelist));
-
-      /* The list is in LIFO order and usually they come in clumps with
-	 ascending addresses.  So fill from the back to probably start with
-	 runs already in order before we sort.  */
-      unsigned int i = nlinelist;
-      while (i-- > 0)
-	{
-	  sortlines[i] = &linelist->line;
-	  linelist = linelist->next;
-	}
-      assert (linelist == NULL);
+	      get_uleb128 (u128, linep);
+	      file = u128;
+	      break;
+
+	    case DW_LNS_set_column:
+	      /* Takes one uleb128 parameter which is stored in column.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1))
+		goto invalid_data;
+
+	      get_uleb128 (u128, linep);
+	      column = u128;
+	      break;
+
+	    case DW_LNS_negate_stmt:
+	      /* Takes no argument.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 0))
+		goto invalid_data;
+
+	      is_stmt = 1 - is_stmt;
+	      break;
+
+	    case DW_LNS_set_basic_block:
+	      /* Takes no argument.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 0))
+		goto invalid_data;
+
+	      basic_block = true;
+	      break;
+
+	    case DW_LNS_const_add_pc:
+	      /* Takes no argument.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 0))
+		goto invalid_data;
+
+	      advance_pc ((255 - opcode_base) / line_range);
+	      break;
+
+	    case DW_LNS_fixed_advance_pc:
+	      /* Takes one 16 bit parameter which is added to the
+		 address.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1)
+		  || unlikely (lineendp - linep < 2))
+		goto invalid_data;
 
-      /* Sort by ascending address.  */
-      qsort (sortlines, nlinelist, sizeof sortlines[0], &compare_lines);
+	      addr += read_2ubyte_unaligned_inc (dbg, linep);
+	      op_index = 0;
+	      break;
 
-      /* Now that they are sorted, put them in the final array.
-	 The buffers overlap, so we've clobbered the early elements
-	 of SORTLINES by the time we're reading the later ones.  */
-      cu->lines = buf;
-      cu->lines->nlines = nlinelist;
-      for (i = 0; i < nlinelist; ++i)
+	    case DW_LNS_set_prologue_end:
+	      /* Takes no argument.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 0))
+		goto invalid_data;
+
+	      prologue_end = true;
+	      break;
+
+	    case DW_LNS_set_epilogue_begin:
+	      /* Takes no argument.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 0))
+		goto invalid_data;
+
+	      epilogue_begin = true;
+	      break;
+
+	    case DW_LNS_set_isa:
+	      /* Takes one uleb128 parameter which is stored in isa.  */
+	      if (unlikely (standard_opcode_lengths[opcode] != 1))
+		goto invalid_data;
+
+	      get_uleb128 (isa, linep);
+	      break;
+	    }
+	}
+      else
 	{
-	  cu->lines->info[i] = *sortlines[i];
-	  cu->lines->info[i].files = files;
+	  /* This is a new opcode the generator but not we know about.
+	     Read the parameters associated with it but then discard
+	     everything.  Read all the parameters for this opcode.  */
+	  for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
+	    get_uleb128 (u128, linep);
+
+	  /* Next round, ignore this opcode.  */
+	  continue;
 	}
+    }
 
-      /* Make sure the highest address for the CU is marked as end_sequence.
-	 This is required by the DWARF spec, but some compilers forget and
-	 dwfl_module_getsrc depends on it.  */
-      if (nlinelist > 0)
-	cu->lines->info[nlinelist - 1].end_sequence = 1;
+  /* Put all the files in an array.  */
+  Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files,
+				    sizeof (Dwarf_Files)
+				    + nfilelist * sizeof (Dwarf_Fileinfo)
+				    + (ndirlist + 1) * sizeof (char *),
+				    1);
+  const char **dirs = (void *) &files->info[nfilelist];
 
-      /* Success.  */
-      res = 0;
+  files->nfiles = nfilelist;
+  while (nfilelist-- > 0)
+    {
+      files->info[nfilelist] = filelist->info;
+      filelist = filelist->next;
     }
-  else if (cu->lines != (void *) -1l)
-    /* We already have the information.  */
-    res = 0;
+  assert (filelist == NULL);
+
+  /* Put all the directory strings in an array.  */
+  files->ndirs = ndirlist;
+  for (unsigned int i = 0; i < ndirlist; ++i)
+    dirs[i] = dirarray[i]->dir;
+  dirs[ndirlist] = NULL;
+
+  /* Pass the file data structure to the caller.  */
+  if (filesp != NULL)
+    *filesp = files;
+
+  void *buf = libdw_alloc (dbg, Dwarf_Lines, (sizeof (Dwarf_Lines)
+					      + (sizeof (Dwarf_Line)
+						 * nlinelist)), 1);
+
+  /* First use the buffer for the pointers, and sort the entries.
+     We'll write the pointers in the end of the buffer, and then
+     copy into the buffer from the beginning so the overlap works.  */
+  assert (sizeof (Dwarf_Line) >= sizeof (Dwarf_Line *));
+  Dwarf_Line **sortlines = (buf + sizeof (Dwarf_Lines)
+			    + ((sizeof (Dwarf_Line)
+				- sizeof (Dwarf_Line *)) * nlinelist));
+
+  /* The list is in LIFO order and usually they come in clumps with
+     ascending addresses.  So fill from the back to probably start with
+     runs already in order before we sort.  */
+  for (unsigned int i = nlinelist; i-- > 0; )
+    {
+      sortlines[i] = &linelist->line;
+      linelist = linelist->next;
+    }
+  assert (linelist == NULL);
 
-  if (likely (res == 0))
+  /* Sort by ascending address.  */
+  qsort (sortlines, nlinelist, sizeof sortlines[0], &compare_lines);
+
+  /* Now that they are sorted, put them in the final array.
+     The buffers overlap, so we've clobbered the early elements
+     of SORTLINES by the time we're reading the later ones.  */
+  Dwarf_Lines *lines = buf;
+  lines->nlines = nlinelist;
+  for (unsigned int i = 0; i < nlinelist; ++i)
     {
-      *lines = cu->lines;
-      *nlines = cu->lines->nlines;
+      lines->info[i] = *sortlines[i];
+      lines->info[i].files = files;
     }
- out:
 
+  /* Make sure the highest address for the CU is marked as end_sequence.
+     This is required by the DWARF spec, but some compilers forget and
+     dwfl_module_getsrc depends on it.  */
+  if (nlinelist > 0)
+    lines->info[nlinelist - 1].end_sequence = 1;
+
+  /* Pass the line structure back to the caller.  */
+  if (linesp != NULL)
+    *linesp = lines;
+
+  /* Success.  */
+  res = 0;
+
+ out:
   /* Free malloced line records, if any.  */
   for (unsigned int i = MAX_STACK_ALLOC; i < nlinelist; i++)
     {
@@ -748,8 +712,123 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
       malloc_linelist = ll;
     }
 
+  return res;
+}
+
+static int
+files_lines_compare (const void *p1, const void *p2)
+{
+  const struct files_lines_s *t1 = p1;
+  const struct files_lines_s *t2 = p2;
+
+  if (t1->debug_line_offset < t2->debug_line_offset)
+    return -1;
+  if (t1->debug_line_offset > t2->debug_line_offset)
+    return 1;
+
+  return 0;
+}
+
+int
+__libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
+		     const char *comp_dir, unsigned address_size,
+		     Dwarf_Lines **linesp, Dwarf_Files **filesp)
+{
+  struct files_lines_s fake = { .debug_line_offset = debug_line_offset };
+  struct files_lines_s **found = tfind (&fake, &dbg->files_lines,
+					files_lines_compare);
+  if (found == NULL)
+    {
+      Elf_Data *data = __libdw_checked_get_data (dbg, IDX_debug_line);
+      if (data == NULL
+	  || __libdw_offset_in_section (dbg, IDX_debug_line,
+					debug_line_offset, 1) != 0)
+	return -1;
+
+      const unsigned char *linep = data->d_buf + debug_line_offset;
+      const unsigned char *lineendp = data->d_buf + data->d_size;
+
+      struct files_lines_s *node = libdw_alloc (dbg, struct files_lines_s,
+						sizeof *node, 1);
+
+      if (read_srclines (dbg, linep, lineendp, comp_dir, address_size,
+			 &node->lines, &node->files) != 0)
+	return -1;
+
+      node->debug_line_offset = debug_line_offset;
+
+      found = tsearch (node, &dbg->files_lines, files_lines_compare);
+      if (found == NULL)
+	{
+	  __libdw_seterrno (DWARF_E_NOMEM);
+	  return -1;
+	}
+    }
+
+  if (linesp != NULL)
+    *linesp = (*found)->lines;
+
+  if (filesp != NULL)
+    *filesp = (*found)->files;
+
+  return 0;
+}
+
+/* Get the compilation directory, if any is set.  */
+const char *
+__libdw_getcompdir (Dwarf_Die *cudie)
+{
+  if (cudie == NULL)
+    return NULL;
+
+  Dwarf_Attribute compdir_attr_mem;
+  Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
+						      DW_AT_comp_dir,
+						      &compdir_attr_mem);
+  return INTUSE(dwarf_formstring) (compdir_attr);
+}
+
+int
+dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
+{
+  if (unlikely (cudie == NULL
+		|| (INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit
+		    && INTUSE(dwarf_tag) (cudie) != DW_TAG_partial_unit)))
+    return -1;
+
+  /* Get the information if it is not already known.  */
+  struct Dwarf_CU *const cu = cudie->cu;
+  if (cu->lines == NULL)
+    {
+      /* Failsafe mode: no data found.  */
+      cu->lines = (void *) -1l;
+      cu->files = (void *) -1l;
+
+      /* The die must have a statement list associated.  */
+      Dwarf_Attribute stmt_list_mem;
+      Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
+						       &stmt_list_mem);
+
+      /* Get the offset into the .debug_line section.  NB: this call
+	 also checks whether the previous dwarf_attr call failed.  */
+      Dwarf_Off debug_line_offset;
+      if (__libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE,
+			   NULL, &debug_line_offset) == NULL)
+	return -1;
+
+      if (__libdw_getsrclines (cu->dbg, debug_line_offset,
+			       __libdw_getcompdir (cudie),
+			       cu->address_size, &cu->lines, &cu->files) < 0)
+	return -1;
+    }
+  else if (cu->lines == (void *) -1l)
+    return -1;
+
+  *lines = cu->lines;
+  *nlines = cu->lines->nlines;
+
   // XXX Eventually: unlocking here.
 
-  return res;
+  return 0;
 }
 INTDEF(dwarf_getsrclines)
diff --git a/libdw/dwarf_macro_param1.c b/libdw/dwarf_macro_param1.c
index 35d4a71..337ad6f 100644
--- a/libdw/dwarf_macro_param1.c
+++ b/libdw/dwarf_macro_param1.c
@@ -1,5 +1,5 @@
 /* Return first macro parameter.
-   Copyright (C) 2005 Red Hat, Inc.
+   Copyright (C) 2005, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2005.
 
@@ -40,7 +40,9 @@ dwarf_macro_param1 (Dwarf_Macro *macro, Dwarf_Word *paramp)
   if (macro == NULL)
     return -1;
 
-  *paramp = macro->param1;
+  Dwarf_Attribute param;
+  if (dwarf_macro_param (macro, 1, &param) != 0)
+    return -1;
 
-  return 0;
+  return dwarf_formudata (&param, paramp);
 }
diff --git a/libdw/dwarf_macro_param2.c b/libdw/dwarf_macro_param2.c
index b49e60e..cc902c9 100644
--- a/libdw/dwarf_macro_param2.c
+++ b/libdw/dwarf_macro_param2.c
@@ -1,5 +1,5 @@
 /* Return second macro parameter.
-   Copyright (C) 2005 Red Hat, Inc.
+   Copyright (C) 2005, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2005.
 
@@ -40,10 +40,16 @@ dwarf_macro_param2 (Dwarf_Macro *macro, Dwarf_Word *paramp, const char **strp)
   if (macro == NULL)
     return -1;
 
-  if (paramp != NULL)
-    *paramp = macro->param2.u;
-  if (strp != NULL)
-    *strp = macro->param2.s;
+  Dwarf_Attribute param;
+  if (dwarf_macro_param (macro, 1, &param) != 0)
+    return -1;
 
-  return 0;
+  if (param.form == DW_FORM_string
+      || param.form == DW_FORM_strp)
+    {
+      *strp = dwarf_formstring (&param);
+      return 0;
+    }
+  else
+    return dwarf_formudata (&param, paramp);
 }
diff --git a/libdw/libdw.h b/libdw/libdw.h
index 196d54a..cd7f5d1 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -826,26 +826,86 @@ extern int dwarf_func_inline_instances (Dwarf_Die *func,
 extern int dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts);
 
 
-/* Call callback function for each of the macro information entry for
-   the CU.  */
+/* Iterate through the macro unit referenced by CUDIE and call
+   CALLBACK for each macro information entry.  Keeps iterating while
+   CALLBACK returns DWARF_CB_OK.  If the callback returns
+   DWARF_CB_ABORT, it stops iterating and returns a continuation
+   token, which can be used to restart the iteration at the point
+   where it ended.  Returns -1 for errors or 0 if there are no more
+   macro entries.
+
+   Note that the Dwarf_Macro pointer passed to the callback is only
+   valid for the duration of the callback invocation.
+
+   Note that this interface will refuse to serve opcode 0xff from
+   .debug_macro sections.  Such opcode is considered invalid and will
+   cause dwarf_getmacros to return with error.  Note that this should
+   be no limitation as of now, as DW_MACRO_GNU_* domain doesn't
+   allocate 0xff.  It is however a theoretical possibility with future
+   Dwarf standards.  */
 extern ptrdiff_t dwarf_getmacros (Dwarf_Die *cudie,
 				  int (*callback) (Dwarf_Macro *, void *),
-				  void *arg, ptrdiff_t offset)
-     __nonnull_attribute__ (2);
+				  void *arg, ptrdiff_t token)
+     __nonnull_attribute__ (2);
+
+/* This is similar in operation to dwarf_getmacros, but selects the
+   unit to iterate through by offset instead of by CU.  This can be
+   used for handling DW_MACRO_GNU_transparent_include's or similar
+   opcodes.  Note that with TOKEN of 0, this will always choose to
+   iterate through .debug_macro, never .debug_macinfo.
+
+   It is not appropriate to obtain macro unit offset by hand from a CU
+   DIE and then request iteration through this interface.  The reason
+   for this is that if a dwarf_macro_getsrcfiles is later called,
+   there would be no way to figure out what DW_AT_comp_dir was present
+   on the CU DIE, and file names referenced in either the macro unit
+   itself, or the .debug_line unit that it references, might be wrong.
+   Use dwarf_getmacro.  */
+extern ptrdiff_t dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+				      int (*callback) (Dwarf_Macro *, void *),
+				      void *arg, ptrdiff_t token)
+  __nonnull_attribute__ (3);
 
-/* Return macro opcode.  */
+/* Get the source files used by the macro entry.  You shouldn't assume
+   that Dwarf_Files references will remain valid after MACRO becomes
+   invalid.  (Which is to say it's only valid within the
+   dwarf_getmacros* callback.)  Returns 0 for success or a negative
+   value in case of an error.  */
+extern int dwarf_macro_getsrcfiles (Dwarf *dbg, Dwarf_Macro *macro,
+				    Dwarf_Files **files, size_t *nfiles)
+  __nonnull_attribute__ (2, 3, 4);
+
+/* Return macro opcode.  That's a constant that can be either from
+   DW_MACINFO_* domain if version of MACRO is 0, or from
+   DW_MACRO_GNU_* domain if the version is 4.  */
 extern int dwarf_macro_opcode (Dwarf_Macro *macro, unsigned int *opcodep)
      __nonnull_attribute__ (2);
 
-/* Return first macro parameter.  */
+/* Get number of parameters of MACRO and store it to *PARAMCNTP.  */
+extern int dwarf_macro_getparamcnt (Dwarf_Macro *macro, size_t *paramcntp);
+
+/* Get IDX-th parameter of MACRO, and stores it to *ATTRIBUTE.
+   Returns 0 on success or -1 for errors.
+
+   After a successful call, you can query ATTRIBUTE by dwarf_whatform
+   to determine which of the dwarf_formX calls to make to get actual
+   value out of ATTRIBUTE.  Note that calling dwarf_whatattr is not
+   meaningful for pseudo-attributes formed this way.  */
+extern int dwarf_macro_param (Dwarf_Macro *macro, size_t idx,
+			      Dwarf_Attribute *attribute);
+
+/* Return first macro parameter.  This will return -1 if the parameter
+   is not an integral value.  Use dwarf_macro_param for more general
+   access.  */
 extern int dwarf_macro_param1 (Dwarf_Macro *macro, Dwarf_Word *paramp)
      __nonnull_attribute__ (2);
 
-/* Return second macro parameter.  */
+/* Return second macro parameter.  This will return -1 if the
+   parameter is not an integral or string value.  Use
+   dwarf_macro_param for more general access.  */
 extern int dwarf_macro_param2 (Dwarf_Macro *macro, Dwarf_Word *paramp,
 			       const char **strp);
 
-
 /* Compute what's known about a call frame when the PC is at ADDRESS.
    Returns 0 for success or -1 for errors.
    On success, *FRAME is a malloc'd pointer.  */
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 55bc537..754fb5f 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -306,4 +306,12 @@ ELFUTILS_0.160 {
   global:
     dwarf_cu_getdwarf;
     dwarf_cu_die;
-} ELFUTILS_0.159;
\ No newline at end of file
+} ELFUTILS_0.159;
+
+ELFUTILS_0.161 {
+  global:
+    dwarf_getmacros_off;
+    dwarf_macro_getsrcfiles;
+    dwarf_macro_getparamcnt;
+    dwarf_macro_param;
+} ELFUTILS_0.160;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index c0f3741..1963ef5 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -59,6 +59,14 @@ struct loc_block_s
   size_t length;
 };
 
+/* Already decoded .debug_line units.  */
+struct files_lines_s
+{
+  Dwarf_Off debug_line_offset;
+  Dwarf_Files *files;
+  Dwarf_Lines *lines;
+};
+
 /* Valid indeces for the section data.  */
 enum
   {
@@ -118,7 +126,8 @@ enum
   DWARF_E_INVALID_OFFSET,
   DWARF_E_NO_DEBUG_RANGES,
   DWARF_E_INVALID_CFI,
-  DWARF_E_NO_ALT_DEBUGLINK
+  DWARF_E_NO_ALT_DEBUGLINK,
+  DWARF_E_INVALID_OPCODE,
 };
 
 
@@ -167,6 +176,12 @@ struct Dwarf
   Dwarf_Off next_tu_offset;
   Dwarf_Sig8_Hash sig8_hash;
 
+  /* Search tree for .debug_macro operator tables.  */
+  void *macro_ops;
+
+  /* Search tree for decoded .debug_line units.  */
+  void *files_lines;
+
   /* Address ranges.  */
   Dwarf_Aranges *aranges;
 
@@ -325,18 +340,58 @@ struct Dwarf_CU
    })									      \
 
 
-/* Macro information.  */
+/* Prototype of a single .debug_macro operator.  */
+typedef struct
+{
+  Dwarf_Word nforms;
+  unsigned char const *forms;
+} Dwarf_Macro_Op_Proto;
+
+/* Prototype table.  */
+typedef struct
+{
+  /* Offset of .debug_macro section.  */
+  Dwarf_Off offset;
+
+  /* Offset of associated .debug_line section.  */
+  Dwarf_Off line_offset;
+
+  /* The source file information.  */
+  Dwarf_Files *files;
+
+  /* If this macro unit was opened through dwarf_getmacros or
+     dwarf_getmacros_die, this caches value of DW_AT_comp_dir, if
+     present.  */
+  const char *comp_dir;
+
+  /* Header length.  */
+  Dwarf_Half header_len;
+
+  uint16_t version;
+  bool is_64bit;
+  uint8_t sec_index;	/* IDX_debug_macro or IDX_debug_macinfo.  */
+
+  /* Shows where in TABLE each opcode is defined.  Since opcode 0 is
+     never used, it stores index of opcode X in X-1'th element.  The
+     value of 0xff means not stored at all.  */
+  unsigned char opcodes[255];
+
+  /* Individual opcode prototypes.  */
+  Dwarf_Macro_Op_Proto table[];
+} Dwarf_Macro_Op_Table;
+
 struct Dwarf_Macro_s
 {
-  unsigned int opcode;
-  Dwarf_Word param1;
-  union
-  {
-    Dwarf_Word u;
-    const char *s;
-  } param2;
+  Dwarf_Macro_Op_Table *table;
+  Dwarf_Attribute *attributes;
+  uint8_t opcode;
 };
 
+static inline Dwarf_Word
+libdw_macro_nforms (Dwarf_Macro *macro)
+{
+  return macro->table->table[macro->table->opcodes[macro->opcode - 1]].nforms;
+}
 
 /* We have to include the file at this point because the inline
    functions access internals of the Dwarf structure.  */
@@ -655,6 +710,20 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
 void __libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu)
   internal_function;
 
+/* Load .debug_line unit at DEBUG_LINE_OFFSET.  COMP_DIR is a value of
+   DW_AT_comp_dir or NULL if that attribute is not available.  Caches
+   the loaded unit and optionally set *LINESP and/or *FILESP (if not
+   NULL) with loaded information.  Returns 0 for success or a negative
+   value for failure.  */
+int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
+			 const char *comp_dir, unsigned address_size,
+			 Dwarf_Lines **linesp, Dwarf_Files **filesp)
+  internal_function
+  __nonnull_attribute__ (1);
+
+/* Load and return value of DW_AT_comp_dir from CUDIE.  */
+const char *__libdw_getcompdir (Dwarf_Die *cudie);
+
 
 /* Aliases to avoid PLTs.  */
 INTDECL (dwarf_aggregate_size)
-- 
2.1.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]