aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/dwarf/dwarf2diehandler.cc183
-rw-r--r--src/common/dwarf/dwarf2diehandler.h345
-rw-r--r--src/common/dwarf/dwarf2diehandler_unittest.cc556
-rw-r--r--src/tools/linux/dump_syms/Makefile22
4 files changed, 1106 insertions, 0 deletions
diff --git a/src/common/dwarf/dwarf2diehandler.cc b/src/common/dwarf/dwarf2diehandler.cc
new file mode 100644
index 00000000..229a8284
--- /dev/null
+++ b/src/common/dwarf/dwarf2diehandler.cc
@@ -0,0 +1,183 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Implementation of dwarf2reader::DieDispatcher class.
+
+#include <cassert>
+
+#include "common/dwarf/dwarf2diehandler.h"
+
+namespace dwarf2reader {
+
+DIEDispatcher::~DIEDispatcher() {
+ while (!die_handlers_.empty()) {
+ HandlerStack &entry = die_handlers_.top();
+ if (entry.handler_ != root_handler_)
+ delete entry.handler_;
+ die_handlers_.pop();
+ }
+}
+
+bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size,
+ uint8 offset_size, uint64 cu_length,
+ uint8 dwarf_version) {
+ return root_handler_->StartCompilationUnit(offset, address_size,
+ offset_size, cu_length,
+ dwarf_version);
+}
+
+bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
+ const AttributeList& attrs) {
+ // The stack entry for the parent of this DIE, if there is one.
+ HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top();
+
+ // Does this call indicate that we're done receiving the parent's
+ // attributes' values? If so, call its EndAttributes member function.
+ if (parent && parent->handler_ && !parent->reported_attributes_end_) {
+ parent->reported_attributes_end_ = true;
+ if (!parent->handler_->EndAttributes()) {
+ // Finish off this handler now. and edit *PARENT to indicate that
+ // we don't want to visit any of the children.
+ parent->handler_->Finish();
+ if (parent->handler_ != root_handler_)
+ delete parent->handler_;
+ parent->handler_ = NULL;
+ return false;
+ }
+ }
+
+ // Find a handler for this DIE.
+ DIEHandler *handler;
+ if (parent) {
+ if (parent->handler_)
+ // Ask the parent to find a handler.
+ handler = parent->handler_->FindChildHandler(offset, tag, attrs);
+ else
+ // No parent handler means we're not interested in any of our
+ // children.
+ handler = NULL;
+ } else {
+ // This is the root DIE. For a non-root DIE, the parent's handler
+ // decides whether to visit it, but the root DIE has no parent
+ // handler, so we have a special method on the root DIE handler
+ // itself to decide.
+ if (root_handler_->StartRootDIE(offset, tag, attrs))
+ handler = root_handler_;
+ else
+ handler = NULL;
+ }
+
+ // Push a handler stack entry for this new handler. As an
+ // optimization, we don't push NULL-handler entries on top of other
+ // NULL-handler entries; we just let the oldest such entry stand for
+ // the whole subtree.
+ if (handler || !parent || parent->handler_) {
+ HandlerStack entry;
+ entry.offset_ = offset;
+ entry.handler_ = handler;
+ entry.reported_attributes_end_ = false;
+ die_handlers_.push(entry);
+ }
+
+ return handler != NULL;
+}
+
+void DIEDispatcher::EndDIE(uint64 offset) {
+ assert(!die_handlers_.empty());
+ HandlerStack *entry = &die_handlers_.top();
+ if (entry->handler_) {
+ // This entry had better be the handler for this DIE.
+ assert(entry->offset_ == offset);
+ // If a DIE has no children, this EndDIE call indicates that we're
+ // done receiving its attributes' values.
+ if (!entry->reported_attributes_end_)
+ entry->handler_->EndAttributes(); // Ignore return value: no children.
+ entry->handler_->Finish();
+ if (entry->handler_ != root_handler_)
+ delete entry->handler_;
+ } else {
+ // If this DIE is within a tree we're ignoring, then don't pop the
+ // handler stack: that entry stands for the whole tree.
+ if (entry->offset_ != offset)
+ return;
+ }
+ die_handlers_.pop();
+}
+
+void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) {
+ HandlerStack &current = die_handlers_.top();
+ // This had better be an attribute of the DIE we were meant to handle.
+ assert(offset == current.offset_);
+ current.handler_->ProcessAttributeUnsigned(attr, form, data);
+}
+
+void DIEDispatcher::ProcessAttributeSigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ int64 data) {
+ HandlerStack &current = die_handlers_.top();
+ // This had better be an attribute of the DIE we were meant to handle.
+ assert(offset == current.offset_);
+ current.handler_->ProcessAttributeSigned(attr, form, data);
+}
+
+void DIEDispatcher::ProcessAttributeReference(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) {
+ HandlerStack &current = die_handlers_.top();
+ // This had better be an attribute of the DIE we were meant to handle.
+ assert(offset == current.offset_);
+ current.handler_->ProcessAttributeReference(attr, form, data);
+}
+
+void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const char* data,
+ uint64 len) {
+ HandlerStack &current = die_handlers_.top();
+ // This had better be an attribute of the DIE we were meant to handle.
+ assert(offset == current.offset_);
+ current.handler_->ProcessAttributeBuffer(attr, form, data, len);
+}
+
+void DIEDispatcher::ProcessAttributeString(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string& data) {
+ HandlerStack &current = die_handlers_.top();
+ // This had better be an attribute of the DIE we were meant to handle.
+ assert(offset == current.offset_);
+ current.handler_->ProcessAttributeString(attr, form, data);
+}
+
+} // namespace dwarf2reader
diff --git a/src/common/dwarf/dwarf2diehandler.h b/src/common/dwarf/dwarf2diehandler.h
new file mode 100644
index 00000000..6834193b
--- /dev/null
+++ b/src/common/dwarf/dwarf2diehandler.h
@@ -0,0 +1,345 @@
+// -*- mode: c++ -*-
+
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// dwarf2reader::CompilationUnit is a simple and direct parser for
+// DWARF data, but its handler interface is not convenient to use. In
+// particular:
+//
+// - CompilationUnit calls Dwarf2Handler's member functions to report
+// every attribute's value, regardless of what sort of DIE it is.
+// As a result, the ProcessAttributeX functions end up looking like
+// this:
+//
+// switch (parent_die_tag) {
+// case DW_TAG_x:
+// switch (attribute_name) {
+// case DW_AT_y:
+// handle attribute y of DIE type x
+// ...
+// } break;
+// ...
+// }
+//
+// In C++ it's much nicer to use virtual function dispatch to find
+// the right code for a given case than to switch on the DIE tag
+// like this.
+//
+// - Processing different kinds of DIEs requires different sets of
+// data: lexical block DIEs have start and end addresses, but struct
+// type DIEs don't. It would be nice to be able to have separate
+// handler classes for separate kinds of DIEs, each with the members
+// appropriate to its role, instead of having one handler class that
+// needs to hold data for all every DIE type.
+//
+// - There should be a separate instance of the appropriate handler
+// class for each DIE, instead of a single object with tables
+// tracking all the dies in the compilation unit.
+//
+// - It's not convenient to take some action after all a DIE's
+// attributes have been seen, but before visiting any of its
+// children. The only indication you have that a DIE's attribute
+// list is complete is that you get either a StartDIE or an EndDIE
+// call.
+//
+// - It's not convenient to make use of the tree structure of the
+// DIEs. Skipping all the children of a given die requires
+// maintaining state and returning false from StartDIE until we get
+// an EndDIE call with the appropriate offset.
+//
+// This interface tries to take care of all that. (You're shocked, I'm sure.)
+//
+// Using the classes here, you provide an initial handler for the root
+// DIE of the compilation unit. Each handler receives its DIE's
+// attributes, and provides fresh handler objects for children of
+// interest, if any. The three classes are:
+//
+// - DIEHandler: the base class for your DIE-type-specific handler
+// classes.
+//
+// - RootDIEHandler: derived from DIEHandler, the base class for your
+// root DIE handler class.
+//
+// - DIEDispatcher: derived from Dwarf2Handler, an instance of this
+// invokes your DIE-type-specific handler objects.
+//
+// In detail:
+//
+// - Define handler classes specialized for the DIE types you're
+// interested in. These handler classes must inherit from
+// DIEHandler. Thus:
+//
+// class My_DW_TAG_X_Handler: public DIEHandler { ... };
+// class My_DW_TAG_Y_Handler: public DIEHandler { ... };
+//
+// DIEHandler subclasses needn't correspond exactly to single DIE
+// types, as shown here; the point is that you can have several
+// different classes appropriate to different kinds of DIEs.
+//
+// - In particular, define a handler class for the compilation
+// unit's root DIE, that inherits from RootDIEHandler:
+//
+// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... };
+//
+// RootDIEHandler inherits from DIEHandler, adding a few additional
+// member functions for examining the compilation unit as a whole,
+// and other quirks of rootness.
+//
+// - Then, create a DIEDispatcher instance, passing it an instance of
+// your root DIE handler class, and use that DIEDispatcher as the
+// dwarf2reader::CompilationUnit's handler:
+//
+// My_DW_TAG_compile_unit_Handler root_die_handler(...);
+// DIEDispatcher die_dispatcher(&root_die_handler);
+// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher);
+//
+// Here, 'die_dispatcher' acts as a shim between 'reader' and the
+// various DIE-specific handlers you have defined.
+//
+// - When you call reader.Start(), die_dispatcher behaves as follows,
+// starting with your root die handler and the compilation unit's
+// root DIE:
+//
+// - It calls the handler's ProcessAttributeX member functions for
+// each of the DIE's attributes.
+//
+// - It calls the handler's EndAttributes member function. This
+// should return true if any of the DIE's children should be
+// visited, in which case:
+//
+// - For each of the DIE's children, die_dispatcher calls the
+// DIE's handler's FindChildHandler member function. If that
+// returns a pointer to a DIEHandler instance, then
+// die_dispatcher uses that handler to process the child, using
+// this procedure recursively. Alternatively, if
+// FindChildHandler returns NULL, die_dispatcher ignores that
+// child and its descendants.
+//
+// - When die_dispatcher has finished processing all the DIE's
+// children, it invokes the handler's Finish() member function,
+// and destroys the handler. (As a special case, it doesn't
+// destroy the root DIE handler.)
+//
+// This allows the code for handling a particular kind of DIE to be
+// gathered together in a single class, makes it easy to skip all the
+// children or individual children of a particular DIE, and provides
+// appropriate parental context for each die.
+
+#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__
+#define COMMON_DWARF_DWARF2DIEHANDLER_H__
+
+#include <stack>
+
+#include "common/dwarf/types.h"
+#include "common/dwarf/dwarf2enums.h"
+#include "common/dwarf/dwarf2reader.h"
+
+namespace dwarf2reader {
+
+// An base class for handlers for specific DIE types. The series of
+// calls made on a DIE handler is as follows:
+//
+// - for each attribute of the DIE:
+// - ProcessAttributeX()
+// - EndAttributes()
+// - if that returned true, then for each child:
+// - FindChildHandler()
+// - if that returns a non-NULL pointer to a new handler:
+// - recurse, with the new handler and the child die
+// - Finish()
+// - destruction
+class DIEHandler {
+ public:
+ DIEHandler() { }
+ virtual ~DIEHandler() { }
+
+ // When we visit a DIE, we first use these member functions to
+ // report the DIE's attributes and their values. These have the
+ // same restrictions as the corresponding member functions of
+ // dwarf2reader::Dwarf2Handler.
+ //
+ // The default definitions ignore the values they are passed.
+ virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) { }
+ virtual void ProcessAttributeSigned(enum DwarfAttribute attr,
+ enum DwarfForm form,
+ int64 data) { }
+ virtual void ProcessAttributeReference(enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) { }
+ virtual void ProcessAttributeBuffer(enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const char* data,
+ uint64 len) { }
+ virtual void ProcessAttributeString(enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string& data) { }
+
+ // Once we have reported all the DIE's attributes' values, we call
+ // this member function. If it returns false, we skip all the DIE's
+ // children. If it returns true, we call FindChildHandler on each
+ // child. If that returns a handler object, we use that to visit
+ // the child; otherwise, we skip the child.
+ //
+ // The default definition elects to ignore the DIE's children.
+ // You'll need to override this if you override FindChildHandler,
+ // but at least the default behavior isn't to pass the children to
+ // FindChildHandler, which then ignores them all.
+ virtual bool EndAttributes() { return false; }
+
+ // If EndAttributes returns true to indicate that some of the DIE's
+ // children might be of interest, then we apply this function to
+ // each of the DIE's children. If it returns a handler object, then
+ // we use that to visit the child DIE. If it returns NULL, we skip
+ // that child DIE (and all its descendants).
+ //
+ // OFFSET is the offset of the child; TAG indicates what kind of DIE
+ // it is; and ATTRS is the list of attributes the DIE will have, and
+ // their forms (their values are not provided).
+ //
+ // The default definition skips all children.
+ virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
+ const AttributeList &attrs) {
+ return NULL;
+ }
+
+ // When we are done processing a DIE, we call this member function.
+ // This happens after the EndAttributes call, all FindChildHandler
+ // calls (if any), and all operations on the children themselves (if
+ // any). We call Finish on every handler --- even if EndAttributes
+ // returns false.
+ virtual void Finish() { };
+};
+
+// A subclass of DIEHandler, with additional kludges for handling the
+// compilation unit's root die.
+class RootDIEHandler: public DIEHandler {
+ public:
+ RootDIEHandler() { }
+ virtual ~RootDIEHandler() { }
+
+ // We pass the values reported via Dwarf2Handler::StartCompilationUnit
+ // to this member function, and skip the entire compilation unit if it
+ // returns false. So the root DIE handler is actually also
+ // responsible for handling the compilation unit metadata.
+ // The default definition always visits the compilation unit.
+ virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
+ uint8 offset_size, uint64 cu_length,
+ uint8 dwarf_version) { return true; }
+
+ // For the root DIE handler only, we pass the offset, tag and
+ // attributes of the compilation unit's root DIE. This is the only
+ // way the root DIE handler can find the root DIE's tag. If this
+ // function returns true, we will visit the root DIE using the usual
+ // DIEHandler methods; otherwise, we skip the entire compilation
+ // unit.
+ //
+ // The default definition elects to visit the root DIE.
+ virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag,
+ const AttributeList& attrs) { return true; }
+};
+
+class DIEDispatcher: public Dwarf2Handler {
+ public:
+ // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for
+ // the compilation unit's root die, as described for the DIEHandler
+ // class.
+ DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { }
+ // Destroying a DIEDispatcher destroys all active handler objects
+ // except the root handler.
+ ~DIEDispatcher();
+ bool StartCompilationUnit(uint64 offset, uint8 address_size,
+ uint8 offset_size, uint64 cu_length,
+ uint8 dwarf_version);
+ bool StartDIE(uint64 offset, enum DwarfTag tag,
+ const AttributeList &attrs);
+ void ProcessAttributeUnsigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data);
+ void ProcessAttributeSigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ int64 data);
+ void ProcessAttributeReference(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data);
+ void ProcessAttributeBuffer(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const char* data,
+ uint64 len);
+ void ProcessAttributeString(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string &data);
+ void EndDIE(uint64 offset);
+
+ private:
+
+ // The type of a handler stack entry. This includes some fields
+ // which don't really need to be on the stack --- they could just be
+ // single data members of DIEDispatcher --- but putting them here
+ // makes it easier to see that the code is correct.
+ struct HandlerStack {
+ // The offset of the DIE for this handler stack entry.
+ uint64 offset_;
+
+ // The handler object interested in this DIE's attributes and
+ // children. If NULL, we're not interested in either.
+ DIEHandler *handler_;
+
+ // Have we reported the end of this DIE's attributes to the handler?
+ bool reported_attributes_end_;
+ };
+
+ // Stack of DIE attribute handlers. At StartDIE(D), the top of the
+ // stack is the handler of D's parent, whom we may ask for a handler
+ // for D itself. At EndDIE(D), the top of the stack is D's handler.
+ // Special cases:
+ //
+ // - Before we've seen the compilation unit's root DIE, the stack is
+ // empty; we'll call root_handler_'s special member functions, and
+ // perhaps push root_handler_ on the stack to look at the root's
+ // immediate children.
+ //
+ // - When we decide to ignore a subtree, we only push an entry on
+ // the stack for the root of the tree being ignored, rather than
+ // pushing lots of stack entries with handler_ set to NULL.
+ stack<HandlerStack> die_handlers_;
+
+ // The root handler. We don't push it on die_handlers_ until we
+ // actually get the StartDIE call for the root.
+ RootDIEHandler *root_handler_;
+};
+
+} // namespace dwarf2reader
+#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__
diff --git a/src/common/dwarf/dwarf2diehandler_unittest.cc b/src/common/dwarf/dwarf2diehandler_unittest.cc
new file mode 100644
index 00000000..a440bd98
--- /dev/null
+++ b/src/common/dwarf/dwarf2diehandler_unittest.cc
@@ -0,0 +1,556 @@
+// -*- mode: c++ -*-
+
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "breakpad_googletest_includes.h"
+
+#include "common/dwarf/dwarf2diehandler.h"
+
+using ::testing::_;
+using ::testing::ContainerEq;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::Sequence;
+using ::testing::StrEq;
+
+using dwarf2reader::AttributeList;
+using dwarf2reader::DIEDispatcher;
+using dwarf2reader::DIEHandler;
+using dwarf2reader::DwarfAttribute;
+using dwarf2reader::DwarfForm;
+using dwarf2reader::DwarfTag;
+using dwarf2reader::RootDIEHandler;
+
+class MockDIEHandler: public DIEHandler {
+ public:
+ MOCK_METHOD3(ProcessAttributeUnsigned,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD3(ProcessAttributeSigned,
+ void(DwarfAttribute, DwarfForm, int64));
+ MOCK_METHOD3(ProcessAttributeReference,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD4(ProcessAttributeBuffer,
+ void(DwarfAttribute, DwarfForm, const char *, uint64));
+ MOCK_METHOD3(ProcessAttributeString,
+ void(DwarfAttribute, DwarfForm, const string &));
+ MOCK_METHOD0(EndAttributes, bool());
+ MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
+ const AttributeList &));
+ MOCK_METHOD0(Finish, void());
+};
+
+class MockRootDIEHandler: public RootDIEHandler {
+ public:
+ MOCK_METHOD3(ProcessAttributeUnsigned,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD3(ProcessAttributeSigned,
+ void(DwarfAttribute, DwarfForm, int64));
+ MOCK_METHOD3(ProcessAttributeReference,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD4(ProcessAttributeBuffer,
+ void(DwarfAttribute, DwarfForm, const char *, uint64));
+ MOCK_METHOD3(ProcessAttributeString,
+ void(DwarfAttribute, DwarfForm, const string &));
+ MOCK_METHOD0(EndAttributes, bool());
+ MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
+ const AttributeList &));
+ MOCK_METHOD0(Finish, void());
+ MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
+ MOCK_METHOD3(StartRootDIE, bool(uint64, DwarfTag, const AttributeList &));
+};
+
+// If the handler elects to skip the compilation unit, the dispatcher
+// should tell the reader so.
+TEST(Dwarf2DIEHandler, SkipCompilationUnit) {
+ Sequence s;
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x8d42aed77cfccf3eLL,
+ 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL,
+ 0x66))
+ .InSequence(s)
+ .WillOnce(Return(false));
+
+ EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
+ 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL,
+ 0x66));
+}
+
+// If the handler elects to skip the root DIE, the dispatcher should
+// tell the reader so.
+TEST(Dwarf2DIEHandler, SkipRootDIE) {
+ Sequence s;
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ AttributeList mock_attribute_list;
+ mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_string));
+
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
+ 0xb00febffa76e2b2bLL, 0x5c))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6,
+ ContainerEq(mock_attribute_list)))
+ .InSequence(s)
+ .WillOnce(Return(false));
+
+ EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,
+ 0xf4, 0x02,
+ 0xb00febffa76e2b2bLL, 0x5c));
+ EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
+ (DwarfTag) 0xb4f98da6,
+ mock_attribute_list));
+ die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
+}
+
+// If the handler elects to skip the root DIE's children, the
+// dispatcher should tell the reader so --- and avoid deleting the
+// root handler.
+TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ AttributeList mock_attribute_list;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0,
+ 0x09f8bf0767f91675LL, 0xdb))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6,
+ ContainerEq(mock_attribute_list)))
+ .WillOnce(Return(true));
+ // Please don't tell me about my children.
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_root_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL,
+ 0x26, 0xa0,
+ 0x09f8bf0767f91675LL, 0xdb));
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
+ (DwarfTag) 0xb4f98da6,
+ mock_attribute_list));
+ EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
+ (DwarfTag) 0xc3a17bba,
+ mock_attribute_list));
+ die_dispatcher.EndDIE(0x435150ceedccda18LL);
+ die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
+}
+
+// The dispatcher should pass attribute values through to the die
+// handler accurately.
+TEST(Dwarf2DIEHandler, PassAttributeValues) {
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ AttributeList mock_attribute_list;
+ mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_string));
+ const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca,
+ 0xcf, 0xa8, 0x84, 0xa7, 0x18 };
+ string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";
+
+ // Set expectations.
+ {
+ InSequence s;
+
+ // We'll like the compilation unit header.
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL, 0x66))
+ .WillOnce(Return(true));
+
+ // We'll like the root DIE.
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c,
+ ContainerEq(mock_attribute_list)))
+ .WillOnce(Return(true));
+
+ // Expect some attribute values.
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed,
+ (DwarfForm) 0x424f1468,
+ 0xa592571997facda1ULL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0x43694dc9,
+ (DwarfForm) 0xf6f78901L,
+ 0x92602a4e3bf1f446LL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeReference((DwarfAttribute) 0x4033e8cL,
+ (DwarfForm) 0xf66fbe0bL,
+ 0x50fddef44734fdecULL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af,
+ (DwarfForm) 0xe99a539a,
+ buffer, sizeof(buffer)))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeString((DwarfAttribute) 0x310ed065,
+ (DwarfForm) 0x15762fec,
+ StrEq(str)))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
+ .Times(0);
+ EXPECT_CALL(mock_root_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ // Drive the dispatcher.
+
+ // Report the CU header.
+ EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
+ 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL,
+ 0x66));
+ // Report the root DIE.
+ EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
+ (DwarfTag) 0x9829445c,
+ mock_attribute_list));
+
+ // Report some attribute values.
+ die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x1cc0bfed,
+ (DwarfForm) 0x424f1468,
+ 0xa592571997facda1ULL);
+ die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x43694dc9,
+ (DwarfForm) 0xf6f78901,
+ 0x92602a4e3bf1f446LL);
+ die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x4033e8c,
+ (DwarfForm) 0xf66fbe0b,
+ 0x50fddef44734fdecULL);
+ die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x25d7e0af,
+ (DwarfForm) 0xe99a539a,
+ buffer, sizeof(buffer));
+ die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x310ed065,
+ (DwarfForm) 0x15762fec,
+ str);
+
+ // Finish the root DIE (and thus the CU).
+ die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
+}
+
+TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
+ MockRootDIEHandler mock_root_handler;
+ MockDIEHandler *mock_child1_handler = new(MockDIEHandler);
+ MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ AttributeList root_attribute_list;
+ root_attribute_list.push_back(make_pair((DwarfAttribute) 0xb01185df,
+ (DwarfForm) 0xbc97cee8));
+ AttributeList child1_attribute_list;
+ child1_attribute_list.push_back(make_pair((DwarfAttribute) 0x41014e43,
+ (DwarfForm) 0x63155f4c));
+ AttributeList grandchild1_attribute_list;
+ grandchild1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf72f823c,
+ (DwarfForm) 0x0ff6a201));
+ AttributeList greatgrandchild1_attribute_list;
+ greatgrandchild1_attribute_list
+ .push_back(make_pair((DwarfAttribute) 0xbe66e5f0, (DwarfForm) 0xb4b24ff7));
+ AttributeList child2_attribute_list;
+ child1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf22df14c,
+ (DwarfForm) 0x20676e7d));
+ AttributeList child3_attribute_list;
+ child3_attribute_list.push_back(make_pair((DwarfAttribute) 0xe8bf1201,
+ (DwarfForm) 0x53a5b7a8));
+
+ {
+ InSequence s;
+
+ // We'll like the compilation unit header.
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
+ 0x47dd3c764275a216LL, 0xa5))
+ .WillOnce(Return(true));
+
+ // Root DIE.
+ {
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59,
+ ContainerEq(root_attribute_list)))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
+ (DwarfForm) 0x2cb63027,
+ 0x18e744661769d08fLL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(true));
+
+ // First child DIE.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x149f644f8116fe8cLL,
+ (DwarfTag) 0xac2cbd8c,
+ ContainerEq(child1_attribute_list)))
+ .WillOnce(Return(mock_child1_handler));
+ {
+ EXPECT_CALL(*mock_child1_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65,
+ (DwarfForm) 0xe4f64c41,
+ 0x1b04e5444a55fe67LL))
+ .WillOnce(Return());
+ EXPECT_CALL(*mock_child1_handler, EndAttributes())
+ .WillOnce(Return(false));
+ // Skip first grandchild DIE and first great-grandchild DIE.
+ EXPECT_CALL(*mock_child1_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ // Second child DIE. Root handler will decline to return a handler
+ // for this child.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x97412be24875de9dLL,
+ (DwarfTag) 0x505a068b,
+ ContainerEq(child2_attribute_list)))
+ .WillOnce(Return((DIEHandler *) NULL));
+
+ // Third child DIE.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x753c964c8ab538aeLL,
+ (DwarfTag) 0x8c22970e,
+ ContainerEq(child3_attribute_list)))
+ .WillOnce(Return(mock_child3_handler));
+ {
+ EXPECT_CALL(*mock_child3_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL))
+ .WillOnce(Return());
+ EXPECT_CALL(*mock_child3_handler, EndAttributes())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_child3_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ EXPECT_CALL(mock_root_handler, Finish())
+ .WillOnce(Return());
+ }
+ }
+
+
+ // Drive the dispatcher.
+
+ // Report the CU header.
+ EXPECT_TRUE(die_dispatcher
+ .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
+ 0x47dd3c764275a216LL, 0xa5));
+ // Report the root DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
+ (DwarfTag) 0xf5d60c59,
+ root_attribute_list));
+ die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
+ (DwarfAttribute) 0xf779a642,
+ (DwarfForm) 0x2cb63027,
+ 0x18e744661769d08fLL);
+
+ // First child DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
+ (DwarfTag) 0xac2cbd8c,
+ child1_attribute_list));
+ die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
+ (DwarfAttribute) 0xa6fd6f65,
+ (DwarfForm) 0xe4f64c41,
+ 0x1b04e5444a55fe67LL);
+
+ // First grandchild DIE. Will be skipped.
+ {
+ EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
+ (DwarfTag) 0x22f05a15,
+ grandchild1_attribute_list));
+ // First great-grandchild DIE. Will be skipped without being
+ // mentioned to any handler.
+ {
+ EXPECT_FALSE(die_dispatcher
+ .StartDIE(0xb3076285d25cac25LL,
+ (DwarfTag) 0xcff4061b,
+ greatgrandchild1_attribute_list));
+ die_dispatcher.EndDIE(0xb3076285d25cac25LL);
+ }
+ die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
+ }
+ die_dispatcher.EndDIE(0x149f644f8116fe8cLL);
+ }
+
+ // Second child DIE. Root handler will decline to find a handler for it.
+ {
+ EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
+ (DwarfTag) 0x505a068b,
+ child2_attribute_list));
+ die_dispatcher.EndDIE(0x97412be24875de9dLL);
+ }
+
+ // Third child DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
+ (DwarfTag) 0x8c22970e,
+ child3_attribute_list));
+ die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
+ (DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL);
+ die_dispatcher.EndDIE(0x753c964c8ab538aeLL);
+ }
+
+ // Finish the root DIE (and thus the CU).
+ die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL);
+ }
+}
+
+// The DIEDispatcher destructor is supposed to delete all handlers on
+// the stack, except for the root.
+TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
+ MockRootDIEHandler mock_root_handler;
+ MockDIEHandler *mock_child_handler = new(MockDIEHandler);
+ MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);
+ AttributeList empty_attribute_list;
+
+ {
+ InSequence s;
+
+ // We'll like the compilation unit header.
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
+ 0x76d392ff393ddda2LL, 0xbf))
+ .WillOnce(Return(true));
+
+ // Root DIE.
+ {
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361,
+ ContainerEq(empty_attribute_list)))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(true));
+
+ // Child DIE.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x058f09240c5fc8c9LL,
+ (DwarfTag) 0x898bf0d0,
+ ContainerEq(empty_attribute_list)))
+ .WillOnce(Return(mock_child_handler));
+ {
+ EXPECT_CALL(*mock_child_handler, EndAttributes())
+ .WillOnce(Return(true));
+
+ // Grandchild DIE.
+ EXPECT_CALL(*mock_child_handler,
+ FindChildHandler(0x32dc00c9945dc0c8LL,
+ (DwarfTag) 0x2802d007,
+ ContainerEq(empty_attribute_list)))
+ .WillOnce(Return(mock_grandchild_handler));
+ {
+ EXPECT_CALL(*mock_grandchild_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL))
+ .WillOnce(Return());
+
+ // At this point, we abandon the traversal, so none of the
+ // usual stuff should get called.
+ EXPECT_CALL(*mock_grandchild_handler, EndAttributes())
+ .Times(0);
+ EXPECT_CALL(*mock_grandchild_handler, Finish())
+ .Times(0);
+ }
+
+ EXPECT_CALL(*mock_child_handler, Finish())
+ .Times(0);
+ }
+
+ EXPECT_CALL(mock_root_handler, Finish())
+ .Times(0);
+ }
+ }
+
+ // The dispatcher.
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ // Report the CU header.
+ EXPECT_TRUE(die_dispatcher
+ .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
+ 0x76d392ff393ddda2LL, 0xbf));
+ // Report the root DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
+ (DwarfTag) 0x98980361,
+ empty_attribute_list));
+
+ // Child DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
+ (DwarfTag) 0x898bf0d0,
+ empty_attribute_list));
+
+ // Grandchild DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
+ (DwarfTag) 0x2802d007,
+ empty_attribute_list));
+ die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
+ (DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL);
+
+ // Stop the traversal abruptly, so that there will still be
+ // handlers on the stack when the dispatcher is destructed.
+
+ // No EndDIE call...
+ }
+ // No EndDIE call...
+ }
+ // No EndDIE call...
+ }
+}
diff --git a/src/tools/linux/dump_syms/Makefile b/src/tools/linux/dump_syms/Makefile
index 78a1a1ab..b944f3cf 100644
--- a/src/tools/linux/dump_syms/Makefile
+++ b/src/tools/linux/dump_syms/Makefile
@@ -77,6 +77,7 @@ dump_syms: \
dump_stabs.o \
dump_syms.o \
dump_symbols.o \
+ dwarf2diehandler.o \
file_id.o \
module.o \
stabs_reader.o \
@@ -97,6 +98,10 @@ COVERAGE_SOURCES += module.cc
stabs_reader.o: stabs_reader.cc
COVERAGE_SOURCES += stabs_reader.cc
+VPATH += $(SRC)/common/dwarf
+dwarf2diehandler.o: dwarf2diehandler.cc
+COVERAGE_SOURCES += dwarf2diehandler.cc
+
### Google C++ Testing Framework.
@@ -180,6 +185,23 @@ clean::
rm -f dump_stabs_unittest
+### Unit tests for dwarf2reader::DwarfDIEDispatcher.
+check: check-dwarf2diehandler_unittest
+check-dwarf2diehandler_unittest: dwarf2diehandler_unittest
+dwarf2diehandler_unittest: \
+ gmock-all.o \
+ gtest-all.o \
+ gtest_main.o \
+ dwarf2diehandler.o \
+ $(empty)
+CPP_EXECUTABLES += dwarf2diehandler_unittest
+dwarf2diehandler_unittest.o: dwarf2diehandler_unittest.cc
+dwarf2diehandler_unittest.o: override CPPFLAGS += $(GTEST_CPPFLAGS) \
+ $(GMOCK_CPPFLAGS)
+clean::
+ rm -f dwarf2diehandler_unittest
+
+
### Generic compilation rules.
# Link C++ executables using the C++ compiler; see CPP_EXECUTABLES above.