// Copyright (c) 2012, 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. // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> // dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit #include <stdlib.h> #include <iostream> #include <string> #include <vector> #include "breakpad_googletest_includes.h" #include "common/dwarf/bytereader-inl.h" #include "common/dwarf/dwarf2reader_test_common.h" #include "common/dwarf/dwarf2reader.h" #include "common/using_std_string.h" #include "google_breakpad/common/breakpad_types.h" using google_breakpad::test_assembler::Endianness; using google_breakpad::test_assembler::Label; using google_breakpad::test_assembler::Section; using google_breakpad::test_assembler::kBigEndian; using google_breakpad::test_assembler::kLittleEndian; using dwarf2reader::ByteReader; using dwarf2reader::CompilationUnit; using dwarf2reader::Dwarf2Handler; using dwarf2reader::DwarfAttribute; using dwarf2reader::DwarfForm; using dwarf2reader::DwarfHasChild; using dwarf2reader::DwarfTag; using dwarf2reader::ENDIANNESS_BIG; using dwarf2reader::ENDIANNESS_LITTLE; using dwarf2reader::SectionMap; using std::vector; using testing::InSequence; using testing::Pointee; using testing::Return; using testing::Sequence; using testing::Test; using testing::TestWithParam; using testing::_; class MockDwarf2Handler: public Dwarf2Handler { public: MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, uint8 offset_size, uint64 cu_length, uint8 dwarf_version)); MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag)); MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, DwarfAttribute attr, enum DwarfForm form, uint64 data)); MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, int64 data)); MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, uint64 data)); MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, const char* data, uint64 len)); MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, const string& data)); MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, DwarfAttribute attr, enum DwarfForm form, uint64 signature)); MOCK_METHOD1(EndDIE, void(uint64 offset)); }; struct DIEFixture { DIEFixture() { // Fix the initial offset of the .debug_info and .debug_abbrev sections. info.start() = 0; abbrevs.start() = 0; // Default expectations for the data handler. EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); EXPECT_CALL(handler, StartDIE(_, _)).Times(0); EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); EXPECT_CALL(handler, EndDIE(_)).Times(0); } // Return a reference to a section map whose .debug_info section refers // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This // function returns a reference to the same SectionMap each time; new // calls wipe out maps established by earlier calls. const SectionMap &MakeSectionMap() { // Copy the sections' contents into strings that will live as long as // the map itself. assert(info.GetContents(&info_contents)); assert(abbrevs.GetContents(&abbrevs_contents)); section_map.clear(); section_map[".debug_info"].first = info_contents.data(); section_map[".debug_info"].second = info_contents.size(); section_map[".debug_abbrev"].first = abbrevs_contents.data(); section_map[".debug_abbrev"].second = abbrevs_contents.size(); return section_map; } TestCompilationUnit info; TestAbbrevTable abbrevs; MockDwarf2Handler handler; string abbrevs_contents, info_contents; SectionMap section_map; }; struct DwarfHeaderParams { DwarfHeaderParams(Endianness endianness, size_t format_size, int version, size_t address_size) : endianness(endianness), format_size(format_size), version(version), address_size(address_size) { } Endianness endianness; size_t format_size; // 4-byte or 8-byte DWARF offsets int version; size_t address_size; }; class DwarfHeader: public DIEFixture, public TestWithParam<DwarfHeaderParams> { }; TEST_P(DwarfHeader, Header) { Label abbrev_table = abbrevs.Here(); abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, dwarf2reader::DW_children_yes) .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) .EndAbbrev() .EndTable(); info.set_format_size(GetParam().format_size); info.set_endianness(GetParam().endianness); info.Header(GetParam().version, abbrev_table, GetParam().address_size) .ULEB128(1) // DW_TAG_compile_unit, with children .AppendCString("sam") // DW_AT_name, DW_FORM_string .D8(0); // end of children info.Finish(); { InSequence s; EXPECT_CALL(handler, StartCompilationUnit(0, GetParam().address_size, GetParam().format_size, _, GetParam().version)) .WillOnce(Return(true)); EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit)) .WillOnce(Return(true)); EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string, "sam")) .WillOnce(Return()); EXPECT_CALL(handler, EndDIE(_)) .WillOnce(Return()); } ByteReader byte_reader(GetParam().endianness == kLittleEndian ? ENDIANNESS_LITTLE : ENDIANNESS_BIG); CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); EXPECT_EQ(parser.Start(), info_contents.size()); } INSTANTIATE_TEST_CASE_P( HeaderVariants, DwarfHeader, ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), DwarfHeaderParams(kLittleEndian, 4, 2, 8), DwarfHeaderParams(kLittleEndian, 4, 3, 4), DwarfHeaderParams(kLittleEndian, 4, 3, 8), DwarfHeaderParams(kLittleEndian, 4, 4, 4), DwarfHeaderParams(kLittleEndian, 4, 4, 8), DwarfHeaderParams(kLittleEndian, 8, 2, 4), DwarfHeaderParams(kLittleEndian, 8, 2, 8), DwarfHeaderParams(kLittleEndian, 8, 3, 4), DwarfHeaderParams(kLittleEndian, 8, 3, 8), DwarfHeaderParams(kLittleEndian, 8, 4, 4), DwarfHeaderParams(kLittleEndian, 8, 4, 8), DwarfHeaderParams(kBigEndian, 4, 2, 4), DwarfHeaderParams(kBigEndian, 4, 2, 8), DwarfHeaderParams(kBigEndian, 4, 3, 4), DwarfHeaderParams(kBigEndian, 4, 3, 8), DwarfHeaderParams(kBigEndian, 4, 4, 4), DwarfHeaderParams(kBigEndian, 4, 4, 8), DwarfHeaderParams(kBigEndian, 8, 2, 4), DwarfHeaderParams(kBigEndian, 8, 2, 8), DwarfHeaderParams(kBigEndian, 8, 3, 4), DwarfHeaderParams(kBigEndian, 8, 3, 8), DwarfHeaderParams(kBigEndian, 8, 4, 4), DwarfHeaderParams(kBigEndian, 8, 4, 8))); struct DwarfFormsFixture: public DIEFixture { // Start a compilation unit, as directed by |params|, containing one // childless DIE of the given tag, with one attribute of the given name // and form. The 'info' fixture member is left just after the abbrev // code, waiting for the attribute value to be appended. void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, DwarfTag tag, DwarfAttribute name, DwarfForm form) { // Create the abbreviation table. Label abbrev_table = abbrevs.Here(); abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) .Attribute(name, form) .EndAbbrev() .EndTable(); // Create the compilation unit, up to the attribute value. info.set_format_size(params.format_size); info.set_endianness(params.endianness); info.Header(params.version, abbrev_table, params.address_size) .ULEB128(1); // abbrev code } // Set up handler to expect a compilation unit matching |params|, // containing one childless DIE of the given tag, in the sequence s. Stop // just before the expectations. void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, DwarfTag tag, uint64 offset=0) { EXPECT_CALL(handler, StartCompilationUnit(offset, params.address_size, params.format_size, _, params.version)) .InSequence(s) .WillOnce(Return(true)); EXPECT_CALL(handler, StartDIE(_, tag)) .InSequence(s) .WillOnce(Return(true)); } void ExpectEndCompilationUnit() { EXPECT_CALL(handler, EndDIE(_)) .InSequence(s) .WillOnce(Return()); } void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { ByteReader byte_reader(params.endianness == kLittleEndian ? ENDIANNESS_LITTLE : ENDIANNESS_BIG); 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. Sequence s; }; struct DwarfForms: public DwarfFormsFixture, public TestWithParam<DwarfHeaderParams> { }; TEST_P(DwarfForms, addr) { StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, dwarf2reader::DW_AT_low_pc, dwarf2reader::DW_FORM_addr); uint64_t value; if (GetParam().address_size == 4) { value = 0xc8e9ffcc; info.D32(value); } else { value = 0xe942517fc2768564ULL; info.D64(value); } info.Finish(); ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc, dwarf2reader::DW_FORM_addr, value)) .InSequence(s) .WillOnce(Return()); ExpectEndCompilationUnit(); ParseCompilationUnit(GetParam()); } TEST_P(DwarfForms, block2_empty) { StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, (DwarfAttribute) 0xe52c4463, dwarf2reader::DW_FORM_block2); info.D16(0); info.Finish(); ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, dwarf2reader::DW_FORM_block2, _, 0)) .InSequence(s) .WillOnce(Return()); ExpectEndCompilationUnit(); ParseCompilationUnit(GetParam()); } TEST_P(DwarfForms, block2) { StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, (DwarfAttribute) 0xe52c4463, dwarf2reader::DW_FORM_block2); unsigned char data[258]; memset(data, '*', sizeof(data)); info.D16(sizeof(data)) .Append(data, sizeof(data)); info.Finish(); ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, dwarf2reader::DW_FORM_block2, Pointee('*'), 258)) .InSequence(s) .WillOnce(Return()); ExpectEndCompilationUnit(); 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); uint64_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( HeaderVariants, DwarfForms, ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), DwarfHeaderParams(kLittleEndian, 4, 2, 8), DwarfHeaderParams(kLittleEndian, 4, 3, 4), DwarfHeaderParams(kLittleEndian, 4, 3, 8), DwarfHeaderParams(kLittleEndian, 4, 4, 4), DwarfHeaderParams(kLittleEndian, 4, 4, 8), DwarfHeaderParams(kLittleEndian, 8, 2, 4), DwarfHeaderParams(kLittleEndian, 8, 2, 8), DwarfHeaderParams(kLittleEndian, 8, 3, 4), DwarfHeaderParams(kLittleEndian, 8, 3, 8), DwarfHeaderParams(kLittleEndian, 8, 4, 4), DwarfHeaderParams(kLittleEndian, 8, 4, 8), DwarfHeaderParams(kBigEndian, 4, 2, 4), DwarfHeaderParams(kBigEndian, 4, 2, 8), DwarfHeaderParams(kBigEndian, 4, 3, 4), DwarfHeaderParams(kBigEndian, 4, 3, 8), DwarfHeaderParams(kBigEndian, 4, 4, 4), DwarfHeaderParams(kBigEndian, 4, 4, 8), DwarfHeaderParams(kBigEndian, 8, 2, 4), DwarfHeaderParams(kBigEndian, 8, 2, 8), DwarfHeaderParams(kBigEndian, 8, 3, 4), DwarfHeaderParams(kBigEndian, 8, 3, 8), DwarfHeaderParams(kBigEndian, 8, 4, 4), DwarfHeaderParams(kBigEndian, 8, 4, 8)));