2018-12-10  Tom de Vries  <tdevr...@suse.de>

        * dwarf.c (enum attr_val_encoding): Add ATTR_VAL_REF_ALT_INFO.
        (struct unit): Add low and high fields.
        (struct unit_vector): New type.
        (struct dwarf_data): Add units and units_counts fields.
        (read_attribute): Handle DW_FORM_GNU_ref_alt using
        ATTR_VAL_REF_ALT_INFO.
        (find_unit): New function.
        (find_address_ranges): Add and handle unit_tag parameter.
        (build_address_map): Add and handle units_vec parameter.
        (read_referenced_name_1): Handle DW_FORM_GNU_ref_alt.
        (build_dwarf_data): Pass units_vec to build_address_map.  Store 
resulting
        units vector.
---
 libbacktrace/dwarf.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 91 insertions(+), 10 deletions(-)

diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c
index 99e5f4c3f51..9a0b93120c8 100644
--- a/libbacktrace/dwarf.c
+++ b/libbacktrace/dwarf.c
@@ -143,6 +143,8 @@ enum attr_val_encoding
   ATTR_VAL_REF_UNIT,
   /* An offset to other data within the .dwarf_info section.  */
   ATTR_VAL_REF_INFO,
+  /* An offset to other data within the alt .dwarf_info section.  */
+  ATTR_VAL_REF_ALT_INFO,
   /* An offset to data in some other section.  */
   ATTR_VAL_REF_SECTION,
   /* A type signature.  */
@@ -281,6 +283,10 @@ struct unit
   /* The offset of UNIT_DATA from the start of the information for
      this compilation unit.  */
   size_t unit_data_offset;
+  /* Start of the compilation unit.  */
+  size_t low;
+  /* End of the compilation unit.  */
+  size_t high;
   /* DWARF version.  */
   int version;
   /* Whether unit is DWARF64.  */
@@ -339,6 +345,14 @@ struct unit_addrs_vector
   size_t count;
 };
 
+/* A growable vector of compilation unit pointer.  */
+
+struct unit_vector
+{
+  struct backtrace_vector vec;
+  size_t count;
+};
+
 /* The information we need to map a PC to a file and line.  */
 
 struct dwarf_data
@@ -353,6 +367,10 @@ struct dwarf_data
   struct unit_addrs *addrs;
   /* Number of address ranges in list.  */
   size_t addrs_count;
+  /* A sorted list of units.  */
+  struct unit **units;
+  /* Number of units in the list.  */
+  size_t units_count;
   /* The unparsed .debug_info section.  */
   const unsigned char *dwarf_info;
   size_t dwarf_info_size;
@@ -840,7 +858,7 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf,
          val->encoding = ATTR_VAL_NONE;
          return 1;
        }
-      val->encoding = ATTR_VAL_REF_SECTION;
+      val->encoding = ATTR_VAL_REF_ALT_INFO;
       return 1;
     case DW_FORM_GNU_strp_alt:
       {
@@ -866,6 +884,34 @@ read_attribute (enum dwarf_form form, struct dwarf_buf 
*buf,
     }
 }
 
+/* Compare a unit offset against a unit for bsearch.  */
+
+static int
+units_search (const void *vkey, const void *ventry)
+{
+  const uintptr_t *key = (const uintptr_t *) vkey;
+  const struct unit *entry = *((const struct unit *const *) ventry);
+  uintptr_t offset;
+
+  offset = *key;
+  if (offset < entry->low)
+    return -1;
+  else if (offset >= entry->high)
+    return 1;
+  else
+    return 0;
+}
+
+/* Find a unit in PU containing OFFSET.  */
+
+static struct unit *
+find_unit (struct unit **pu, size_t units_count, size_t offset)
+{
+  struct unit **u;
+  u = bsearch (&offset, pu, units_count, sizeof (struct unit *), units_search);
+  return u == NULL ? NULL : *u;
+}
+
 /* Compare function_addrs for qsort.  When ranges are nested, make the
    smallest one sort last.  */
 
@@ -1299,7 +1345,7 @@ find_address_ranges (struct backtrace_state *state, 
uintptr_t base_address,
                     int is_bigendian, backtrace_error_callback error_callback,
                     void *data, struct unit *u,
                     struct unit_addrs_vector *addrs,
-                    struct dwarf_data *altlink)
+                    struct dwarf_data *altlink, enum dwarf_tag *unit_tag)
 {
   while (unit_buf->left > 0)
     {
@@ -1322,6 +1368,9 @@ find_address_ranges (struct backtrace_state *state, 
uintptr_t base_address,
       if (abbrev == NULL)
        return 0;
 
+      if (unit_tag != NULL)
+       *unit_tag = abbrev->tag;
+
       lowpc = 0;
       have_lowpc = 0;
       highpc = 0;
@@ -1434,7 +1483,7 @@ find_address_ranges (struct backtrace_state *state, 
uintptr_t base_address,
                                    dwarf_str, dwarf_str_size,
                                    dwarf_ranges, dwarf_ranges_size,
                                    is_bigendian, error_callback, data,
-                                   u, addrs, altlink))
+                                   u, addrs, altlink, NULL))
            return 0;
        }
     }
@@ -1454,6 +1503,7 @@ build_address_map (struct backtrace_state *state, 
uintptr_t base_address,
                   const unsigned char *dwarf_str, size_t dwarf_str_size,
                   int is_bigendian, backtrace_error_callback error_callback,
                   void *data, struct unit_addrs_vector *addrs,
+                  struct unit_vector *unit_vec,
                   struct dwarf_data *altlink)
 {
   struct dwarf_buf info;
@@ -1462,9 +1512,12 @@ build_address_map (struct backtrace_state *state, 
uintptr_t base_address,
   size_t i;
   struct unit **pu;
   size_t prev_addrs_count;
+  size_t unit_offset = 0;
 
   memset (&addrs->vec, 0, sizeof addrs->vec);
+  memset (&unit_vec->vec, 0, sizeof unit_vec->vec);
   addrs->count = 0;
+  unit_vec->count = 0;
   prev_addrs_count = 0;
 
   /* Read through the .debug_info section.  FIXME: Should we use the
@@ -1493,6 +1546,7 @@ build_address_map (struct backtrace_state *state, 
uintptr_t base_address,
       uint64_t abbrev_offset;
       int addrsize;
       struct unit *u;
+      enum dwarf_tag unit_tag;
 
       if (info.reported_underflow)
        goto fail;
@@ -1535,6 +1589,9 @@ build_address_map (struct backtrace_state *state, 
uintptr_t base_address,
 
       addrsize = read_byte (&unit_buf);
 
+      u->low = unit_offset;
+      unit_offset += len + (is_dwarf64 ? 12 : 4);
+      u->high = unit_offset;
       u->unit_data = unit_buf.buf;
       u->unit_data_len = unit_buf.left;
       u->unit_data_offset = unit_buf.buf - unit_data_start;
@@ -1556,13 +1613,13 @@ build_address_map (struct backtrace_state *state, 
uintptr_t base_address,
                                dwarf_str, dwarf_str_size,
                                dwarf_ranges, dwarf_ranges_size,
                                is_bigendian, error_callback, data,
-                               u, addrs, altlink))
+                               u, addrs, altlink, &unit_tag))
        goto fail;
 
       if (unit_buf.reported_underflow)
        goto fail;
 
-      if (addrs->count == prev_addrs_count)
+      if (unit_tag != DW_TAG_partial_unit && addrs->count == prev_addrs_count)
        {
          --units_count;
          units.size -= sizeof (u);
@@ -1576,11 +1633,8 @@ build_address_map (struct backtrace_state *state, 
uintptr_t base_address,
   if (info.reported_underflow)
     goto fail;
 
-  // We only kept the list of units to free them on failure.  On
-  // success the units are retained, pointed to by the entries in
-  // addrs.
-  backtrace_vector_free (state, &units, error_callback, data);
-
+  unit_vec->vec = units;
+  unit_vec->count = units_count;
   return 1;
 
  fail:
@@ -2144,6 +2198,22 @@ read_referenced_name_1 (struct dwarf_data *ddata, struct 
unit *u,
       || val->encoding == ATTR_VAL_REF_UNIT)
     return read_referenced_name (ddata, u, val->u.uint, error_callback, data);
 
+  if (val->encoding == ATTR_VAL_REF_ALT_INFO)
+    {
+      struct unit *alt_unit
+       = find_unit (ddata->altlink->units, ddata->altlink->units_count,
+                    val->u.uint);
+      if (alt_unit == NULL)
+       {
+         error_callback (data,
+                         "Could not find unit for DW_FORM_GNU_ref_alt", 0);
+         return NULL;
+       }
+      uint64_t unit_offset = val->u.uint - alt_unit->low;
+      return read_referenced_name (ddata->altlink, alt_unit, unit_offset,
+                                  error_callback, data);
+    }
+
   return NULL;
 }
 
@@ -3028,21 +3098,30 @@ build_dwarf_data (struct backtrace_state *state,
   struct unit_addrs_vector addrs_vec;
   struct unit_addrs *addrs;
   size_t addrs_count;
+  struct unit_vector units_vec;
+  struct unit **units;
+  size_t units_count;
   struct dwarf_data *fdata;
 
   if (!build_address_map (state, base_address, dwarf_info, dwarf_info_size,
                          dwarf_abbrev, dwarf_abbrev_size, dwarf_ranges,
                          dwarf_ranges_size, dwarf_str, dwarf_str_size,
                          is_bigendian, error_callback, data, &addrs_vec,
+                         &units_vec,
                          altlink))
     return NULL;
 
   if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data))
     return NULL;
+  if (!backtrace_vector_release (state, &units_vec.vec, error_callback, data))
+    return NULL;
   addrs = (struct unit_addrs *) addrs_vec.vec.base;
+  units = (struct unit **) units_vec.vec.base;
   addrs_count = addrs_vec.count;
+  units_count = units_vec.count;
   backtrace_qsort (addrs, addrs_count, sizeof (struct unit_addrs),
                   unit_addrs_compare);
+  /* No qsort for units required, already sorted.  */
 
   fdata = ((struct dwarf_data *)
           backtrace_alloc (state, sizeof (struct dwarf_data),
@@ -3055,6 +3134,8 @@ build_dwarf_data (struct backtrace_state *state,
   fdata->base_address = base_address;
   fdata->addrs = addrs;
   fdata->addrs_count = addrs_count;
+  fdata->units = units;
+  fdata->units_count = units_count;
   fdata->dwarf_info = dwarf_info;
   fdata->dwarf_info_size = dwarf_info_size;
   fdata->dwarf_line = dwarf_line;
-- 
2.16.4

Reply via email to