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