The scenario goes like this.
* end iterator has an offset of 1
* begin iterator has an offset of 0
* operator ++ does essentially the following:
_m_offset = ::dwarf_getattrs (&_m_die._m_die, &getattrs_callback,
(void *) this, _m_offset);
* operator == takes into account "parental" DIE and _m_offset
getattrs_callback is done such that it answers CB_OK when called the
first time (then it also initializes _m_attr.value), and CB_ABORT when
called the second time. This way dwarf_getattrs returns the position of
_next_ attribute in sequence. But that means that when the last
argument is loaded in _m_attr, _m_offset already has a value of 1, and
evaluates as equal to the end iterator.
As a fix for that, I propose to consider also NULL-ness of _m_attr.value
when doing the iterator comparison. On empty sequences, the callback is
never called, thus leaving _m_attr.value NULL. On sequence of one
element, _m_offset will end up being 1, but _m_attr.value will be
non-NULL. Only after the call to ++ will _m_attr.value become NULL, and
dwarf_getattrs, seeing offset of 1, will immediately return 1 again
without calling the callback. Etc.
Patch attached.
PM
From 3e9c7e605e666ef32d1dddd93f9d303ebd801e67 Mon Sep 17 00:00:00 2001
From: Petr Machata <pmachata(a)redhat.com>
Date: Tue, 24 Mar 2009 15:04:53 +0100
Subject: [PATCH] <dwarf>: Don't swallow last attribute
---
libdw/ChangeLog | 5 +++++
libdw/c++/dwarf | 11 ++++++++---
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index fc08176..d68075d 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2009-03-24 Petr Machata <pmachata(a)redhat.com>
+
+ * c++/dwarf (dwarf::debug_info_entry::raw_attributes):
+ Fix iteration over attributes.
+
2009-02-26 Roland McGrath <roland(a)redhat.com>
* c++/dwarf (dwarf::attr_value): Add _m_tag private member.
diff --git a/libdw/c++/dwarf b/libdw/c++/dwarf
index 862b9a1..33e7250 100644
--- a/libdw/c++/dwarf
+++ b/libdw/c++/dwarf
@@ -626,7 +626,10 @@ namespace elfutils
}
inline const_iterator (const debug_info_entry &die, ptrdiff_t offset)
- : _m_die (die), _m_offset (offset) {}
+ : _m_die (die), _m_offset (offset)
+ {
+ _m_attr.valp = NULL;
+ }
public:
inline const_iterator (const const_iterator &i)
@@ -643,7 +646,8 @@ namespace elfutils
inline bool operator== (const const_iterator &other) const
{
return (_m_die._m_die.addr == other._m_die._m_die.addr
- && _m_offset == other._m_offset);
+ && _m_offset == other._m_offset
+ && !_m_attr.valp == !other._m_attr.valp);
}
inline bool operator!= (const const_iterator &other) const
{
@@ -659,6 +663,7 @@ namespace elfutils
_m_offset = result;
return *this;
}
+
inline const_iterator operator++ (int) // postfix
{
const_iterator prev = *this;
@@ -668,7 +673,7 @@ namespace elfutils
inline attribute operator* () const
{
- if (unlikely (_m_offset == 1))
+ if (unlikely (_m_offset == 1 && _m_attr.valp == NULL))
throw std::runtime_error ("dereferencing end iterator");
return attribute (_m_die, _m_attr);
}
--
1.6.0.6