public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-20  9:28 Mark Wielaard
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Wielaard @ 2015-04-20  9:28 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 4309 bytes --]

Hi Petr,

On Fri, 2015-04-17 at 18:54 +0200, Petr Machata wrote:
> Mark Wielaard <mjw@redhat.com> writes:
> > On Thu, 2015-04-16 at 17:42 +0200, Petr Machata wrote:
> >> Mark Wielaard <mjw@redhat.com> writes:
> >> As long as your iteration is limited to well-formed sub-tree (i.e. you
> >> don't wan't to e.g. start at one leaf node and progress through half the
> >> CU to another leaf node), the simple vector of offsets would, I think,
> >> be still enough to keep all the context that you need.  There might be
> >> code that assumes that iteration starts and ends at CU DIE's, but that
> >> could be transparently fixed.
> >
> > So, this does concern me a little. We have to be absolutely sure that
> > keeping the state as a vector of offsets is all we ever need.
> 
> The only problem that I see is what to do with the m_cuit.  For
> DIE-to-DIE iteration, it would probably be set to unit_iterator::end().
> That now represents an end-iterator.  So end iterator would be
> represented as m_die.addr == NULL instead--no valid DIE will have a NULL
> address.

OK. I keep mixing issues. But indeed subtree DIE iteration is a separate
from issue the logical/raw walks.

> >> I'm not sure.  I don't think so, at least you would need a different way
> >> of keeping track of context.
> >
> > Could we maybe abstract away the m_stack context (maybe pimpl it)?
> 
> Yes, if you decide to overload the iterator to do both logical and raw
> iteration.  I don't think that's the right approach.

OK.

> I meant the new iterator.  Splitting the concerns into a raw iterator
> and a high-level one that builds upon the raw one absolutely seems the
> right approach to me.  The raw iterator doesn't end up paying the
> overhead for raw iteration, and the complexities of both raw and logical
> iteration will be kept to their respective silos.
> 
> Just to make sure I'm not talking out of my ass, I put together a
> logical_die_tree_iterator.  It's on pmachata/iterators.

Thanks. I'll work on the plain C implementation of "logical" DIE walks,
then we can use that in the future.

> >> I still think exposing raw iterator is the right way forward, because it
> >> is a logical building block.  A logical tree iterator can be built on
> >> top of that even without C support, if that's what's deemed best.
> >
> > If none of the above convinced you otherwise then lets just go with the
> > code you wrote now. But only on the condition that you come up with a
> > non-stupid name for the future "logical_die_tree_iterator" :)
> 
> Note that the raw/logical distinction applies at least to child_iterator
> as well

Yes. If a DIE has a DW_TAG_imported_unit as child, the logical thing to
do is to iterate over its children too.

> , maybe also to unit_iterator.

When using a unit_iterator you should be able to iterate over
(combinations) of the different unit types. But I don't think there is a
logical/raw distinction.

And that is also the only thing I am still slightly confused about.
Because the die_tree_iterator takes a Dwarf or unit_iterator to iterate
over all the DIEs in the Dwarf or (remaining) units, I would like to
indicate which units are interesting (e.g. I only want to iterate over
DIEs in the compile_units and type_units, but not the partial_units).
Should this depend on the type (raw or logical) of the
die_tree_iterator?

>   I think something can be done to
> make logical_die_tree_iterator::move configurable, so that it's useful
> in child iterator as well.
> 
> (One could almost write a template for logical iteration that is
> parametrized by iterator type, but not quite: for the tree iterator one
> needs to skip the root that it's constructed from, but with child
> iterator that's ignored implicitly.  Besides, templates are ABI
> nightmare.)

If we end up with a template implementation, I think it would be better
to just add a boolean flag to the constructors instead to indicate
raw/logical walks.

> Regarding the names, I don't have any good ideas.  In dwgrep I ended up
> with raw and cooked, which is in hindsight perhaps too cute.  Maybe we
> can rename the current bunch to raw_*, and add the logical ones without
> a prefix.

I think that would be best.

Cheers,

Mark

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-17 16:54 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-17 16:54 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 4612 bytes --]

Mark Wielaard <mjw@redhat.com> writes:

> On Thu, 2015-04-16 at 17:42 +0200, Petr Machata wrote:
>> Mark Wielaard <mjw@redhat.com> writes:
>> > The only disadvantage of that seems to be that it would immediately
>> > introduce a v2 variant if we do it after a public release.
>> 
>> I don't even think it would.  Adding a new constructor doesn't break any
>> existing clients.  API-wise it would be stable as well, this adds use
>> cases, doesn't change any (not even by way of implicit conversions from,
>> say, Dwarf_Die * to unit_iterator).
>
> That is a nice surprise. Looking for more background on C++ ABI
> compatibility issues I found the following overview handy:
> https://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++

Yes, that's a great resource.

> So you can always add new functions, including constructors, to classes
> and don't break ABI. But because our classes are now not pimpl'd we have

Non-virtual member function, including constructors, are essentially the
good old C functions with funny names.  Things break around virtuals.

> to be careful not to change/add/delete any state data member field or we
> will break ABI (and will have to introduce a new v2 namespace).

Yes.

>> As long as your iteration is limited to well-formed sub-tree (i.e. you
>> don't wan't to e.g. start at one leaf node and progress through half the
>> CU to another leaf node), the simple vector of offsets would, I think,
>> be still enough to keep all the context that you need.  There might be
>> code that assumes that iteration starts and ends at CU DIE's, but that
>> could be transparently fixed.
>
> So, this does concern me a little. We have to be absolutely sure that
> keeping the state as a vector of offsets is all we ever need.

The only problem that I see is what to do with the m_cuit.  For
DIE-to-DIE iteration, it would probably be set to unit_iterator::end().
That now represents an end-iterator.  So end iterator would be
represented as m_die.addr == NULL instead--no valid DIE will have a NULL
address.

>> I'm not sure.  I don't think so, at least you would need a different way
>> of keeping track of context.
>
> Could we maybe abstract away the m_stack context (maybe pimpl it)?

Yes, if you decide to overload the iterator to do both logical and raw
iteration.  I don't think that's the right approach.

>> Personally I think adding a new iterator that builds on the raw one
>> is the best option.
>
>> One could also make the new iterator configurable, like Frank says in
>> his mail.  Then you would have a single tree iterator type that can
>> behave either way.
>
> Somehow this appeals more to me. But I admit to not know why. Less
> classes feels better somehow. Or maybe it is just that the name
> die_tree_iterator sounds more generic than it actually is. Those might
> be totally misguided feelings though.

I meant the new iterator.  Splitting the concerns into a raw iterator
and a high-level one that builds upon the raw one absolutely seems the
right approach to me.  The raw iterator doesn't end up paying the
overhead for raw iteration, and the complexities of both raw and logical
iteration will be kept to their respective silos.

Just to make sure I'm not talking out of my ass, I put together a
logical_die_tree_iterator.  It's on pmachata/iterators.

>> I still think exposing raw iterator is the right way forward, because it
>> is a logical building block.  A logical tree iterator can be built on
>> top of that even without C support, if that's what's deemed best.
>
> If none of the above convinced you otherwise then lets just go with the
> code you wrote now. But only on the condition that you come up with a
> non-stupid name for the future "logical_die_tree_iterator" :)

Note that the raw/logical distinction applies at least to child_iterator
as well, maybe also to unit_iterator.  I think something can be done to
make logical_die_tree_iterator::move configurable, so that it's useful
in child iterator as well.

(One could almost write a template for logical iteration that is
parametrized by iterator type, but not quite: for the tree iterator one
needs to skip the root that it's constructed from, but with child
iterator that's ignored implicitly.  Besides, templates are ABI
nightmare.)

Regarding the names, I don't have any good ideas.  In dwgrep I ended up
with raw and cooked, which is in hindsight perhaps too cute.  Maybe we
can rename the current bunch to raw_*, and add the logical ones without
a prefix.

Thanks,
Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-16 19:23 Mark Wielaard
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Wielaard @ 2015-04-16 19:23 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 4211 bytes --]

On Thu, 2015-04-16 at 17:42 +0200, Petr Machata wrote:
> Mark Wielaard <mjw@redhat.com> writes:
> 
> > But given the nice idea above of defining a "next iterator as end" for
> > another. Would adding that functionality just be introducing a new
> > constructor that takes a Dwarf_Die and a next_sibling () method that
> > returns an iterator that one can use as above in a for loop to test
> > whether one has walked a subtree?
> 
> Yes, that's how I'd do it.
> 
> > The only disadvantage of that seems to be that it would immediately
> > introduce a v2 variant if we do it after a public release.
> 
> I don't even think it would.  Adding a new constructor doesn't break any
> existing clients.  API-wise it would be stable as well, this adds use
> cases, doesn't change any (not even by way of implicit conversions from,
> say, Dwarf_Die * to unit_iterator).

That is a nice surprise. Looking for more background on C++ ABI
compatibility issues I found the following overview handy:
https://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++

So you can always add new functions, including constructors, to classes
and don't break ABI. But because our classes are now not pimpl'd we have
to be careful not to change/add/delete any state data member field or we
will break ABI (and will have to introduce a new v2 namespace).

> As long as your iteration is limited to well-formed sub-tree (i.e. you
> don't wan't to e.g. start at one leaf node and progress through half the
> CU to another leaf node), the simple vector of offsets would, I think,
> be still enough to keep all the context that you need.  There might be
> code that assumes that iteration starts and ends at CU DIE's, but that
> could be transparently fixed.

So, this does concern me a little. We have to be absolutely sure that
keeping the state as a vector of offsets is all we ever need.

> > I was thinking just doing it on the C++ level. But yes, it would be nice
> > to also have that on the C level libdw interface. That was this
> > discussion where you came up with a pretty nice plan:
> > https://lists.fedorahosted.org/pipermail/elfutils-devel/2014-October/004204.html
> >
> > Again my worry is about whether retrofitting such a design into the
> > current class is possible or not. Would it be as simple as adding a new
> > constructor, a boolean field and using the new C functions (and updating
> > the version) or would we need a whole new class/interface for it?
> 
> I'm not sure.  I don't think so, at least you would need a different way
> of keeping track of context.

Could we maybe abstract away the m_stack context (maybe pimpl it)?

>   Personally I think adding a new iterator
> that builds on the raw one is the best option.
> 
> That would mean that you couldn't pass a logical iterator to a function
> that expects a raw one.  But you can't likewise pass, say, a child
> iterator to such function--the genericity is just not there.  This sort
> of thing would have to be achieved by making the function template.

The important thing seems to be to be able to turn the iterator into an
actual Dwarf_Die. Which is possible by simply dereferencing the child of
die_tree iterator. So I agree that it isn't too important to be able to
mix-and-match different kinds of iterators.

> One could also make the new iterator configurable, like Frank says in
> his mail.  Then you would have a single tree iterator type that can
> behave either way.

Somehow this appeals more to me. But I admit to not know why. Less
classes feels better somehow. Or maybe it is just that the name
die_tree_iterator sounds more generic than it actually is. Those might
be totally misguided feelings though.

> I still think exposing raw iterator is the right way forward, because it
> is a logical building block.  A logical tree iterator can be built on
> top of that even without C support, if that's what's deemed best.

If none of the above convinced you otherwise then lets just go with the
code you wrote now. But only on the condition that you come up with a
non-stupid name for the future "logical_die_tree_iterator" :)

Thanks,

Mark

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-16 15:42 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-16 15:42 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 2638 bytes --]

Mark Wielaard <mjw@redhat.com> writes:

> But given the nice idea above of defining a "next iterator as end" for
> another. Would adding that functionality just be introducing a new
> constructor that takes a Dwarf_Die and a next_sibling () method that
> returns an iterator that one can use as above in a for loop to test
> whether one has walked a subtree?

Yes, that's how I'd do it.

> The only disadvantage of that seems to be that it would immediately
> introduce a v2 variant if we do it after a public release.

I don't even think it would.  Adding a new constructor doesn't break any
existing clients.  API-wise it would be stable as well, this adds use
cases, doesn't change any (not even by way of implicit conversions from,
say, Dwarf_Die * to unit_iterator).

As long as your iteration is limited to well-formed sub-tree (i.e. you
don't wan't to e.g. start at one leaf node and progress through half the
CU to another leaf node), the simple vector of offsets would, I think,
be still enough to keep all the context that you need.  There might be
code that assumes that iteration starts and ends at CU DIE's, but that
could be transparently fixed.

> I was thinking just doing it on the C++ level. But yes, it would be nice
> to also have that on the C level libdw interface. That was this
> discussion where you came up with a pretty nice plan:
> https://lists.fedorahosted.org/pipermail/elfutils-devel/2014-October/004204.html
>
> Again my worry is about whether retrofitting such a design into the
> current class is possible or not. Would it be as simple as adding a new
> constructor, a boolean field and using the new C functions (and updating
> the version) or would we need a whole new class/interface for it?

I'm not sure.  I don't think so, at least you would need a different way
of keeping track of context.  Personally I think adding a new iterator
that builds on the raw one is the best option.

That would mean that you couldn't pass a logical iterator to a function
that expects a raw one.  But you can't likewise pass, say, a child
iterator to such function--the genericity is just not there.  This sort
of thing would have to be achieved by making the function template.

One could also make the new iterator configurable, like Frank says in
his mail.  Then you would have a single tree iterator type that can
behave either way.

I still think exposing raw iterator is the right way forward, because it
is a logical building block.  A logical tree iterator can be built on
top of that even without C support, if that's what's deemed best.

Thanks,
Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-16 15:06 Mark Wielaard
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Wielaard @ 2015-04-16 15:06 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 3193 bytes --]

On Thu, 2015-04-16 at 15:52 +0200, Petr Machata wrote:
> Mark Wielaard <mjw@redhat.com> writes:
> 
> > Is that a common use case? I would have imagined that you would iterate
> > over just the Dwarf_Dies of one particular unit separately or given a
> 
> That would be along these lines:
> 
>   for (die_tree_iterator it (uit), end (++uit); it != end; ++it)
>     ;

O, that is pretty nice. I hadn't thought of that.

> > particular Dwarf_Die would like it to iterate over just the die_tree
> > under that DIE. Given the current design of the die_tree_iterator how
> > would one do that?
> 
> This is not implemented, though I admit it would be a useful feature.
> At this point however I would very much like to keep the scope of the
> patch as it is now.

My problem is that I am not yet so fluent in my C++ thinking to know
whether that would fit the current design or that it would need a
completely different class and implementation. I would very much like to
avoid adding code that we then have to maintain but that we cannot
easily extend to similar use cases and end up with lots of interfaces
and implementations.

But given the nice idea above of defining a "next iterator as end" for
another. Would adding that functionality just be introducing a new
constructor that takes a Dwarf_Die and a next_sibling () method that
returns an iterator that one can use as above in a for loop to test
whether one has walked a subtree?

The only disadvantage of that seems to be that it would immediately
introduce a v2 variant if we do it after a public release.

> > Also I think that we really should provide an easy way to walk the
> > logical DIE tree. Which means resolving DW_TAG_imported_unit DIEs and
> > just continue with the children of the imported unit at that point.
> > There should of course also be an option to walk the "raw" DIE tree. But
> > it feels wrong to let the user do logical walks on their own, checking
> > for imported_unit tags, walking the imported_unit children tree, keep
> > their own parent stack, etc.
> 
> I agree, this would be tremendously useful--we discussed this recently
> on this list after all.  But it should be implemented in C libdw, and
> only then (optionally) reused in the C++ wrappers.

I was thinking just doing it on the C++ level. But yes, it would be nice
to also have that on the C level libdw interface. That was this
discussion where you came up with a pretty nice plan:
https://lists.fedorahosted.org/pipermail/elfutils-devel/2014-October/004204.html

Again my worry is about whether retrofitting such a design into the
current class is possible or not. Would it be as simple as adding a new
constructor, a boolean field and using the new C functions (and updating
the version) or would we need a whole new class/interface for it?

So my only worry about just adding this code as is now, is that I don't
have a good feeling for how to evolve this C++ code without creating an
unmaintainable mess or abandoning the current code/design. If you could
handwave a plausible path forward to add these things then I would agree
with adding the code as is.

Thanks,

Mark

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-16 14:07 Frank Ch. Eigler
  0 siblings, 0 replies; 16+ messages in thread
From: Frank Ch. Eigler @ 2015-04-16 14:07 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 594 bytes --]

Hi -

On Thu, Apr 16, 2015 at 03:29:27PM +0200, Mark Wielaard wrote:

> [...]  I have a small concern about the unit_iterator to make it
> "future proof". One of the suggestions for DWARFv5 is to merge the
> type units into .debug_info from the separate .debug_type
> section. [...]

Just a general comment about such configurability: it could be
expressed by flags set on the iterator, or alternate iterator-creation
functions (::begin_typerec() for a type-unit-recursive variant).  Sort
of similar how in systemtap we have many variants of
parse-tree-visitor classes.

- FChE

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-16 13:52 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-16 13:52 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 1653 bytes --]

Mark Wielaard <mjw@redhat.com> writes:

> Is that a common use case? I would have imagined that you would iterate
> over just the Dwarf_Dies of one particular unit separately or given a

That would be along these lines:

  for (die_tree_iterator it (uit), end (++uit); it != end; ++it)
    ;

> particular Dwarf_Die would like it to iterate over just the die_tree
> under that DIE. Given the current design of the die_tree_iterator how
> would one do that?

This is not implemented, though I admit it would be a useful feature.
At this point however I would very much like to keep the scope of the
patch as it is now.

> Also I think that we really should provide an easy way to walk the
> logical DIE tree. Which means resolving DW_TAG_imported_unit DIEs and
> just continue with the children of the imported unit at that point.
> There should of course also be an option to walk the "raw" DIE tree. But
> it feels wrong to let the user do logical walks on their own, checking
> for imported_unit tags, walking the imported_unit children tree, keep
> their own parent stack, etc.

I agree, this would be tremendously useful--we discussed this recently
on this list after all.  But it should be implemented in C libdw, and
only then (optionally) reused in the C++ wrappers.

> That of course does mean the current internal tracking using just a
> Dwarf_Off is not enough, because resolving imported_units might cross
> Dwarfs.

One option would be to build the logical iterator on top of the raw
iterator, and track import history in a vector of raw iterators.  That's
essentially what dwgrep does.

Thanks,
Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-16 13:29 Mark Wielaard
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Wielaard @ 2015-04-16 13:29 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 4467 bytes --]

Hi Petr,

Sorry for being slow. Struggling with both the DWARF and C++ concepts at
the same time is a little daunting. I am happy with the implementation
and C++ concepts used. But still have some questions on how we expose
some of the DWARF concepts.

On Tue, 2015-04-14 at 16:47 +0200, Petr Machata wrote:
> - every iterator implemented in an own .cc module
> 
> - new header files libdw, libdwfl with iterator declarations, and a new
>   library libdwpp (-ldwpp) that ships the code
> 
> - the iterators are in versioned namespaces.  When changes are
>   introduced, new version namespace is added, in which the new
>   interfaces are specified and then the old namespace is imported,
>   bringing is the rest that wasn't changed.  The new namespace then gets
>   imported into the top-level "elfutils" namespace, so that people can
>   simply write elfutils::die_tree_iterator.  Under the hood, this
>   resolves to, say, elfutils::v1::die_tree_iterator, and that's what the
>   ABI-level symbol is.
> 
>   This allows us to expose the layout of the individual classes, and
>   avoid the overhead of pimpl.  It also avoids the messiness of symbol
>   version files.
> 
>   Select private symbols that need header-file visibility were defined
>   as attribute_hidden, the rest is either visible, or stashed in
>   anonymous namespaces in .cc.

I like all this. Thanks.

> - Add unit_iterator, child_iterator, die_tree_iterator and
>   attr_iterator for libdw, and dwfl_module_iterator for libdwfl.

I like the child, attr and dwfl_module iterators.

I have a small concern about the unit_iterator to make it "future
proof". One of the suggestions for DWARFv5 is to merge the type units
into .debug_info from the separate .debug_type section. See 
http://dwarfstd.org/ShowIssue.php?issue=130526.1
This will cause us some pain because it changes the CU header adding an
extra field (for all CU types). Luckily the length and version fields
stay at the start, so the libdw parsing code should be able to cope when
we see the new version. But it might cause some brainteasers to keep the
dwarf_next_unit, dwarf_offdie and dwarf_offdie_types functions sane
(maybe we'll just need new versions to cope with DWARFv5). It looks like
the C++ unit_iterator interface abstracts all these implementation
issues away. But it might be a good idea to already add the notion of
unit_type to the struct unit_info.

Or maybe not, you get the Dwarf_Die cudie so you can already inspect the
tag. And we iterate over all unit types already (compile, partial,
type). So the only thing to change for handling DWARFv5 correctly would
be the private unit_iterator::move () implementation. We just should
make clear the order is not guaranteed. OK. I don't have any concern
anymore. It is good as it is :)

But I am a little confused about the die_tree_iterator. There are two
issues I don't fully understand. That might be because my imagined use
case just doesn't the use case this was designed for.

It looks like the use case this was designed for was to iterate over all
Dwarf_Dies in one single Dwarf file or starting at a particular
unit_iterator to iterate over all remaining units in a Dwarf file and
provide all (raw) Dwarf_Dies.

Is that a common use case? I would have imagined that you would iterate
over just the Dwarf_Dies of one particular unit separately or given a
particular Dwarf_Die would like it to iterate over just the die_tree
under that DIE. Given the current design of the die_tree_iterator how
would one do that?

Also I think that we really should provide an easy way to walk the
logical DIE tree. Which means resolving DW_TAG_imported_unit DIEs and
just continue with the children of the imported unit at that point.
There should of course also be an option to walk the "raw" DIE tree. But
it feels wrong to let the user do logical walks on their own, checking
for imported_unit tags, walking the imported_unit children tree, keep
their own parent stack, etc.

That of course does mean the current internal tracking using just a
Dwarf_Off is not enough, because resolving imported_units might cross
Dwarfs.

Let me know what you think of the use case that involves just iterating
over a specific (logical) Dwarf_Die subtree. Should we introduce some
completely different iterator for that? Or is that done easily "by hand"
using the current iterators?

Thanks,

Mark

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 2/2] Add C++ iterators
@ 2015-04-14 14:47 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-14 14:47 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 51333 bytes --]

Hi there,

I think there's enough context on the list for the patch, so just a
quick recap:

- every iterator implemented in an own .cc module

- new header files libdw, libdwfl with iterator declarations, and a new
  library libdwpp (-ldwpp) that ships the code

- the iterators are in versioned namespaces.  When changes are
  introduced, new version namespace is added, in which the new
  interfaces are specified and then the old namespace is imported,
  bringing is the rest that wasn't changed.  The new namespace then gets
  imported into the top-level "elfutils" namespace, so that people can
  simply write elfutils::die_tree_iterator.  Under the hood, this
  resolves to, say, elfutils::v1::die_tree_iterator, and that's what the
  ABI-level symbol is.

  This allows us to expose the layout of the individual classes, and
  avoid the overhead of pimpl.  It also avoids the messiness of symbol
  version files.

  Select private symbols that need header-file visibility were defined
  as attribute_hidden, the rest is either visible, or stashed in
  anonymous namespaces in .cc.

- dwgrep has been ported to this and works (though this is not pushed
  yet for obvious reasons), the elfutils-internal test case has been
  extended a bit to cover more of the API.

Thanks,
Petr

--- 8< ----------------------------------------------------------
- Add unit_iterator, child_iterator, die_tree_iterator and
  attr_iterator for libdw, and dwfl_module_iterator for libdwfl.

- The code is shipped in a new library called libdwpp.

Signed-off-by: Petr Machata <pmachata@redhat.com>
---
 ChangeLog                           |   4 +
 NEWS                                |   5 +-
 libdw/ChangeLog                     |  19 +++
 libdw/Makefile.am                   |  59 ++++++++-
 libdw/c++/attr_iterator.cc          | 144 +++++++++++++++++++++
 libdw/c++/child_iterator.cc         |  98 ++++++++++++++
 libdw/c++/die_tree_iterator.cc      | 178 +++++++++++++++++++++++++
 libdw/c++/libdw                     | 251 ++++++++++++++++++++++++++++++++++++
 libdw/c++/libdwP.hh                 |  88 +++++++++++++
 libdw/c++/unit_iterator.cc          | 146 +++++++++++++++++++++
 libdwfl/ChangeLog                   |  13 ++
 libdwfl/Makefile.am                 |  33 ++++-
 libdwfl/c++/dwfl_module_iterator.cc | 128 ++++++++++++++++++
 libdwfl/c++/libdwfl                 |  78 +++++++++++
 libdwfl/c++/libdwflP.hh             |  44 +++++++
 tests/ChangeLog                     |   7 +
 tests/Makefile.am                   |  13 +-
 tests/run-test-iterators.sh         |  52 ++++++++
 tests/test-iterators.cc             |  87 +++++++++++++
 19 files changed, 1440 insertions(+), 7 deletions(-)
 create mode 100644 libdw/c++/attr_iterator.cc
 create mode 100644 libdw/c++/child_iterator.cc
 create mode 100644 libdw/c++/die_tree_iterator.cc
 create mode 100644 libdw/c++/libdw
 create mode 100644 libdw/c++/libdwP.hh
 create mode 100644 libdw/c++/unit_iterator.cc
 create mode 100644 libdwfl/c++/dwfl_module_iterator.cc
 create mode 100644 libdwfl/c++/libdwfl
 create mode 100644 libdwfl/c++/libdwflP.hh
 create mode 100755 tests/run-test-iterators.sh
 create mode 100644 tests/test-iterators.cc

diff --git a/ChangeLog b/ChangeLog
index 65c3ef5..9633bfa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2015-04-14  Petr Machata  <pmachata@redhat.com>
 
+	* NEWS: Mention <elfutils/libdw>, <elfutils/libdwfl>, and libdwpp.
+
+2015-04-14  Petr Machata  <pmachata@redhat.com>
+
 	* configure.ac (HAVE_CXX): New conditional.
 
 2015-03-13  Mark Wielaard  <mjw@redhat.com>
diff --git a/NEWS b/NEWS
index 60aa995..02f8691 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 Version 0.162
 
-libdw: Install new header elfutils/known-dwarf.h.
+libdw: Install new headers elfutils/known-dwarf.h, elfutils/libdw.
+libdwfl: Install new header elfutils/libdwfl.
+libdwpp: New library, contains the code for <elfutils/libdw> and
+         <elfutils/libdwfl> C++ interfaces.
 
 Version 0.161
 
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 3abb382..a03d119 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,22 @@
+2015-04-14  Petr Machata  <pmachata@redhat.com>
+
+	* c++: New directory.
+	* c++/libdw, c++/libdwP.hh: New header files.
+	* c++/attr_iterator.cc, c++/child_iterator.cc: New files.
+	* c++/die_tree_iterator.cc, c++/unit_iterator.cc: Likewise.
+	* Makefile.am (AUTOMAKE_OPTIONS): New variable.
+	[HAVE_CXX] (lib_LIBRARIES): Add libdwpp.a.
+	[HAVE_CXX] (noinst_LIBRARIES): Add libdwpp_pic.a.
+	[HAVE_CXX] (pkginclude_HEADERS): Add c++/libdw.
+	[HAVE_CXX] (libdwpp_a_SOURCES, libdwpp_so_SOURCES): New variables.
+	[HAVE_CXX] (libdwpp_pic_a_SOURCES, am_libdwpp_pic_a_OBJECTS): Likewise.
+	[HAVE_CXX] (libdwpp.so$(EXEEXT)): New rule.
+	(INSTALL_DEPS): New variable, contains libdwpp.so if HAVE_CXX.
+	(install): Depend on $(INSTALL_DEPS).
+	[HAVE_CXX] (install): Install libdwpp.so et.al.
+	[HAVE_CXX] (uninstall): Uninstall them.
+	(MOSTLYCLEANFILES): Add libdwpp-related files.
+
 2015-04-01  Petr Machata  <pmachata@redhat.com>
 
 	* libdwP.h (DWARF_E_NOT_CUDIE): New enumerator.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 272289c..43e83b5 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2002-2010, 2012, 2014 Red Hat, Inc.
+## Copyright (C) 2002-2010, 2012, 2014, 2015 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -34,12 +34,26 @@ endif
 AM_CPPFLAGS += -I$(srcdir)/../libelf
 VERSION = 1
 
+# For c++/ subdir.
+AUTOMAKE_OPTIONS = subdir-objects
+
 lib_LIBRARIES = libdw.a
+if HAVE_CXX
+lib_LIBRARIES += libdwpp.a
+endif
+
 noinst_LIBRARIES = libdw_pic.a
+if HAVE_CXX
+noinst_LIBRARIES += libdwpp_pic.a
+endif
+
 noinst_PROGRAMS = $(noinst_LIBRARIES:_pic.a=.so)
 
 include_HEADERS = dwarf.h
 pkginclude_HEADERS = libdw.h known-dwarf.h
+if HAVE_CXX
+pkginclude_HEADERS += c++/libdw
+endif
 
 libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_getpubnames.c dwarf_getabbrev.c dwarf_tag.c \
@@ -91,6 +105,11 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_getalt.c dwarf_setalt.c dwarf_cu_getdwarf.c \
 		  dwarf_cu_die.c dwarf_peel_type.c
 
+if HAVE_CXX
+libdwpp_a_SOURCES = c++/unit_iterator.cc c++/die_tree_iterator.cc	\
+		    c++/child_iterator.cc c++/attr_iterator.cc
+endif
+
 if MAINTAINER_MODE
 BUILT_SOURCES = $(srcdir)/known-dwarf.h
 MAINTAINERCLEANFILES = $(srcdir)/known-dwarf.h
@@ -102,6 +121,11 @@ endif
 libdw_pic_a_SOURCES =
 am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os)
 
+if HAVE_CXX
+libdwpp_pic_a_SOURCES =
+am_libdwpp_pic_a_OBJECTS = $(libdwpp_a_SOURCES:.cc=.os)
+endif
+
 libdw_so_SOURCES =
 libdw.so$(EXEEXT): $(srcdir)/libdw.map libdw_pic.a ../libdwelf/libdwelf_pic.a \
 	  ../libdwfl/libdwfl_pic.a ../libebl/libebl.a \
@@ -116,16 +140,44 @@ libdw.so$(EXEEXT): $(srcdir)/libdw.map libdw_pic.a ../libdwelf/libdwelf_pic.a \
 	@$(textrel_check)
 	ln -fs $@ $@.$(VERSION)
 
-install: install-am libdw.so
+if HAVE_CXX
+libdwpp_so_SOURCES =
+libdwpp.so$(EXEEXT): libdwpp_pic.a ../libdwfl/libdwflpp_pic.a	\
+		     libdw.so$(EXEEXT)
+	$(CXXLINK) -shared -o $@ -Wl,--soname,$@.$(VERSION),-z,defs \
+		-Wl,--enable-new-dtags,--no-undefined \
+		-Wl,--whole-archive $^ -Wl,--no-whole-archive\
+		libdw.so$(EXEEXT)
+	@$(textrel_check)
+	ln -fs $@ $@.$(VERSION)
+endif
+
+if HAVE_CXX
+INSTALL_DEPS = libdwpp.so
+else
+INSTALL_DEPS =
+endif
+
+install: install-am libdw.so $(INSTALL_DEPS)
 	$(mkinstalldirs) $(DESTDIR)$(libdir)
 	$(INSTALL_PROGRAM) libdw.so $(DESTDIR)$(libdir)/libdw-$(PACKAGE_VERSION).so
 	ln -fs libdw-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdw.so.$(VERSION)
 	ln -fs libdw.so.$(VERSION) $(DESTDIR)$(libdir)/libdw.so
+if HAVE_CXX
+	$(INSTALL_PROGRAM) libdwpp.so $(DESTDIR)$(libdir)/libdwpp-$(PACKAGE_VERSION).so
+	ln -fs libdwpp-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdwpp.so.$(VERSION)
+	ln -fs libdwpp.so.$(VERSION) $(DESTDIR)$(libdir)/libdwpp.so
+endif
 
 uninstall: uninstall-am
 	rm -f $(DESTDIR)$(libdir)/libdw-$(PACKAGE_VERSION).so
 	rm -f $(DESTDIR)$(libdir)/libdw.so.$(VERSION)
 	rm -f $(DESTDIR)$(libdir)/libdw.so
+if HAVE_CXX
+	rm -f $(DESTDIR)$(libdir)/libdwpp-$(PACKAGE_VERSION).so
+	rm -f $(DESTDIR)$(libdir)/libdwpp.so.$(VERSION)
+	rm -f $(DESTDIR)$(libdir)/libdwpp.so
+endif
 	rmdir --ignore-fail-on-non-empty $(DESTDIR)$(includedir)/elfutils
 
 libdwfl_objects = $(shell $(AR) t ../libdwfl/libdwfl.a)
@@ -139,4 +191,5 @@ noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \
 
 EXTRA_DIST = libdw.map
 
-MOSTLYCLEANFILES = $(am_libdw_pic_a_OBJECTS) libdw.so.$(VERSION)
+MOSTLYCLEANFILES = $(am_libdw_pic_a_OBJECTS) libdw.so.$(VERSION) \
+	$(am_libdwpp_pic_a_OBJECTS) libdwpp.so.$(VERSION)
diff --git a/libdw/c++/attr_iterator.cc b/libdw/c++/attr_iterator.cc
new file mode 100644
index 0000000..7c4f59b
--- /dev/null
+++ b/libdw/c++/attr_iterator.cc
@@ -0,0 +1,144 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdw"
+#include "libdwP.hh"
+
+namespace
+{
+  struct cb_data
+  {
+    // The visited attribute.
+    Dwarf_Attribute *at;
+
+    // Whether this is second pass through the callback.
+    bool been;
+  };
+
+  int
+  callback (Dwarf_Attribute *at, void *data)
+  {
+    cb_data *d = static_cast <cb_data *> (data);
+    if (d->been)
+      return DWARF_CB_ABORT;
+
+    *d->at = *at;
+    d->been = true;
+
+    // Do a second iteration to find the next offset.
+    return DWARF_CB_OK;
+  }
+}
+
+attribute_hidden
+bool
+elfutils::v1::attr_iterator::move ()
+{
+  cb_data data = {&m_at, false};
+
+  // m_offset of 1 means there are no more attributes.
+  if (m_offset != 1)
+    {
+      m_offset = dwarf_getattrs (m_die, &callback, &data, m_offset);
+      if (m_offset == -1)
+	throw_libdw ();
+    }
+
+  return data.been;
+}
+
+attribute_hidden
+elfutils::v1::attr_iterator::attr_iterator (end_it)
+  : m_die (NULL)
+{}
+
+elfutils::v1::attr_iterator::attr_iterator (Dwarf_Die *die)
+  : m_die (die)
+  , m_at ((Dwarf_Attribute) {0, 0, NULL, NULL})
+  , m_offset (0)
+{
+  // Initial move, which can turn this into an end iterator.
+  ++*this;
+}
+
+elfutils::v1::attr_iterator
+elfutils::v1::attr_iterator::end ()
+{
+  return attr_iterator (end_it ());
+}
+
+bool
+elfutils::v1::attr_iterator::operator== (attr_iterator const &that) const
+{
+  return (m_die == NULL && that.m_die == NULL)
+    || (m_die != NULL && that.m_die != NULL
+	&& m_offset == that.m_offset
+	&& m_at.code == that.m_at.code);
+}
+
+bool
+elfutils::v1::attr_iterator::operator!= (attr_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::v1::attr_iterator &
+elfutils::v1::attr_iterator::operator++ ()
+{
+  assert (m_die != NULL);
+
+  if (! move ())
+    *this = end ();
+
+  return *this;
+}
+
+elfutils::v1::attr_iterator
+elfutils::v1::attr_iterator::operator++ (int)
+{
+  attr_iterator tmp = *this;
+  ++*this;
+  return tmp;
+}
+
+Dwarf_Attribute &
+elfutils::v1::attr_iterator::operator* ()
+{
+  assert (m_die != NULL);
+  return m_at;
+}
+
+Dwarf_Attribute *
+elfutils::v1::attr_iterator::operator-> ()
+{
+  return &**this;
+}
diff --git a/libdw/c++/child_iterator.cc b/libdw/c++/child_iterator.cc
new file mode 100644
index 0000000..63414d5
--- /dev/null
+++ b/libdw/c++/child_iterator.cc
@@ -0,0 +1,98 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdw"
+#include "libdwP.hh"
+
+#include <cstring>
+
+attribute_hidden
+elfutils::v1::child_iterator::child_iterator (end_it)
+{
+  m_die.addr = NULL;
+}
+
+elfutils::v1::child_iterator::child_iterator (Dwarf_Die parent)
+{
+  if (! dwpp_child (parent, m_die))
+    *this = end ();
+}
+
+elfutils::v1::child_iterator
+elfutils::v1::child_iterator::end ()
+{
+  return child_iterator (end_it ());
+}
+
+bool
+elfutils::v1::child_iterator::operator== (child_iterator const &that) const
+{
+  return m_die.addr == that.m_die.addr;
+}
+
+bool
+elfutils::v1::child_iterator::operator!= (child_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::v1::child_iterator &
+elfutils::v1::child_iterator::operator++ ()
+{
+  assert (m_die.addr != NULL);
+
+  if (! dwpp_siblingof (m_die, m_die))
+    *this = end ();
+
+  return *this;
+}
+
+elfutils::v1::child_iterator
+elfutils::v1::child_iterator::operator++ (int)
+{
+  child_iterator ret = *this;
+  ++*this;
+  return ret;
+}
+
+Dwarf_Die &
+elfutils::v1::child_iterator::operator* ()
+{
+  assert (m_die.addr != NULL);
+  return m_die;
+}
+
+Dwarf_Die *
+elfutils::v1::child_iterator::operator-> ()
+{
+  return &**this;
+}
diff --git a/libdw/c++/die_tree_iterator.cc b/libdw/c++/die_tree_iterator.cc
new file mode 100644
index 0000000..9e2cd35
--- /dev/null
+++ b/libdw/c++/die_tree_iterator.cc
@@ -0,0 +1,178 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+#include <algorithm>
+
+#include "libdw"
+#include "libdwP.hh"
+
+namespace
+{
+  Dwarf_Die
+  offdie (elfutils::v1::unit_iterator &cuit, Dwarf_Off offset)
+  {
+    return (dwarf_tag (&cuit->cudie) == DW_TAG_type_unit
+	    ? dwpp_offdie_types : dwpp_offdie)
+      (dwarf_cu_getdwarf (cuit->cudie.cu), offset);
+  }
+}
+
+attribute_hidden
+bool
+elfutils::v1::die_tree_iterator::move ()
+{
+  Dwarf_Die child;
+  if (dwpp_child (m_die, child))
+    {
+      m_stack.push_back (dwarf_dieoffset (&m_die));
+      m_die = child;
+      return true;
+    }
+
+  do
+    if (dwpp_siblingof (m_die, m_die))
+      return true;
+    else
+      // No sibling found.  Go a level up and retry, unless this
+      // was a sole, childless CU DIE.
+      if (! m_stack.empty ())
+	{
+	  m_die = offdie (m_cuit, m_stack.back ());
+	  m_stack.pop_back ();
+	}
+  while (! m_stack.empty ());
+
+  if (++m_cuit == elfutils::v1::unit_iterator::end ())
+    return false;
+
+  m_die = m_cuit->cudie;
+  return true;
+}
+
+attribute_hidden
+elfutils::v1::die_tree_iterator::die_tree_iterator (end_it)
+  : m_cuit (unit_iterator::end ())
+{}
+
+elfutils::v1::die_tree_iterator::die_tree_iterator (Dwarf *dw)
+  : m_cuit (unit_iterator (dw))
+{
+  if (m_cuit != unit_iterator::end ())
+    m_die = m_cuit->cudie;
+}
+
+elfutils::v1::die_tree_iterator::die_tree_iterator (unit_iterator const &cuit)
+  : m_cuit (cuit)
+{
+  if (m_cuit != unit_iterator::end ())
+    m_die = m_cuit->cudie;
+}
+
+elfutils::v1::die_tree_iterator
+elfutils::v1::die_tree_iterator::end ()
+{
+  return die_tree_iterator (end_it ());
+}
+
+bool
+elfutils::v1::die_tree_iterator::operator== (die_tree_iterator
+						const &that) const
+{
+  return m_cuit == that.m_cuit
+    && (m_cuit == unit_iterator::end () || m_die.addr == that.m_die.addr);
+}
+
+bool
+elfutils::v1::die_tree_iterator::operator!= (die_tree_iterator
+						const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::v1::die_tree_iterator &
+elfutils::v1::die_tree_iterator::operator++ ()
+{
+  assert (m_cuit != unit_iterator::end ());
+
+  if (! move ())
+    *this = end ();
+
+  return *this;
+}
+
+elfutils::v1::die_tree_iterator
+elfutils::v1::die_tree_iterator::operator++ (int)
+{
+  die_tree_iterator ret = *this;
+  ++*this;
+  return ret;
+}
+
+Dwarf_Die &
+elfutils::v1::die_tree_iterator::operator* ()
+{
+  assert (m_cuit != unit_iterator::end ());
+  return m_die;
+}
+
+Dwarf_Die *
+elfutils::v1::die_tree_iterator::operator-> ()
+{
+  return &**this;
+}
+
+elfutils::v1::die_tree_iterator
+elfutils::v1::die_tree_iterator::parent ()
+{
+  assert (m_cuit != unit_iterator::end ());
+
+  if (m_stack.empty ())
+    return end ();
+
+  die_tree_iterator ret = *this;
+  ret.m_die = offdie (m_cuit, m_stack.back ());
+  ret.m_stack.pop_back ();
+
+  return ret;
+}
+
+std::vector <Dwarf_Die>
+elfutils::v1::path_from_root (die_tree_iterator &it)
+{
+  std::vector <Dwarf_Die> ret;
+  for (die_tree_iterator end = die_tree_iterator::end ();
+       it != end; it = it.parent ())
+    ret.push_back (*it);
+  std::reverse (ret.begin (), ret.end ());
+  return ret;
+}
diff --git a/libdw/c++/libdw b/libdw/c++/libdw
new file mode 100644
index 0000000..e9bd4bb
--- /dev/null
+++ b/libdw/c++/libdw
@@ -0,0 +1,251 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBDW_CPP
+#define _LIBDW_CPP	1
+
+#include <vector>
+#include <iterator>
+
+#include "libdw.h"
+
+namespace elfutils
+{
+namespace v1
+{
+  // The iterators presented here have operators * and -> as non-const
+  // member functions.  The reason is that the libdw interfaces do not
+  // take, say, Dwarf_Die const *, but Dwarf_Die *, and therefore
+  // returning a const reference of some sort would not be useful.  We
+  // don't want to give out copies either, as that adds unnecessary
+  // overhead.  And we simply don't care much if anyone does end up
+  // changing the internal copy of the current CU, DIE, or whatever.
+
+
+  // An iterator that goes over compile units (full or partial) in a
+  // given DWARF file.  The type that it points to (yields on
+  // dereference) is CU_INFO (see below).
+  //
+  // Example usage:
+  // {
+  //   std::vector <Dwarf_Off> v
+  //   for (elfutils::cu_iterator jt (dw);
+  //        jt != elfutils::cu_iterator::end (); ++jt)
+  //     v.push_back (dwarf_dieoffset (&jt->cudie));
+  // }
+  class unit_iterator;
+
+  // Helper structure with data about each compile unit.
+  struct unit_info
+  {
+    Dwarf_Die cudie;
+    Dwarf_Off abbrev_offset;
+    uint64_t type_signature; // Valid for DW_TAG_type_unit
+    Dwarf_Half version;
+    uint8_t address_size;
+    uint8_t offset_size;
+  };
+
+  class unit_iterator
+    : public std::iterator <std::input_iterator_tag, unit_info>
+  {
+    Dwarf *m_dw;
+    Dwarf_Off m_offset;
+    Dwarf_Off m_old_offset;
+    unit_info m_info;
+    bool m_types;
+
+    struct end_it {};
+    explicit unit_iterator (end_it);
+
+    bool move ();
+
+  public:
+    // Construct a unit iterator that will iterate through all units
+    // in DW.
+    explicit unit_iterator (Dwarf *dw);
+
+    // Construct a unit iterator for DW such that it points to a
+    // compile (or other type of) unit represented by CUDIE.
+    unit_iterator (Dwarf *dw, Dwarf_Die cudie);
+
+    // Return a unit_iterator pointing one after the last actual CU.
+    static unit_iterator end ();
+
+    bool operator== (unit_iterator const &that) const;
+    bool operator!= (unit_iterator const &that) const;
+
+    unit_iterator &operator++ ();
+    unit_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    unit_info &operator* ();
+    unit_info *operator-> ();
+  };
+
+
+  // An iterator that goes through children of a given DIE.
+  // Example usage:
+  // {
+  //    size_t nchildren = std::distance (elfutils::child_iterator (type_die),
+  //                                      elfutils::child_iterator::end ());
+  // }
+
+  class child_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Die>
+  {
+    Dwarf_Die m_die;
+
+    struct end_it {};
+    child_iterator (end_it);
+
+  public:
+    explicit child_iterator (Dwarf_Die parent);
+
+    // Return a child_iterator pointing one after the last actual
+    // child.
+    static child_iterator end ();
+
+    bool operator== (child_iterator const &that) const;
+    bool operator!= (child_iterator const &that) const;
+
+    child_iterator &operator++ ();
+    child_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+    Dwarf_Die &operator* ();
+    Dwarf_Die *operator-> ();
+  };
+
+
+  // Tree flattening iterator.  It pre-order iterates all DIEs in
+  // given DWARF file, optionally starting from a given CU iterator.
+  // It keeps track of path from CU root to the current DIE, and that
+  // can be requested through stack() member function.
+  //
+  // Example usage:
+  // {
+  //   for (elfutils::die_tree_iterator it (dw);
+  //        it != elfutils::die_tree_iterator::end (); ++it)
+  //     {
+  //       typedef elfutils::die_tree_iterator::stack_type stack_type;
+  //       stack_type const &stack = it.stack ();
+  //       for (stack_type::const_iterator jt = stack.begin ();
+  //            jt != stack.end (); ++jt)
+  //         ...;
+  //     }
+  // }
+  class die_tree_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Die>
+  {
+    unit_iterator m_cuit;
+    // Internally, only offsets are kept on the stack.
+    std::vector <Dwarf_Off> m_stack;
+    Dwarf_Die m_die;
+
+    struct end_it {};
+    die_tree_iterator (end_it);
+
+    bool move ();
+
+  public:
+    explicit die_tree_iterator (Dwarf *dw);
+    explicit die_tree_iterator (unit_iterator const &cuit);
+
+    // Return a die_tree_iterator pointing one after the last actual
+    // DIE.
+    static die_tree_iterator end ();
+
+    bool operator== (die_tree_iterator const &that) const;
+    bool operator!= (die_tree_iterator const &that) const;
+
+    die_tree_iterator &operator++ ();
+    die_tree_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Die &operator* ();
+    Dwarf_Die *operator-> ();
+
+    // Return a die_tree_iterator referencing a parent of a DIE that
+    // this iterator points at.  Returns an end iterator if there is
+    // no parent.  Technically a const, but can't be one due to
+    // dwarf_tag call inside.
+    die_tree_iterator parent ();
+  };
+
+  // Return a list of DIE's representing the path from CU DIE to the
+  // current DIE (both ends inclusive).  The first element of the
+  // returned stack is the CU DIE, the last one the current DIE.
+  std::vector <Dwarf_Die> path_from_root (die_tree_iterator &it);
+
+  // An attribute iterator goes through attributes of a given DIE.
+  class attr_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Attribute>
+  {
+    Dwarf_Die *m_die;
+    Dwarf_Attribute m_at;
+    ptrdiff_t m_offset;
+
+    struct end_it {};
+    attr_iterator (end_it);
+
+    bool move ();
+
+  public:
+    attr_iterator (Dwarf_Die *die);
+
+    // Return an attr_iterator pointing one after the last actual
+    // attribute.
+    static attr_iterator end ();
+
+    bool operator== (attr_iterator const &that) const;
+    bool operator!= (attr_iterator const &that) const;
+
+    attr_iterator &operator++ ();
+    attr_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Attribute &operator* ();
+    Dwarf_Attribute *operator-> ();
+  };
+}
+}
+
+namespace elfutils
+{
+  using namespace elfutils::v1;
+}
+
+#endif
diff --git a/libdw/c++/libdwP.hh b/libdw/c++/libdwP.hh
new file mode 100644
index 0000000..cb6a145
--- /dev/null
+++ b/libdw/c++/libdwP.hh
@@ -0,0 +1,88 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CPP_LIBDWP_H
+#define _CPP_LIBDWP_H 1
+
+#include <stdexcept>
+#include <cassert>
+#include <cstdlib>
+
+inline void
+throw_libdw (int dwerr = 0)
+{
+  if (dwerr == 0)
+    dwerr = dwarf_errno ();
+  assert (dwerr != 0);
+  throw std::runtime_error (dwarf_errmsg (dwerr));
+}
+
+inline bool
+dwpp_child (Dwarf_Die &die, Dwarf_Die &result)
+{
+  int ret = dwarf_child (&die, &result);
+  if (ret < 0)
+    throw_libdw ();
+  return ret == 0;
+}
+
+inline bool
+dwpp_siblingof (Dwarf_Die &die, Dwarf_Die &result)
+{
+  switch (dwarf_siblingof (&die, &result))
+    {
+    case -1:
+      throw_libdw ();
+    case 0:
+      return true;
+    case 1:
+      return false;
+    default:
+      std::abort ();
+    }
+}
+
+inline Dwarf_Die
+dwpp_offdie (Dwarf *dbg, Dwarf_Off offset)
+{
+  Dwarf_Die result;
+  if (dwarf_offdie (dbg, offset, &result) == NULL)
+    throw_libdw ();
+  return result;
+}
+
+inline Dwarf_Die
+dwpp_offdie_types (Dwarf *dbg, Dwarf_Off offset)
+{
+  Dwarf_Die result;
+  if (dwarf_offdie_types (dbg, offset, &result) == NULL)
+    throw_libdw ();
+  return result;
+}
+
+#endif
diff --git a/libdw/c++/unit_iterator.cc b/libdw/c++/unit_iterator.cc
new file mode 100644
index 0000000..c692d16
--- /dev/null
+++ b/libdw/c++/unit_iterator.cc
@@ -0,0 +1,146 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+
+#include "libdw"
+#include "libdwP.hh"
+
+attribute_hidden
+bool
+elfutils::v1::unit_iterator::move ()
+{
+  m_old_offset = m_offset;
+  size_t hsize;
+  int rc = dwarf_next_unit (m_dw, m_offset, &m_offset, &hsize,
+			    &m_info.version, &m_info.abbrev_offset,
+			    &m_info.address_size, &m_info.offset_size,
+			    m_types ? &m_info.type_signature : NULL, NULL);
+  if (rc < 0)
+    throw_libdw ();
+
+  if (rc != 0 && m_types)
+    return false;
+  else if (rc != 0)
+    {
+      m_types = true;
+      m_offset = 0;
+      m_old_offset = 0;
+      return move ();
+    }
+  else
+    {
+      m_info.cudie = (m_types ? dwpp_offdie_types : dwpp_offdie)
+	(m_dw, m_old_offset + hsize);
+      return true;
+    }
+}
+
+attribute_hidden
+elfutils::v1::unit_iterator::unit_iterator (end_it)
+  : m_dw (NULL)
+{}
+
+elfutils::v1::unit_iterator::unit_iterator (Dwarf *dw)
+  : m_dw (dw)
+  , m_offset (0)
+  , m_old_offset (0)
+  , m_types (false)
+{
+  // Initial move which may turn this into an end iterator.
+  ++*this;
+}
+
+elfutils::v1::unit_iterator::unit_iterator (Dwarf *dw, Dwarf_Die cudie)
+  : m_dw (dw)
+  , m_offset (dwarf_dieoffset (&cudie) - dwarf_cuoffset (&cudie))
+  , m_old_offset (0)
+  , m_types (dwarf_tag (&cudie) == DW_TAG_type_unit)
+{
+  // Initial move, which shouldn't change this into an end iterator,
+  // we were given a valid CU DIE!
+  bool alive = move ();
+  assert (alive);
+}
+
+elfutils::v1::unit_iterator
+elfutils::v1::unit_iterator::end ()
+{
+  return unit_iterator (end_it ());
+}
+
+bool
+elfutils::v1::unit_iterator::operator== (unit_iterator const &that) const
+{
+  return (m_dw == NULL && that.m_dw == NULL)
+    || (m_dw != NULL && that.m_dw != NULL
+	&& m_types == that.m_types
+	&& m_offset == that.m_offset);
+}
+
+bool
+elfutils::v1::unit_iterator::operator!= (unit_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::v1::unit_iterator &
+elfutils::v1::unit_iterator::operator++ ()
+{
+  assert (m_dw != NULL);
+
+  if (! move ())
+    *this = end ();
+
+  return *this;
+}
+
+elfutils::v1::unit_iterator
+elfutils::v1::unit_iterator::operator++ (int)
+{
+  unit_iterator tmp = *this;
+  ++*this;
+  return tmp;
+}
+
+elfutils::v1::unit_info &
+elfutils::v1::unit_iterator::operator* ()
+{
+  assert (m_dw != NULL);
+  return m_info;
+}
+
+elfutils::v1::unit_info *
+elfutils::v1::unit_iterator::operator-> ()
+{
+  return &**this;
+}
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index d40dbae..652428b 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,16 @@
+2015-04-14  Petr Machata  <pmachata@redhat.com>
+
+	* c++: New directory.
+	* c++/libdwfl, c++/libdwflP.hh: New header files.
+	* Makefile.am (AUTOMAKE_OPTIONS): New variable.
+	[HAVE_CXX] (pkginclude_HEADERS): Add c++/libdwfl.
+	[HAVE_CXX] (noinst_LIBRARIES): Add libdwflpp.a, libdwflpp_pic.a.
+	[HAVE_CXX] (libdwflpp_a_SOURCES, libdwflpp, libdwpp): New variables.
+	[HAVE_CXX] (libdwflpp_pic_a_SOURCES): Likewise.
+	[HAVE_CXX] (am_libdwflpp_pic_a_OBJECTS): Likewise.
+	(noinst_HEADERS): Add c++/libdwflP.hh.
+	[HAVE_CXX] (CLEANFILES): Add $(am_libdwflpp_pic_a_OBJECTS).
+
 2015-01-26  Mark Wielaard  <mjw@redhat.com>
 
 	* dwfl_module_getdwarf.c (find_symtab): Explicitly clear symdata,
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index 72c980b..21e1844 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -2,7 +2,7 @@
 ##
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+## Copyright (C) 2005-2010, 2013, 2015 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -34,10 +34,23 @@ AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
 	   -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf
 VERSION = 1
 
+# For c++/ subdir.
+AUTOMAKE_OPTIONS = subdir-objects
+
 noinst_LIBRARIES = libdwfl.a
+if HAVE_CXX
+noinst_LIBRARIES += libdwflpp.a
+endif
+
 noinst_LIBRARIES += libdwfl_pic.a
+if HAVE_CXX
+noinst_LIBRARIES += libdwflpp_pic.a
+endif
 
 pkginclude_HEADERS = libdwfl.h
+if HAVE_CXX
+pkginclude_HEADERS += c++/libdwfl
+endif
 
 libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
 		    dwfl_module.c dwfl_report_elf.c relocate.c \
@@ -70,6 +83,10 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
 		    dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \
 		    linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c
 
+if HAVE_CXX
+libdwflpp_a_SOURCES = c++/dwfl_module_iterator.cc
+endif
+
 if ZLIB
 libdwfl_a_SOURCES += gzip.c
 endif
@@ -82,6 +99,10 @@ endif
 
 libdwfl = $(libdw)
 libdw = ../libdw/libdw.so
+if HAVE_CXX
+libdwflpp = $(libdwpp)
+libdwpp = ../libdw/libdwpp.so
+endif
 libelf = ../libelf/libelf.so
 libebl = ../libebl/libebl.a
 libeu = ../lib/libeu.a
@@ -89,6 +110,14 @@ libeu = ../lib/libeu.a
 libdwfl_pic_a_SOURCES =
 am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os)
 
-noinst_HEADERS = libdwflP.h
+if HAVE_CXX
+libdwflpp_pic_a_SOURCES =
+am_libdwflpp_pic_a_OBJECTS = $(libdwflpp_a_SOURCES:.cc=.os)
+endif
+
+noinst_HEADERS = libdwflP.h c++/libdwflP.hh
 
 CLEANFILES += $(am_libdwfl_pic_a_OBJECTS)
+if HAVE_CXX
+CLEANFILES += $(am_libdwflpp_pic_a_OBJECTS)
+endif
diff --git a/libdwfl/c++/dwfl_module_iterator.cc b/libdwfl/c++/dwfl_module_iterator.cc
new file mode 100644
index 0000000..9eb9790
--- /dev/null
+++ b/libdwfl/c++/dwfl_module_iterator.cc
@@ -0,0 +1,128 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwfl"
+#include "libdwflP.hh"
+
+namespace
+{
+  struct cb_data
+  {
+    Dwfl_Module **m_modulep;
+  };
+
+  int
+  callback (Dwfl_Module *mod, void **, const char *,
+	    Dwarf_Addr, void *arg)
+  {
+    cb_data *d = static_cast <cb_data *> (arg);
+    *d->m_modulep = mod;
+    return DWARF_CB_ABORT;
+  }
+}
+
+attribute_hidden
+bool
+elfutils::v1::dwfl_module_iterator::move ()
+{
+  cb_data d = {&m_module};
+  m_offset = dwfl_getmodules (m_dwfl, callback, &d, m_offset);
+  if (m_offset == -1)
+    throw_libdwfl ();
+  return m_offset != 0;
+}
+
+attribute_hidden
+elfutils::v1::dwfl_module_iterator::dwfl_module_iterator (end_it)
+  : m_dwfl (NULL)
+{}
+
+elfutils::v1::dwfl_module_iterator::dwfl_module_iterator (Dwfl *dwfl)
+  : m_dwfl (dwfl)
+  , m_offset (0)
+{
+  // Initial move, which can turn this into an end iterator.
+  ++*this;
+}
+
+elfutils::v1::dwfl_module_iterator
+elfutils::v1::dwfl_module_iterator::end ()
+{
+  return dwfl_module_iterator (end_it ());
+}
+
+elfutils::v1::dwfl_module_iterator &
+elfutils::v1::dwfl_module_iterator::operator++ ()
+{
+  assert (m_dwfl != NULL);
+
+  if (! move ())
+    *this = end ();
+
+  return *this;
+}
+
+elfutils::v1::dwfl_module_iterator
+elfutils::v1::dwfl_module_iterator::operator++ (int)
+{
+  dwfl_module_iterator ret = *this;
+  ++*this;
+  return ret;
+}
+
+bool
+elfutils::v1::dwfl_module_iterator::operator== (dwfl_module_iterator
+							const &that) const
+{
+  return m_dwfl == that.m_dwfl
+    && (m_dwfl == NULL || m_offset == that.m_offset);
+}
+
+bool
+elfutils::v1::dwfl_module_iterator::operator!= (dwfl_module_iterator
+							const &that) const
+{
+  return ! (*this == that);
+}
+
+Dwfl_Module &
+elfutils::v1::dwfl_module_iterator::operator* () const
+{
+  assert (m_dwfl != NULL);
+  return *m_module;
+}
+
+Dwfl_Module *
+elfutils::v1::dwfl_module_iterator::operator-> () const
+{
+  return &**this;
+}
diff --git a/libdwfl/c++/libdwfl b/libdwfl/c++/libdwfl
new file mode 100644
index 0000000..88462fb
--- /dev/null
+++ b/libdwfl/c++/libdwfl
@@ -0,0 +1,78 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBDWFL_CPP
+#define _LIBDWFL_CPP	1
+
+#include <iterator>
+#include "libdwfl.h"
+
+namespace elfutils
+{
+namespace v1
+{
+  // Given a Dwfl, iterates through its Dwfl_Module's.
+  //
+  // Example usage:
+  // std::vector <Dwfl_Module *> mods (elfutils::dwfl_module_iterator (dwfl),
+  //				       elfutils::dwfl_module_iterator::end ());
+
+  class dwfl_module_iterator
+    : public std::iterator <std::input_iterator_tag, Dwfl_Module *>
+  {
+    Dwfl *m_dwfl;
+    ptrdiff_t m_offset;
+    Dwfl_Module *m_module;
+
+    struct end_it {};
+    explicit dwfl_module_iterator (end_it);
+
+    bool move ();
+
+  public:
+    explicit dwfl_module_iterator (Dwfl *dwfl);
+    static dwfl_module_iterator end ();
+
+    dwfl_module_iterator &operator++ ();
+    dwfl_module_iterator operator++ (int);
+
+    Dwfl_Module &operator* () const;
+    Dwfl_Module *operator-> () const;
+
+    bool operator== (dwfl_module_iterator const &that) const;
+    bool operator!= (dwfl_module_iterator const &that) const;
+  };
+}
+}
+
+namespace elfutils
+{
+  using namespace elfutils::v1;
+}
+
+#endif
diff --git a/libdwfl/c++/libdwflP.hh b/libdwfl/c++/libdwflP.hh
new file mode 100644
index 0000000..c3f2792
--- /dev/null
+++ b/libdwfl/c++/libdwflP.hh
@@ -0,0 +1,44 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CPP_LIBDWFLP_H
+#define _CPP_LIBDWFLP_H 1
+
+#include <stdexcept>
+#include <cassert>
+
+static inline void
+throw_libdwfl (int dwflerr = 0)
+{
+  if (dwflerr == 0)
+    dwflerr = dwfl_errno ();
+  assert (dwflerr != 0);
+  throw std::runtime_error (dwfl_errmsg (dwflerr));
+}
+
+#endif
diff --git a/tests/ChangeLog b/tests/ChangeLog
index b686184..728058f 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,10 @@
+2015-04-14  Petr Machata  <pmachata@redhat.com>
+
+	* test-iterators.cc, run-test-iterators.sh: New files.
+	* Makefile.am (check_PROGRAMS) [HAVE_CXX]: Add test-iterators.
+	(TESTS) [HAVE_CXX]: Add run-test-iterators.sh.
+	(libdwpp): New variable.
+
 2015-04-01  H.J. Lu  <hjl.tools@gmail.com>
 
 	* Makefile.am (TESTS): Add run-strip-test10.sh.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 45bb74d..5c5cfbf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -143,6 +143,11 @@ check_PROGRAMS += $(asm_TESTS)
 TESTS += $(asm_TESTS)
 endif
 
+if HAVE_CXX
+check_PROGRAMS += test-iterators
+TESTS += run-test-iterators.sh
+endif
+
 EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     run-show-die-info.sh run-get-files.sh run-get-lines.sh \
 	     run-get-pubnames.sh run-get-aranges.sh \
@@ -293,7 +298,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     run-getsrc-die.sh run-strptr.sh \
 	     testfile-x32-core.bz2 testfile-x32.bz2 \
 	     backtrace.x32.core.bz2 backtrace.x32.exec.bz2 \
-	     testfile-x32-s.bz2 testfile-x32-d.bz2 testfile-x32-debug.bz2
+	     testfile-x32-s.bz2 testfile-x32-d.bz2 testfile-x32-debug.bz2 \
+	     run-test-iterators.sh
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
@@ -337,16 +343,19 @@ endif !STANDALONE
 
 if STANDALONE
 libdw = -ldw
+libdwpp = -ldwpp
 libelf = -lelf
 libasm = -lasm
 libebl = -lebl
 else !STANDALONE
 if BUILD_STATIC
 libdw = ../libdw/libdw.a $(zip_LIBS) $(libelf) $(libebl) -ldl
+libdwpp = ../libdw/libdwpp.a $(libdw)
 libelf = ../libelf/libelf.a
 libasm = ../libasm/libasm.a
 else
 libdw = ../libdw/libdw.so
+libdwpp = ../libdw/libdwpp.so
 libelf = ../libelf/libelf.so
 libasm = ../libasm/libasm.so
 endif
@@ -438,6 +447,8 @@ getsrc_die_LDADD = $(libdw) $(libelf)
 strptr_LDADD = $(libelf)
 newdata_LDADD = $(libelf)
 elfstrtab_LDADD = $(libelf)
+test_iterators_SOURCES = test-iterators.cc
+test_iterators_LDADD = $(libdw) $(libdwpp)
 
 if GCOV
 check: check-am coverage
diff --git a/tests/run-test-iterators.sh b/tests/run-test-iterators.sh
new file mode 100755
index 0000000..6942570
--- /dev/null
+++ b/tests/run-test-iterators.sh
@@ -0,0 +1,52 @@
+#! /bin/sh
+# Copyright (C) 2015 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile39 testfile-debug-types
+
+testrun_compare ${abs_top_builddir}/tests/test-iterators testfile39 <<\EOF
+0xb
+0x9e
+0x135
+0x1c8
+0 7
+0 7
+0 7
+0 7
+EOF
+
+testrun_compare ${abs_top_builddir}/tests/test-iterators testfile-debug-types <<\EOF
+0xb
+0x17
+0x5a
+4 6
+0 9
+0 3
+0 6
+0 6
+2 3
+1 4
+0 2
+0 5
+1 3
+2 4
+0 3
+0 5
+EOF
+
+exit 0
diff --git a/tests/test-iterators.cc b/tests/test-iterators.cc
new file mode 100644
index 0000000..987bc6a
--- /dev/null
+++ b/tests/test-iterators.cc
@@ -0,0 +1,87 @@
+/* Copyright (C) 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <iostream>
+#include <cassert>
+#include <stdexcept>
+
+#include "../libdw/c++/libdw"
+#include "../libdwfl/c++/libdwfl"
+#include "../libdw/c++/libdwP.hh"
+#include "../libdwfl/c++/libdwflP.hh"
+
+int
+main (int, char *argv[])
+{
+  int fd = open (argv[1], O_RDONLY);
+  if (fd < 0)
+    throw std::runtime_error (strerror (errno));
+
+  const static Dwfl_Callbacks callbacks =
+    {
+      .find_elf = dwfl_build_id_find_elf,
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .section_address = dwfl_offline_section_address,
+      .debuginfo_path = NULL,
+    };
+
+  Dwfl *dwfl = dwfl_begin (&callbacks);
+  if (dwfl == NULL)
+    throw_libdwfl ();
+
+  dwfl_report_begin (dwfl);
+  if (dwfl_report_offline (dwfl, argv[1], argv[1], fd) == NULL)
+    throw_libdwfl ();
+  dwfl_report_end (dwfl, NULL, NULL);
+
+  for (elfutils::dwfl_module_iterator modit (dwfl);
+       modit != elfutils::dwfl_module_iterator::end (); ++modit)
+    {
+      Dwarf_Off bias;
+      Dwarf *dw = dwfl_module_getdwarf (&*modit, &bias);
+      std::vector <std::pair <elfutils::unit_iterator, Dwarf_Die> > cudies;
+      for (elfutils::unit_iterator it (dw); it != elfutils::unit_iterator::end (); ++it)
+	cudies.push_back (std::make_pair (it, it->cudie));
+
+      for (size_t i = 0; i < cudies.size (); ++i)
+	{
+	  elfutils::unit_iterator jt (dw, cudies[i].second);
+	  std::cerr << std::hex << std::showbase
+		    << dwarf_dieoffset (&jt->cudie) << std::endl;
+	  for (size_t j = i; jt != elfutils::unit_iterator::end (); ++jt, ++j)
+	    assert (jt == cudies[j].first);
+	}
+
+      assert (elfutils::die_tree_iterator (elfutils::unit_iterator::end ())
+	      == elfutils::die_tree_iterator::end ());
+
+      for (elfutils::die_tree_iterator it (dw);
+	   it != elfutils::die_tree_iterator::end (); ++it)
+	std::cerr << std::dec
+		  << std::distance (elfutils::child_iterator (*it),
+				    elfutils::child_iterator::end ()) << ' '
+		  << std::distance (elfutils::attr_iterator (&*it),
+				    elfutils::attr_iterator::end ())
+		  << std::endl;
+    }
+
+  dwfl_end (dwfl);
+}
-- 
2.1.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-14 13:53 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-14 13:53 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 98 bytes --]

Petr Machata <pmachata@redhat.com> writes:

> Ping.

Sorry, pinging the wrong patch.

Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-14 13:52 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-14 13:52 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 7 bytes --]

Ping.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-03 21:34 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-03 21:34 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 1686 bytes --]

Petr Machata <pmachata@redhat.com> writes:

>> I am happy we have an explicit ABI now. But I am somewhat concerned
>> about how libdw/libdwpp.map looks. Is this really the best we can do? It
>> feels somewhat unmaintainable if we have to hand edit this version
>> script. How did you generate this?
>
> Cut'n'paste from symbol tables.
>
> One alternative way would be to use version namespace instead.  Using
> version script may have been too much of cargo culting for C++
> interfaces.

The branch now holds the code that does symbol versioning by using
namespaces.  The basic organization is like this:

namespace elfutils {
  namespace v1 {
    class one_iterator;
    class another_iterator;
  }

  using namespace elfutils::v1;
}

The using directive brings the contents of the inner namespace to the
outer one, so that the symbols are accessible as elfutils::one_iterator
and elfutils::another_iterator.  But their full name and corresponding
mangling is elfutils::v1::one_iterator etc.

When ABI changes, we do this:

namespace elfutils {
  namespace v1 {
    class one_iterator;
    class another_iterator;
  }

  namespace v2 {
    class one_iterator; // New version.
    using namespace elfutils::v1; // Import the rest.
  }

  using namespace elfutils::v2;
}

Since the code in v1:: is still there, those symbols are still
generated, and old code can dynamically link to them.  There would
additionally be elfutils::v2::one_iterator with the new stuff.

Hiding individual symbols can still be done through GCC attributes, and
in fact that's now on the branch.  The private methods are not even
exported.

Thanks,
Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-03 13:39 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-03 13:39 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 1817 bytes --]

Mark Wielaard <mjw@redhat.com> writes:

> I like this because it makes things easy and clear on the ABI side.
> I am not so concerned about the PLT call, but the new allocation and
> delete in the destructor is indeed a little heavy-weight since I assume
> iterators are often short lived so it will cause lots of small
> new/deletes. I guess we just will have to live with that if we want ABI
> stability. Do others also think this is a acceptable tradeoff?

One thing just occured to me.  We could represent end iterator as an
iterator without associated pimpl.  That way, the typical C++ idiom:

for (elfutils::some_iterator it (dw);
     it != elfutils::some_iterator::end (); ++it)
  Stuff;

... wouldn't end up being horribly terribly expensive.

>> Symbols are versioned.  This ties us closely to the GCC ABI, but I think
>> everybody uses Itanium ABI these days anyway.  This also means that if
>> there should be a change in behavior that would constitute an ABI
>> breakage, it can be mitigated the same way as with C symbols.
>> 
>> I hid many symbols as well.  Namely the private constructors are not
>> exported at all, and I have also hidden the not-in-charge constructor
>> and destructor variants, because those should only come into play when
>> the iterators are inherited from, which they are not designed for.
>
> I am happy we have an explicit ABI now. But I am somewhat concerned
> about how libdw/libdwpp.map looks. Is this really the best we can do? It
> feels somewhat unmaintainable if we have to hand edit this version
> script. How did you generate this?

Cut'n'paste from symbol tables.

One alternative way would be to use version namespace instead.  Using
version script may have been too much of cargo culting for C++
interfaces.

Thanks,
Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-03 12:56 Mark Wielaard
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Wielaard @ 2015-04-03 12:56 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 2714 bytes --]

On Fri, 2015-04-03 at 10:51 +0200, Petr Machata wrote:
> as requested by Mark, I made the iterator interfaces ABI stable, so that
> they can be safely used on API boundaries of third-party libraries.
> This patch should also include all the other points that we talked about
> (from the top of my head: returning reference from operator++, dropping
> of offset member function, change from cu_iterator to unit_iterator),
> except for exceptions, which are still std::runtime_error.

Thanks. I do think one of elfutils strength's is that it has a stable
ABI so people can just use it for years without worrying whether or not
to upgrade to get some new bug fixes or features. But if I am overdoing
it, because C++ and abi stability just don't mix, please do feel free to
push back a bit more.

> This adds a new library, libdwpp, which implements these interfaces.
> The interfaces themselves are fully pimpl'd.

pimpl'd? Aha. It is an actual term :)
https://en.wikipedia.org/wiki/Opaque_pointer
the "Pimpl idiom" (for "pointer to implementation idiom").

> Actual class layout
> etc. is fully internal detail--on the outside we publish one pointer
> field.  Due to this, the iterators are more heavyweight, involving a
> dynamic allocation for each instance, and PLT call for each operation,
> but on the other hand, virtually no ABI breakage is possible.

I like this because it makes things easy and clear on the ABI side.
I am not so concerned about the PLT call, but the new allocation and
delete in the destructor is indeed a little heavy-weight since I assume
iterators are often short lived so it will cause lots of small
new/deletes. I guess we just will have to live with that if we want ABI
stability. Do others also think this is a acceptable tradeoff?

> Symbols are versioned.  This ties us closely to the GCC ABI, but I think
> everybody uses Itanium ABI these days anyway.  This also means that if
> there should be a change in behavior that would constitute an ABI
> breakage, it can be mitigated the same way as with C symbols.
> 
> I hid many symbols as well.  Namely the private constructors are not
> exported at all, and I have also hidden the not-in-charge constructor
> and destructor variants, because those should only come into play when
> the iterators are inherited from, which they are not designed for.

I am happy we have an explicit ABI now. But I am somewhat concerned
about how libdw/libdwpp.map looks. Is this really the best we can do? It
feels somewhat unmaintainable if we have to hand edit this version
script. How did you generate this?

Still reading through the implementation, but it looks good to me.

Thanks,

Mark

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] Add C++ iterators
@ 2015-04-03 11:27 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-03 11:27 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 161 bytes --]

I see this wouldn't apply cleanly anymore, due to recent x32 patches.  I
put the code on the branch pmachata/iterators, for those interested.

Thanks,
Petr

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 2/2] Add C++ iterators
@ 2015-04-03  8:51 Petr Machata
  0 siblings, 0 replies; 16+ messages in thread
From: Petr Machata @ 2015-04-03  8:51 UTC (permalink / raw)
  To: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 61579 bytes --]

Hello,

as requested by Mark, I made the iterator interfaces ABI stable, so that
they can be safely used on API boundaries of third-party libraries.
This patch should also include all the other points that we talked about
(from the top of my head: returning reference from operator++, dropping
of offset member function, change from cu_iterator to unit_iterator),
except for exceptions, which are still std::runtime_error.

This adds a new library, libdwpp, which implements these interfaces.
The interfaces themselves are fully pimpl'd.  Actual class layout
etc. is fully internal detail--on the outside we publish one pointer
field.  Due to this, the iterators are more heavyweight, involving a
dynamic allocation for each instance, and PLT call for each operation,
but on the other hand, virtually no ABI breakage is possible.

Symbols are versioned.  This ties us closely to the GCC ABI, but I think
everybody uses Itanium ABI these days anyway.  This also means that if
there should be a change in behavior that would constitute an ABI
breakage, it can be mitigated the same way as with C symbols.

I hid many symbols as well.  Namely the private constructors are not
exported at all, and I have also hidden the not-in-charge constructor
and destructor variants, because those should only come into play when
the iterators are inherited from, which they are not designed for.

The test case was extended to at least use each of the iterators, albeit
in somewhat limited manner.  dwgrep, when adapted, builds and passes
test suite fine as well.

Thanks,
Petr

--- 8< ---------------------------------------------------------------

- Add unit_iterator, child_iterator, die_tree_iterator and
  attr_iterator for libdw, and dwfl_module_iterator for libdwfl.

- The code is shipped in a new library called libdwpp with versioned
  symbols.

Signed-off-by: Petr Machata <pmachata@redhat.com>
---
 ChangeLog                           |   4 +
 NEWS                                |   5 +-
 libdw/ChangeLog                     |  21 ++++
 libdw/Makefile.am                   |  62 ++++++++-
 libdw/c++/attr_iterator.cc          | 172 +++++++++++++++++++++++++
 libdw/c++/child_iterator.cc         | 143 +++++++++++++++++++++
 libdw/c++/die_tree_iterator.cc      | 213 +++++++++++++++++++++++++++++++
 libdw/c++/libdw                     | 245 ++++++++++++++++++++++++++++++++++++
 libdw/c++/libdwP.hh                 |  88 +++++++++++++
 libdw/c++/unit_iterator.cc          | 204 ++++++++++++++++++++++++++++++
 libdw/libdwpp.map                   | 199 +++++++++++++++++++++++++++++
 libdwfl/ChangeLog                   |  13 ++
 libdwfl/Makefile.am                 |  33 ++++-
 libdwfl/c++/dwfl_module_iterator.cc | 154 +++++++++++++++++++++++
 libdwfl/c++/libdwfl                 |  70 +++++++++++
 libdwfl/c++/libdwflP.hh             |  44 +++++++
 tests/ChangeLog                     |   7 ++
 tests/Makefile.am                   |  12 +-
 tests/run-test-iterators.sh         |  52 ++++++++
 tests/test-iterators.cc             |  87 +++++++++++++
 20 files changed, 1820 insertions(+), 8 deletions(-)
 create mode 100644 libdw/c++/attr_iterator.cc
 create mode 100644 libdw/c++/child_iterator.cc
 create mode 100644 libdw/c++/die_tree_iterator.cc
 create mode 100644 libdw/c++/libdw
 create mode 100644 libdw/c++/libdwP.hh
 create mode 100644 libdw/c++/unit_iterator.cc
 create mode 100644 libdw/libdwpp.map
 create mode 100644 libdwfl/c++/dwfl_module_iterator.cc
 create mode 100644 libdwfl/c++/libdwfl
 create mode 100644 libdwfl/c++/libdwflP.hh
 create mode 100755 tests/run-test-iterators.sh
 create mode 100644 tests/test-iterators.cc

diff --git a/ChangeLog b/ChangeLog
index 6c6f216..99c2450 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2015-04-03  Petr Machata  <pmachata@redhat.com>
+
+	* NEWS: Mention <elfutils/libdw>, <elfutils/libdwfl>, and libdwpp.
+
 2015-03-18  Petr Machata  <pmachata@redhat.com>
 
 	* configure.ac (HAVE_CXX): New conditional.
diff --git a/NEWS b/NEWS
index 60aa995..02f8691 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 Version 0.162
 
-libdw: Install new header elfutils/known-dwarf.h.
+libdw: Install new headers elfutils/known-dwarf.h, elfutils/libdw.
+libdwfl: Install new header elfutils/libdwfl.
+libdwpp: New library, contains the code for <elfutils/libdw> and
+         <elfutils/libdwfl> C++ interfaces.
 
 Version 0.161
 
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 4fb4763..30bb80c 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -7,6 +7,27 @@
 	of white-listing valid tags.
 	* dwarf_getsrclines.c (dwarf_getsrclines.c): Likewise.
 
+2015-03-17  Petr Machata  <pmachata@redhat.com>
+
+	* c++: New directory.
+	* c++/libdw, c++/libdwP.hh: New header files.
+	* libdwpp.map: New version script file.
+	* c++/attr_iterator.cc, c++/child_iterator.cc: New files.
+	* c++/die_tree_iterator.cc, c++/unit_iterator.cc: Likewise.
+	* Makefile.am (AUTOMAKE_OPTIONS): New variable.
+	[HAVE_CXX] (lib_LIBRARIES): Add libdwpp.a.
+	[HAVE_CXX] (noinst_LIBRARIES): Add libdwpp_pic.a.
+	[HAVE_CXX] (pkginclude_HEADERS): Add c++/libdw.
+	[HAVE_CXX] (libdwpp_a_SOURCES, libdwpp_so_SOURCES): New variables.
+	[HAVE_CXX] (libdwpp_pic_a_SOURCES, am_libdwpp_pic_a_OBJECTS): Likewise.
+	[HAVE_CXX] (libdwpp.so$(EXEEXT)): New rule.
+	(INSTALL_DEPS): New variable, contains libdwpp.so if HAVE_CXX.
+	(install): Depend on $(INSTALL_DEPS).
+	[HAVE_CXX] (install): Install libdwpp.so et.al.
+	[HAVE_CXX] (uninstall): Uninstall them.
+	(EXTRA_DIST): Add libdwpp.map.
+	(MOSTLYCLEANFILES): Add libdwpp-related files.
+
 2015-03-18  Petr Machata  <pmachata@redhat.com>
 
 	* Makefile.am (pkginclude_HEADERS): Add known-dwarf.h.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 272289c..8f43e76 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2002-2010, 2012, 2014 Red Hat, Inc.
+## Copyright (C) 2002-2010, 2012, 2014, 2015 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -34,12 +34,26 @@ endif
 AM_CPPFLAGS += -I$(srcdir)/../libelf
 VERSION = 1
 
+# For c++/ subdir.
+AUTOMAKE_OPTIONS = subdir-objects
+
 lib_LIBRARIES = libdw.a
+if HAVE_CXX
+lib_LIBRARIES += libdwpp.a
+endif
+
 noinst_LIBRARIES = libdw_pic.a
+if HAVE_CXX
+noinst_LIBRARIES += libdwpp_pic.a
+endif
+
 noinst_PROGRAMS = $(noinst_LIBRARIES:_pic.a=.so)
 
 include_HEADERS = dwarf.h
 pkginclude_HEADERS = libdw.h known-dwarf.h
+if HAVE_CXX
+pkginclude_HEADERS += c++/libdw
+endif
 
 libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_getpubnames.c dwarf_getabbrev.c dwarf_tag.c \
@@ -91,6 +105,11 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_getalt.c dwarf_setalt.c dwarf_cu_getdwarf.c \
 		  dwarf_cu_die.c dwarf_peel_type.c
 
+if HAVE_CXX
+libdwpp_a_SOURCES = c++/unit_iterator.cc c++/die_tree_iterator.cc	\
+		    c++/child_iterator.cc c++/attr_iterator.cc
+endif
+
 if MAINTAINER_MODE
 BUILT_SOURCES = $(srcdir)/known-dwarf.h
 MAINTAINERCLEANFILES = $(srcdir)/known-dwarf.h
@@ -102,6 +121,11 @@ endif
 libdw_pic_a_SOURCES =
 am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os)
 
+if HAVE_CXX
+libdwpp_pic_a_SOURCES =
+am_libdwpp_pic_a_OBJECTS = $(libdwpp_a_SOURCES:.cc=.os)
+endif
+
 libdw_so_SOURCES =
 libdw.so$(EXEEXT): $(srcdir)/libdw.map libdw_pic.a ../libdwelf/libdwelf_pic.a \
 	  ../libdwfl/libdwfl_pic.a ../libebl/libebl.a \
@@ -116,16 +140,45 @@ libdw.so$(EXEEXT): $(srcdir)/libdw.map libdw_pic.a ../libdwelf/libdwelf_pic.a \
 	@$(textrel_check)
 	ln -fs $@ $@.$(VERSION)
 
-install: install-am libdw.so
+if HAVE_CXX
+libdwpp_so_SOURCES =
+libdwpp.so$(EXEEXT): $(srcdir)/libdwpp.map libdwpp_pic.a ../libdwfl/libdwflpp_pic.a \
+		     libdw.so$(EXEEXT)
+	$(CXXLINK) -shared -o $@ -Wl,--soname,$@.$(VERSION),-z,defs \
+		-Wl,--enable-new-dtags \
+		-Wl,--version-script,$<,--no-undefined \
+		-Wl,--whole-archive $(filter-out $<,$^) -Wl,--no-whole-archive\
+		libdw.so$(EXEEXT)
+	@$(textrel_check)
+	ln -fs $@ $@.$(VERSION)
+endif
+
+if HAVE_CXX
+INSTALL_DEPS = libdwpp.so
+else
+INSTALL_DEPS =
+endif
+
+install: install-am libdw.so $(INSTALL_DEPS)
 	$(mkinstalldirs) $(DESTDIR)$(libdir)
 	$(INSTALL_PROGRAM) libdw.so $(DESTDIR)$(libdir)/libdw-$(PACKAGE_VERSION).so
 	ln -fs libdw-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdw.so.$(VERSION)
 	ln -fs libdw.so.$(VERSION) $(DESTDIR)$(libdir)/libdw.so
+if HAVE_CXX
+	$(INSTALL_PROGRAM) libdwpp.so $(DESTDIR)$(libdir)/libdwpp-$(PACKAGE_VERSION).so
+	ln -fs libdwpp-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdwpp.so.$(VERSION)
+	ln -fs libdwpp.so.$(VERSION) $(DESTDIR)$(libdir)/libdwpp.so
+endif
 
 uninstall: uninstall-am
 	rm -f $(DESTDIR)$(libdir)/libdw-$(PACKAGE_VERSION).so
 	rm -f $(DESTDIR)$(libdir)/libdw.so.$(VERSION)
 	rm -f $(DESTDIR)$(libdir)/libdw.so
+if HAVE_CXX
+	rm -f $(DESTDIR)$(libdir)/libdwpp-$(PACKAGE_VERSION).so
+	rm -f $(DESTDIR)$(libdir)/libdwpp.so.$(VERSION)
+	rm -f $(DESTDIR)$(libdir)/libdwpp.so
+endif
 	rmdir --ignore-fail-on-non-empty $(DESTDIR)$(includedir)/elfutils
 
 libdwfl_objects = $(shell $(AR) t ../libdwfl/libdwfl.a)
@@ -137,6 +190,7 @@ libdw_a_LIBADD += $(addprefix ../libdwelf/,$(libdwelf_objects))
 noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \
 		 dwarf_sig8_hash.h cfi.h encoded-value.h
 
-EXTRA_DIST = libdw.map
+EXTRA_DIST = libdw.map libdwpp.map
 
-MOSTLYCLEANFILES = $(am_libdw_pic_a_OBJECTS) libdw.so.$(VERSION)
+MOSTLYCLEANFILES = $(am_libdw_pic_a_OBJECTS) libdw.so.$(VERSION) \
+	$(am_libdwpp_pic_a_OBJECTS) libdwpp.so.$(VERSION)
diff --git a/libdw/c++/attr_iterator.cc b/libdw/c++/attr_iterator.cc
new file mode 100644
index 0000000..ade1b53
--- /dev/null
+++ b/libdw/c++/attr_iterator.cc
@@ -0,0 +1,172 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "libdw"
+#include "libdwP.hh"
+
+struct elfutils::attr_iterator::pimpl
+{
+  Dwarf_Die *m_die;
+  Dwarf_Attribute m_at;
+  ptrdiff_t m_offset;
+
+  struct cb_data
+  {
+    // The visited attribute.
+    Dwarf_Attribute *at;
+
+    // Whether this is second pass through the callback.
+    bool been;
+  };
+
+  static int
+  callback (Dwarf_Attribute *at, void *data)
+  {
+    cb_data *d = static_cast <cb_data *> (data);
+    if (d->been)
+      return DWARF_CB_ABORT;
+
+    *d->at = *at;
+    d->been = true;
+
+    // Do a second iteration to find the next offset.
+    return DWARF_CB_OK;
+  }
+
+  void
+  move ()
+  {
+    // If m_offset is already 1, we are done iterating.
+    if (m_offset == 1)
+      {
+	*this = pimpl ((ptrdiff_t) 1);
+	return;
+      }
+
+    cb_data data = {&m_at, false};
+    m_offset = dwarf_getattrs (m_die, &callback, &data, m_offset);
+    if (m_offset == -1)
+      throw_libdw ();
+  }
+
+  pimpl (ptrdiff_t offset)
+    : m_die (NULL)
+    , m_at ((Dwarf_Attribute) {0, 0, NULL, NULL})
+    , m_offset (offset)
+  {}
+
+  pimpl (Dwarf_Die *die)
+    : m_die (die)
+    , m_at ((Dwarf_Attribute) {0, 0, NULL, NULL})
+    , m_offset (0)
+  {
+    move ();
+  }
+
+  bool
+  operator== (pimpl const &other) const
+  {
+    return m_offset == other.m_offset
+      && m_at.code == other.m_at.code;
+  }
+};
+
+elfutils::attr_iterator::attr_iterator (ptrdiff_t offset)
+  : m_pimpl (new pimpl (offset))
+{}
+
+elfutils::attr_iterator::attr_iterator (Dwarf_Die *die)
+  : m_pimpl (new pimpl (die))
+{}
+
+elfutils::attr_iterator::attr_iterator (attr_iterator const &that)
+  : m_pimpl (new pimpl (*that.m_pimpl))
+{}
+
+elfutils::attr_iterator::~attr_iterator ()
+{
+  delete m_pimpl;
+}
+
+elfutils::attr_iterator &
+elfutils::attr_iterator::operator= (attr_iterator const &that)
+{
+  if (this != &that)
+    {
+      pimpl *npimpl = new pimpl (*that.m_pimpl);
+      delete m_pimpl;
+      m_pimpl = npimpl;
+    }
+
+  return *this;
+}
+
+elfutils::attr_iterator
+elfutils::attr_iterator::end ()
+{
+  return attr_iterator ((ptrdiff_t) 1);
+}
+
+bool
+elfutils::attr_iterator::operator== (attr_iterator const &that) const
+{
+  return *m_pimpl == *that.m_pimpl;
+}
+
+bool
+elfutils::attr_iterator::operator!= (attr_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::attr_iterator &
+elfutils::attr_iterator::operator++ ()
+{
+  m_pimpl->move ();
+  return *this;
+}
+
+elfutils::attr_iterator
+elfutils::attr_iterator::operator++ (int)
+{
+  attr_iterator tmp = *this;
+  ++*this;
+  return tmp;
+}
+
+Dwarf_Attribute &
+elfutils::attr_iterator::operator* ()
+{
+  return m_pimpl->m_at;
+}
+
+Dwarf_Attribute *
+elfutils::attr_iterator::operator-> ()
+{
+  return &**this;
+}
diff --git a/libdw/c++/child_iterator.cc b/libdw/c++/child_iterator.cc
new file mode 100644
index 0000000..e4dbff8
--- /dev/null
+++ b/libdw/c++/child_iterator.cc
@@ -0,0 +1,143 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "libdw"
+#include "libdwP.hh"
+
+#include <cstring>
+
+struct elfutils::child_iterator::pimpl
+{
+  Dwarf_Die m_die;
+
+  // Create pimpl for end-iterator.
+  pimpl ()
+  {
+    std::memset (&m_die, 0, sizeof m_die);
+    m_die.addr = (void *) -1;
+  }
+
+  explicit pimpl (Dwarf_Die parent)
+  {
+    if (! dwpp_child (parent, m_die))
+      *this = pimpl ();
+  }
+
+  pimpl (pimpl const &that)
+    : m_die (that.m_die)
+  {}
+
+  bool
+  operator== (pimpl const &that) const
+  {
+    return m_die.addr == that.m_die.addr;
+  }
+
+  void
+  move ()
+  {
+    assert (! (*this == pimpl ()));
+    if (! dwpp_siblingof (m_die, m_die))
+      *this = pimpl ();
+  }
+};
+
+elfutils::child_iterator::child_iterator ()
+  : m_pimpl (new pimpl ())
+{}
+
+elfutils::child_iterator::child_iterator (Dwarf_Die parent)
+  : m_pimpl (new pimpl (parent))
+{}
+
+elfutils::child_iterator::child_iterator (child_iterator const &that)
+  : m_pimpl (new pimpl (*that.m_pimpl))
+{}
+
+elfutils::child_iterator::~child_iterator ()
+{
+  delete m_pimpl;
+}
+
+elfutils::child_iterator &
+elfutils::child_iterator::operator= (child_iterator const &that)
+{
+  if (this != &that)
+    {
+      pimpl *npimpl = new pimpl (*that.m_pimpl);
+      delete m_pimpl;
+      m_pimpl = npimpl;
+    }
+
+  return *this;
+}
+
+elfutils::child_iterator
+elfutils::child_iterator::end ()
+{
+  return child_iterator ();
+}
+
+bool
+elfutils::child_iterator::operator== (child_iterator const &that) const
+{
+  return *m_pimpl == *that.m_pimpl;
+}
+
+bool
+elfutils::child_iterator::operator!= (child_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::child_iterator &
+elfutils::child_iterator::operator++ ()
+{
+  m_pimpl->move ();
+  return *this;
+}
+
+elfutils::child_iterator
+elfutils::child_iterator::operator++ (int)
+{
+  child_iterator ret = *this;
+  ++*this;
+  return ret;
+}
+
+Dwarf_Die &
+elfutils::child_iterator::operator* ()
+{
+  return m_pimpl->m_die;
+}
+
+Dwarf_Die *
+elfutils::child_iterator::operator-> ()
+{
+  return &**this;
+}
diff --git a/libdw/c++/die_tree_iterator.cc b/libdw/c++/die_tree_iterator.cc
new file mode 100644
index 0000000..11b1bf6
--- /dev/null
+++ b/libdw/c++/die_tree_iterator.cc
@@ -0,0 +1,213 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include <dwarf.h>
+#include <algorithm>
+
+#include "libdw"
+#include "libdwP.hh"
+
+namespace
+{
+  Dwarf *
+  getdw (elfutils::unit_iterator &cuit)
+  {
+    return dwarf_cu_getdwarf (cuit->cudie.cu);
+  }
+}
+
+struct elfutils::die_tree_iterator::pimpl
+{
+  unit_iterator m_cuit;
+  // Internally, only offsets are kept on the stack.
+  std::vector <Dwarf_Off> m_stack;
+  Dwarf_Die m_die;
+
+  pimpl (Dwarf_Off)
+    : m_cuit (unit_iterator::end ())
+  {}
+
+  explicit pimpl (Dwarf *dw)
+    : m_cuit (unit_iterator (dw))
+    , m_die (m_cuit->cudie)
+  {}
+
+  explicit pimpl (unit_iterator const &cuit)
+    : m_cuit (cuit)
+    , m_die (m_cuit->cudie)
+  {}
+
+  pimpl (pimpl const &that)
+    : m_cuit (that.m_cuit)
+    , m_stack (that.m_stack)
+    , m_die (that.m_die)
+  {}
+
+  bool
+  operator== (pimpl const &that) const
+  {
+    return m_cuit == that.m_cuit
+      && m_stack == that.m_stack
+      // xxx fishy.  What if other is end()?
+      && (m_cuit == unit_iterator::end ()
+	  || m_die.addr == that.m_die.addr);
+  }
+
+  void
+  move ()
+  {
+    Dwarf_Die child;
+    if (dwpp_child (m_die, child))
+      {
+	m_stack.push_back (dwarf_dieoffset (&m_die));
+	m_die = child;
+	return;
+      }
+
+    do
+      if (dwpp_siblingof (m_die, m_die))
+	return;
+      else
+	// No sibling found.  Go a level up and retry, unless this
+	// was a sole, childless CU DIE.
+	if (! m_stack.empty ())
+	  {
+	    m_die = (dwarf_tag (&m_cuit->cudie) == DW_TAG_type_unit
+		     ? dwpp_offdie_types : dwpp_offdie)
+	      (getdw (m_cuit), m_stack.back ());
+	    m_stack.pop_back ();
+	  }
+    while (! m_stack.empty ());
+
+    m_die = (++m_cuit)->cudie;
+  }
+};
+
+elfutils::die_tree_iterator::die_tree_iterator (Dwarf_Off off)
+  : m_pimpl (new pimpl (off))
+{}
+
+elfutils::die_tree_iterator::die_tree_iterator (Dwarf *dw)
+  : m_pimpl (new pimpl (dw))
+{}
+
+elfutils::die_tree_iterator::die_tree_iterator (unit_iterator const &cuit)
+  : m_pimpl (new pimpl (cuit))
+{}
+
+elfutils::die_tree_iterator::die_tree_iterator (die_tree_iterator const &that)
+  : m_pimpl (new pimpl (*that.m_pimpl))
+{}
+
+elfutils::die_tree_iterator::~die_tree_iterator ()
+{
+  delete m_pimpl;
+}
+
+elfutils::die_tree_iterator &
+elfutils::die_tree_iterator::operator= (die_tree_iterator const &that)
+{
+  if (this != &that)
+    {
+      pimpl *npimpl = new pimpl (*that.m_pimpl);
+      delete m_pimpl;
+      m_pimpl = npimpl;
+    }
+  return *this;
+}
+
+elfutils::die_tree_iterator
+elfutils::die_tree_iterator::end ()
+{
+  return die_tree_iterator ((Dwarf_Off) -1);
+}
+
+bool
+elfutils::die_tree_iterator::operator== (die_tree_iterator const &that) const
+{
+  return *m_pimpl == *that.m_pimpl;
+}
+
+bool
+elfutils::die_tree_iterator::operator!= (die_tree_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::die_tree_iterator &
+elfutils::die_tree_iterator::operator++ ()
+{
+  m_pimpl->move ();
+  return *this;
+}
+
+elfutils::die_tree_iterator
+elfutils::die_tree_iterator::operator++ (int)
+{
+  die_tree_iterator ret = *this;
+  ++*this;
+  return ret;
+}
+
+Dwarf_Die &
+elfutils::die_tree_iterator::operator* ()
+{
+  return m_pimpl->m_die;
+}
+
+Dwarf_Die *
+elfutils::die_tree_iterator::operator-> ()
+{
+  return &**this;
+}
+
+elfutils::die_tree_iterator::stack_type
+elfutils::die_tree_iterator::stack () const
+{
+  stack_type ret;
+  for (die_tree_iterator it = *this; it != end (); it = it.parent ())
+    ret.push_back (*it);
+  std::reverse (ret.begin (), ret.end ());
+  return ret;
+}
+
+elfutils::die_tree_iterator
+elfutils::die_tree_iterator::parent ()
+{
+  assert (*this != end ());
+  if (m_pimpl->m_stack.empty ())
+    return end ();
+
+  die_tree_iterator ret = *this;
+  ret.m_pimpl->m_die = (dwarf_tag (&m_pimpl->m_cuit->cudie) == DW_TAG_type_unit
+			? dwpp_offdie_types : dwpp_offdie)
+    (getdw (m_pimpl->m_cuit), m_pimpl->m_stack.back ());
+  ret.m_pimpl->m_stack.pop_back ();
+
+  return ret;
+}
diff --git a/libdw/c++/libdw b/libdw/c++/libdw
new file mode 100644
index 0000000..0b01b2d
--- /dev/null
+++ b/libdw/c++/libdw
@@ -0,0 +1,245 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBDW_CPP
+#define _LIBDW_CPP	1
+
+#include <vector>
+#include <iterator>
+
+#include <elfutils/libdw.h>
+
+namespace elfutils
+{
+  // The iterators presented here have operators * and -> as non-const
+  // member functions.  The reason is that the libdw interfaces do not
+  // take, say, Dwarf_Die const *, but Dwarf_Die *, and therefore
+  // returning a const reference of some sort would not be useful.  We
+  // don't want to give out copies either, as that adds unnecessary
+  // overhead.  And we simply don't care much if anyone does end up
+  // changing the internal copy of the current CU, DIE, or whatever.
+
+
+  // An iterator that goes over compile units (full or partial) in a
+  // given DWARF file.  The type that it points to (yields on
+  // dereference) is CU_INFO (see below).
+  //
+  // Example usage:
+  // {
+  //   std::vector <Dwarf_Off> v
+  //   for (elfutils::cu_iterator jt (dw);
+  //        jt != elfutils::cu_iterator::end (); ++jt)
+  //     v.push_back (dwarf_dieoffset (&jt->cudie));
+  // }
+  class unit_iterator;
+
+  // Helper structure with data about each compile unit.
+  struct unit_info
+  {
+    Dwarf_Die cudie;
+    Dwarf_Off abbrev_offset;
+    uint64_t type_signature; // Valid for DW_TAG_type_unit
+    Dwarf_Half version;
+    uint8_t address_size;
+    uint8_t offset_size;
+  };
+
+  class unit_iterator
+    : public std::iterator <std::input_iterator_tag, unit_info>
+  {
+    struct pimpl;
+    pimpl *m_pimpl;
+
+    explicit unit_iterator (Dwarf_Off off, bool types);
+
+  public:
+    // Construct a unit iterator that will iterate through all units
+    // in DW.
+    explicit unit_iterator (Dwarf *dw);
+
+    // Construct a unit iterator for DW such that it points to a
+    // compile (or other type of) unit represented by CUDIE.
+    unit_iterator (Dwarf *dw, Dwarf_Die cudie);
+
+    unit_iterator (unit_iterator const &that);
+    ~unit_iterator ();
+    unit_iterator &operator= (unit_iterator const &that);
+
+    // Return a unit_iterator pointing one after the last actual CU.
+    static unit_iterator end ();
+
+    bool operator== (unit_iterator const &that) const;
+    bool operator!= (unit_iterator const &that) const;
+
+    unit_iterator &operator++ ();
+    unit_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    unit_info &operator* ();
+    unit_info *operator-> ();
+  };
+
+
+  // An iterator that goes through children of a given DIE.
+  // Example usage:
+  // {
+  //    size_t nchildren = std::distance (elfutils::child_iterator (type_die),
+  //                                      elfutils::child_iterator::end ());
+  // }
+
+  class child_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Die>
+  {
+    struct pimpl;
+    pimpl *m_pimpl;
+
+    child_iterator ();
+
+  public:
+    explicit child_iterator (Dwarf_Die parent);
+    child_iterator (child_iterator const &that);
+    ~child_iterator ();
+    child_iterator &operator= (child_iterator const &that);
+
+    // Return a child_iterator pointing one after the last actual
+    // child.
+    static child_iterator end ();
+
+    bool operator== (child_iterator const &that) const;
+    bool operator!= (child_iterator const &that) const;
+
+    child_iterator &operator++ ();
+    child_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+    Dwarf_Die &operator* ();
+    Dwarf_Die *operator-> ();
+  };
+
+
+  // Tree flattening iterator.  It pre-order iterates all DIEs in
+  // given DWARF file, optionally starting from a given CU iterator.
+  // It keeps track of path from CU root to the current DIE, and that
+  // can be requested through stack() member function.
+  //
+  // Example usage:
+  // {
+  //   for (elfutils::die_tree_iterator it (dw);
+  //        it != elfutils::die_tree_iterator::end (); ++it)
+  //     {
+  //       typedef elfutils::die_tree_iterator::stack_type stack_type;
+  //       stack_type const &stack = it.stack ();
+  //       for (stack_type::const_iterator jt = stack.begin ();
+  //            jt != stack.end (); ++jt)
+  //         ...;
+  //     }
+  // }
+  class die_tree_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Die>
+  {
+    struct pimpl;
+    pimpl *m_pimpl;
+
+    die_tree_iterator (Dwarf_Off);
+
+  public:
+    typedef std::vector <Dwarf_Die> stack_type;
+
+    explicit die_tree_iterator (Dwarf *dw);
+    explicit die_tree_iterator (unit_iterator const &cuit);
+
+    die_tree_iterator (die_tree_iterator const &that);
+    ~die_tree_iterator ();
+    die_tree_iterator &operator= (die_tree_iterator const &that);
+
+    // Return a die_tree_iterator pointing one after the last actual
+    // DIE.
+    static die_tree_iterator end ();
+
+    bool operator== (die_tree_iterator const &that) const;
+    bool operator!= (die_tree_iterator const &that) const;
+
+    die_tree_iterator &operator++ ();
+    die_tree_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Die &operator* ();
+    Dwarf_Die *operator-> ();
+
+    // Return a stack of DIE's representing the path from CU DIE to
+    // the current DIE (both ends inclusive).  The first element of
+    // the returned stack is the CU DIE, the last one the current DIE.
+    stack_type stack () const;
+
+    // Return a die_tree_iterator referencing a parent of a DIE that
+    // this iterator points at.  Returns an end iterator if there is
+    // no parent.  Technically a const, but can't be one due to
+    // dwarf_tag call inside.
+    die_tree_iterator parent ();
+  };
+
+
+  // An attribute iterator goes through attributes of a given DIE.
+  class attr_iterator
+    : public std::iterator <std::input_iterator_tag, Dwarf_Attribute>
+  {
+    struct pimpl;
+    pimpl *m_pimpl;
+
+    attr_iterator (ptrdiff_t offset);
+
+  public:
+    attr_iterator (Dwarf_Die *die);
+    attr_iterator (attr_iterator const &that);
+    ~attr_iterator ();
+    attr_iterator &operator= (attr_iterator const &that);
+
+    // Return an attr_iterator pointing one after the last actual
+    // attribute.
+    static attr_iterator end ();
+
+    bool operator== (attr_iterator const &that) const;
+    bool operator!= (attr_iterator const &that) const;
+
+    attr_iterator &operator++ ();
+    attr_iterator operator++ (int);
+
+    // N.B. see top of the file for explanation of non-constness of
+    // operators * and ->.
+
+    Dwarf_Attribute &operator* ();
+    Dwarf_Attribute *operator-> ();
+  };
+}
+
+#endif
diff --git a/libdw/c++/libdwP.hh b/libdw/c++/libdwP.hh
new file mode 100644
index 0000000..cb6a145
--- /dev/null
+++ b/libdw/c++/libdwP.hh
@@ -0,0 +1,88 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CPP_LIBDWP_H
+#define _CPP_LIBDWP_H 1
+
+#include <stdexcept>
+#include <cassert>
+#include <cstdlib>
+
+inline void
+throw_libdw (int dwerr = 0)
+{
+  if (dwerr == 0)
+    dwerr = dwarf_errno ();
+  assert (dwerr != 0);
+  throw std::runtime_error (dwarf_errmsg (dwerr));
+}
+
+inline bool
+dwpp_child (Dwarf_Die &die, Dwarf_Die &result)
+{
+  int ret = dwarf_child (&die, &result);
+  if (ret < 0)
+    throw_libdw ();
+  return ret == 0;
+}
+
+inline bool
+dwpp_siblingof (Dwarf_Die &die, Dwarf_Die &result)
+{
+  switch (dwarf_siblingof (&die, &result))
+    {
+    case -1:
+      throw_libdw ();
+    case 0:
+      return true;
+    case 1:
+      return false;
+    default:
+      std::abort ();
+    }
+}
+
+inline Dwarf_Die
+dwpp_offdie (Dwarf *dbg, Dwarf_Off offset)
+{
+  Dwarf_Die result;
+  if (dwarf_offdie (dbg, offset, &result) == NULL)
+    throw_libdw ();
+  return result;
+}
+
+inline Dwarf_Die
+dwpp_offdie_types (Dwarf *dbg, Dwarf_Off offset)
+{
+  Dwarf_Die result;
+  if (dwarf_offdie_types (dbg, offset, &result) == NULL)
+    throw_libdw ();
+  return result;
+}
+
+#endif
diff --git a/libdw/c++/unit_iterator.cc b/libdw/c++/unit_iterator.cc
new file mode 100644
index 0000000..f49b363
--- /dev/null
+++ b/libdw/c++/unit_iterator.cc
@@ -0,0 +1,204 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include <dwarf.h>
+
+#include "libdw"
+#include "libdwP.hh"
+
+struct elfutils::unit_iterator::pimpl
+{
+  Dwarf *m_dw;
+  Dwarf_Off m_offset;
+  Dwarf_Off m_old_offset;
+  unit_info m_info;
+  bool m_types;
+
+  bool
+  is_end ()
+  {
+    return m_offset == (Dwarf_Off) -1 && m_types;
+  }
+
+  void
+  move ()
+  {
+    assert (! is_end ());
+    m_old_offset = m_offset;
+    size_t hsize;
+    int rc = dwarf_next_unit (m_dw, m_offset, &m_offset, &hsize,
+			      &m_info.version, &m_info.abbrev_offset,
+			      &m_info.address_size, &m_info.offset_size,
+			      m_types ? &m_info.type_signature : NULL, NULL);
+    if (rc < 0)
+      throw_libdw ();
+
+    if (rc != 0 && m_types)
+      done ();
+    else if (rc != 0)
+      {
+	m_types = true;
+	m_offset = 0;
+	m_old_offset = 0;
+	move ();
+      }
+    else
+      m_info.cudie = (m_types ? dwpp_offdie_types : dwpp_offdie)
+	(m_dw, m_old_offset + hsize);
+  }
+
+  void
+  done ()
+  {
+    *this = *end ().m_pimpl;
+    assert (is_end ());
+  }
+
+  pimpl (Dwarf_Off off, bool types)
+    : m_dw (NULL)
+    , m_offset (off)
+    , m_old_offset (0)
+    , m_types (types)
+  {}
+
+  explicit pimpl (Dwarf *dw)
+    : m_dw (dw)
+    , m_offset (0)
+    , m_old_offset (0)
+    , m_types (false)
+  {
+    move ();
+  }
+
+  // Construct a CU iterator for DW such that it points to a compile
+  // unit represented by CUDIE.
+  pimpl (Dwarf *dw, Dwarf_Die cudie)
+    : m_dw (dw)
+    , m_offset (dwarf_dieoffset (&cudie) - dwarf_cuoffset (&cudie))
+    , m_old_offset (0)
+    , m_types (dwarf_tag (&cudie) == DW_TAG_type_unit)
+  {
+    move ();
+  }
+
+  bool
+  operator== (pimpl const &that) const
+  {
+    return m_types == that.m_types && m_offset == that.m_offset;
+  }
+
+  unit_info &
+  deref ()
+  {
+    return m_info;
+  }
+};
+
+elfutils::unit_iterator::unit_iterator (Dwarf_Off off, bool types)
+  : m_pimpl (new pimpl (off, types))
+{
+}
+
+elfutils::unit_iterator::unit_iterator (Dwarf *dw)
+  : m_pimpl (new pimpl (dw))
+{
+}
+
+elfutils::unit_iterator::unit_iterator (Dwarf *dw, Dwarf_Die cudie)
+  : m_pimpl (new pimpl (dw, cudie))
+{
+}
+
+elfutils::unit_iterator::unit_iterator (unit_iterator const &that)
+  : m_pimpl (new pimpl (*that.m_pimpl))
+{
+}
+
+elfutils::unit_iterator::~unit_iterator ()
+{
+  delete m_pimpl;
+}
+
+elfutils::unit_iterator &
+elfutils::unit_iterator::operator= (unit_iterator const &that)
+{
+  if (this != &that)
+    {
+      pimpl *npimpl = new pimpl (*that.m_pimpl);
+      delete m_pimpl;
+      m_pimpl = npimpl;
+    }
+  return *this;
+}
+
+elfutils::unit_iterator
+elfutils::unit_iterator::end ()
+{
+  unit_iterator ret ((Dwarf_Off) -1, true);
+  assert (ret.m_pimpl->is_end ());
+  return ret;
+}
+
+bool
+elfutils::unit_iterator::operator== (unit_iterator const &that) const
+{
+  return *m_pimpl == *that.m_pimpl;
+}
+
+bool
+elfutils::unit_iterator::operator!= (unit_iterator const &that) const
+{
+  return ! (*this == that);
+}
+
+elfutils::unit_iterator &
+elfutils::unit_iterator::operator++ ()
+{
+  m_pimpl->move ();
+  return *this;
+}
+
+elfutils::unit_iterator
+elfutils::unit_iterator::operator++ (int)
+{
+  unit_iterator tmp = *this;
+  ++*this;
+  return tmp;
+}
+
+elfutils::unit_info &
+elfutils::unit_iterator::operator* ()
+{
+  return m_pimpl->deref ();
+}
+
+elfutils::unit_info *
+elfutils::unit_iterator::operator-> ()
+{
+  return &**this;
+}
diff --git a/libdw/libdwpp.map b/libdw/libdwpp.map
new file mode 100644
index 0000000..7d85575
--- /dev/null
+++ b/libdw/libdwpp.map
@@ -0,0 +1,199 @@
+ELFUTILS_0 { };
+
+ELFUTILS_0.162 {
+  global:
+
+    # ### elfutils::die_tree_iterator ###
+
+    # die_tree_iterator(Dwarf*)
+    _ZN8elfutils17die_tree_iteratorC1EP5Dwarf;
+
+    # die_tree_iterator(elfutils::die_tree_iterator const&)
+    _ZN8elfutils17die_tree_iteratorC1ERKS0_;
+
+    # die_tree_iterator(elfutils::unit_iterator const&)
+    _ZN8elfutils17die_tree_iteratorC1ERKNS_13unit_iteratorE;
+
+    # ~die_tree_iterator()
+    _ZN8elfutils17die_tree_iteratorD1Ev;
+
+    # operator=(elfutils::die_tree_iterator const&)
+    _ZN8elfutils17die_tree_iteratoraSERKS0_;
+
+    # parent()
+    _ZN8elfutils17die_tree_iterator6parentEv;
+
+    # stack() const
+    _ZNK8elfutils17die_tree_iterator5stackEv;
+
+    # end()
+    _ZN8elfutils17die_tree_iterator3endEv;
+
+    # operator->()
+    _ZN8elfutils17die_tree_iteratorptEv;
+
+    # operator*()
+    _ZN8elfutils17die_tree_iteratordeEv;
+
+    # operator++()
+    _ZN8elfutils17die_tree_iteratorppEv;
+
+    # operator++(int)
+    _ZN8elfutils17die_tree_iteratorppEi;
+
+    # operator==(elfutils::die_tree_iterator const&) const
+    _ZNK8elfutils17die_tree_iteratoreqERKS0_;
+
+    # operator!=(elfutils::die_tree_iterator const&) const
+    _ZNK8elfutils17die_tree_iteratorneERKS0_;
+
+
+    # ### elfutils::unit_iterator ###
+
+    # unit_iterator(Dwarf*)
+    _ZN8elfutils13unit_iteratorC1EP5Dwarf;
+
+    # unit_iterator(Dwarf*, Dwarf_Die)
+    _ZN8elfutils13unit_iteratorC1EP5Dwarf9Dwarf_Die;
+
+    # unit_iterator(elfutils::unit_iterator const&)
+    _ZN8elfutils13unit_iteratorC1ERKS0_;
+
+    # ~unit_iterator()
+    _ZN8elfutils13unit_iteratorD1Ev;
+
+    # operator=(elfutils::unit_iterator const&)
+    _ZN8elfutils13unit_iteratoraSERKS0_;
+
+    # end()
+    _ZN8elfutils13unit_iterator3endEv;
+
+    # operator->()
+    _ZN8elfutils13unit_iteratorptEv;
+
+    # operator*()
+    _ZN8elfutils13unit_iteratordeEv;
+
+    # operator++()
+    _ZN8elfutils13unit_iteratorppEv;
+
+    # operator++(int)
+    _ZN8elfutils13unit_iteratorppEi;
+
+    # operator==(elfutils::unit_iterator const&) const
+    _ZNK8elfutils13unit_iteratoreqERKS0_;
+
+    # operator!=(elfutils::unit_iterator const&) const
+    _ZNK8elfutils13unit_iteratorneERKS0_;
+
+
+    # ### elfutils::child_iterator ###
+
+    # child_iterator(elfutils::child_iterator const&)
+    _ZN8elfutils14child_iteratorC1ERKS0_;
+
+    # child_iterator(Dwarf_Die)
+    _ZN8elfutils14child_iteratorC1E9Dwarf_Die;
+
+    # ~child_iterator()
+    _ZN8elfutils14child_iteratorD1Ev;
+
+    # operator=(elfutils::child_iterator const&)
+    _ZN8elfutils14child_iteratoraSERKS0_;
+
+    # end()
+    _ZN8elfutils14child_iterator3endEv;
+
+    # operator==(elfutils::child_iterator const&) const
+    _ZNK8elfutils14child_iteratoreqERKS0_;
+
+    # operator!=(elfutils::child_iterator const&) const
+    _ZNK8elfutils14child_iteratorneERKS0_;
+
+    # operator*()
+    _ZN8elfutils14child_iteratordeEv;
+
+    # operator->()
+    _ZN8elfutils14child_iteratorptEv;
+
+    # operator++()
+    _ZN8elfutils14child_iteratorppEv;
+
+    # operator++(int)
+    _ZN8elfutils14child_iteratorppEi;
+
+
+    # ### elfutils::attr_iterator ###
+
+    # attr_iterator(Dwarf_Die*)
+    _ZN8elfutils13attr_iteratorC1EP9Dwarf_Die;
+
+    # attr_iterator(elfutils::attr_iterator const&)
+    _ZN8elfutils13attr_iteratorC1ERKS0_;
+
+    # ~attr_iterator()
+    _ZN8elfutils13attr_iteratorD1Ev;
+
+    # operator=(elfutils::attr_iterator const&)
+    _ZN8elfutils13attr_iteratoraSERKS0_;
+
+    # end()
+    _ZN8elfutils13attr_iterator3endEv;
+
+    # operator++()
+    _ZN8elfutils13attr_iteratorppEv;
+
+    # operator++(int)
+    _ZN8elfutils13attr_iteratorppEi;
+
+    # operator*()
+    _ZN8elfutils13attr_iteratordeEv;
+
+    # operator->()
+    _ZN8elfutils13attr_iteratorptEv;
+
+    # operator==(elfutils::attr_iterator const&) const
+    _ZNK8elfutils13attr_iteratoreqERKS0_;
+
+    # operator!=(elfutils::attr_iterator const&) const
+    _ZNK8elfutils13attr_iteratorneERKS0_;
+
+
+    # ### elfutils::dwfl_module_iterator ###
+
+    # dwfl_module_iterator(Dwfl*)
+    _ZN8elfutils20dwfl_module_iteratorC1EP4Dwfl;
+
+    # dwfl_module_iterator(elfutils::dwfl_module_iterator const&)
+    _ZN8elfutils20dwfl_module_iteratorC1ERKS0_;
+
+    # ~dwfl_module_iterator()
+    _ZN8elfutils20dwfl_module_iteratorD1Ev;
+
+    # operator=(elfutils::dwfl_module_iterator const&)
+    _ZN8elfutils20dwfl_module_iteratoraSERKS0_;
+
+    # end()
+    _ZN8elfutils20dwfl_module_iterator3endEv;
+
+    # operator++()
+    _ZN8elfutils20dwfl_module_iteratorppEv;
+
+    # operator++(int)
+    _ZN8elfutils20dwfl_module_iteratorppEi;
+
+    # operator*() const
+    _ZNK8elfutils20dwfl_module_iteratordeEv;
+
+    # operator->() const
+    _ZNK8elfutils20dwfl_module_iteratorptEv;
+
+    # operator==(elfutils::dwfl_module_iterator const&) const
+    _ZNK8elfutils20dwfl_module_iteratoreqERKS0_;
+
+    # operator!=(elfutils::dwfl_module_iterator const&) const
+    _ZNK8elfutils20dwfl_module_iteratorneERKS0_;
+
+  local:
+    *;
+} ELFUTILS_0;
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index d40dbae..b5cfdb9 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,16 @@
+2015-03-17  Petr Machata  <pmachata@redhat.com>
+
+	* c++: New directory.
+	* c++/libdwfl, c++/libdwflP.hh: New header files.
+	* Makefile.am (AUTOMAKE_OPTIONS): New variable.
+	[HAVE_CXX] (pkginclude_HEADERS): Add c++/libdwfl.
+	[HAVE_CXX] (noinst_LIBRARIES): Add libdwflpp.a, libdwflpp_pic.a.
+	[HAVE_CXX] (libdwflpp_a_SOURCES, libdwflpp, libdwpp): New variables.
+	[HAVE_CXX] (libdwflpp_pic_a_SOURCES): Likewise.
+	[HAVE_CXX] (am_libdwflpp_pic_a_OBJECTS): Likewise.
+	(noinst_HEADERS): Add c++/libdwflP.hh.
+	[HAVE_CXX] (CLEANFILES): Add $(am_libdwflpp_pic_a_OBJECTS).
+
 2015-01-26  Mark Wielaard  <mjw@redhat.com>
 
 	* dwfl_module_getdwarf.c (find_symtab): Explicitly clear symdata,
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index 72c980b..21e1844 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -2,7 +2,7 @@
 ##
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+## Copyright (C) 2005-2010, 2013, 2015 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -34,10 +34,23 @@ AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
 	   -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf
 VERSION = 1
 
+# For c++/ subdir.
+AUTOMAKE_OPTIONS = subdir-objects
+
 noinst_LIBRARIES = libdwfl.a
+if HAVE_CXX
+noinst_LIBRARIES += libdwflpp.a
+endif
+
 noinst_LIBRARIES += libdwfl_pic.a
+if HAVE_CXX
+noinst_LIBRARIES += libdwflpp_pic.a
+endif
 
 pkginclude_HEADERS = libdwfl.h
+if HAVE_CXX
+pkginclude_HEADERS += c++/libdwfl
+endif
 
 libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
 		    dwfl_module.c dwfl_report_elf.c relocate.c \
@@ -70,6 +83,10 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
 		    dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \
 		    linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c
 
+if HAVE_CXX
+libdwflpp_a_SOURCES = c++/dwfl_module_iterator.cc
+endif
+
 if ZLIB
 libdwfl_a_SOURCES += gzip.c
 endif
@@ -82,6 +99,10 @@ endif
 
 libdwfl = $(libdw)
 libdw = ../libdw/libdw.so
+if HAVE_CXX
+libdwflpp = $(libdwpp)
+libdwpp = ../libdw/libdwpp.so
+endif
 libelf = ../libelf/libelf.so
 libebl = ../libebl/libebl.a
 libeu = ../lib/libeu.a
@@ -89,6 +110,14 @@ libeu = ../lib/libeu.a
 libdwfl_pic_a_SOURCES =
 am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os)
 
-noinst_HEADERS = libdwflP.h
+if HAVE_CXX
+libdwflpp_pic_a_SOURCES =
+am_libdwflpp_pic_a_OBJECTS = $(libdwflpp_a_SOURCES:.cc=.os)
+endif
+
+noinst_HEADERS = libdwflP.h c++/libdwflP.hh
 
 CLEANFILES += $(am_libdwfl_pic_a_OBJECTS)
+if HAVE_CXX
+CLEANFILES += $(am_libdwflpp_pic_a_OBJECTS)
+endif
diff --git a/libdwfl/c++/dwfl_module_iterator.cc b/libdwfl/c++/dwfl_module_iterator.cc
new file mode 100644
index 0000000..26f96fe
--- /dev/null
+++ b/libdwfl/c++/dwfl_module_iterator.cc
@@ -0,0 +1,154 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "libdwfl"
+#include "libdwflP.hh"
+
+struct elfutils::dwfl_module_iterator::pimpl
+{
+  Dwfl *m_dwfl;
+  ptrdiff_t m_offset;
+  Dwfl_Module *m_module;
+
+  static int
+  module_cb (Dwfl_Module *mod, void **, const char *,
+	     Dwarf_Addr, void *arg)
+  {
+    pimpl *self = static_cast <pimpl *> (arg);
+    self->m_module = mod;
+    return DWARF_CB_ABORT;
+  }
+
+  void
+  move ()
+  {
+    m_offset = dwfl_getmodules (m_dwfl, module_cb, this, m_offset);
+    if (m_offset == -1)
+      throw_libdwfl ();
+  }
+
+  explicit pimpl (ptrdiff_t off)
+    : m_dwfl (NULL)
+    , m_offset (off)
+  {}
+
+  explicit pimpl (Dwfl *dwfl)
+    : m_dwfl (dwfl)
+    , m_offset (0)
+  {
+    move ();
+  }
+
+  pimpl (pimpl const &that)
+    : m_dwfl (that.m_dwfl)
+    , m_offset (that.m_offset)
+    , m_module (that.m_module)
+  {}
+
+  bool
+  operator== (pimpl const &that) const
+  {
+    assert (m_dwfl == NULL || that.m_dwfl == NULL || m_dwfl == that.m_dwfl);
+    return m_offset == that.m_offset;
+  }
+};
+
+elfutils::dwfl_module_iterator::dwfl_module_iterator (ptrdiff_t off)
+  : m_pimpl (new pimpl (off))
+{}
+
+elfutils::dwfl_module_iterator::dwfl_module_iterator (Dwfl *dwfl)
+  : m_pimpl (new pimpl (dwfl))
+{}
+
+elfutils::dwfl_module_iterator::dwfl_module_iterator (dwfl_module_iterator const &that)
+  : m_pimpl (new pimpl (*that.m_pimpl))
+{}
+
+elfutils::dwfl_module_iterator::~dwfl_module_iterator ()
+{
+  delete m_pimpl;
+}
+
+elfutils::dwfl_module_iterator &
+elfutils::dwfl_module_iterator::operator= (dwfl_module_iterator const &that)
+{
+  if (this != &that)
+    {
+      pimpl *npimpl = new pimpl (*that.m_pimpl);
+      delete m_pimpl;
+      m_pimpl = npimpl;
+    }
+
+  return *this;
+}
+
+elfutils::dwfl_module_iterator
+elfutils::dwfl_module_iterator::end ()
+{
+  return dwfl_module_iterator ((ptrdiff_t) 0);
+}
+
+elfutils::dwfl_module_iterator &
+elfutils::dwfl_module_iterator::operator++ ()
+{
+  m_pimpl->move ();
+  return *this;
+}
+
+elfutils::dwfl_module_iterator
+elfutils::dwfl_module_iterator::operator++ (int)
+{
+  dwfl_module_iterator ret = *this;
+  ++*this;
+  return ret;
+}
+
+Dwfl_Module &
+elfutils::dwfl_module_iterator::operator* () const
+{
+  return *m_pimpl->m_module;
+}
+
+Dwfl_Module *
+elfutils::dwfl_module_iterator::operator-> () const
+{
+  return &**this;
+}
+
+bool
+elfutils::dwfl_module_iterator::operator== (dwfl_module_iterator const &that) const
+{
+  return *m_pimpl == *that.m_pimpl;
+}
+
+bool
+elfutils::dwfl_module_iterator::operator!= (dwfl_module_iterator const &that) const
+{
+  return ! (*this == that);
+}
diff --git a/libdwfl/c++/libdwfl b/libdwfl/c++/libdwfl
new file mode 100644
index 0000000..42eb584
--- /dev/null
+++ b/libdwfl/c++/libdwfl
@@ -0,0 +1,70 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBDWFL_CPP
+#define _LIBDWFL_CPP	1
+
+#include <iterator>
+#include <elfutils/libdwfl.h>
+
+namespace elfutils
+{
+  // Given a Dwfl, iterates through its Dwfl_Module's.
+  //
+  // Example usage:
+  // std::vector <Dwfl_Module *> mods (elfutils::dwfl_module_iterator (dwfl),
+  //				       elfutils::dwfl_module_iterator::end ());
+
+  class dwfl_module_iterator
+    : public std::iterator <std::input_iterator_tag, Dwfl_Module *>
+  {
+    struct pimpl;
+    pimpl *m_pimpl;
+
+    explicit dwfl_module_iterator (ptrdiff_t off);
+
+  public:
+    explicit dwfl_module_iterator (Dwfl *dwfl);
+    dwfl_module_iterator (dwfl_module_iterator const &that);
+    ~dwfl_module_iterator ();
+    dwfl_module_iterator &operator= (dwfl_module_iterator const &that);
+
+    static dwfl_module_iterator end ();
+
+    dwfl_module_iterator &operator++ ();
+    dwfl_module_iterator operator++ (int);
+
+    Dwfl_Module &operator* () const;
+    Dwfl_Module *operator-> () const;
+
+    bool operator== (dwfl_module_iterator const &that) const;
+    bool operator!= (dwfl_module_iterator const &that) const;
+  };
+}
+
+#endif
diff --git a/libdwfl/c++/libdwflP.hh b/libdwfl/c++/libdwflP.hh
new file mode 100644
index 0000000..c3f2792
--- /dev/null
+++ b/libdwfl/c++/libdwflP.hh
@@ -0,0 +1,44 @@
+/* -*-c++-*-
+   Copyright (C) 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CPP_LIBDWFLP_H
+#define _CPP_LIBDWFLP_H 1
+
+#include <stdexcept>
+#include <cassert>
+
+static inline void
+throw_libdwfl (int dwflerr = 0)
+{
+  if (dwflerr == 0)
+    dwflerr = dwfl_errno ();
+  assert (dwflerr != 0);
+  throw std::runtime_error (dwfl_errmsg (dwflerr));
+}
+
+#endif
diff --git a/tests/ChangeLog b/tests/ChangeLog
index a029645..f50da48 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,5 +1,12 @@
 2015-03-18  Petr Machata  <pmachata@redhat.com>
 
+	* test-iterators.cc, run-test-iterators.sh: New files.
+	* Makefile.am (check_PROGRAMS) [HAVE_CXX]: Add test-iterators.
+	(TESTS) [HAVE_CXX]: Add run-test-iterators.sh.
+	(libdwpp): New variable.
+
+2015-03-18  Petr Machata  <pmachata@redhat.com>
+
 	* addrcfi.c (op_name): Adjust uses of know-dwarf.h macros to match
 	the API changes.
 	* allregs.c (dwarf_encoding_string): Likewise.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9e8bc02..abe6da7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -141,6 +141,11 @@ check_PROGRAMS += $(asm_TESTS)
 TESTS += $(asm_TESTS)
 endif
 
+if HAVE_CXX
+check_PROGRAMS += test-iterators
+TESTS += run-test-iterators.sh
+endif
+
 EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     run-show-die-info.sh run-get-files.sh run-get-lines.sh \
 	     run-get-pubnames.sh run-get-aranges.sh \
@@ -286,7 +291,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     testfile-sizes3.o.bz2 \
 	     run-readelf-A.sh testfileppc32attrs.o.bz2 \
 	     testfile-debug-types.bz2 \
-	     run-getsrc-die.sh run-strptr.sh
+	     run-getsrc-die.sh run-strptr.sh run-test-iterators.sh
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
@@ -330,16 +335,19 @@ endif !STANDALONE
 
 if STANDALONE
 libdw = -ldw
+libdwpp = -ldwpp
 libelf = -lelf
 libasm = -lasm
 libebl = -lebl
 else !STANDALONE
 if BUILD_STATIC
 libdw = ../libdw/libdw.a $(zip_LIBS) $(libelf) $(libebl) -ldl
+libdwpp = ../libdw/libdwpp.a $(libdw)
 libelf = ../libelf/libelf.a
 libasm = ../libasm/libasm.a
 else
 libdw = ../libdw/libdw.so
+libdwpp = ../libdw/libdwpp.so
 libelf = ../libelf/libelf.so
 libasm = ../libasm/libasm.so
 endif
@@ -431,6 +439,8 @@ getsrc_die_LDADD = $(libdw) $(libelf)
 strptr_LDADD = $(libelf)
 newdata_LDADD = $(libelf)
 elfstrtab_LDADD = $(libelf)
+test_iterators_SOURCES = test-iterators.cc
+test_iterators_LDADD = $(libdw) $(libdwpp)
 
 if GCOV
 check: check-am coverage
diff --git a/tests/run-test-iterators.sh b/tests/run-test-iterators.sh
new file mode 100755
index 0000000..6942570
--- /dev/null
+++ b/tests/run-test-iterators.sh
@@ -0,0 +1,52 @@
+#! /bin/sh
+# Copyright (C) 2015 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile39 testfile-debug-types
+
+testrun_compare ${abs_top_builddir}/tests/test-iterators testfile39 <<\EOF
+0xb
+0x9e
+0x135
+0x1c8
+0 7
+0 7
+0 7
+0 7
+EOF
+
+testrun_compare ${abs_top_builddir}/tests/test-iterators testfile-debug-types <<\EOF
+0xb
+0x17
+0x5a
+4 6
+0 9
+0 3
+0 6
+0 6
+2 3
+1 4
+0 2
+0 5
+1 3
+2 4
+0 3
+0 5
+EOF
+
+exit 0
diff --git a/tests/test-iterators.cc b/tests/test-iterators.cc
new file mode 100644
index 0000000..987bc6a
--- /dev/null
+++ b/tests/test-iterators.cc
@@ -0,0 +1,87 @@
+/* Copyright (C) 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <iostream>
+#include <cassert>
+#include <stdexcept>
+
+#include "../libdw/c++/libdw"
+#include "../libdwfl/c++/libdwfl"
+#include "../libdw/c++/libdwP.hh"
+#include "../libdwfl/c++/libdwflP.hh"
+
+int
+main (int, char *argv[])
+{
+  int fd = open (argv[1], O_RDONLY);
+  if (fd < 0)
+    throw std::runtime_error (strerror (errno));
+
+  const static Dwfl_Callbacks callbacks =
+    {
+      .find_elf = dwfl_build_id_find_elf,
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .section_address = dwfl_offline_section_address,
+      .debuginfo_path = NULL,
+    };
+
+  Dwfl *dwfl = dwfl_begin (&callbacks);
+  if (dwfl == NULL)
+    throw_libdwfl ();
+
+  dwfl_report_begin (dwfl);
+  if (dwfl_report_offline (dwfl, argv[1], argv[1], fd) == NULL)
+    throw_libdwfl ();
+  dwfl_report_end (dwfl, NULL, NULL);
+
+  for (elfutils::dwfl_module_iterator modit (dwfl);
+       modit != elfutils::dwfl_module_iterator::end (); ++modit)
+    {
+      Dwarf_Off bias;
+      Dwarf *dw = dwfl_module_getdwarf (&*modit, &bias);
+      std::vector <std::pair <elfutils::unit_iterator, Dwarf_Die> > cudies;
+      for (elfutils::unit_iterator it (dw); it != elfutils::unit_iterator::end (); ++it)
+	cudies.push_back (std::make_pair (it, it->cudie));
+
+      for (size_t i = 0; i < cudies.size (); ++i)
+	{
+	  elfutils::unit_iterator jt (dw, cudies[i].second);
+	  std::cerr << std::hex << std::showbase
+		    << dwarf_dieoffset (&jt->cudie) << std::endl;
+	  for (size_t j = i; jt != elfutils::unit_iterator::end (); ++jt, ++j)
+	    assert (jt == cudies[j].first);
+	}
+
+      assert (elfutils::die_tree_iterator (elfutils::unit_iterator::end ())
+	      == elfutils::die_tree_iterator::end ());
+
+      for (elfutils::die_tree_iterator it (dw);
+	   it != elfutils::die_tree_iterator::end (); ++it)
+	std::cerr << std::dec
+		  << std::distance (elfutils::child_iterator (*it),
+				    elfutils::child_iterator::end ()) << ' '
+		  << std::distance (elfutils::attr_iterator (&*it),
+				    elfutils::attr_iterator::end ())
+		  << std::endl;
+    }
+
+  dwfl_end (dwfl);
+}
-- 
2.1.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2015-04-20  9:28 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-20  9:28 [PATCH 2/2] Add C++ iterators Mark Wielaard
  -- strict thread matches above, loose matches on Subject: below --
2015-04-17 16:54 Petr Machata
2015-04-16 19:23 Mark Wielaard
2015-04-16 15:42 Petr Machata
2015-04-16 15:06 Mark Wielaard
2015-04-16 14:07 Frank Ch. Eigler
2015-04-16 13:52 Petr Machata
2015-04-16 13:29 Mark Wielaard
2015-04-14 14:47 Petr Machata
2015-04-14 13:53 Petr Machata
2015-04-14 13:52 Petr Machata
2015-04-03 21:34 Petr Machata
2015-04-03 13:39 Petr Machata
2015-04-03 12:56 Mark Wielaard
2015-04-03 11:27 Petr Machata
2015-04-03  8:51 Petr Machata

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).