libdw: unify die->abbrev lookups

Add a new internal function, __libdw_dieabbrev, which deals with checking
a die for an abbrev, and setting it as needed.

Signed-off-by: Josh Stone <jistone@redhat.com>
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index b9b868b..0d3150b 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,11 @@
+2014-12-10  Josh Stone  <jistone@redhat.com>
+
+	* libdwP.h (__libdw_dieabbrev): New die->abbrev lookup function.
+	* dwarf_child.c (__libdw_find_attr, dwarf_child): Use it.
+	* dwarf_getattrs.c (dwarf_getattrs): Likewise.
+	* dwarf_haschildren.c (dwarf_haschildren): Likewise.
+	* dwarf_tag.c (dwarf_tag): Likewise.
+
 2014-12-04  Mark Wielaard  <mjw@redhat.com>
 
 	* libdwP.h (__libdw_form_val_compute_len): Add endp argument.
diff --git a/libdw/dwarf_child.c b/libdw/dwarf_child.c
index fa31600..daf4c26 100644
--- a/libdw/dwarf_child.c
+++ b/libdw/dwarf_child.c
@@ -44,21 +44,11 @@
 		   unsigned int *codep, unsigned int *formp)
 {
   Dwarf *dbg = die->cu->dbg;
-  const unsigned char *readp = (unsigned char *) die->addr;
-
-  /* First we have to get the abbreviation code so that we can decode
-     the data in the DIE.  */
-  unsigned int abbrev_code;
-  get_uleb128 (abbrev_code, readp);
+  const unsigned char *readp;
 
   /* Find the abbreviation entry.  */
-  Dwarf_Abbrev *abbrevp = die->abbrev;
-  if (abbrevp == NULL)
-    {
-      abbrevp = __libdw_findabbrev (die->cu, abbrev_code);
-      die->abbrev = abbrevp ?: DWARF_END_ABBREV;
-    }
-  if (unlikely (die->abbrev == DWARF_END_ABBREV))
+  Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, &readp);
+  if (unlikely (abbrevp == DWARF_END_ABBREV))
     {
     invalid_dwarf:
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
@@ -70,7 +60,7 @@
     = ((unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf
        + dbg->sectiondata[IDX_debug_abbrev]->d_size);
 
-  const unsigned char *attrp = die->abbrev->attrp;
+  const unsigned char *attrp = abbrevp->attrp;
   while (1)
     {
       /* Are we still in bounds?  This test needs to be refined.  */
@@ -137,21 +127,21 @@
   if (die == NULL)
     return -1;
 
-  /* Skip past the last attribute.  */
-  void *addr = NULL;
+  /* Find the abbreviation entry.  */
+  Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, NULL);
+  if (unlikely (abbrevp == DWARF_END_ABBREV))
+    {
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return -1;
+    }
 
-  /* If we already know there are no children do not search.  */
-  if (die->abbrev != DWARF_END_ABBREV
-      && (die->abbrev == NULL || die->abbrev->has_children))
-    addr = __libdw_find_attr (die, INVALID, NULL, NULL);
-  if (unlikely (die->abbrev == (Dwarf_Abbrev *) -1l))
-    return -1;
-
-  /* Make sure the DIE really has children.  */
-  if (! die->abbrev->has_children)
-    /* There cannot be any children.  */
+  /* If there are no children, do not search.  */
+  if (! abbrevp->has_children)
     return 1;
 
+  /* Skip past the last attribute.  */
+  void *addr = __libdw_find_attr (die, INVALID, NULL, NULL);
+
   if (addr == NULL)
     return -1;
 
diff --git a/libdw/dwarf_getattrs.c b/libdw/dwarf_getattrs.c
index 627a851..9ea70fc 100644
--- a/libdw/dwarf_getattrs.c
+++ b/libdw/dwarf_getattrs.c
@@ -44,17 +44,12 @@
   if (unlikely (offset == 1))
     return 1;
 
-  const unsigned char *die_addr = die->addr;
+  const unsigned char *die_addr;
 
-  /* Get the abbreviation code.  */
-  unsigned int u128;
-  get_uleb128 (u128, die_addr);
+  /* Find the abbreviation entry.  */
+  Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, &die_addr);
 
-  if (die->abbrev == NULL)
-    /* Find the abbreviation.  */
-    die->abbrev = __libdw_findabbrev (die->cu, u128);
-
-  if (unlikely (die->abbrev == DWARF_END_ABBREV))
+  if (unlikely (abbrevp == DWARF_END_ABBREV))
     {
     invalid_dwarf:
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
@@ -62,8 +57,8 @@
     }
 
   /* This is where the attributes start.  */
-  const unsigned char *attrp = die->abbrev->attrp;
-  const unsigned char *const offset_attrp = die->abbrev->attrp + offset;
+  const unsigned char *attrp = abbrevp->attrp;
+  const unsigned char *const offset_attrp = abbrevp->attrp + offset;
 
   /* Go over the list of attributes.  */
   Dwarf *dbg = die->cu->dbg;
@@ -105,7 +100,7 @@
 	    /* Return the offset of the start of the attribute, so that
 	       dwarf_getattrs() can be restarted from this point if the
 	       caller so desires.  */
-	    return remembered_attrp - die->abbrev->attrp;
+	    return remembered_attrp - abbrevp->attrp;
 	}
 
       /* Skip over the rest of this attribute (if there is any).  */
diff --git a/libdw/dwarf_haschildren.c b/libdw/dwarf_haschildren.c
index b227398..d0ce51e 100644
--- a/libdw/dwarf_haschildren.c
+++ b/libdw/dwarf_haschildren.c
@@ -1,5 +1,5 @@
 /* Return string associated with given attribute.
-   Copyright (C) 2003, 2005, 2008 Red Hat, Inc.
+   Copyright (C) 2003, 2005, 2008, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -40,25 +40,13 @@
      Dwarf_Die *die;
 {
   /* Find the abbreviation entry.  */
-  Dwarf_Abbrev *abbrevp = die->abbrev;
-  if (abbrevp != DWARF_END_ABBREV)
-    {
-      const unsigned char *readp = (unsigned char *) die->addr;
-
-      /* First we have to get the abbreviation code so that we can decode
-	 the data in the DIE.  */
-      unsigned int abbrev_code;
-      get_uleb128 (abbrev_code, readp);
-
-      abbrevp = __libdw_findabbrev (die->cu, abbrev_code);
-      die->abbrev = abbrevp ?: DWARF_END_ABBREV;
-    }
-  if (unlikely (die->abbrev == DWARF_END_ABBREV))
+  Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, NULL);
+  if (unlikely (abbrevp == DWARF_END_ABBREV))
     {
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
       return -1;
     }
 
-  return die->abbrev->has_children;
+  return abbrevp->has_children;
 }
 INTDEF (dwarf_haschildren)
diff --git a/libdw/dwarf_tag.c b/libdw/dwarf_tag.c
index ff012cf..0b1a4b0 100644
--- a/libdw/dwarf_tag.c
+++ b/libdw/dwarf_tag.c
@@ -1,5 +1,5 @@
 /* Return tag of given DIE.
-   Copyright (C) 2003-2011 Red Hat, Inc.
+   Copyright (C) 2003-2011, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -82,24 +82,14 @@
 dwarf_tag (die)
      Dwarf_Die *die;
 {
-  /* Do we already know the abbreviation?  */
-  if (die->abbrev == NULL)
-    {
-      /* Get the abbreviation code.  */
-      unsigned int u128;
-      const unsigned char *addr = die->addr;
-      get_uleb128 (u128, addr);
-
-      /* Find the abbreviation.  */
-      die->abbrev = __libdw_findabbrev (die->cu, u128);
-    }
-
-  if (unlikely (die->abbrev == DWARF_END_ABBREV))
+  /* Find the abbreviation entry.  */
+  Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, NULL);
+  if (unlikely (abbrevp == DWARF_END_ABBREV))
     {
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
       return DW_TAG_invalid;
     }
 
-  return die->abbrev->tag;
+  return abbrevp->tag;
 }
 INTDEF(dwarf_tag)
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 351c5b4..0633853 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -444,7 +444,7 @@
 extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset, bool tu)
      __nonnull_attribute__ (1) internal_function;
 
-/* Return tag of given DIE.  */
+/* Get abbreviation with given code.  */
 extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
 					 unsigned int code)
      __nonnull_attribute__ (1) internal_function;
@@ -455,6 +455,29 @@
 					Dwarf_Abbrev *result)
      __nonnull_attribute__ (1) internal_function;
 
+/* Get abbreviation of given DIE, and optionally set *READP to the DIE memory
+   just past the abbreviation code.  */
+static inline Dwarf_Abbrev *
+__nonnull_attribute__ (1)
+__libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp)
+{
+  /* Do we need to get the abbreviation, or need to read after the code?  */
+  if (die->abbrev == NULL || readp != NULL)
+    {
+      /* Get the abbreviation code.  */
+      unsigned int code;
+      const unsigned char *addr = die->addr;
+      get_uleb128 (code, addr);
+      if (readp != NULL)
+	*readp = addr;
+
+      /* Find the abbreviation.  */
+      if (die->abbrev == NULL)
+	die->abbrev = __libdw_findabbrev (die->cu, code);
+    }
+  return die->abbrev;
+}
+
 /* Helper functions for form handling.  */
 extern size_t __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
 					    unsigned int form,