mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-01-01 04:04:32 +01:00
Add some unit tests for Linux WriteSymbolFile
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
This commit is contained in:
parent
b2f96f314c
commit
3ca4a120de
16 changed files with 958 additions and 228 deletions
|
|
@ -46,6 +46,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
|
@ -721,25 +722,32 @@ std::string BaseFileName(const std::string &filename) {
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &debug_dir, FILE *sym_file) {
|
||||
MmapWrapper map_wrapper;
|
||||
ElfW(Ehdr) *elf_header = NULL;
|
||||
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
|
||||
// Not explicitly exported, but not static so it can be used in unit tests.
|
||||
// Ideally obj_file would be const, but internally this code does write
|
||||
// to some ELF header fields to make its work simpler.
|
||||
bool WriteSymbolFileInternal(uint8_t* obj_file,
|
||||
const std::string &obj_filename,
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream) {
|
||||
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_file);
|
||||
|
||||
if (!IsValidElf(elf_header)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char identifier[16];
|
||||
google_breakpad::FileID file_id(obj_file.c_str());
|
||||
if (!file_id.ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
|
||||
if (!google_breakpad::FileID::ElfFileIdentifierFromMappedFile(elf_header,
|
||||
identifier)) {
|
||||
fprintf(stderr, "%s: unable to generate file identifier\n",
|
||||
obj_file.c_str());
|
||||
obj_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *architecture = ElfArchitecture(elf_header);
|
||||
if (!architecture) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
|
||||
obj_file.c_str(), elf_header->e_machine);
|
||||
obj_filename.c_str(), elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -748,13 +756,13 @@ bool WriteSymbolFile(const std::string &obj_file,
|
|||
if (!ElfEndianness(elf_header, &big_endian))
|
||||
return false;
|
||||
|
||||
std::string name = BaseFileName(obj_file);
|
||||
std::string name = BaseFileName(obj_filename);
|
||||
std::string os = "Linux";
|
||||
std::string id = FormatIdentifier(identifier);
|
||||
|
||||
LoadSymbolsInfo info(debug_dir);
|
||||
Module module(name, os, architecture, id);
|
||||
if (!LoadSymbols(obj_file, big_endian, elf_header, !debug_dir.empty(),
|
||||
if (!LoadSymbols(obj_filename, big_endian, elf_header, !debug_dir.empty(),
|
||||
&info, &module)) {
|
||||
const std::string debuglink_file = info.debuglink_file();
|
||||
if (debuglink_file.empty())
|
||||
|
|
@ -777,7 +785,7 @@ bool WriteSymbolFile(const std::string &obj_file,
|
|||
fprintf(stderr, "%s with ELF machine architecture %s does not match "
|
||||
"%s with ELF architecture %s\n",
|
||||
debuglink_file.c_str(), debug_architecture,
|
||||
obj_file.c_str(), architecture);
|
||||
obj_filename.c_str(), architecture);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -786,7 +794,7 @@ bool WriteSymbolFile(const std::string &obj_file,
|
|||
return false;
|
||||
if (debug_big_endian != big_endian) {
|
||||
fprintf(stderr, "%s and %s does not match in endianness\n",
|
||||
obj_file.c_str(), debuglink_file.c_str());
|
||||
obj_filename.c_str(), debuglink_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -795,10 +803,22 @@ bool WriteSymbolFile(const std::string &obj_file,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (!module.Write(sym_file))
|
||||
if (!module.Write(sym_stream))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream) {
|
||||
MmapWrapper map_wrapper;
|
||||
ElfW(Ehdr) *elf_header = NULL;
|
||||
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
|
||||
return false;
|
||||
|
||||
return WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(elf_header),
|
||||
obj_file, debug_dir, sym_stream);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
|||
|
|
@ -35,19 +35,19 @@
|
|||
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
#define COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Find all the debugging information in OBJ_FILE, an ELF executable
|
||||
// or shared library, and write it to SYM_FILE in the Breakpad symbol
|
||||
// or shared library, and write it to SYM_STREAM in the Breakpad symbol
|
||||
// file format.
|
||||
// If OBJ_FILE has been stripped but contains a .gnu_debuglink section,
|
||||
// then look for the debug file in DEBUG_DIR.
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &debug_dir, FILE *sym_file);
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
|
|
|||
162
src/common/linux/dump_symbols_unittest.cc
Normal file
162
src/common/linux/dump_symbols_unittest.cc
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// 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>
|
||||
|
||||
// dump_symbols_unittest.cc:
|
||||
// Unittests for google_breakpad::DumpSymbols
|
||||
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
bool WriteSymbolFileInternal(uint8_t* obj_file,
|
||||
const std::string &obj_filename,
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream);
|
||||
}
|
||||
|
||||
using google_breakpad::synth_elf::ELF;
|
||||
using google_breakpad::synth_elf::StringTable;
|
||||
using google_breakpad::synth_elf::SymbolTable;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using google_breakpad::WriteSymbolFileInternal;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::vector;
|
||||
using ::testing::Test;
|
||||
|
||||
class DumpSymbols : public Test {
|
||||
public:
|
||||
void GetElfContents(ELF& elf) {
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_LT(0, contents.size());
|
||||
|
||||
elfdata_v.clear();
|
||||
elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
|
||||
elfdata = &elfdata_v[0];
|
||||
}
|
||||
|
||||
vector<uint8_t> elfdata_v;
|
||||
uint8_t* elfdata;
|
||||
};
|
||||
|
||||
TEST_F(DumpSymbols, Invalid) {
|
||||
Elf32_Ehdr header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
stringstream s;
|
||||
EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(&header),
|
||||
"foo",
|
||||
"",
|
||||
s));
|
||||
}
|
||||
|
||||
// TODO(ted): Fix the dump_symbols code to deal with cross-word-size
|
||||
// ELF files.
|
||||
#if __ELF_NATIVE_CLASS == 32
|
||||
TEST_F(DumpSymbols, SimplePublic32) {
|
||||
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
|
||||
// Add a public symbol.
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 4, table);
|
||||
syms.AddSymbol("superfunc", (uint32_t)0x1000, (uint32_t)0x10,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
elf.AddSection(".dynsym", syms,
|
||||
SHT_DYNSYM, // type
|
||||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(Elf32_Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
GetElfContents(elf);
|
||||
|
||||
stringstream s;
|
||||
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
|
||||
"foo",
|
||||
"",
|
||||
s));
|
||||
EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __ELF_NATIVE_CLASS == 64
|
||||
TEST_F(DumpSymbols, SimplePublic64) {
|
||||
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
|
||||
// Add a public symbol.
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 8, table);
|
||||
syms.AddSymbol("superfunc", (uint64_t)0x1000, (uint64_t)0x10,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
elf.AddSection(".dynsym", syms,
|
||||
SHT_DYNSYM, // type
|
||||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(Elf64_Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
GetElfContents(elf);
|
||||
|
||||
stringstream s;
|
||||
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
|
||||
"foo",
|
||||
"",
|
||||
s));
|
||||
EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
}
|
||||
#endif
|
||||
|
|
@ -39,14 +39,15 @@
|
|||
|
||||
#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::kUnsetEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using ::testing::Test;
|
||||
|
|
@ -54,70 +55,6 @@ using ::testing::TestWithParam;
|
|||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// String Tables are used in ELF headers, add a class
|
||||
// for convenience.
|
||||
class StringTable : public Section {
|
||||
public:
|
||||
StringTable(Endianness endianness = kUnsetEndian)
|
||||
: Section(endianness) {
|
||||
start() = 0;
|
||||
empty_string = Add("");
|
||||
}
|
||||
|
||||
// Add the string s to the string table, and return
|
||||
// a label containing the offset into the string table
|
||||
// at which it was added.
|
||||
Label Add(const string& s) {
|
||||
Label string_label(Here());
|
||||
AppendCString(s);
|
||||
return string_label;
|
||||
}
|
||||
|
||||
// All StringTables contain an empty string as their first
|
||||
// entry.
|
||||
Label empty_string;
|
||||
};
|
||||
|
||||
class StringTableTest : public Test {
|
||||
public:
|
||||
StringTableTest() : table(kLittleEndian) {}
|
||||
|
||||
StringTable table;
|
||||
};
|
||||
|
||||
TEST_F(StringTableTest, Empty) {
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
const string kExpectedContents = "\0";
|
||||
EXPECT_EQ(0,
|
||||
memcmp(kExpectedContents.c_str(), contents.c_str(), table.Size()));
|
||||
ASSERT_TRUE(table.empty_string.IsKnownConstant());
|
||||
EXPECT_EQ(0, table.empty_string.Value());
|
||||
}
|
||||
|
||||
TEST_F(StringTableTest, Basic) {
|
||||
const string s1("table fills with strings");
|
||||
const string s2("offsets preserved as labels");
|
||||
const string s3("verified with tests");
|
||||
const string kExpectedContents =
|
||||
"\0table fills with strings\0"
|
||||
"offsets preserved as labels\0"
|
||||
"verified with tests\0";
|
||||
Label l1(table.Add(s1));
|
||||
Label l2(table.Add(s2));
|
||||
Label l3(table.Add(s3));
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
EXPECT_EQ(0,
|
||||
memcmp(kExpectedContents.c_str(), contents.c_str(), table.Size()));
|
||||
// empty_string is at zero, other strings start at 1.
|
||||
ASSERT_TRUE(l1.IsKnownConstant());
|
||||
EXPECT_EQ(1, l1.Value());
|
||||
// Each string has an extra byte for a trailing null.
|
||||
EXPECT_EQ(1 + s1.length() + 1, l2.Value());
|
||||
EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value());
|
||||
}
|
||||
|
||||
class ELFSymbolsToModuleTestFixture {
|
||||
public:
|
||||
ELFSymbolsToModuleTestFixture(Endianness endianness,
|
||||
|
|
|
|||
174
src/common/linux/synth_elf.cc
Normal file
174
src/common/linux/synth_elf.cc
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#include "common/linux/synth_elf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace synth_elf {
|
||||
|
||||
ELF::ELF(uint16_t machine,
|
||||
uint8_t file_class,
|
||||
Endianness endianness)
|
||||
: Section(endianness),
|
||||
addr_size_(file_class == ELFCLASS64 ? 8 : 4),
|
||||
program_count_(0),
|
||||
section_count_(0),
|
||||
section_header_table_(endianness),
|
||||
section_header_strings_(endianness) {
|
||||
// Could add support for more machine types here if needed.
|
||||
assert(machine == EM_386 ||
|
||||
machine == EM_X86_64 ||
|
||||
machine == EM_ARM);
|
||||
assert(file_class == ELFCLASS32 || file_class == ELFCLASS64);
|
||||
|
||||
start() = 0;
|
||||
// Add ELF header
|
||||
// e_ident
|
||||
// EI_MAG0...EI_MAG3
|
||||
D8(ELFMAG0);
|
||||
D8(ELFMAG1);
|
||||
D8(ELFMAG2);
|
||||
D8(ELFMAG3);
|
||||
// EI_CLASS
|
||||
D8(file_class);
|
||||
// EI_DATA
|
||||
D8(endianness == kLittleEndian ? ELFDATA2LSB : ELFDATA2MSB);
|
||||
// EI_VERSION
|
||||
D8(EV_CURRENT);
|
||||
// EI_OSABI
|
||||
D8(ELFOSABI_SYSV);
|
||||
// EI_ABIVERSION
|
||||
D8(0);
|
||||
// EI_PAD
|
||||
Append(7, 0);
|
||||
assert(Size() == EI_NIDENT);
|
||||
|
||||
// e_type
|
||||
D16(ET_EXEC); //TODO: allow passing ET_DYN?
|
||||
// e_machine
|
||||
D16(machine);
|
||||
// e_version
|
||||
D32(EV_CURRENT);
|
||||
// e_entry
|
||||
Append(endianness, addr_size_, 0);
|
||||
// e_phoff
|
||||
Append(endianness, addr_size_, program_header_label_);
|
||||
// e_shoff
|
||||
Append(endianness, addr_size_, section_header_label_);
|
||||
// e_flags
|
||||
D32(0);
|
||||
// e_ehsize
|
||||
D16(addr_size_ == 8 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr));
|
||||
// e_phentsize
|
||||
D16(addr_size_ == 8 ? sizeof(Elf64_Phdr) : sizeof(Elf32_Phdr));
|
||||
// e_phnum
|
||||
D16(program_count_label_);
|
||||
// e_shentsize
|
||||
D16(addr_size_ == 8 ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr));
|
||||
// e_shnum
|
||||
D16(section_count_label_);
|
||||
// e_shstrndx
|
||||
D16(section_header_string_index_);
|
||||
|
||||
// Add an empty section for SHN_UNDEF.
|
||||
Section shn_undef;
|
||||
AddSection("", shn_undef, SHT_NULL);
|
||||
}
|
||||
|
||||
int ELF::AddSection(const string& name, const Section& section,
|
||||
uint32_t type, uint32_t flags, uint64_t addr,
|
||||
uint32_t link, uint64_t entsize, uint64_t offset) {
|
||||
Label offset_label;
|
||||
Label string_label(section_header_strings_.Add(name));
|
||||
size_t size = section.Size();
|
||||
|
||||
int index = section_count_;
|
||||
++section_count_;
|
||||
|
||||
section_header_table_
|
||||
// sh_name
|
||||
.D32(string_label)
|
||||
// sh_type
|
||||
.D32(type)
|
||||
// sh_flags
|
||||
.Append(endianness(), addr_size_, flags)
|
||||
// sh_addr
|
||||
.Append(endianness(), addr_size_, addr)
|
||||
// sh_offset
|
||||
.Append(endianness(), addr_size_, offset_label)
|
||||
// sh_size
|
||||
.Append(endianness(), addr_size_, size)
|
||||
// sh_link
|
||||
.D32(link)
|
||||
// sh_info
|
||||
.D32(0)
|
||||
// sh_addralign
|
||||
.Append(endianness(), addr_size_, 0)
|
||||
// sh_entsize
|
||||
.Append(endianness(), addr_size_, entsize);
|
||||
|
||||
// NULL and NOBITS sections have no content, so they
|
||||
// don't need to be written to the file.
|
||||
if (type == SHT_NULL) {
|
||||
offset_label = 0;
|
||||
} else if (type == SHT_NOBITS) {
|
||||
offset_label = offset;
|
||||
} else {
|
||||
Mark(&offset_label);
|
||||
Append(section);
|
||||
Align(4);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void ELF::Finish() {
|
||||
// Add the section header string table at the end.
|
||||
section_header_string_index_ = section_count_;
|
||||
//printf(".shstrtab size: %ld\n", section_header_strings_.Size());
|
||||
AddSection(".shstrtab", section_header_strings_, SHT_STRTAB);
|
||||
//printf("section_count_: %ld, sections_.size(): %ld\n",
|
||||
// section_count_, sections_.size());
|
||||
section_count_label_ = section_count_;
|
||||
program_count_label_ = program_count_;
|
||||
// TODO: allow adding entries to program header table
|
||||
program_header_label_ = 0;
|
||||
|
||||
// Section header table starts here.
|
||||
Mark(§ion_header_label_);
|
||||
Append(section_header_table_);
|
||||
}
|
||||
|
||||
SymbolTable::SymbolTable(Endianness endianness,
|
||||
size_t addr_size,
|
||||
StringTable& table) : Section(endianness),
|
||||
addr_size_(addr_size),
|
||||
table_(table) {
|
||||
assert(addr_size_ == 4 || addr_size_ == 8);
|
||||
}
|
||||
|
||||
void SymbolTable::AddSymbol(const string& name, uint32_t value,
|
||||
uint32_t size, unsigned info, uint16_t shndx) {
|
||||
assert(addr_size_ == 4);
|
||||
D32(table_.Add(name));
|
||||
D32(value);
|
||||
D32(size);
|
||||
D8(info);
|
||||
D8(0); // other
|
||||
D16(shndx);
|
||||
}
|
||||
|
||||
void SymbolTable::AddSymbol(const string& name, uint64_t value,
|
||||
uint64_t size, unsigned info, uint16_t shndx) {
|
||||
assert(addr_size_ == 8);
|
||||
D32(table_.Add(name));
|
||||
D8(info);
|
||||
D8(0); // other
|
||||
D16(shndx);
|
||||
D64(value);
|
||||
D64(size);
|
||||
}
|
||||
|
||||
} // namespace synth_elf
|
||||
} // namespace google_breakpad
|
||||
|
||||
155
src/common/linux/synth_elf.h
Normal file
155
src/common/linux/synth_elf.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// -*- mode: 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>
|
||||
|
||||
// synth_elf.h: Interface to synth_elf::ELF: fake ELF generator.
|
||||
|
||||
#ifndef COMMON_LINUX_SYNTH_ELF_H_
|
||||
#define COMMON_LINUX_SYNTH_ELF_H_
|
||||
|
||||
#include "common/test_assembler.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace synth_elf {
|
||||
|
||||
using std::list;
|
||||
using std::map;
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using test_assembler::Endianness;
|
||||
using test_assembler::kLittleEndian;
|
||||
using test_assembler::kUnsetEndian;
|
||||
using test_assembler::Label;
|
||||
using test_assembler::Section;
|
||||
|
||||
// String tables are common in ELF headers, so subclass Section
|
||||
// to make them easy to generate.
|
||||
class StringTable : public Section {
|
||||
public:
|
||||
StringTable(Endianness endianness = kUnsetEndian)
|
||||
: Section(endianness) {
|
||||
start() = 0;
|
||||
empty_string = Add("");
|
||||
}
|
||||
|
||||
// Add the string s to the string table, and return
|
||||
// a label containing the offset into the string table
|
||||
// at which it was added.
|
||||
Label Add(const string& s) {
|
||||
if (strings_.find(s) != strings_.end())
|
||||
return strings_[s];
|
||||
|
||||
Label string_label(Here());
|
||||
AppendCString(s);
|
||||
strings_[s] = string_label;
|
||||
return string_label;
|
||||
}
|
||||
|
||||
// All StringTables contain an empty string as their first
|
||||
// entry.
|
||||
Label empty_string;
|
||||
|
||||
// Avoid inserting duplicate strings.
|
||||
map<string,Label> strings_;
|
||||
};
|
||||
|
||||
// A Section representing an entire ELF file.
|
||||
class ELF : public Section {
|
||||
public:
|
||||
ELF(uint16_t machine, // EM_386, etc
|
||||
uint8_t file_class, // ELFCLASS{32,64}
|
||||
Endianness endianness = kLittleEndian);
|
||||
|
||||
// Add the Section section to the section header table and append it
|
||||
// to the file. Returns the index of the section in the section
|
||||
// header table.
|
||||
int AddSection(const string& name, const Section& section,
|
||||
uint32_t type, uint32_t flags = 0, uint64_t addr = 0,
|
||||
uint32_t link = 0, uint64_t entsize = 0, uint64_t offset = 0);
|
||||
|
||||
// Write out all data. GetContents may be used after this.
|
||||
void Finish();
|
||||
|
||||
private:
|
||||
// Size of an address, in bytes.
|
||||
const size_t addr_size_;
|
||||
|
||||
// Offset to the program header table.
|
||||
Label program_header_label_;
|
||||
// Number of entries in the program header table.
|
||||
int program_count_;
|
||||
Label program_count_label_;
|
||||
|
||||
// Offset to the section header table.
|
||||
Label section_header_label_;
|
||||
// Number of entries in the section header table.
|
||||
int section_count_;
|
||||
Label section_count_label_;
|
||||
// The section header table itself.
|
||||
Section section_header_table_;
|
||||
|
||||
// Index of the section header string table in the section
|
||||
// header table.
|
||||
Label section_header_string_index_;
|
||||
// Section containing the names of section header table entries.
|
||||
StringTable section_header_strings_;
|
||||
};
|
||||
|
||||
// A class to build .symtab or .dynsym sections.
|
||||
class SymbolTable : public Section {
|
||||
public:
|
||||
// table is the StringTable that contains symbol names. The caller
|
||||
// must ensure that it remains alive for the life of the
|
||||
// SymbolTable.
|
||||
SymbolTable(Endianness endianness, size_t addr_size, StringTable& table);
|
||||
|
||||
// Add an Elf32_Sym.
|
||||
void AddSymbol(const string& name, uint32_t value,
|
||||
uint32_t size, unsigned info, uint16_t shndx);
|
||||
// Add an Elf64_Sym.
|
||||
void AddSymbol(const string& name, uint64_t value,
|
||||
uint64_t size, unsigned info, uint16_t shndx);
|
||||
|
||||
private:
|
||||
size_t addr_size_;
|
||||
StringTable& table_;
|
||||
};
|
||||
|
||||
} // namespace synth_elf
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_SYNTH_ELF_H_
|
||||
265
src/common/linux/synth_elf_unittest.cc
Normal file
265
src/common/linux/synth_elf_unittest.cc
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
// 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>
|
||||
|
||||
// synth_elf_unittest.cc:
|
||||
// Unittests for google_breakpad::synth_elf::ELF
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
|
||||
using google_breakpad::synth_elf::ELF;
|
||||
using google_breakpad::synth_elf::StringTable;
|
||||
using google_breakpad::synth_elf::SymbolTable;
|
||||
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 std::string;
|
||||
using ::testing::Test;
|
||||
|
||||
class StringTableTest : public Test {
|
||||
public:
|
||||
StringTableTest() : table(kLittleEndian) {}
|
||||
|
||||
StringTable table;
|
||||
};
|
||||
|
||||
TEST_F(StringTableTest, Empty) {
|
||||
EXPECT_EQ(1, table.Size());
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
const char* kExpectedContents = "\0";
|
||||
EXPECT_EQ(0, memcmp(kExpectedContents,
|
||||
contents.c_str(),
|
||||
contents.size()));
|
||||
ASSERT_TRUE(table.empty_string.IsKnownConstant());
|
||||
EXPECT_EQ(0, table.empty_string.Value());
|
||||
}
|
||||
|
||||
TEST_F(StringTableTest, Basic) {
|
||||
const string s1("table fills with strings");
|
||||
const string s2("offsets preserved as labels");
|
||||
const string s3("verified with tests");
|
||||
const char* kExpectedContents =
|
||||
"\0table fills with strings\0"
|
||||
"offsets preserved as labels\0"
|
||||
"verified with tests\0";
|
||||
Label l1(table.Add(s1));
|
||||
Label l2(table.Add(s2));
|
||||
Label l3(table.Add(s3));
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
EXPECT_EQ(0, memcmp(kExpectedContents,
|
||||
contents.c_str(),
|
||||
contents.size()));
|
||||
// empty_string is at zero, other strings start at 1.
|
||||
ASSERT_TRUE(l1.IsKnownConstant());
|
||||
EXPECT_EQ(1, l1.Value());
|
||||
// Each string has an extra byte for a trailing null.
|
||||
EXPECT_EQ(1 + s1.length() + 1, l2.Value());
|
||||
EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value());
|
||||
}
|
||||
|
||||
TEST_F(StringTableTest, Duplicates) {
|
||||
const string s1("string 1");
|
||||
const string s2("string 2");
|
||||
const string s3("");
|
||||
const char* kExpectedContents = "\0string 1\0string 2\0";
|
||||
Label l1(table.Add(s1));
|
||||
Label l2(table.Add(s2));
|
||||
// Adding strings twice should return the same Label.
|
||||
Label l3(table.Add(s3));
|
||||
Label l4(table.Add(s2));
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
EXPECT_EQ(0, memcmp(kExpectedContents,
|
||||
contents.c_str(),
|
||||
contents.size()));
|
||||
EXPECT_EQ(0, table.empty_string.Value());
|
||||
EXPECT_EQ(table.empty_string.Value(), l3.Value());
|
||||
EXPECT_EQ(l2.Value(), l4.Value());
|
||||
}
|
||||
|
||||
class SymbolTableTest : public Test {};
|
||||
|
||||
TEST_F(SymbolTableTest, Simple32) {
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 4, table);
|
||||
|
||||
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;
|
||||
|
||||
syms.AddSymbol(kFuncName1, kFuncAddr1, kFuncSize1,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
syms.AddSymbol(kFuncName2, kFuncAddr2, kFuncSize2,
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
SHN_UNDEF + 2);
|
||||
syms.AddSymbol(kFuncName3, kFuncAddr3, kFuncSize3,
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
SHN_UNDEF + 3);
|
||||
|
||||
const char kExpectedStringTable[] = "\0superfunc\0awesomefunc\0megafunc";
|
||||
const size_t kExpectedStringTableSize = sizeof(kExpectedStringTable);
|
||||
EXPECT_EQ(kExpectedStringTableSize, table.Size());
|
||||
string table_contents;
|
||||
table.GetContents(&table_contents);
|
||||
EXPECT_EQ(0, memcmp(kExpectedStringTable,
|
||||
table_contents.c_str(),
|
||||
table_contents.size()));
|
||||
|
||||
const uint8_t kExpectedSymbolContents[] = {
|
||||
// Symbol 1
|
||||
0x01, 0x00, 0x00, 0x00, // name
|
||||
0x00, 0x10, 0x00, 0x10, // value
|
||||
0x10, 0x00, 0x00, 0x00, // size
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // info
|
||||
0x00, // other
|
||||
0x01, 0x00, // shndx
|
||||
// Symbol 2
|
||||
0x0B, 0x00, 0x00, 0x00, // name
|
||||
0x00, 0x20, 0x00, 0x20, // value
|
||||
0x2f, 0x00, 0x00, 0x00, // size
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info
|
||||
0x00, // other
|
||||
0x02, 0x00, // shndx
|
||||
// Symbol 3
|
||||
0x17, 0x00, 0x00, 0x00, // name
|
||||
0x00, 0x30, 0x00, 0x30, // value
|
||||
0x3c, 0x00, 0x00, 0x00, // size
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info
|
||||
0x00, // other
|
||||
0x03, 0x00, // shndx
|
||||
};
|
||||
const size_t kExpectedSymbolSize = sizeof(kExpectedSymbolContents);
|
||||
EXPECT_EQ(kExpectedSymbolSize, syms.Size());
|
||||
|
||||
string symbol_contents;
|
||||
syms.GetContents(&symbol_contents);
|
||||
EXPECT_EQ(0, memcmp(kExpectedSymbolContents,
|
||||
symbol_contents.c_str(),
|
||||
symbol_contents.size()));
|
||||
}
|
||||
|
||||
class BasicElf : public Test {};
|
||||
|
||||
// Doesn't seem worthwhile writing the tests to be endian-independent
|
||||
// when they're unlikely to ever be run on big-endian systems.
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
TEST_F(BasicElf, EmptyLE32) {
|
||||
const size_t kStringTableSize = sizeof("\0.shstrtab");
|
||||
const size_t kStringTableAlign = 4 - kStringTableSize % 4;
|
||||
const size_t kExpectedSize = sizeof(Elf32_Ehdr) +
|
||||
// Two sections, SHT_NULL + the section header string table.
|
||||
2 * sizeof(Elf32_Shdr) +
|
||||
kStringTableSize + kStringTableAlign;
|
||||
|
||||
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
|
||||
elf.Finish();
|
||||
EXPECT_EQ(kExpectedSize, elf.Size());
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_EQ(kExpectedSize, contents.size());
|
||||
const Elf32_Ehdr* header =
|
||||
reinterpret_cast<const Elf32_Ehdr*>(contents.data());
|
||||
const uint8_t kIdent[] = {
|
||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent)));
|
||||
EXPECT_EQ(ET_EXEC, header->e_type);
|
||||
EXPECT_EQ(EM_386, header->e_machine);
|
||||
EXPECT_EQ(EV_CURRENT, header->e_version);
|
||||
EXPECT_EQ(0, header->e_entry);
|
||||
EXPECT_EQ(0, header->e_phoff);
|
||||
EXPECT_EQ(sizeof(Elf32_Ehdr) + kStringTableSize + kStringTableAlign,
|
||||
header->e_shoff);
|
||||
EXPECT_EQ(0, header->e_flags);
|
||||
EXPECT_EQ(sizeof(Elf32_Ehdr), header->e_ehsize);
|
||||
EXPECT_EQ(sizeof(Elf32_Phdr), header->e_phentsize);
|
||||
EXPECT_EQ(0, header->e_phnum);
|
||||
EXPECT_EQ(sizeof(Elf32_Shdr), header->e_shentsize);
|
||||
EXPECT_EQ(2, header->e_shnum);
|
||||
EXPECT_EQ(1, header->e_shstrndx);
|
||||
}
|
||||
|
||||
TEST_F(BasicElf, EmptyLE64) {
|
||||
const size_t kStringTableSize = sizeof("\0.shstrtab");
|
||||
const size_t kStringTableAlign = 4 - kStringTableSize % 4;
|
||||
const size_t kExpectedSize = sizeof(Elf64_Ehdr) +
|
||||
// Two sections, SHT_NULL + the section header string table.
|
||||
2 * sizeof(Elf64_Shdr) +
|
||||
kStringTableSize + kStringTableAlign;
|
||||
|
||||
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
elf.Finish();
|
||||
EXPECT_EQ(kExpectedSize, elf.Size());
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_EQ(kExpectedSize, contents.size());
|
||||
const Elf64_Ehdr* header =
|
||||
reinterpret_cast<const Elf64_Ehdr*>(contents.data());
|
||||
const uint8_t kIdent[] = {
|
||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||
ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent)));
|
||||
EXPECT_EQ(ET_EXEC, header->e_type);
|
||||
EXPECT_EQ(EM_X86_64, header->e_machine);
|
||||
EXPECT_EQ(EV_CURRENT, header->e_version);
|
||||
EXPECT_EQ(0, header->e_entry);
|
||||
EXPECT_EQ(0, header->e_phoff);
|
||||
EXPECT_EQ(sizeof(Elf64_Ehdr) + kStringTableSize + kStringTableAlign,
|
||||
header->e_shoff);
|
||||
EXPECT_EQ(0, header->e_flags);
|
||||
EXPECT_EQ(sizeof(Elf64_Ehdr), header->e_ehsize);
|
||||
EXPECT_EQ(sizeof(Elf64_Phdr), header->e_phentsize);
|
||||
EXPECT_EQ(0, header->e_phnum);
|
||||
EXPECT_EQ(sizeof(Elf64_Shdr), header->e_shentsize);
|
||||
EXPECT_EQ(2, header->e_shnum);
|
||||
EXPECT_EQ(1, header->e_shstrndx);
|
||||
}
|
||||
|
||||
#endif // defined(__i386__) || defined(__x86_64__)
|
||||
Loading…
Add table
Add a link
Reference in a new issue