// 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)));