// Copyright (c) 2011 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Original author: Ted Mielczarek <ted.mielczarek@gmail.com> // elf_symbols_to_module_unittest.cc: // Unittests for google_breakpad::ELFSymbolsToModule #include <elf.h> #include <string> #include <vector> #include "breakpad_googletest_includes.h" #include "common/linux/elf_symbols_to_module.h" #include "common/linux/synth_elf.h" #include "common/module.h" #include "common/test_assembler.h" #include "common/using_std_string.h" using google_breakpad::Module; using google_breakpad::synth_elf::StringTable; using google_breakpad::test_assembler::Endianness; using google_breakpad::test_assembler::kBigEndian; using google_breakpad::test_assembler::kLittleEndian; using google_breakpad::test_assembler::Label; using google_breakpad::test_assembler::Section; using ::testing::Test; using ::testing::TestWithParam; using std::vector; class ELFSymbolsToModuleTestFixture { public: ELFSymbolsToModuleTestFixture(Endianness endianness, size_t value_size) : module("a", "b", "c", "d"), section(endianness), table(endianness), value_size(value_size) {} bool ProcessSection() { string section_contents, table_contents; section.GetContents(§ion_contents); table.GetContents(&table_contents); bool ret = ELFSymbolsToModule(reinterpret_cast<const uint8_t*>(section_contents.data()), section_contents.size(), reinterpret_cast<const uint8_t*>(table_contents.data()), table_contents.size(), section.endianness() == kBigEndian, value_size, &module); module.GetExterns(&externs, externs.end()); return ret; } Module module; Section section; StringTable table; string section_contents; // 4 or 8 (bytes) size_t value_size; vector<Module::Extern *> externs; }; class ELFSymbolsToModuleTest32 : public ELFSymbolsToModuleTestFixture, public TestWithParam<Endianness> { public: ELFSymbolsToModuleTest32() : ELFSymbolsToModuleTestFixture(GetParam(), 4) {} void AddElf32Sym(const string& name, uint32_t value, uint32_t size, unsigned info, uint16_t shndx) { section .D32(table.Add(name)) .D32(value) .D32(size) .D8(info) .D8(0) // other .D16(shndx); } }; TEST_P(ELFSymbolsToModuleTest32, NoFuncs) { ProcessSection(); ASSERT_EQ((size_t)0, externs.size()); } TEST_P(ELFSymbolsToModuleTest32, OneFunc) { const string kFuncName = "superfunc"; const uint32_t kFuncAddr = 0x1000; const uint32_t kFuncSize = 0x10; AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 1); ProcessSection(); ASSERT_EQ((size_t)1, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); } TEST_P(ELFSymbolsToModuleTest32, NameOutOfBounds) { const string kFuncName = ""; const uint32_t kFuncAddr = 0x1000; const uint32_t kFuncSize = 0x10; table.Add("Foo"); table.Add("Bar"); // Can't use AddElf32Sym because it puts in a valid string offset. section .D32((uint32_t)table.Here().Value() + 1) .D32(kFuncAddr) .D32(kFuncSize) .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) .D8(0) // other .D16(SHN_UNDEF + 1); ProcessSection(); ASSERT_EQ((size_t)1, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); } TEST_P(ELFSymbolsToModuleTest32, NonTerminatedStringTable) { const string kFuncName = ""; const uint32_t kFuncAddr = 0x1000; const uint32_t kFuncSize = 0x10; table.Add("Foo"); table.Add("Bar"); // Add a non-null-terminated string to the end of the string table Label l; table .Mark(&l) .Append("Unterminated"); // Can't use AddElf32Sym because it puts in a valid string offset. section .D32((uint32_t)l.Value()) .D32(kFuncAddr) .D32(kFuncSize) .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) .D8(0) // other .D16(SHN_UNDEF + 1); ProcessSection(); ASSERT_EQ((size_t)1, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); } TEST_P(ELFSymbolsToModuleTest32, MultipleFuncs) { const string kFuncName1 = "superfunc"; const uint32_t kFuncAddr1 = 0x10001000; const uint32_t kFuncSize1 = 0x10; const string kFuncName2 = "awesomefunc"; const uint32_t kFuncAddr2 = 0x20002000; const uint32_t kFuncSize2 = 0x2f; const string kFuncName3 = "megafunc"; const uint32_t kFuncAddr3 = 0x30003000; const uint32_t kFuncSize3 = 0x3c; AddElf32Sym(kFuncName1, kFuncAddr1, kFuncSize1, ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 1); AddElf32Sym(kFuncName2, kFuncAddr2, kFuncSize2, ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 2); AddElf32Sym(kFuncName3, kFuncAddr3, kFuncSize3, ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 3); ProcessSection(); ASSERT_EQ((size_t)3, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName1, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); Module::Extern *extern2 = externs[1]; EXPECT_EQ(kFuncName2, extern2->name); EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); Module::Extern *extern3 = externs[2]; EXPECT_EQ(kFuncName3, extern3->name); EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); } TEST_P(ELFSymbolsToModuleTest32, SkipStuff) { const string kFuncName = "superfunc"; const uint32_t kFuncAddr = 0x1000; const uint32_t kFuncSize = 0x10; // Should skip functions in SHN_UNDEF AddElf32Sym("skipme", 0xFFFF, 0x10, ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), SHN_UNDEF); AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 1); // Should skip non-STT_FUNC entries. AddElf32Sym("skipmetoo", 0xAAAA, 0x10, ELF32_ST_INFO(STB_GLOBAL, STT_FILE), SHN_UNDEF + 1); ProcessSection(); ASSERT_EQ((size_t)1, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); } // Run all the 32-bit tests with both endianness INSTANTIATE_TEST_CASE_P(Endian, ELFSymbolsToModuleTest32, ::testing::Values(kLittleEndian, kBigEndian)); // Similar tests, but with 64-bit values. Ostensibly this could be // shoehorned into the parameterization by using ::testing::Combine, // but that would make it difficult to get the types right since these // actual test cases aren't parameterized. This could also be written // as a type-parameterized test, but combining that with a value-parameterized // test seemed really ugly, and also makes it harder to test 64-bit // values. class ELFSymbolsToModuleTest64 : public ELFSymbolsToModuleTestFixture, public TestWithParam<Endianness> { public: ELFSymbolsToModuleTest64() : ELFSymbolsToModuleTestFixture(GetParam(), 8) {} void AddElf64Sym(const string& name, uint64_t value, uint64_t size, unsigned info, uint16_t shndx) { section .D32(table.Add(name)) .D8(info) .D8(0) // other .D16(shndx) .D64(value) .D64(size); } }; TEST_P(ELFSymbolsToModuleTest64, NoFuncs) { ProcessSection(); ASSERT_EQ((size_t)0, externs.size()); } TEST_P(ELFSymbolsToModuleTest64, OneFunc) { const string kFuncName = "superfunc"; const uint64_t kFuncAddr = 0x1000200030004000ULL; const uint64_t kFuncSize = 0x1000; AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 1); ProcessSection(); ASSERT_EQ((size_t)1, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); } TEST_P(ELFSymbolsToModuleTest64, MultipleFuncs) { const string kFuncName1 = "superfunc"; const uint64_t kFuncAddr1 = 0x1000100010001000ULL; const uint64_t kFuncSize1 = 0x1000; const string kFuncName2 = "awesomefunc"; const uint64_t kFuncAddr2 = 0x2000200020002000ULL; const uint64_t kFuncSize2 = 0x2f00; const string kFuncName3 = "megafunc"; const uint64_t kFuncAddr3 = 0x3000300030003000ULL; const uint64_t kFuncSize3 = 0x3c00; AddElf64Sym(kFuncName1, kFuncAddr1, kFuncSize1, ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 1); AddElf64Sym(kFuncName2, kFuncAddr2, kFuncSize2, ELF64_ST_INFO(STB_LOCAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 2); AddElf64Sym(kFuncName3, kFuncAddr3, kFuncSize3, ELF64_ST_INFO(STB_LOCAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 3); ProcessSection(); ASSERT_EQ((size_t)3, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName1, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); Module::Extern *extern2 = externs[1]; EXPECT_EQ(kFuncName2, extern2->name); EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); Module::Extern *extern3 = externs[2]; EXPECT_EQ(kFuncName3, extern3->name); EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); } TEST_P(ELFSymbolsToModuleTest64, SkipStuff) { const string kFuncName = "superfunc"; const uint64_t kFuncAddr = 0x1000100010001000ULL; const uint64_t kFuncSize = 0x1000; // Should skip functions in SHN_UNDEF AddElf64Sym("skipme", 0xFFFF, 0x10, ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), SHN_UNDEF); AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), // Doesn't really matter, just can't be SHN_UNDEF. SHN_UNDEF + 1); // Should skip non-STT_FUNC entries. AddElf64Sym("skipmetoo", 0xAAAA, 0x10, ELF64_ST_INFO(STB_GLOBAL, STT_FILE), SHN_UNDEF + 1); ProcessSection(); ASSERT_EQ((size_t)1, externs.size()); Module::Extern *extern1 = externs[0]; EXPECT_EQ(kFuncName, extern1->name); EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); } // Run all the 64-bit tests with both endianness INSTANTIATE_TEST_CASE_P(Endian, ELFSymbolsToModuleTest64, ::testing::Values(kLittleEndian, kBigEndian));