]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Support .debug_macro
authorPetr Machata <pmachata@redhat.com>
Fri, 17 Oct 2014 00:47:03 +0000 (02:47 +0200)
committerPetr Machata <pmachata@redhat.com>
Fri, 17 Oct 2014 00:51:22 +0000 (02:51 +0200)
- 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
libdw/Makefile.am
libdw/dwarf_end.c
libdw/dwarf_error.c
libdw/dwarf_getmacros.c
libdw/dwarf_getsrclines.c
libdw/dwarf_macro_param1.c
libdw/dwarf_macro_param2.c
libdw/libdw.h
libdw/libdw.map
libdw/libdwP.h

index 89b2735fbc40504299b553fcb0a69d15751ac8e5..37e8700a75e0cb72a1897244bd13dee91d933a54 100644 (file)
@@ -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.
index 2e42a3764e0d63c0624078f7f589856e54e381a1..12e0fff19e2b5c8d3a1eeb3d3508e80d786fc2c2 100644 (file)
@@ -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 \
index 241a257a234108d3728833a6c6f2838461141976..647a1b8d1c749954292e6578540da98233de06fd 100644 (file)
@@ -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)
index 229291414666276ea77937428445f432f1159d65..08b691aac5fe8b3d9390bd8ec1652a304254a849 100644 (file)
@@ -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]))
 
index a3d2c81662fd19cc65707bf6948772269d3e8431..7f889a91fb506e87d4f94f1e14907e5bfd90a032 100644 (file)
@@ -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.
 
 # 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);
 }
index 74d626cf9b574a0bf32dad0bc579f548b340d16d..1f460ec26bdfccda2dd74855cb636d56393f63e4 100644 (file)
@@ -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)
index 35d4a7180891f9f8eb7035754a427f7bf4ffdc2e..337ad6f008d2e0d853e7912f235ff6468816d884 100644 (file)
@@ -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);
 }
index b49e60ea215c2c262eeea603f9725d09dccbd5c1..cc902c9997e5a384a09b7521b7839eece9c3a80c 100644 (file)
@@ -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);
 }
index 196d54ae0c1d5bb8b0c7e08ecf2775cfd65432b4..cd7f5d15e55a50c77dc63bc83a88fe37e1ed7310 100644 (file)
@@ -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.  */
index 55bc5371a25967de247ace601f25762d70280bf7..754fb5fb443eb5285f7b15648d86e9ab6d88c710 100644 (file)
@@ -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;
index c0f3741c85191c7e318eebe8f2d6eec9b8c31f8b..1963ef58b7cd1e0412869d091eada167f01d7f8c 100644 (file)
@@ -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)