On 17-01-19 01:35, Ian Lance Taylor wrote:
> On Wed, Jan 16, 2019 at 4:17 PM Tom de Vries <tdevr...@suse.de> wrote:
>>
>> this handles DW_FORM_GNU_ref_alt which references the .debug_info
>> section in the .gnu_debugaltlink file.
>>
>> OK for trunk?
>>
>> Thanks,
>> - Tom
>>
>> On 11-12-18 11:14, Tom de Vries wrote:
>>> 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.
> 
> 
>>> @@ -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;
> 
> The comments should say what low and high are measured in, which I
> guess is file offset.  Or is it offset from the start of UNIT_DATA?
> Either way, If that is right, then the fields should be named
> low_offset and high_offset.  Otherwise it seems easy to confuse with
> function_addrs, where low and high refer to PC values.
> 

Done.

> Also if they are offsets from UNIT_DATA then size_t is OK, but if the
> are file offsets they should be off_t.
> 

AFAIU, in the case where off_t vs size_t would make a difference, we're
running into trouble much earlier.  I've filed PR 88890 - "libbacktrace
on 32-bit system with _FILE_OFFSET_BITS == 64" (
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88890 ) about this.

Anyway, I've made the conservative choice of using off_t for now (but I
could argue that it's a memory offset, given that the assumption is that
the entire debug section is read into memory).

>>> @@ -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);
> 
> s/Could/could/
> 
> or maybe just skip this error_callback call as discussed earlier.
> 
> 

Skipped.

>>> +       return NULL;
>>> +     }
>>> +      uint64_t unit_offset = val->u.uint - alt_unit->low;
> 
> Earlier a unit_offset was the offset of the unit within unit_data, but
> here this is an offset within a single unit.  This should just be
> called offset, which is the name used by read_referenced_name.
> 

Done.

> This is OK with those changes.

Committed in two parts.

First part ...


[libbacktrace] Add find_unit

Add a function that finds the unit given an offset into .debug_info.

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

	* dwarf.c (struct unit): Add low_offset and high_offset fields.
	(struct unit_vector): New type.
	(struct dwarf_data): Add units and units_counts fields.
	(find_unit): New function.
	(find_address_ranges): Add and handle unit_tag parameter.
	(build_address_map): Add and handle units_vec parameter.
	(build_dwarf_data): Pass units_vec to build_address_map.  Store resulting
	units vector.

---
 libbacktrace/dwarf.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 76 insertions(+), 11 deletions(-)

diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c
index 45691b4ba69..6f56c46774b 100644
--- a/libbacktrace/dwarf.c
+++ b/libbacktrace/dwarf.c
@@ -281,6 +281,12 @@ struct unit
   /* The offset of UNIT_DATA from the start of the information for
      this compilation unit.  */
   size_t unit_data_offset;
+  /* Offset of the start of the compilation unit from the start of the
+     .debug_info section.  */
+  off_t low_offset;
+  /* Offset of the end of the compilation unit from the start of the
+     .debug_info section.  */
+  off_t high_offset;
   /* 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;
@@ -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 off_t *key = (const off_t *) vkey;
+  const struct unit *entry = *((const struct unit *const *) ventry);
+  off_t offset;
+
+  offset = *key;
+  if (offset < entry->low_offset)
+    return -1;
+  else if (offset >= entry->high_offset)
+    return 1;
+  else
+    return 0;
+}
+
+/* Find a unit in PU containing OFFSET.  */
+
+static struct unit *
+find_unit (struct unit **pu, size_t units_count, off_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.  */
 
@@ -1298,7 +1344,8 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
 		     size_t dwarf_ranges_size,
 		     int is_bigendian, struct dwarf_data *altlink,
 		     backtrace_error_callback error_callback, void *data,
-		     struct unit *u, struct unit_addrs_vector *addrs)
+		     struct unit *u, struct unit_addrs_vector *addrs,
+		     enum dwarf_tag *unit_tag)
 {
   while (unit_buf->left > 0)
     {
@@ -1321,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;
@@ -1433,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, altlink, error_callback, data,
-				    u, addrs))
+				    u, addrs, NULL))
 	    return 0;
 	}
     }
@@ -1453,7 +1503,8 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address,
 		   const unsigned char *dwarf_str, size_t dwarf_str_size,
 		   int is_bigendian, struct dwarf_data *altlink,
 		   backtrace_error_callback error_callback, void *data,
-		   struct unit_addrs_vector *addrs)
+		   struct unit_addrs_vector *addrs,
+		   struct unit_vector *unit_vec)
 {
   struct dwarf_buf info;
   struct backtrace_vector units;
@@ -1461,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;
+  off_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
@@ -1492,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;
@@ -1534,6 +1589,9 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address,
 
       addrsize = read_byte (&unit_buf);
 
+      u->low_offset = unit_offset;
+      unit_offset += len + (is_dwarf64 ? 12 : 4);
+      u->high_offset = 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;
@@ -1555,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, altlink, error_callback, data,
-				u, addrs))
+				u, addrs, &unit_tag))
 	goto fail;
 
       if (unit_buf.reported_underflow)
 	goto fail;
 
-      if (addrs->count > prev_addrs_count)
+      if (addrs->count > prev_addrs_count || unit_tag == DW_TAG_partial_unit)
 	prev_addrs_count = addrs->count;
       else
 	{
@@ -1576,11 +1634,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:
@@ -3031,21 +3086,29 @@ 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, altlink, error_callback, data,
-			  &addrs_vec))
+			  &addrs_vec, &units_vec))
     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),
@@ -3058,6 +3121,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;

Reply via email to