michael@0: // Copyright (c) 2010, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: michael@0: // The '.eh_frame' format, used by the Linux C++ ABI for exception michael@0: // handling, is poorly specified. To help test our support for .eh_frame, michael@0: // if you #define WRITE_ELF while compiling this file, and add the michael@0: // 'include' directory from the binutils, gcc, or gdb source tree to the michael@0: // #include path, then each test that calls the michael@0: // PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will write michael@0: // an ELF file containing a .debug_frame or .eh_frame section; you can then michael@0: // use tools like readelf to examine the test data, and check the tools' michael@0: // interpretation against the test's intentions. Each ELF file is named michael@0: // "cfitest-TEST", where TEST identifies the particular test. michael@0: #ifdef WRITE_ELF michael@0: #include michael@0: #include michael@0: #include michael@0: extern "C" { michael@0: // To compile with WRITE_ELF, you should add the 'include' directory michael@0: // of the binutils, gcc, or gdb source tree to your #include path; michael@0: // that directory contains this header. michael@0: #include "elf/common.h" michael@0: } michael@0: #endif michael@0: michael@0: #include "breakpad_googletest_includes.h" michael@0: #include "common/dwarf/bytereader-inl.h" michael@0: #include "common/dwarf/cfi_assembler.h" michael@0: #include "common/dwarf/dwarf2reader.h" michael@0: #include "common/using_std_string.h" michael@0: #include "google_breakpad/common/breakpad_types.h" michael@0: michael@0: using google_breakpad::CFISection; michael@0: using google_breakpad::test_assembler::Label; michael@0: using google_breakpad::test_assembler::kBigEndian; michael@0: using google_breakpad::test_assembler::kLittleEndian; michael@0: using google_breakpad::test_assembler::Section; michael@0: michael@0: using dwarf2reader::DwarfPointerEncoding; michael@0: using dwarf2reader::ENDIANNESS_BIG; michael@0: using dwarf2reader::ENDIANNESS_LITTLE; michael@0: using dwarf2reader::ByteReader; michael@0: using dwarf2reader::CallFrameInfo; michael@0: michael@0: using std::vector; michael@0: using testing::InSequence; michael@0: using testing::Return; michael@0: using testing::Sequence; michael@0: using testing::Test; michael@0: using testing::_; michael@0: michael@0: #ifdef WRITE_ELF michael@0: void WriteELFFrameSection(const char *filename, const char *section_name, michael@0: const CFISection §ion); michael@0: #define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) \ michael@0: WriteELFFrameSection("cfitest-" name, ".debug_frame", section); michael@0: #define PERHAPS_WRITE_EH_FRAME_FILE(name, section) \ michael@0: WriteELFFrameSection("cfitest-" name, ".eh_frame", section); michael@0: #else michael@0: #define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) michael@0: #define PERHAPS_WRITE_EH_FRAME_FILE(name, section) michael@0: #endif michael@0: michael@0: class MockCallFrameInfoHandler: public CallFrameInfo::Handler { michael@0: public: michael@0: MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length, michael@0: uint8 version, const string &augmentation, michael@0: unsigned return_address)); michael@0: MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg)); michael@0: MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg)); michael@0: MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register, michael@0: long offset)); michael@0: MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register, michael@0: long offset)); michael@0: MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register)); michael@0: MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg, michael@0: const string &expression)); michael@0: MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg, michael@0: const string &expression)); michael@0: MOCK_METHOD0(End, bool()); michael@0: MOCK_METHOD2(PersonalityRoutine, bool(uint64 address, bool indirect)); michael@0: MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64 address, bool indirect)); michael@0: MOCK_METHOD0(SignalHandler, bool()); michael@0: }; michael@0: michael@0: class MockCallFrameErrorReporter: public CallFrameInfo::Reporter { michael@0: public: michael@0: MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { } michael@0: MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind)); michael@0: MOCK_METHOD1(EarlyEHTerminator, void(uint64)); michael@0: MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64)); michael@0: MOCK_METHOD2(BadCIEId, void(uint64, uint64)); michael@0: MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version)); michael@0: MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &)); michael@0: MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8)); michael@0: MOCK_METHOD2(UnusablePointerEncoding, void(uint64, uint8)); michael@0: MOCK_METHOD2(RestoreInCIE, void(uint64, uint64)); michael@0: MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64)); michael@0: MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64)); michael@0: MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64)); michael@0: }; michael@0: michael@0: struct CFIFixture { michael@0: michael@0: enum { kCFARegister = CallFrameInfo::Handler::kCFARegister }; michael@0: michael@0: CFIFixture() { michael@0: // Default expectations for the data handler. michael@0: // michael@0: // - Leave Entry and End without expectations, as it's probably a michael@0: // good idea to set those explicitly in each test. michael@0: // michael@0: // - Expect the *Rule functions to not be called, michael@0: // so that each test can simply list the calls they expect. michael@0: // michael@0: // I gather I could use StrictMock for this, but the manual seems michael@0: // to suggest using that only as a last resort, and this isn't so michael@0: // bad. michael@0: EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0); michael@0: EXPECT_CALL(handler, SameValueRule(_, _)).Times(0); michael@0: EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0); michael@0: EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0); michael@0: EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0); michael@0: EXPECT_CALL(handler, PersonalityRoutine(_, _)).Times(0); michael@0: EXPECT_CALL(handler, LanguageSpecificDataArea(_, _)).Times(0); michael@0: EXPECT_CALL(handler, SignalHandler()).Times(0); michael@0: michael@0: // Default expectations for the error/warning reporer. michael@0: EXPECT_CALL(reporter, Incomplete(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, EarlyEHTerminator(_)).Times(0); michael@0: EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, InvalidPointerEncoding(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, UnusablePointerEncoding(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0); michael@0: EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0); michael@0: EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0); michael@0: EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0); michael@0: } michael@0: michael@0: MockCallFrameInfoHandler handler; michael@0: MockCallFrameErrorReporter reporter; michael@0: }; michael@0: michael@0: class CFI: public CFIFixture, public Test { }; michael@0: michael@0: TEST_F(CFI, EmptyRegion) { michael@0: EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, End()).Times(0); michael@0: static const char data[1] = { 42 }; michael@0: michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: TEST_F(CFI, IncompleteLength32) { michael@0: CFISection section(kBigEndian, 8); michael@0: section michael@0: // Not even long enough for an initial length. michael@0: .D16(0xa0f) michael@0: // Padding to keep valgrind happy. We subtract these off when we michael@0: // construct the parser. michael@0: .D16(0); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, End()).Times(0); michael@0: michael@0: EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) michael@0: .WillOnce(Return()); michael@0: michael@0: string contents; michael@0: ASSERT_TRUE(section.GetContents(&contents)); michael@0: michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(8); michael@0: CallFrameInfo parser(contents.data(), contents.size() - 2, michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: TEST_F(CFI, IncompleteLength64) { michael@0: CFISection section(kLittleEndian, 4); michael@0: section michael@0: // An incomplete 64-bit DWARF initial length. michael@0: .D32(0xffffffff).D32(0x71fbaec2) michael@0: // Padding to keep valgrind happy. We subtract these off when we michael@0: // construct the parser. michael@0: .D32(0); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, End()).Times(0); michael@0: michael@0: EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) michael@0: .WillOnce(Return()); michael@0: michael@0: string contents; michael@0: ASSERT_TRUE(section.GetContents(&contents)); michael@0: michael@0: ByteReader byte_reader(ENDIANNESS_LITTLE); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size() - 4, michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: TEST_F(CFI, IncompleteId32) { michael@0: CFISection section(kBigEndian, 8); michael@0: section michael@0: .D32(3) // Initial length, not long enough for id michael@0: .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id michael@0: .CIEHeader(8727, 3983, 8889, 3, "") michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, End()).Times(0); michael@0: michael@0: EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) michael@0: .WillOnce(Return()); michael@0: michael@0: string contents; michael@0: ASSERT_TRUE(section.GetContents(&contents)); michael@0: michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(8); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: TEST_F(CFI, BadId32) { michael@0: CFISection section(kBigEndian, 8); michael@0: section michael@0: .D32(0x100) // Initial length michael@0: .D32(0xe802fade) // bogus ID michael@0: .Append(0x100 - 4, 0x42); // make the length true michael@0: section michael@0: .CIEHeader(1672, 9872, 8529, 3, "") michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, End()).Times(0); michael@0: michael@0: EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade)) michael@0: .WillOnce(Return()); michael@0: michael@0: string contents; michael@0: ASSERT_TRUE(section.GetContents(&contents)); michael@0: michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(8); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: // A lone CIE shouldn't cause any handler calls. michael@0: TEST_F(CFI, SingleCIE) { michael@0: CFISection section(kLittleEndian, 4); michael@0: section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, ""); michael@0: section.Append(10, dwarf2reader::DW_CFA_nop); michael@0: section.FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("SingleCIE", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); michael@0: EXPECT_CALL(handler, End()).Times(0); michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_LITTLE); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: // One FDE, one CIE. michael@0: TEST_F(CFI, OneFDE) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "") michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0x7714740d, 0x3d5a10cd) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("OneFDE", section); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: // Two FDEs share a CIE. michael@0: TEST_F(CFI, TwoFDEsOneCIE) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie; michael@0: section michael@0: // First FDE. readelf complains about this one because it makes michael@0: // a forward reference to its CIE. michael@0: .FDEHeader(cie, 0xa42744df, 0xa3b42121) michael@0: .FinishEntry() michael@0: // CIE. michael@0: .Mark(&cie) michael@0: .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "") michael@0: .FinishEntry() michael@0: // Second FDE. michael@0: .FDEHeader(cie, 0x6057d391, 0x700f608d) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsOneCIE", section); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: // Two FDEs, two CIEs. michael@0: TEST_F(CFI, TwoFDEsTwoCIEs) { michael@0: CFISection section(kLittleEndian, 8); michael@0: Label cie1, cie2; michael@0: section michael@0: // First CIE. michael@0: .Mark(&cie1) michael@0: .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "") michael@0: .FinishEntry() michael@0: // First FDE which cites second CIE. readelf complains about michael@0: // this one because it makes a forward reference to its CIE. michael@0: .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL) michael@0: .FinishEntry() michael@0: // Second FDE, which cites first CIE. michael@0: .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL) michael@0: .FinishEntry() michael@0: // Second CIE. michael@0: .Mark(&cie2) michael@0: .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "") michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsTwoCIEs", section); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2, michael@0: "", 0x61d2c581)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3, michael@0: "", 0xbf45e65a)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_LITTLE); michael@0: byte_reader.SetAddressSize(8); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: // An FDE whose CIE specifies a version we don't recognize. michael@0: TEST_F(CFI, BadVersion) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie1, cie2; michael@0: section michael@0: .Mark(&cie1) michael@0: .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "") michael@0: .FinishEntry() michael@0: // We should skip this entry, as its CIE specifies a version we michael@0: // don't recognize. michael@0: .FDEHeader(cie1, 0x08852292, 0x2204004a) michael@0: .FinishEntry() michael@0: // Despite the above, we should visit this entry. michael@0: .Mark(&cie2) michael@0: .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "") michael@0: .FinishEntry() michael@0: .FDEHeader(cie2, 0x2094735a, 0x6e875501) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("BadVersion", section); michael@0: michael@0: EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52)) michael@0: .WillOnce(Return()); michael@0: michael@0: { michael@0: InSequence s; michael@0: // We should see no mention of the first FDE, but we should get michael@0: // a call to Entry for the second. michael@0: EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "", michael@0: 0x96cb3264)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: // An FDE whose CIE specifies an augmentation we don't recognize. michael@0: TEST_F(CFI, BadAugmentation) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie1, cie2; michael@0: section michael@0: .Mark(&cie1) michael@0: .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!") michael@0: .FinishEntry() michael@0: // We should skip this entry, as its CIE specifies an michael@0: // augmentation we don't recognize. michael@0: .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd) michael@0: .FinishEntry() michael@0: // Despite the above, we should visit this entry. michael@0: .Mark(&cie2) michael@0: .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "") michael@0: .FinishEntry() michael@0: .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("BadAugmentation", section); michael@0: michael@0: EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!")) michael@0: .WillOnce(Return()); michael@0: michael@0: { michael@0: InSequence s; michael@0: // We should see no mention of the first FDE, but we should get michael@0: // a call to Entry for the second. michael@0: EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "", michael@0: 0xf2f519b2)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: // The return address column field is a byte in CFI version 1 michael@0: // (DWARF2), but a ULEB128 value in version 3 (DWARF3). michael@0: TEST_F(CFI, CIEVersion1ReturnColumn) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie; michael@0: section michael@0: // CIE, using the version 1 format: return column is a ubyte. michael@0: .Mark(&cie) michael@0: // Use a value for the return column that is parsed differently michael@0: // as a ubyte and as a ULEB128. michael@0: .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "") michael@0: .FinishEntry() michael@0: // FDE, citing that CIE. michael@0: .FDEHeader(cie, 0xb8d347b5, 0x825e55dc) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion1ReturnColumn", section); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: // The return address column field is a byte in CFI version 1 michael@0: // (DWARF2), but a ULEB128 value in version 3 (DWARF3). michael@0: TEST_F(CFI, CIEVersion3ReturnColumn) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie; michael@0: section michael@0: // CIE, using the version 3 format: return column is a ULEB128. michael@0: .Mark(&cie) michael@0: // Use a value for the return column that is parsed differently michael@0: // as a ubyte and as a ULEB128. michael@0: .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "") michael@0: .FinishEntry() michael@0: // FDE, citing that CIE. michael@0: .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: string contents; michael@0: EXPECT_TRUE(section.GetContents(&contents)); michael@0: ByteReader byte_reader(ENDIANNESS_BIG); michael@0: byte_reader.SetAddressSize(4); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: EXPECT_TRUE(parser.Start()); michael@0: } michael@0: michael@0: struct CFIInsnFixture: public CFIFixture { michael@0: CFIInsnFixture() : CFIFixture() { michael@0: data_factor = 0xb6f; michael@0: return_register = 0x9be1ed9f; michael@0: version = 3; michael@0: cfa_base_register = 0x383a3aa; michael@0: cfa_offset = 0xf748; michael@0: } michael@0: michael@0: // Prepare SECTION to receive FDE instructions. michael@0: // michael@0: // - Append a stock CIE header that establishes the fixture's michael@0: // code_factor, data_factor, return_register, version, and michael@0: // augmentation values. michael@0: // - Have the CIE set up a CFA rule using cfa_base_register and michael@0: // cfa_offset. michael@0: // - Append a stock FDE header, referring to the above CIE, for the michael@0: // fde_size bytes at fde_start. Choose fde_start and fde_size michael@0: // appropriately for the section's address size. michael@0: // - Set appropriate expectations on handler in sequence s for the michael@0: // frame description entry and the CIE's CFA rule. michael@0: // michael@0: // On return, SECTION is ready to have FDE instructions appended to michael@0: // it, and its FinishEntry member called. michael@0: void StockCIEAndFDE(CFISection *section) { michael@0: // Choose appropriate constants for our address size. michael@0: if (section->AddressSize() == 4) { michael@0: fde_start = 0xc628ecfbU; michael@0: fde_size = 0x5dee04a2; michael@0: code_factor = 0x60b; michael@0: } else { michael@0: assert(section->AddressSize() == 8); michael@0: fde_start = 0x0005c57ce7806bd3ULL; michael@0: fde_size = 0x2699521b5e333100ULL; michael@0: code_factor = 0x01008e32855274a8ULL; michael@0: } michael@0: michael@0: // Create the CIE. michael@0: (*section) michael@0: .Mark(&cie_label) michael@0: .CIEHeader(code_factor, data_factor, return_register, version, michael@0: "") michael@0: .D8(dwarf2reader::DW_CFA_def_cfa) michael@0: .ULEB128(cfa_base_register) michael@0: .ULEB128(cfa_offset) michael@0: .FinishEntry(); michael@0: michael@0: // Create the FDE. michael@0: section->FDEHeader(cie_label, fde_start, fde_size); michael@0: michael@0: // Expect an Entry call for the FDE and a ValOffsetRule call for the michael@0: // CIE's CFA rule. michael@0: EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "", michael@0: return_register)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister, michael@0: cfa_base_register, cfa_offset)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: } michael@0: michael@0: // Run the contents of SECTION through a CallFrameInfo parser, michael@0: // expecting parser.Start to return SUCCEEDS michael@0: void ParseSection(CFISection *section, bool succeeds = true) { michael@0: string contents; michael@0: EXPECT_TRUE(section->GetContents(&contents)); michael@0: dwarf2reader::Endianness endianness; michael@0: if (section->endianness() == kBigEndian) michael@0: endianness = ENDIANNESS_BIG; michael@0: else { michael@0: assert(section->endianness() == kLittleEndian); michael@0: endianness = ENDIANNESS_LITTLE; michael@0: } michael@0: ByteReader byte_reader(endianness); michael@0: byte_reader.SetAddressSize(section->AddressSize()); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter); michael@0: if (succeeds) michael@0: EXPECT_TRUE(parser.Start()); michael@0: else michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: Label cie_label; michael@0: Sequence s; michael@0: uint64 code_factor; michael@0: int data_factor; michael@0: unsigned return_register; michael@0: unsigned version; michael@0: unsigned cfa_base_register; michael@0: int cfa_offset; michael@0: uint64 fde_start, fde_size; michael@0: }; michael@0: michael@0: class CFIInsn: public CFIInsnFixture, public Test { }; michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_set_loc) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_set_loc).D32(0xb1ee3e7a) michael@0: // Use DW_CFA_def_cfa to force a handler call that we can use to michael@0: // check the effect of the DW_CFA_set_loc. michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_set_loc", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_advance_loc) { michael@0: CFISection section(kBigEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0x2a) michael@0: // Use DW_CFA_def_cfa to force a handler call that we can use to michael@0: // check the effect of the DW_CFA_advance_loc. michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start + 0x2a * code_factor, michael@0: kCFARegister, 0x5bbb3715, 0x0186c7bf)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_advance_loc1) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_advance_loc1).D8(0xd8) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc1", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule((fde_start + 0xd8 * code_factor), michael@0: kCFARegister, 0x69d5696a, 0x1eb7fc93)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_advance_loc2) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_advance_loc2).D16(0x3adb) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc2", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule((fde_start + 0x3adb * code_factor), michael@0: kCFARegister, 0x3a368bed, 0x3194ee37)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_advance_loc4) { michael@0: CFISection section(kBigEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_advance_loc4).D32(0x15813c88) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc4", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule((fde_start + 0x15813c88ULL * code_factor), michael@0: kCFARegister, 0x135270c5, 0x24bad7cb)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_MIPS_advance_loc8) { michael@0: code_factor = 0x2d; michael@0: CFISection section(kBigEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc8", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor), michael@0: kCFARegister, 0xe17ed602, 0x3d162e7f)) michael@0: .InSequence(s) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_def_cfa", section); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_sf) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7, michael@0: 0x9ea * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da, michael@0: -0x40a2 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_register) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: // DW_CFA_def_cfa_register should have no effect when applied to a michael@0: // non-base/offset rule. michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_registerBadRule) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("needle in a haystack") michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValExpressionRule(fde_start, kCFARegister, michael@0: "needle in a haystack")) michael@0: .WillRepeatedly(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_offset) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, cfa_base_register, michael@0: 0x1e8e3b9b)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_offset_sf) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(0x970) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, cfa_base_register, michael@0: 0x970 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, cfa_base_register, michael@0: -0x2cd * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: // DW_CFA_def_cfa_offset should have no effect when applied to a michael@0: // non-base/offset rule. michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_offsetBadRule) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("six ways to Sunday") michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValExpressionRule(fde_start, kCFARegister, "six ways to Sunday")) michael@0: .WillRepeatedly(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_def_cfa_expression) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("eating crow") michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister, michael@0: "eating crow")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_undefined) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x300ce45d) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_same_value) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3865a760) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_offset) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x9f6) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_offset_extended) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x402b, kCFARegister, 0xb48 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_offset_extended_sf) { michael@0: CFISection section(kBigEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_offset_extended_sf) michael@0: .ULEB128(0x997c23ee).LEB128(0x2d00) michael@0: .D8(dwarf2reader::DW_CFA_offset_extended_sf) michael@0: .ULEB128(0x9519eb82).LEB128(-0xa77) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x997c23ee, michael@0: kCFARegister, 0x2d00 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x9519eb82, michael@0: kCFARegister, -0xa77 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_val_offset) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, 0x623562fe, michael@0: kCFARegister, 0x673 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_val_offset_sf) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab) michael@0: .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, 0x6f4f, michael@0: kCFARegister, 0xaab * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, 0x2483, michael@0: kCFARegister, -0x8a2 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_register) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_expression) { michael@0: CFISection section(kBigEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xa1619fb2) michael@0: .Block("plus ça change, plus c'est la même chose") michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ExpressionRule(fde_start, 0xa1619fb2, michael@0: "plus ça change, plus c'est la même chose")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_val_expression) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xc5e4a9e3) michael@0: .Block("he who has the gold makes the rules") michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValExpressionRule(fde_start, 0xc5e4a9e3, michael@0: "he who has the gold makes the rules")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_restore) { michael@0: CFISection section(kLittleEndian, 8); michael@0: code_factor = 0x01bd188a9b1fa083ULL; michael@0: data_factor = -0x1ac8; michael@0: return_register = 0x8c35b049; michael@0: version = 2; michael@0: fde_start = 0x2d70fe998298bbb1ULL; michael@0: fde_size = 0x46ccc2e63cf0b108ULL; michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(code_factor, data_factor, return_register, version, michael@0: "") michael@0: // Provide a CFA rule, because register rules require them. michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8) michael@0: // Provide an offset(N) rule for register 0x3c. michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0xb348) michael@0: .FinishEntry() michael@0: // In the FDE... michael@0: .FDEHeader(cie, fde_start, fde_size) michael@0: // At a second address, provide a new offset(N) rule for register 0x3c. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0x13) michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0x9a50) michael@0: // At a third address, restore the original rule for register 0x3c. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0x01) michael@0: .D8(dwarf2reader::DW_CFA_restore | 0x3c) michael@0: .FinishEntry(); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, fde_start, fde_size, version, "", return_register)) michael@0: .WillOnce(Return(true)); michael@0: // CIE's CFA rule. michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x6ca1d50e, 0x372e38e8)) michael@0: .WillOnce(Return(true)); michael@0: // CIE's rule for register 0x3c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x3c, kCFARegister, 0xb348 * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: // FDE's rule for register 0x3c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start + 0x13 * code_factor, 0x3c, michael@0: kCFARegister, 0x9a50 * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: // Restore CIE's rule for register 0x3c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c, michael@0: kCFARegister, 0xb348 * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_restoreNoRule) { michael@0: CFISection section(kBigEndian, 4); michael@0: code_factor = 0x005f78143c1c3b82ULL; michael@0: data_factor = 0x25d0; michael@0: return_register = 0xe8; michael@0: version = 1; michael@0: fde_start = 0x4062e30f; michael@0: fde_size = 0x5302a389; michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(code_factor, data_factor, return_register, version, "") michael@0: // Provide a CFA rule, because register rules require them. michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127) michael@0: .FinishEntry() michael@0: // In the FDE... michael@0: .FDEHeader(cie, fde_start, fde_size) michael@0: // At a second address, provide an offset(N) rule for register 0x2c. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0x7) michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x1f47) michael@0: // At a third address, restore the (missing) CIE rule for register 0x2c. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0xb) michael@0: .D8(dwarf2reader::DW_CFA_restore | 0x2c) michael@0: .FinishEntry(); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, fde_start, fde_size, version, "", return_register)) michael@0: .WillOnce(Return(true)); michael@0: // CIE's CFA rule. michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x470aa334, 0x099ef127)) michael@0: .WillOnce(Return(true)); michael@0: // FDE's rule for register 0x2c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start + 0x7 * code_factor, 0x2c, michael@0: kCFARegister, 0x1f47 * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: // Restore CIE's (missing) rule for register 0x2c. michael@0: EXPECT_CALL(handler, michael@0: SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_restore_extended) { michael@0: CFISection section(kBigEndian, 4); michael@0: code_factor = 0x126e; michael@0: data_factor = -0xd8b; michael@0: return_register = 0x77711787; michael@0: version = 3; michael@0: fde_start = 0x01f55a45; michael@0: fde_size = 0x452adb80; michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(code_factor, data_factor, return_register, version, michael@0: "", true /* dwarf64 */ ) michael@0: // Provide a CFA rule, because register rules require them. michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5) michael@0: // Provide an offset(N) rule for register 0x0f9b8a1c. michael@0: .D8(dwarf2reader::DW_CFA_offset_extended) michael@0: .ULEB128(0x0f9b8a1c).ULEB128(0xc979) michael@0: .FinishEntry() michael@0: // In the FDE... michael@0: .FDEHeader(cie, fde_start, fde_size) michael@0: // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0x3) michael@0: .D8(dwarf2reader::DW_CFA_offset_extended) michael@0: .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b) michael@0: // At a third address, restore the original rule for register 0x0f9b8a1c. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 0x04) michael@0: .D8(dwarf2reader::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c) michael@0: .FinishEntry(); michael@0: michael@0: { michael@0: InSequence s; michael@0: EXPECT_CALL(handler, michael@0: Entry(_, fde_start, fde_size, version, "", return_register)) michael@0: .WillOnce(Return(true)); michael@0: // CIE's CFA rule. michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5)) michael@0: .WillOnce(Return(true)); michael@0: // CIE's rule for register 0x0f9b8a1c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister, michael@0: 0xc979 * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: // FDE's rule for register 0x0f9b8a1c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c, michael@0: kCFARegister, 0x3b7b * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: // Restore CIE's rule for register 0x0f9b8a1c. michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c, michael@0: kCFARegister, 0xc979 * data_factor)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: } michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_remember_and_restore_state) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: michael@0: // We create a state, save it, modify it, and then restore. We michael@0: // refer to the state that is overridden the restore as the michael@0: // "outgoing" state, and the restored state the "incoming" state. michael@0: // michael@0: // Register outgoing incoming expect michael@0: // 1 offset(N) no rule new "same value" rule michael@0: // 2 register(R) offset(N) report changed rule michael@0: // 3 offset(N) offset(M) report changed offset michael@0: // 4 offset(N) offset(N) no report michael@0: // 5 offset(N) no rule new "same value" rule michael@0: section michael@0: // Create the "incoming" state, which we will save and later restore. michael@0: .D8(dwarf2reader::DW_CFA_offset | 2).ULEB128(0x9806) michael@0: .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0x995d) michael@0: .D8(dwarf2reader::DW_CFA_offset | 4).ULEB128(0x7055) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: // Advance to a new instruction; an implementation could legitimately michael@0: // ignore all but the final rule for a given register at a given address. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: // Create the "outgoing" state, which we will discard. michael@0: .D8(dwarf2reader::DW_CFA_offset | 1).ULEB128(0xea1a) michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767) michael@0: .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0xdd29) michael@0: .D8(dwarf2reader::DW_CFA_offset | 5).ULEB128(0xf1ce) michael@0: // At a third address, restore the incoming state. michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: uint64 addr = fde_start; michael@0: michael@0: // Expect the incoming rules to be reported. michael@0: EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: addr += code_factor; michael@0: michael@0: // After the save, we establish the outgoing rule set. michael@0: EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: addr += code_factor; michael@0: michael@0: // Finally, after the restore, expect to see the differences from michael@0: // the outgoing to the incoming rules reported. michael@0: EXPECT_CALL(handler, SameValueRule(addr, 1)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, SameValueRule(addr, 5)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: // Check that restoring a rule set reports changes to the CFA rule. michael@0: TEST_F(CFIInsn, DW_CFA_remember_and_restore_stateCFA) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x90481102) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister, michael@0: cfa_base_register, 0x90481102)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister, michael@0: cfa_base_register, cfa_offset)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_nop) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_nop) michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b) michael@0: .D8(dwarf2reader::DW_CFA_nop) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_GNU_window_save) { michael@0: CFISection section(kBigEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_GNU_window_save) michael@0: .FinishEntry(); michael@0: michael@0: // Don't include all the rules in any particular sequence. michael@0: michael@0: // The caller's %o0-%o7 have become the callee's %i0-%i7. This is michael@0: // the GCC register numbering. michael@0: for (int i = 8; i < 16; i++) michael@0: EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16)) michael@0: .WillOnce(Return(true)); michael@0: // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of michael@0: // its frame. michael@0: for (int i = 16; i < 32; i++) michael@0: EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4)) michael@0: .WillOnce(Return(true)); michael@0: michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_GNU_args_size) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_GNU_args_size).ULEB128(0xeddfa520) michael@0: // Verify that we see this, meaning we parsed the above properly. michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x23).ULEB128(0x269) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIInsn, DW_CFA_GNU_negative_offset_extended) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_GNU_negative_offset_extended) michael@0: .ULEB128(0x430cc87a).ULEB128(0x613) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, michael@0: OffsetRule(fde_start, 0x430cc87a, michael@0: kCFARegister, -0x613 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: // Three FDEs: skip the second michael@0: TEST_F(CFIInsn, SkipFDE) { michael@0: CFISection section(kBigEndian, 4); michael@0: Label cie; michael@0: section michael@0: // CIE, used by all FDEs. michael@0: .Mark(&cie) michael@0: .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "") michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad) michael@0: .FinishEntry() michael@0: // First FDE. michael@0: .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4) michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf) michael@0: .FinishEntry() michael@0: // Second FDE. michael@0: .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */) michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18) michael@0: .FinishEntry() michael@0: // Third FDE. michael@0: .FDEHeader(cie, 0xf681cfc8, 0x7e4594e) michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4) michael@0: .FinishEntry(); michael@0: michael@0: { michael@0: InSequence s; michael@0: michael@0: // Process the first FDE. michael@0: EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister, michael@0: 0x42ed390b, 0x98f43aad)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .WillOnce(Return(true)); michael@0: michael@0: // Skip the second FDE. michael@0: EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849)) michael@0: .WillOnce(Return(false)); michael@0: michael@0: // Process the third FDE. michael@0: EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister, michael@0: 0x42ed390b, 0x98f43aad)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4)) michael@0: .WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .WillOnce(Return(true)); michael@0: } michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: // Quit processing in the middle of an entry's instructions. michael@0: TEST_F(CFIInsn, QuitMidentry) { michael@0: CFISection section(kLittleEndian, 8); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431) michael@0: .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat") michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431)) michael@0: .InSequence(s).WillOnce(Return(false)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion, false); michael@0: } michael@0: michael@0: class CFIRestore: public CFIInsnFixture, public Test { }; michael@0: michael@0: TEST_F(CFIRestore, RestoreUndefinedRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x0bac878e) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreUndefinedRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x7dedff5f) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x7dedff5f) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreSameValueRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0xadbc9b3a) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreSameValueRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3d90dcb5) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x3d90dcb5) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreOffsetRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x14).ULEB128(0xb6f) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, OffsetRule(fde_start, 0x14, michael@0: kCFARegister, 0xb6f * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreOffsetRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xeb7) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x21) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, michael@0: kCFARegister, 0xeb7 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, michael@0: kCFARegister, 0xeb7 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreOffsetRuleChangedOffset) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0x134) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xf4f) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, michael@0: kCFARegister, 0x134 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21, michael@0: kCFARegister, 0xf4f * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, michael@0: kCFARegister, 0x134 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreValOffsetRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6, michael@0: kCFARegister, 0xe4c * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreValOffsetRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xf17c36d6) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6, michael@0: kCFARegister, 0xeb7 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6, michael@0: kCFARegister, 0xeb7 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreValOffsetRuleChangedValOffset) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b, michael@0: kCFARegister, 0x562 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b, michael@0: kCFARegister, 0xe88 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b, michael@0: kCFARegister, 0x562 * data_factor)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreRegisterRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreRegisterRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xe39acce5) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5, michael@0: 0x095f1559)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreRegisterRuleChangedRegister) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a) michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1, michael@0: 0xbabb4742)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1, michael@0: 0x16607d6a)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreExpressionRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf") michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreExpressionRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf") michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, michael@0: "elf")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreExpressionRuleChangedExpression) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf") michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("orc") michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739, michael@0: "orc")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: // Expectations are not wishes. michael@0: EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739, michael@0: "smurf")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreValExpressionRuleUnchanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x666ae152) michael@0: .Block("hideous") michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreValExpressionRuleChanged) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xb5ca5c46) michael@0: .Block("revolting") michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChanged", section); michael@0: michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, michael@0: "revolting")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: TEST_F(CFIRestore, RestoreValExpressionRuleChangedValExpression) { michael@0: CFISection section(kLittleEndian, 4); michael@0: StockCIEAndFDE(§ion); michael@0: section michael@0: .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) michael@0: .Block("repulsive") michael@0: .D8(dwarf2reader::DW_CFA_remember_state) michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) michael@0: .Block("nauseous") michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 1) michael@0: .D8(dwarf2reader::DW_CFA_restore_state) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChangedValExpression", michael@0: section); michael@0: michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739, michael@0: "nauseous")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: // Expectations are not wishes. michael@0: EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739, michael@0: "repulsive")) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()).WillOnce(Return(true)); michael@0: michael@0: ParseSection(§ion); michael@0: } michael@0: michael@0: struct EHFrameFixture: public CFIInsnFixture { michael@0: EHFrameFixture() michael@0: : CFIInsnFixture(), section(kBigEndian, 4, true) { michael@0: encoded_pointer_bases.cfi = 0x7f496cb2; michael@0: encoded_pointer_bases.text = 0x540f67b6; michael@0: encoded_pointer_bases.data = 0xe3eab768; michael@0: section.SetEncodedPointerBases(encoded_pointer_bases); michael@0: } michael@0: CFISection section; michael@0: CFISection::EncodedPointerBases encoded_pointer_bases; michael@0: michael@0: // Parse CFIInsnFixture::ParseSection, but parse the section as michael@0: // .eh_frame data, supplying stock base addresses. michael@0: void ParseEHFrameSection(CFISection *section, bool succeeds = true) { michael@0: EXPECT_TRUE(section->ContainsEHFrame()); michael@0: string contents; michael@0: EXPECT_TRUE(section->GetContents(&contents)); michael@0: dwarf2reader::Endianness endianness; michael@0: if (section->endianness() == kBigEndian) michael@0: endianness = ENDIANNESS_BIG; michael@0: else { michael@0: assert(section->endianness() == kLittleEndian); michael@0: endianness = ENDIANNESS_LITTLE; michael@0: } michael@0: ByteReader byte_reader(endianness); michael@0: byte_reader.SetAddressSize(section->AddressSize()); michael@0: byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi, contents.data()); michael@0: byte_reader.SetTextBase(encoded_pointer_bases.text); michael@0: byte_reader.SetDataBase(encoded_pointer_bases.data); michael@0: CallFrameInfo parser(contents.data(), contents.size(), michael@0: &byte_reader, &handler, &reporter, true); michael@0: if (succeeds) michael@0: EXPECT_TRUE(parser.Start()); michael@0: else michael@0: EXPECT_FALSE(parser.Start()); michael@0: } michael@0: michael@0: }; michael@0: michael@0: class EHFrame: public EHFrameFixture, public Test { }; michael@0: michael@0: // A simple CIE, an FDE, and a terminator. michael@0: TEST_F(EHFrame, Terminator) { michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(9968, 2466, 67, 1, "") michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372) michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0x848037a1, 0x7b30475e) michael@0: .D8(dwarf2reader::DW_CFA_set_loc).D32(0x17713850) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(5721) michael@0: .FinishEntry() michael@0: .D32(0) // Terminate the sequence. michael@0: // This FDE should be ignored. michael@0: .FDEHeader(cie, 0xf19629fe, 0x439fb09b) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.Terminator", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0x848037a1, 0x7b30475e, 1, "", 67)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(0x848037a1, kCFARegister, 3772, 1372)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(0x17713850, 5721)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(reporter, EarlyEHTerminator(_)) michael@0: .InSequence(s).WillOnce(Return()); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: // The parser should recognize the Linux Standards Base 'z' augmentations. michael@0: TEST_F(EHFrame, SimpleFDE) { michael@0: DwarfPointerEncoding lsda_encoding = michael@0: DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect michael@0: | dwarf2reader::DW_EH_PE_datarel michael@0: | dwarf2reader::DW_EH_PE_sdata2); michael@0: DwarfPointerEncoding fde_encoding = michael@0: DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel michael@0: | dwarf2reader::DW_EH_PE_udata2); michael@0: michael@0: section.SetPointerEncoding(fde_encoding); michael@0: section.SetEncodedPointerBases(encoded_pointer_bases); michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(4873, 7012, 100, 1, "zSLPR") michael@0: .ULEB128(7) // Augmentation data length michael@0: .D8(lsda_encoding) // LSDA pointer format michael@0: .D8(dwarf2reader::DW_EH_PE_pcrel) // personality pointer format michael@0: .EncodedPointer(0x97baa00, dwarf2reader::DW_EH_PE_pcrel) // and value michael@0: .D8(fde_encoding) // FDE pointer format michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(6706).ULEB128(31) michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0x540f6b56, 0xf686) michael@0: .ULEB128(2) // Augmentation data length michael@0: .EncodedPointer(0xe3eab475, lsda_encoding) // LSDA pointer, signed michael@0: .D8(dwarf2reader::DW_CFA_set_loc) michael@0: .EncodedPointer(0x540fa4ce, fde_encoding) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x675e) michael@0: .FinishEntry() michael@0: .D32(0); // terminator michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.SimpleFDE", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0x540f6b56, 0xf686, 1, "zSLPR", 100)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, PersonalityRoutine(0x97baa00, false)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, LanguageSpecificDataArea(0xe3eab475, true)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, SignalHandler()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(0x540f6b56, kCFARegister, 6706, 31)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(0x540fa4ce, 0x675e)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: // Check that we can handle an empty 'z' augmentation. michael@0: TEST_F(EHFrame, EmptyZ) { michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(5955, 5805, 228, 1, "z") michael@0: .ULEB128(0) // Augmentation data length michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247) michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0xda007738, 0xfb55c641) michael@0: .ULEB128(0) // Augmentation data length michael@0: .D8(dwarf2reader::DW_CFA_advance_loc1).D8(11) michael@0: .D8(dwarf2reader::DW_CFA_undefined).ULEB128(3769) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.EmptyZ", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0xda007738, 0xfb55c641, 1, "z", 228)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, ValOffsetRule(0xda007738, kCFARegister, 3629, 247)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, UndefinedRule(0xda007738 + 11 * 5955, 3769)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: // Check that we recognize bad 'z' augmentation characters. michael@0: TEST_F(EHFrame, BadZ) { michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(6937, 1045, 142, 1, "zQ") michael@0: .ULEB128(0) // Augmentation data length michael@0: .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725) michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0x1293efa8, 0x236f53f2) michael@0: .ULEB128(0) // Augmentation data length michael@0: .D8(dwarf2reader::DW_CFA_advance_loc | 12) michael@0: .D8(dwarf2reader::DW_CFA_register).ULEB128(5667).ULEB128(3462) michael@0: .FinishEntry(); michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.BadZ", section); michael@0: michael@0: EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "zQ")) michael@0: .WillOnce(Return()); michael@0: michael@0: ParseEHFrameSection(§ion, false); michael@0: } michael@0: michael@0: TEST_F(EHFrame, zL) { michael@0: Label cie; michael@0: DwarfPointerEncoding lsda_encoding = michael@0: DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel michael@0: | dwarf2reader::DW_EH_PE_udata2); michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(9285, 9959, 54, 1, "zL") michael@0: .ULEB128(1) // Augmentation data length michael@0: .D8(lsda_encoding) // encoding for LSDA pointer in FDE michael@0: michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) michael@0: .ULEB128(2) // Augmentation data length michael@0: .EncodedPointer(0xd40099cd, lsda_encoding) // LSDA pointer michael@0: .FinishEntry() michael@0: .D32(0); // terminator michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zL", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zL", 54)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, LanguageSpecificDataArea(0xd40099cd, false)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: TEST_F(EHFrame, zP) { michael@0: Label cie; michael@0: DwarfPointerEncoding personality_encoding = michael@0: DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel michael@0: | dwarf2reader::DW_EH_PE_udata2); michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(1097, 6313, 17, 1, "zP") michael@0: .ULEB128(3) // Augmentation data length michael@0: .D8(personality_encoding) // encoding for personality routine michael@0: .EncodedPointer(0xe3eaccac, personality_encoding) // value michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0x0c8350c9, 0xbef11087) michael@0: .ULEB128(0) // Augmentation data length michael@0: .FinishEntry() michael@0: .D32(0); // terminator michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zP", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0x0c8350c9, 0xbef11087, 1, "zP", 17)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, PersonalityRoutine(0xe3eaccac, false)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: TEST_F(EHFrame, zR) { michael@0: Label cie; michael@0: DwarfPointerEncoding pointer_encoding = michael@0: DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel michael@0: | dwarf2reader::DW_EH_PE_sdata2); michael@0: section.SetPointerEncoding(pointer_encoding); michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(8011, 5496, 75, 1, "zR") michael@0: .ULEB128(1) // Augmentation data length michael@0: .D8(pointer_encoding) // encoding for FDE addresses michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0x540f9431, 0xbd0) michael@0: .ULEB128(0) // Augmentation data length michael@0: .FinishEntry() michael@0: .D32(0); // terminator michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zR", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0x540f9431, 0xbd0, 1, "zR", 75)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: TEST_F(EHFrame, zS) { michael@0: Label cie; michael@0: section michael@0: .Mark(&cie) michael@0: .CIEHeader(9217, 7694, 57, 1, "zS") michael@0: .ULEB128(0) // Augmentation data length michael@0: .FinishEntry() michael@0: .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) michael@0: .ULEB128(0) // Augmentation data length michael@0: .FinishEntry() michael@0: .D32(0); // terminator michael@0: michael@0: PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zS", section); michael@0: michael@0: EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zS", 57)) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, SignalHandler()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: EXPECT_CALL(handler, End()) michael@0: .InSequence(s).WillOnce(Return(true)); michael@0: michael@0: ParseEHFrameSection(§ion); michael@0: } michael@0: michael@0: // These tests require manual inspection of the test output. michael@0: struct CFIReporterFixture { michael@0: CFIReporterFixture() : reporter("test file name", "test section name") { } michael@0: CallFrameInfo::Reporter reporter; michael@0: }; michael@0: michael@0: class CFIReporter: public CFIReporterFixture, public Test { }; michael@0: michael@0: TEST_F(CFIReporter, Incomplete) { michael@0: reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, EarlyEHTerminator) { michael@0: reporter.EarlyEHTerminator(0x0102030405060708ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, CIEPointerOutOfRange) { michael@0: reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, BadCIEId) { michael@0: reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, UnrecognizedVersion) { michael@0: reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, UnrecognizedAugmentation) { michael@0: reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles"); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, InvalidPointerEncoding) { michael@0: reporter.InvalidPointerEncoding(0x0123456789abcdefULL, 0x42); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, UnusablePointerEncoding) { michael@0: reporter.UnusablePointerEncoding(0x0123456789abcdefULL, 0x42); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, RestoreInCIE) { michael@0: reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, BadInstruction) { michael@0: reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE, michael@0: 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, NoCFARule) { michael@0: reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE, michael@0: 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, EmptyStateStack) { michael@0: reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kTerminator, michael@0: 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: TEST_F(CFIReporter, ClearingCFARule) { michael@0: reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE, michael@0: 0xfedcba9876543210ULL); michael@0: } michael@0: michael@0: #ifdef WRITE_ELF michael@0: // See comments at the top of the file mentioning WRITE_ELF for details. michael@0: michael@0: using google_breakpad::test_assembler::Section; michael@0: michael@0: struct ELFSectionHeader { michael@0: ELFSectionHeader(unsigned int set_type) michael@0: : type(set_type), flags(0), address(0), link(0), info(0), michael@0: alignment(1), entry_size(0) { } michael@0: Label name; michael@0: unsigned int type; michael@0: uint64_t flags; michael@0: uint64_t address; michael@0: Label file_offset; michael@0: Label file_size; michael@0: unsigned int link; michael@0: unsigned int info; michael@0: uint64_t alignment; michael@0: uint64_t entry_size; michael@0: }; michael@0: michael@0: void AppendSectionHeader(CFISection *table, const ELFSectionHeader &header) { michael@0: (*table) michael@0: .D32(header.name) // name, index in string tbl michael@0: .D32(header.type) // type michael@0: .Address(header.flags) // flags michael@0: .Address(header.address) // address in memory michael@0: .Address(header.file_offset) // offset in ELF file michael@0: .Address(header.file_size) // length in bytes michael@0: .D32(header.link) // link to related section michael@0: .D32(header.info) // miscellaneous michael@0: .Address(header.alignment) // alignment michael@0: .Address(header.entry_size); // entry size michael@0: } michael@0: michael@0: void WriteELFFrameSection(const char *filename, const char *cfi_name, michael@0: const CFISection &cfi) { michael@0: int elf_class = cfi.AddressSize() == 4 ? ELFCLASS32 : ELFCLASS64; michael@0: int elf_data = (cfi.endianness() == kBigEndian michael@0: ? ELFDATA2MSB : ELFDATA2LSB); michael@0: CFISection elf(cfi.endianness(), cfi.AddressSize()); michael@0: Label elf_header_size, section_table_offset; michael@0: elf michael@0: .Append("\x7f" "ELF") michael@0: .D8(elf_class) // 32-bit or 64-bit ELF michael@0: .D8(elf_data) // endianness michael@0: .D8(1) // ELF version michael@0: .D8(ELFOSABI_LINUX) // Operating System/ABI indication michael@0: .D8(0) // ABI version michael@0: .Append(7, 0xda) // padding michael@0: .D16(ET_EXEC) // file type: executable file michael@0: .D16(EM_386) // architecture: Intel IA-32 michael@0: .D32(EV_CURRENT); // ELF version michael@0: elf michael@0: .Address(0x0123456789abcdefULL) // program entry point michael@0: .Address(0) // program header offset michael@0: .Address(section_table_offset) // section header offset michael@0: .D32(0) // processor-specific flags michael@0: .D16(elf_header_size) // ELF header size in bytes */ michael@0: .D16(elf_class == ELFCLASS32 ? 32 : 56) // program header entry size michael@0: .D16(0) // program header table entry count michael@0: .D16(elf_class == ELFCLASS32 ? 40 : 64) // section header entry size michael@0: .D16(3) // section count michael@0: .D16(1) // section name string table michael@0: .Mark(&elf_header_size); michael@0: michael@0: // The null section. Every ELF file has one, as the first entry in michael@0: // the section header table. michael@0: ELFSectionHeader null_header(SHT_NULL); michael@0: null_header.file_offset = 0; michael@0: null_header.file_size = 0; michael@0: michael@0: // The CFI section. The whole reason for writing out this ELF file michael@0: // is to put this in it so that we can run other dumping programs on michael@0: // it to check its contents. michael@0: ELFSectionHeader cfi_header(SHT_PROGBITS); michael@0: cfi_header.file_size = cfi.Size(); michael@0: michael@0: // The section holding the names of the sections. This is the michael@0: // section whose index appears in the e_shstrndx member of the ELF michael@0: // header. michael@0: ELFSectionHeader section_names_header(SHT_STRTAB); michael@0: CFISection section_names(cfi.endianness(), cfi.AddressSize()); michael@0: section_names michael@0: .Mark(&null_header.name) michael@0: .AppendCString("") michael@0: .Mark(§ion_names_header.name) michael@0: .AppendCString(".shstrtab") michael@0: .Mark(&cfi_header.name) michael@0: .AppendCString(cfi_name) michael@0: .Mark(§ion_names_header.file_size); michael@0: michael@0: // Create the section table. The ELF header's e_shoff member refers michael@0: // to this, and the e_shnum member gives the number of entries it michael@0: // contains. michael@0: CFISection section_table(cfi.endianness(), cfi.AddressSize()); michael@0: AppendSectionHeader(§ion_table, null_header); michael@0: AppendSectionHeader(§ion_table, section_names_header); michael@0: AppendSectionHeader(§ion_table, cfi_header); michael@0: michael@0: // Append the section table and the section contents to the ELF file. michael@0: elf michael@0: .Mark(§ion_table_offset) michael@0: .Append(section_table) michael@0: .Mark(§ion_names_header.file_offset) michael@0: .Append(section_names) michael@0: .Mark(&cfi_header.file_offset) michael@0: .Append(cfi); michael@0: michael@0: string contents; michael@0: if (!elf.GetContents(&contents)) { michael@0: fprintf(stderr, "failed to get ELF file contents\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: FILE *out = fopen(filename, "w"); michael@0: if (!out) { michael@0: fprintf(stderr, "error opening ELF file '%s': %s\n", michael@0: filename, strerror(errno)); michael@0: exit(1); michael@0: } michael@0: michael@0: if (fwrite(contents.data(), 1, contents.size(), out) != contents.size()) { michael@0: fprintf(stderr, "error writing ELF data to '%s': %s\n", michael@0: filename, strerror(errno)); michael@0: exit(1); michael@0: } michael@0: michael@0: if (fclose(out) == EOF) { michael@0: fprintf(stderr, "error closing ELF file '%s': %s\n", michael@0: filename, strerror(errno)); michael@0: exit(1); michael@0: } michael@0: } michael@0: #endif