michael@0: // Copyright (c) 2011 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: Ted Mielczarek michael@0: michael@0: // synth_elf_unittest.cc: michael@0: // Unittests for google_breakpad::synth_elf::ELF michael@0: michael@0: #include michael@0: michael@0: #include "breakpad_googletest_includes.h" michael@0: #include "common/linux/elfutils.h" michael@0: #include "common/linux/synth_elf.h" michael@0: #include "common/using_std_string.h" michael@0: michael@0: using google_breakpad::ElfClass32; michael@0: using google_breakpad::ElfClass64; michael@0: using google_breakpad::synth_elf::ELF; michael@0: using google_breakpad::synth_elf::Notes; michael@0: using google_breakpad::synth_elf::Section; michael@0: using google_breakpad::synth_elf::StringTable; michael@0: using google_breakpad::synth_elf::SymbolTable; michael@0: using google_breakpad::test_assembler::Endianness; michael@0: using google_breakpad::test_assembler::kBigEndian; michael@0: using google_breakpad::test_assembler::kLittleEndian; michael@0: using google_breakpad::test_assembler::Label; michael@0: using ::testing::Test; michael@0: using ::testing::Types; michael@0: michael@0: class StringTableTest : public Test { michael@0: public: michael@0: StringTableTest() : table(kLittleEndian) {} michael@0: michael@0: StringTable table; michael@0: }; michael@0: michael@0: TEST_F(StringTableTest, Empty) { michael@0: EXPECT_EQ(1U, table.Size()); michael@0: string contents; michael@0: ASSERT_TRUE(table.GetContents(&contents)); michael@0: const char* kExpectedContents = "\0"; michael@0: EXPECT_EQ(0, memcmp(kExpectedContents, michael@0: contents.c_str(), michael@0: contents.size())); michael@0: ASSERT_TRUE(table.empty_string.IsKnownConstant()); michael@0: EXPECT_EQ(0U, table.empty_string.Value()); michael@0: } michael@0: michael@0: TEST_F(StringTableTest, Basic) { michael@0: const string s1("table fills with strings"); michael@0: const string s2("offsets preserved as labels"); michael@0: const string s3("verified with tests"); michael@0: const char* kExpectedContents = michael@0: "\0table fills with strings\0" michael@0: "offsets preserved as labels\0" michael@0: "verified with tests\0"; michael@0: Label l1(table.Add(s1)); michael@0: Label l2(table.Add(s2)); michael@0: Label l3(table.Add(s3)); michael@0: string contents; michael@0: ASSERT_TRUE(table.GetContents(&contents)); michael@0: EXPECT_EQ(0, memcmp(kExpectedContents, michael@0: contents.c_str(), michael@0: contents.size())); michael@0: // empty_string is at zero, other strings start at 1. michael@0: ASSERT_TRUE(l1.IsKnownConstant()); michael@0: EXPECT_EQ(1U, l1.Value()); michael@0: // Each string has an extra byte for a trailing null. michael@0: EXPECT_EQ(1 + s1.length() + 1, l2.Value()); michael@0: EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value()); michael@0: } michael@0: michael@0: TEST_F(StringTableTest, Duplicates) { michael@0: const string s1("string 1"); michael@0: const string s2("string 2"); michael@0: const string s3(""); michael@0: const char* kExpectedContents = "\0string 1\0string 2\0"; michael@0: Label l1(table.Add(s1)); michael@0: Label l2(table.Add(s2)); michael@0: // Adding strings twice should return the same Label. michael@0: Label l3(table.Add(s3)); michael@0: Label l4(table.Add(s2)); michael@0: string contents; michael@0: ASSERT_TRUE(table.GetContents(&contents)); michael@0: EXPECT_EQ(0, memcmp(kExpectedContents, michael@0: contents.c_str(), michael@0: contents.size())); michael@0: EXPECT_EQ(0U, table.empty_string.Value()); michael@0: EXPECT_EQ(table.empty_string.Value(), l3.Value()); michael@0: EXPECT_EQ(l2.Value(), l4.Value()); michael@0: } michael@0: michael@0: class SymbolTableTest : public Test {}; michael@0: michael@0: TEST_F(SymbolTableTest, Simple32) { michael@0: StringTable table(kLittleEndian); michael@0: SymbolTable syms(kLittleEndian, 4, table); michael@0: michael@0: const string kFuncName1 = "superfunc"; michael@0: const uint32_t kFuncAddr1 = 0x10001000; michael@0: const uint32_t kFuncSize1 = 0x10; michael@0: const string kFuncName2 = "awesomefunc"; michael@0: const uint32_t kFuncAddr2 = 0x20002000; michael@0: const uint32_t kFuncSize2 = 0x2f; michael@0: const string kFuncName3 = "megafunc"; michael@0: const uint32_t kFuncAddr3 = 0x30003000; michael@0: const uint32_t kFuncSize3 = 0x3c; michael@0: michael@0: syms.AddSymbol(kFuncName1, kFuncAddr1, kFuncSize1, michael@0: ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), michael@0: SHN_UNDEF + 1); michael@0: syms.AddSymbol(kFuncName2, kFuncAddr2, kFuncSize2, michael@0: ELF32_ST_INFO(STB_LOCAL, STT_FUNC), michael@0: SHN_UNDEF + 2); michael@0: syms.AddSymbol(kFuncName3, kFuncAddr3, kFuncSize3, michael@0: ELF32_ST_INFO(STB_LOCAL, STT_FUNC), michael@0: SHN_UNDEF + 3); michael@0: michael@0: const char kExpectedStringTable[] = "\0superfunc\0awesomefunc\0megafunc"; michael@0: const size_t kExpectedStringTableSize = sizeof(kExpectedStringTable); michael@0: EXPECT_EQ(kExpectedStringTableSize, table.Size()); michael@0: string table_contents; michael@0: table.GetContents(&table_contents); michael@0: EXPECT_EQ(0, memcmp(kExpectedStringTable, michael@0: table_contents.c_str(), michael@0: table_contents.size())); michael@0: michael@0: const uint8_t kExpectedSymbolContents[] = { michael@0: // Symbol 1 michael@0: 0x01, 0x00, 0x00, 0x00, // name michael@0: 0x00, 0x10, 0x00, 0x10, // value michael@0: 0x10, 0x00, 0x00, 0x00, // size michael@0: ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // info michael@0: 0x00, // other michael@0: 0x01, 0x00, // shndx michael@0: // Symbol 2 michael@0: 0x0B, 0x00, 0x00, 0x00, // name michael@0: 0x00, 0x20, 0x00, 0x20, // value michael@0: 0x2f, 0x00, 0x00, 0x00, // size michael@0: ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info michael@0: 0x00, // other michael@0: 0x02, 0x00, // shndx michael@0: // Symbol 3 michael@0: 0x17, 0x00, 0x00, 0x00, // name michael@0: 0x00, 0x30, 0x00, 0x30, // value michael@0: 0x3c, 0x00, 0x00, 0x00, // size michael@0: ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info michael@0: 0x00, // other michael@0: 0x03, 0x00, // shndx michael@0: }; michael@0: const size_t kExpectedSymbolSize = sizeof(kExpectedSymbolContents); michael@0: EXPECT_EQ(kExpectedSymbolSize, syms.Size()); michael@0: michael@0: string symbol_contents; michael@0: syms.GetContents(&symbol_contents); michael@0: EXPECT_EQ(0, memcmp(kExpectedSymbolContents, michael@0: symbol_contents.c_str(), michael@0: symbol_contents.size())); michael@0: } michael@0: michael@0: template michael@0: class BasicElf : public Test {}; michael@0: michael@0: // Doesn't seem worthwhile writing the tests to be endian-independent michael@0: // when they're unlikely to ever be run on big-endian systems. michael@0: #if defined(__i386__) || defined(__x86_64__) michael@0: michael@0: typedef Types ElfClasses; michael@0: michael@0: TYPED_TEST_CASE(BasicElf, ElfClasses); michael@0: michael@0: TYPED_TEST(BasicElf, EmptyLE) { michael@0: typedef typename TypeParam::Ehdr Ehdr; michael@0: typedef typename TypeParam::Phdr Phdr; michael@0: typedef typename TypeParam::Shdr Shdr; michael@0: const size_t kStringTableSize = sizeof("\0.shstrtab"); michael@0: const size_t kStringTableAlign = 4 - kStringTableSize % 4; michael@0: const size_t kExpectedSize = sizeof(Ehdr) + michael@0: // Two sections, SHT_NULL + the section header string table. michael@0: 2 * sizeof(Shdr) + michael@0: kStringTableSize + kStringTableAlign; michael@0: michael@0: // It doesn't really matter that the machine type is right for the class. michael@0: ELF elf(EM_386, TypeParam::kClass, kLittleEndian); michael@0: elf.Finish(); michael@0: EXPECT_EQ(kExpectedSize, elf.Size()); michael@0: michael@0: string contents; michael@0: ASSERT_TRUE(elf.GetContents(&contents)); michael@0: ASSERT_EQ(kExpectedSize, contents.size()); michael@0: const Ehdr* header = michael@0: reinterpret_cast(contents.data()); michael@0: const uint8_t kIdent[] = { michael@0: ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, michael@0: TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, michael@0: 0, 0, 0, 0, 0, 0, 0, 0 michael@0: }; michael@0: EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); michael@0: EXPECT_EQ(ET_EXEC, header->e_type); michael@0: EXPECT_EQ(EM_386, header->e_machine); michael@0: EXPECT_EQ(static_cast(EV_CURRENT), header->e_version); michael@0: EXPECT_EQ(0U, header->e_entry); michael@0: EXPECT_EQ(0U, header->e_phoff); michael@0: EXPECT_EQ(sizeof(Ehdr) + kStringTableSize + kStringTableAlign, michael@0: header->e_shoff); michael@0: EXPECT_EQ(0U, header->e_flags); michael@0: EXPECT_EQ(sizeof(Ehdr), header->e_ehsize); michael@0: EXPECT_EQ(sizeof(Phdr), header->e_phentsize); michael@0: EXPECT_EQ(0, header->e_phnum); michael@0: EXPECT_EQ(sizeof(Shdr), header->e_shentsize); michael@0: EXPECT_EQ(2, header->e_shnum); michael@0: EXPECT_EQ(1, header->e_shstrndx); michael@0: michael@0: const Shdr* shdr = michael@0: reinterpret_cast(contents.data() + header->e_shoff); michael@0: EXPECT_EQ(0U, shdr[0].sh_name); michael@0: EXPECT_EQ(static_cast(SHT_NULL), shdr[0].sh_type); michael@0: EXPECT_EQ(0U, shdr[0].sh_flags); michael@0: EXPECT_EQ(0U, shdr[0].sh_addr); michael@0: EXPECT_EQ(0U, shdr[0].sh_offset); michael@0: EXPECT_EQ(0U, shdr[0].sh_size); michael@0: EXPECT_EQ(0U, shdr[0].sh_link); michael@0: EXPECT_EQ(0U, shdr[0].sh_info); michael@0: EXPECT_EQ(0U, shdr[0].sh_addralign); michael@0: EXPECT_EQ(0U, shdr[0].sh_entsize); michael@0: michael@0: EXPECT_EQ(1U, shdr[1].sh_name); michael@0: EXPECT_EQ(static_cast(SHT_STRTAB), shdr[1].sh_type); michael@0: EXPECT_EQ(0U, shdr[1].sh_flags); michael@0: EXPECT_EQ(0U, shdr[1].sh_addr); michael@0: EXPECT_EQ(sizeof(Ehdr), shdr[1].sh_offset); michael@0: EXPECT_EQ(kStringTableSize, shdr[1].sh_size); michael@0: EXPECT_EQ(0U, shdr[1].sh_link); michael@0: EXPECT_EQ(0U, shdr[1].sh_info); michael@0: EXPECT_EQ(0U, shdr[1].sh_addralign); michael@0: EXPECT_EQ(0U, shdr[1].sh_entsize); michael@0: } michael@0: michael@0: TYPED_TEST(BasicElf, BasicLE) { michael@0: typedef typename TypeParam::Ehdr Ehdr; michael@0: typedef typename TypeParam::Phdr Phdr; michael@0: typedef typename TypeParam::Shdr Shdr; michael@0: const size_t kStringTableSize = sizeof("\0.text\0.bss\0.shstrtab"); michael@0: const size_t kStringTableAlign = 4 - kStringTableSize % 4; michael@0: const size_t kExpectedSize = sizeof(Ehdr) + michael@0: // Four sections, SHT_NULL + the section header string table + michael@0: // 4096 bytes of the size-aligned .text section + one program header. michael@0: sizeof(Phdr) + 4 * sizeof(Shdr) + 4096 + michael@0: kStringTableSize + kStringTableAlign; michael@0: michael@0: // It doesn't really matter that the machine type is right for the class. michael@0: ELF elf(EM_386, TypeParam::kClass, kLittleEndian); michael@0: Section text(kLittleEndian); michael@0: text.Append(4094, 0); michael@0: int text_idx = elf.AddSection(".text", text, SHT_PROGBITS); michael@0: Section bss(kLittleEndian); michael@0: bss.Append(16, 0); michael@0: int bss_idx = elf.AddSection(".bss", bss, SHT_NOBITS); michael@0: elf.AddSegment(text_idx, bss_idx, PT_LOAD); michael@0: elf.Finish(); michael@0: EXPECT_EQ(kExpectedSize, elf.Size()); michael@0: michael@0: string contents; michael@0: ASSERT_TRUE(elf.GetContents(&contents)); michael@0: ASSERT_EQ(kExpectedSize, contents.size()); michael@0: const Ehdr* header = michael@0: reinterpret_cast(contents.data()); michael@0: const uint8_t kIdent[] = { michael@0: ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, michael@0: TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, michael@0: 0, 0, 0, 0, 0, 0, 0, 0 michael@0: }; michael@0: EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); michael@0: EXPECT_EQ(ET_EXEC, header->e_type); michael@0: EXPECT_EQ(EM_386, header->e_machine); michael@0: EXPECT_EQ(static_cast(EV_CURRENT), header->e_version); michael@0: EXPECT_EQ(0U, header->e_entry); michael@0: EXPECT_EQ(sizeof(Ehdr), header->e_phoff); michael@0: EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096 + kStringTableSize + michael@0: kStringTableAlign, header->e_shoff); michael@0: EXPECT_EQ(0U, header->e_flags); michael@0: EXPECT_EQ(sizeof(Ehdr), header->e_ehsize); michael@0: EXPECT_EQ(sizeof(Phdr), header->e_phentsize); michael@0: EXPECT_EQ(1, header->e_phnum); michael@0: EXPECT_EQ(sizeof(Shdr), header->e_shentsize); michael@0: EXPECT_EQ(4, header->e_shnum); michael@0: EXPECT_EQ(3, header->e_shstrndx); michael@0: michael@0: const Shdr* shdr = michael@0: reinterpret_cast(contents.data() + header->e_shoff); michael@0: EXPECT_EQ(0U, shdr[0].sh_name); michael@0: EXPECT_EQ(static_cast(SHT_NULL), shdr[0].sh_type); michael@0: EXPECT_EQ(0U, shdr[0].sh_flags); michael@0: EXPECT_EQ(0U, shdr[0].sh_addr); michael@0: EXPECT_EQ(0U, shdr[0].sh_offset); michael@0: EXPECT_EQ(0U, shdr[0].sh_size); michael@0: EXPECT_EQ(0U, shdr[0].sh_link); michael@0: EXPECT_EQ(0U, shdr[0].sh_info); michael@0: EXPECT_EQ(0U, shdr[0].sh_addralign); michael@0: EXPECT_EQ(0U, shdr[0].sh_entsize); michael@0: michael@0: EXPECT_EQ(1U, shdr[1].sh_name); michael@0: EXPECT_EQ(static_cast(SHT_PROGBITS), shdr[1].sh_type); michael@0: EXPECT_EQ(0U, shdr[1].sh_flags); michael@0: EXPECT_EQ(0U, shdr[1].sh_addr); michael@0: EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), shdr[1].sh_offset); michael@0: EXPECT_EQ(4094U, shdr[1].sh_size); michael@0: EXPECT_EQ(0U, shdr[1].sh_link); michael@0: EXPECT_EQ(0U, shdr[1].sh_info); michael@0: EXPECT_EQ(0U, shdr[1].sh_addralign); michael@0: EXPECT_EQ(0U, shdr[1].sh_entsize); michael@0: michael@0: EXPECT_EQ(sizeof("\0.text"), shdr[2].sh_name); michael@0: EXPECT_EQ(static_cast(SHT_NOBITS), shdr[2].sh_type); michael@0: EXPECT_EQ(0U, shdr[2].sh_flags); michael@0: EXPECT_EQ(0U, shdr[2].sh_addr); michael@0: EXPECT_EQ(0U, shdr[2].sh_offset); michael@0: EXPECT_EQ(16U, shdr[2].sh_size); michael@0: EXPECT_EQ(0U, shdr[2].sh_link); michael@0: EXPECT_EQ(0U, shdr[2].sh_info); michael@0: EXPECT_EQ(0U, shdr[2].sh_addralign); michael@0: EXPECT_EQ(0U, shdr[2].sh_entsize); michael@0: michael@0: EXPECT_EQ(sizeof("\0.text\0.bss"), shdr[3].sh_name); michael@0: EXPECT_EQ(static_cast(SHT_STRTAB), shdr[3].sh_type); michael@0: EXPECT_EQ(0U, shdr[3].sh_flags); michael@0: EXPECT_EQ(0U, shdr[3].sh_addr); michael@0: EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096, shdr[3].sh_offset); michael@0: EXPECT_EQ(kStringTableSize, shdr[3].sh_size); michael@0: EXPECT_EQ(0U, shdr[3].sh_link); michael@0: EXPECT_EQ(0U, shdr[3].sh_info); michael@0: EXPECT_EQ(0U, shdr[3].sh_addralign); michael@0: EXPECT_EQ(0U, shdr[3].sh_entsize); michael@0: michael@0: const Phdr* phdr = michael@0: reinterpret_cast(contents.data() + header->e_phoff); michael@0: EXPECT_EQ(static_cast(PT_LOAD), phdr->p_type); michael@0: EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), phdr->p_offset); michael@0: EXPECT_EQ(0U, phdr->p_vaddr); michael@0: EXPECT_EQ(0U, phdr->p_paddr); michael@0: EXPECT_EQ(4096U, phdr->p_filesz); michael@0: EXPECT_EQ(4096U + 16U, phdr->p_memsz); michael@0: EXPECT_EQ(0U, phdr->p_flags); michael@0: EXPECT_EQ(0U, phdr->p_align); michael@0: } michael@0: michael@0: class ElfNotesTest : public Test {}; michael@0: michael@0: TEST_F(ElfNotesTest, Empty) { michael@0: Notes notes(kLittleEndian); michael@0: string contents; michael@0: ASSERT_TRUE(notes.GetContents(&contents)); michael@0: EXPECT_EQ(0U, contents.size()); michael@0: } michael@0: michael@0: TEST_F(ElfNotesTest, Notes) { michael@0: Notes notes(kLittleEndian); michael@0: notes.AddNote(1, "Linux", reinterpret_cast("\x42\x02\0\0"), michael@0: 4); michael@0: notes.AddNote(2, "a", reinterpret_cast("foobar"), michael@0: sizeof("foobar") - 1); michael@0: michael@0: const uint8_t kExpectedNotesContents[] = { michael@0: // Note 1 michael@0: 0x06, 0x00, 0x00, 0x00, // name size, including terminating zero michael@0: 0x04, 0x00, 0x00, 0x00, // desc size michael@0: 0x01, 0x00, 0x00, 0x00, // type michael@0: 'L', 'i', 'n', 'u', 'x', 0x00, 0x00, 0x00, // padded "Linux" michael@0: 0x42, 0x02, 0x00, 0x00, // desc michael@0: // Note 2 michael@0: 0x02, 0x00, 0x00, 0x00, // name size michael@0: 0x06, 0x00, 0x00, 0x00, // desc size michael@0: 0x02, 0x00, 0x00, 0x00, // type michael@0: 'a', 0x00, 0x00, 0x00, // padded "a" michael@0: 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, // padded "foobar" michael@0: }; michael@0: const size_t kExpectedNotesSize = sizeof(kExpectedNotesContents); michael@0: EXPECT_EQ(kExpectedNotesSize, notes.Size()); michael@0: michael@0: string notes_contents; michael@0: ASSERT_TRUE(notes.GetContents(¬es_contents)); michael@0: EXPECT_EQ(0, memcmp(kExpectedNotesContents, michael@0: notes_contents.data(), michael@0: notes_contents.size())); michael@0: } michael@0: michael@0: #endif // defined(__i386__) || defined(__x86_64__)