From 5e4f6feaf64a5a85690916e5f34a889f9d2a0c45 Mon Sep 17 00:00:00 2001
From: jimblandy <jimblandy@4c0a9323-5329-0410-9bdc-e9ce6186880e>
Date: Wed, 1 Feb 2012 15:01:54 +0000
Subject: [PATCH] Breakpad DWARF: Add support for DWARF 4 attribute forms.

This patch allows Breakpad's DWARF reader to at least read or skip
attributes using the new forms defined in version 4 of the DWARF
specification, instead of crashing.

Attributes encoded using DW_FORM_flag_present, DW_FORM_sec_offset, and
DW_FORM_exprloc should work fine now. However, compilation units using
DW_FORM_ref_sig8 to refer to types in .debug_types will need further work
to support. (GCC 4.6.2 does not emit .debug_types sections.)

Specifically:

- dwarf2reader::DwarfForm gets new values.
- dwarf2reader::Dwarf2Handler and dwarf2reader::DIEHandler get new handler
  methods, named ProcessAttributeSignature, for DW_FORM_ref_sig8 attributes.
- dwarf2reader::CompilationUnit reads DW_FORM_ref_sig8 attributes, and
  passes them to ProcessAttributeSignature. It also gets support for
  DW_FORM_sec_offset, DW_FORM_exprloc, and DW_FORM_flag_present, using the
  existing appropriate ProcessAttribute* methods.
- dwarf2reader::DIEDispatcher passes through ProcessAttributeSignature
  attributes to its DIEHandler.
- Unit tests are updated.

a=jimb, r=ted.mielczarek
Review URL: http://breakpad.appspot.com/343003/


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@912 4c0a9323-5329-0410-9bdc-e9ce6186880e
---
 src/common/dwarf/dwarf2diehandler.cc          |  10 ++
 src/common/dwarf/dwarf2diehandler.h           |   7 ++
 src/common/dwarf/dwarf2diehandler_unittest.cc |  13 ++
 src/common/dwarf/dwarf2enums.h                |   8 +-
 src/common/dwarf/dwarf2reader.cc              |  21 +++-
 src/common/dwarf/dwarf2reader.h               |   9 ++
 src/common/dwarf/dwarf2reader_die_unittest.cc | 119 +++++++++++++++++-
 7 files changed, 179 insertions(+), 8 deletions(-)

diff --git a/src/common/dwarf/dwarf2diehandler.cc b/src/common/dwarf/dwarf2diehandler.cc
index d9abe958..16399547 100644
--- a/src/common/dwarf/dwarf2diehandler.cc
+++ b/src/common/dwarf/dwarf2diehandler.cc
@@ -183,4 +183,14 @@ void DIEDispatcher::ProcessAttributeString(uint64 offset,
   current.handler_->ProcessAttributeString(attr, form, data);
 }
 
+void DIEDispatcher::ProcessAttributeSignature(uint64 offset,
+                                              enum DwarfAttribute attr,
+                                              enum DwarfForm form,
+                                              uint64 signature) {
+  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_->ProcessAttributeSignature(attr, form, signature);
+}
+
 } // namespace dwarf2reader
diff --git a/src/common/dwarf/dwarf2diehandler.h b/src/common/dwarf/dwarf2diehandler.h
index 401dd2b8..5d899bf8 100644
--- a/src/common/dwarf/dwarf2diehandler.h
+++ b/src/common/dwarf/dwarf2diehandler.h
@@ -209,6 +209,9 @@ class DIEHandler {
   virtual void ProcessAttributeString(enum DwarfAttribute attr,
                                       enum DwarfForm form,
                                       const std::string& data) { }
+  virtual void ProcessAttributeSignature(enum DwarfAttribute attr,
+                                         enum DwarfForm form,
+                                         uint64 signture) { }
 
   // 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
@@ -314,6 +317,10 @@ class DIEDispatcher: public Dwarf2Handler {
                               enum DwarfAttribute attr,
                               enum DwarfForm form,
                               const std::string &data);
+  void ProcessAttributeSignature(uint64 offset,
+                                 enum DwarfAttribute attr,
+                                 enum DwarfForm form,
+                                 uint64 signature);
   void EndDIE(uint64 offset);
 
  private:
diff --git a/src/common/dwarf/dwarf2diehandler_unittest.cc b/src/common/dwarf/dwarf2diehandler_unittest.cc
index 1e05fd5c..186b951c 100644
--- a/src/common/dwarf/dwarf2diehandler_unittest.cc
+++ b/src/common/dwarf/dwarf2diehandler_unittest.cc
@@ -71,6 +71,8 @@ class MockDIEHandler: public DIEHandler {
                void(DwarfAttribute, DwarfForm, const char *, uint64));
   MOCK_METHOD3(ProcessAttributeString,
                void(DwarfAttribute, DwarfForm, const string &));
+  MOCK_METHOD3(ProcessAttributeSignature,
+               void(DwarfAttribute, DwarfForm, uint64));
   MOCK_METHOD0(EndAttributes, bool());
   MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
                                               const AttributeList &));
@@ -89,6 +91,8 @@ class MockRootDIEHandler: public RootDIEHandler {
                void(DwarfAttribute, DwarfForm, const char *, uint64));
   MOCK_METHOD3(ProcessAttributeString,
                void(DwarfAttribute, DwarfForm, const string &));
+  MOCK_METHOD3(ProcessAttributeSignature,
+               void(DwarfAttribute, DwarfForm, uint64));
   MOCK_METHOD0(EndAttributes, bool());
   MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
                                               const AttributeList &));
@@ -244,6 +248,11 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
                                        (DwarfForm) 0x15762fec,
                                        StrEq(str)))
       .WillOnce(Return());
+    EXPECT_CALL(mock_root_handler,
+                ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
+                                          (DwarfForm) 0x4159f138,
+                                          0x94682463613e6a5fULL))
+      .WillOnce(Return());
     EXPECT_CALL(mock_root_handler, EndAttributes())
       .WillOnce(Return(true));
     EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
@@ -285,6 +294,10 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
                                         (DwarfAttribute) 0x310ed065,
                                         (DwarfForm) 0x15762fec,
                                         str);
+  die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
+                                           (DwarfAttribute) 0x58790d72,
+                                           (DwarfForm) 0x4159f138,
+                                           0x94682463613e6a5fULL);
 
   // Finish the root DIE (and thus the CU).
   die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h
index 832a17ca..5565d66e 100644
--- a/src/common/dwarf/dwarf2enums.h
+++ b/src/common/dwarf/dwarf2enums.h
@@ -143,7 +143,13 @@ enum DwarfForm {
   DW_FORM_ref4 = 0x13,
   DW_FORM_ref8 = 0x14,
   DW_FORM_ref_udata = 0x15,
-  DW_FORM_indirect = 0x16
+  DW_FORM_indirect = 0x16,
+
+  // Added in DWARF 4:
+  DW_FORM_sec_offset = 0x17,
+  DW_FORM_exprloc = 0x18,
+  DW_FORM_flag_present = 0x19,
+  DW_FORM_ref_sig8 = 0x20
 };
 
 // Attribute names and codes
diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc
index 2d2edf45..fadce485 100644
--- a/src/common/dwarf/dwarf2reader.cc
+++ b/src/common/dwarf/dwarf2reader.cc
@@ -151,6 +151,8 @@ const char* CompilationUnit::SkipAttribute(const char* start,
       start += len;
       return SkipAttribute(start, form);
 
+    case DW_FORM_flag_present:
+      return start;
     case DW_FORM_data1:
     case DW_FORM_flag:
     case DW_FORM_ref1:
@@ -163,6 +165,7 @@ const char* CompilationUnit::SkipAttribute(const char* start,
       return start + 4;
     case DW_FORM_ref8:
     case DW_FORM_data8:
+    case DW_FORM_ref_sig8:
       return start + 8;
     case DW_FORM_string:
       return start + strlen(start) + 1;
@@ -192,11 +195,13 @@ const char* CompilationUnit::SkipAttribute(const char* start,
       return start + 2 + reader_->ReadTwoBytes(start);
     case DW_FORM_block4:
       return start + 4 + reader_->ReadFourBytes(start);
-    case DW_FORM_block: {
+    case DW_FORM_block:
+    case DW_FORM_exprloc: {
       uint64 size = reader_->ReadUnsignedLEB128(start, &len);
       return start + size + len;
     }
     case DW_FORM_strp:
+    case DW_FORM_sec_offset:
       return start + reader_->OffsetSize();
   }
   fprintf(stderr,"Unhandled form type");
@@ -310,6 +315,9 @@ const char* CompilationUnit::ProcessAttribute(
       start += len;
       return ProcessAttribute(dieoffset, start, attr, form);
 
+    case DW_FORM_flag_present:
+      handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1);
+      return start;
     case DW_FORM_data1:
     case DW_FORM_flag:
       handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
@@ -347,6 +355,10 @@ const char* CompilationUnit::ProcessAttribute(
       handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
                                          reader_->ReadAddress(start));
       return start + reader_->AddressSize();
+    case DW_FORM_sec_offset:
+      handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
+                                         reader_->ReadOffset(start));
+      return start + reader_->OffsetSize();
 
     case DW_FORM_ref1:
       handler_->ProcessAttributeReference(dieoffset, attr, form,
@@ -388,6 +400,10 @@ const char* CompilationUnit::ProcessAttribute(
         return start + reader_->OffsetSize();
       }
       break;
+    case DW_FORM_ref_sig8:
+      handler_->ProcessAttributeSignature(dieoffset, attr, form,
+                                          reader_->ReadEightBytes(start));
+      return start + 8;
 
     case DW_FORM_block1: {
       uint64 datalen = reader_->ReadOneByte(start);
@@ -407,7 +423,8 @@ const char* CompilationUnit::ProcessAttribute(
                                        datalen);
       return start + 4 + datalen;
     }
-    case DW_FORM_block: {
+    case DW_FORM_block:
+    case DW_FORM_exprloc: {
       uint64 datalen = reader_->ReadUnsignedLEB128(start, &len);
       handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len,
                                        datalen);
diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h
index c43bb93b..cd61fb55 100644
--- a/src/common/dwarf/dwarf2reader.h
+++ b/src/common/dwarf/dwarf2reader.h
@@ -393,6 +393,15 @@ class Dwarf2Handler {
                                       enum DwarfForm form,
                                       const std::string& data) { }
 
+  // Called when we have an attribute whose value is the 64-bit signature
+  // of a type unit in the .debug_types section. OFFSET is the offset of
+  // the DIE whose attribute we're reporting. ATTR and FORM are the
+  // attribute's name and form. SIGNATURE is the type unit's signature.
+  virtual void ProcessAttributeSignature(uint64 offset,
+                                         enum DwarfAttribute attr,
+                                         enum DwarfForm form,
+                                         uint64 signature) { }
+
   // Called when finished processing the DIE at OFFSET.
   // Because DWARF2/3 specifies a tree of DIEs, you may get starts
   // before ends of the previous DIE, as we process children before
diff --git a/src/common/dwarf/dwarf2reader_die_unittest.cc b/src/common/dwarf/dwarf2reader_die_unittest.cc
index 50c01dd6..e76fcae7 100644
--- a/src/common/dwarf/dwarf2reader_die_unittest.cc
+++ b/src/common/dwarf/dwarf2reader_die_unittest.cc
@@ -99,6 +99,10 @@ class MockDwarf2Handler: public Dwarf2Handler {
                                             enum DwarfAttribute attr,
                                             enum DwarfForm form,
                                             const std::string& data));
+  MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
+                                               DwarfAttribute attr,
+                                               enum DwarfForm form,
+                                               uint64 signature));
   MOCK_METHOD1(EndDIE, void(uint64 offset));
 };
 
@@ -251,9 +255,9 @@ struct DwarfFormsFixture: public DIEFixture {
   // containing one childless DIE of the given tag, in the sequence s. Stop
   // just before the expectations.
   void ExpectBeginCompilationUnit(const DwarfHeaderParams &params,
-                                  DwarfTag tag) {
+                                  DwarfTag tag, uint64 offset=0) {
     EXPECT_CALL(handler,
-                StartCompilationUnit(0, params.address_size,
+                StartCompilationUnit(offset, params.address_size,
                                      params.format_size, _,
                                      params.version))
         .InSequence(s)
@@ -269,11 +273,11 @@ struct DwarfFormsFixture: public DIEFixture {
         .WillOnce(Return());
   }
 
-  void ParseCompilationUnit(const DwarfHeaderParams &params) {
+  void ParseCompilationUnit(const DwarfHeaderParams &params, uint64 offset=0) {
     ByteReader byte_reader(params.endianness == kLittleEndian ?
                            ENDIANNESS_LITTLE : ENDIANNESS_BIG);
-    CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler);
-    EXPECT_EQ(parser.Start(), info_contents.size());
+    CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler);
+    EXPECT_EQ(offset + parser.Start(), info_contents.size());
   }
 
   // The sequence to which the fixture's methods append expectations.
@@ -347,6 +351,111 @@ TEST_P(DwarfForms, block2) {
   ParseCompilationUnit(GetParam());
 }
 
+TEST_P(DwarfForms, flag_present) {
+  StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2,
+                          (DwarfAttribute) 0x359d1972,
+                          dwarf2reader::DW_FORM_flag_present);
+  // DW_FORM_flag_present occupies no space in the DIE.
+  info.Finish();
+
+  ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2);
+  EXPECT_CALL(handler,
+              ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972,
+                                       dwarf2reader::DW_FORM_flag_present,
+                                       1))
+      .InSequence(s)
+      .WillOnce(Return());
+  ExpectEndCompilationUnit();
+
+  ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, sec_offset) {
+  StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
+                          (DwarfAttribute) 0xa060bfd1,
+                          dwarf2reader::DW_FORM_sec_offset);
+  u_int64_t value;
+  if (GetParam().format_size == 4) {
+    value = 0xacc9c388;
+    info.D32(value);
+  } else {
+    value = 0xcffe5696ffe3ed0aULL;
+    info.D64(value);
+  }
+  info.Finish();
+
+  ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689);
+  EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1,
+                                                dwarf2reader::DW_FORM_sec_offset,
+                                                value))
+      .InSequence(s)
+      .WillOnce(Return());
+  ExpectEndCompilationUnit();
+
+  ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, exprloc) {
+  StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb,
+                          (DwarfAttribute) 0xba3ae5cb,
+                          dwarf2reader::DW_FORM_exprloc);
+  info.ULEB128(29)
+      .Append(29, 173);
+  info.Finish();
+
+  ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb);
+  EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb,
+                                              dwarf2reader::DW_FORM_exprloc,
+                                              Pointee(173), 29))
+      .InSequence(s)
+      .WillOnce(Return());
+  ExpectEndCompilationUnit();
+
+  ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, ref_sig8) {
+  StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
+                          (DwarfAttribute) 0xd708d908,
+                          dwarf2reader::DW_FORM_ref_sig8);
+  info.D64(0xf72fa0cb6ddcf9d6ULL);
+  info.Finish();
+
+  ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b);
+  EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
+                                                 dwarf2reader::DW_FORM_ref_sig8,
+                                                 0xf72fa0cb6ddcf9d6ULL))
+      .InSequence(s)
+      .WillOnce(Return());
+  ExpectEndCompilationUnit();
+
+  ParseCompilationUnit(GetParam());
+}
+
+// A value passed to ProcessAttributeSignature is just an absolute number,
+// not an offset within the compilation unit as most of the other
+// DW_FORM_ref forms are. Check that the reader doesn't try to apply any
+// offset to the signature, by reading it from a compilation unit that does
+// not start at the beginning of the section.
+TEST_P(DwarfForms, ref_sig8_not_first) {
+  info.Append(98, '*');
+  StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
+                          (DwarfAttribute) 0xd708d908,
+                          dwarf2reader::DW_FORM_ref_sig8);
+  info.D64(0xf72fa0cb6ddcf9d6ULL);
+  info.Finish();
+
+  ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98);
+  EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
+                                                 dwarf2reader::DW_FORM_ref_sig8,
+                                                 0xf72fa0cb6ddcf9d6ULL))
+      .InSequence(s)
+      .WillOnce(Return());
+  ExpectEndCompilationUnit();
+
+  ParseCompilationUnit(GetParam(), 98);
+}
+
 // Tests for the other attribute forms could go here.
 
 INSTANTIATE_TEST_CASE_P(