mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-11-13 09:04:59 +00:00
This patch adds synth_elf::{StringTable,SymbolTable,ELF} classes to
produce in-memory ELF files to properly test the Linux symbol dumping
code. It also uses those classes to add some basic tests for
the WriteSymbolFile function.
R=jimb at http://breakpad.appspot.com/277001/show
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@794 4c0a9323-5329-0410-9bdc-e9ce6186880e
371 lines
13 KiB
C++
371 lines
13 KiB
C++
// 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"
|
|
|
|
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::string;
|
|
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));
|