text: // 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 <stdint.h>
  #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 uint8_t *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
        = reinterpret_cast<const uint8_t *>(info_contents.data());
      section_map[".debug_info"].second = info_contents.size();
      section_map[".debug_abbrev"].first
        = reinterpret_cast<const uint8_t *>(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 &params,
                                 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 &params,
                                    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 &params, 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)));
  