From 3ca4a120de8ec3f35e972e4b23f527bb8f65c479 Mon Sep 17 00:00:00 2001 From: "ted.mielczarek" Date: Wed, 6 Jul 2011 17:05:59 +0000 Subject: 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 --- src/common/dwarf_cu_to_module.cc | 1 + src/common/dwarf_line_to_module.cc | 2 + src/common/linux/dump_symbols.cc | 48 ++-- src/common/linux/dump_symbols.h | 8 +- src/common/linux/dump_symbols_unittest.cc | 162 +++++++++++++ src/common/linux/elf_symbols_to_module_unittest.cc | 67 +----- src/common/linux/synth_elf.cc | 174 ++++++++++++++ src/common/linux/synth_elf.h | 155 ++++++++++++ src/common/linux/synth_elf_unittest.cc | 265 +++++++++++++++++++++ src/common/module.cc | 88 ++++--- src/common/module.h | 7 +- src/common/module_unittest.cc | 136 +++-------- src/common/stabs_to_module.cc | 1 + 13 files changed, 887 insertions(+), 227 deletions(-) create mode 100644 src/common/linux/dump_symbols_unittest.cc create mode 100644 src/common/linux/synth_elf.cc create mode 100644 src/common/linux/synth_elf.h create mode 100644 src/common/linux/synth_elf_unittest.cc (limited to 'src/common') diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index 1922066a..2a8b76ba 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -40,6 +40,7 @@ #include #include +#include #include #include diff --git a/src/common/dwarf_line_to_module.cc b/src/common/dwarf_line_to_module.cc index 75afd93c..60922cb4 100644 --- a/src/common/dwarf_line_to_module.cc +++ b/src/common/dwarf_line_to_module.cc @@ -34,6 +34,8 @@ #include "common/dwarf_line_to_module.h" +#include + // Trying to support Windows paths in a reasonable way adds a lot of // variations to test; it would be better to just put off dealing with // it until we actually have to deal with DWARF on Windows. diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 77455a57..fc312983 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -46,6 +46,7 @@ #include #include +#include #include #include #include @@ -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(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(elf_header), + obj_file, debug_dir, sym_stream); +} + } // namespace google_breakpad diff --git a/src/common/linux/dump_symbols.h b/src/common/linux/dump_symbols.h index e1a930ac..3749d1f6 100644 --- a/src/common/linux/dump_symbols.h +++ b/src/common/linux/dump_symbols.h @@ -35,19 +35,19 @@ #ifndef COMMON_LINUX_DUMP_SYMBOLS_H__ #define COMMON_LINUX_DUMP_SYMBOLS_H__ -#include - +#include #include 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 diff --git a/src/common/linux/dump_symbols_unittest.cc b/src/common/linux/dump_symbols_unittest.cc new file mode 100644 index 00000000..06263076 --- /dev/null +++ b/src/common/linux/dump_symbols_unittest.cc @@ -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 + +// dump_symbols_unittest.cc: +// Unittests for google_breakpad::DumpSymbols + +#include +#include +#include + +#include +#include +#include + +#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 elfdata_v; + uint8_t* elfdata; +}; + +TEST_F(DumpSymbols, Invalid) { + Elf32_Ehdr header; + memset(&header, 0, sizeof(header)); + stringstream s; + EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast(&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 diff --git a/src/common/linux/elf_symbols_to_module_unittest.cc b/src/common/linux/elf_symbols_to_module_unittest.cc index f7ab49c0..a3b874f9 100644 --- a/src/common/linux/elf_symbols_to_module_unittest.cc +++ b/src/common/linux/elf_symbols_to_module_unittest.cc @@ -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, diff --git a/src/common/linux/synth_elf.cc b/src/common/linux/synth_elf.cc new file mode 100644 index 00000000..8f8aad32 --- /dev/null +++ b/src/common/linux/synth_elf.cc @@ -0,0 +1,174 @@ +#include "common/linux/synth_elf.h" + +#include +#include +#include + +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 + diff --git a/src/common/linux/synth_elf.h b/src/common/linux/synth_elf.h new file mode 100644 index 00000000..215fd997 --- /dev/null +++ b/src/common/linux/synth_elf.h @@ -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 + +// 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 +#include +#include +#include + +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 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_ diff --git a/src/common/linux/synth_elf_unittest.cc b/src/common/linux/synth_elf_unittest.cc new file mode 100644 index 00000000..8cc7ad28 --- /dev/null +++ b/src/common/linux/synth_elf_unittest.cc @@ -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 + +// synth_elf_unittest.cc: +// Unittests for google_breakpad::synth_elf::ELF + +#include + +#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(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(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__) diff --git a/src/common/module.cc b/src/common/module.cc index ffaeb87a..f02b26ea 100644 --- a/src/common/module.cc +++ b/src/common/module.cc @@ -35,10 +35,18 @@ #include #include +#include #include +#include + namespace google_breakpad { +using std::dec; +using std::endl; +using std::hex; + + Module::Module(const string &name, const string &os, const string &architecture, const string &id) : name_(name), @@ -182,22 +190,20 @@ bool Module::ReportError() { return false; } -bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) { +bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { for (RuleMap::const_iterator it = rule_map.begin(); it != rule_map.end(); it++) { - if (it != rule_map.begin() && - 0 > putc(' ', stream)) - return false; - if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str())) - return false; + if (it != rule_map.begin()) + stream << ' '; + stream << it->first << ": " << it->second; } - return true; + return stream.good(); } -bool Module::Write(FILE *stream) { - if (0 > fprintf(stream, "MODULE %s %s %s %s\n", - os_.c_str(), architecture_.c_str(), id_.c_str(), - name_.c_str())) +bool Module::Write(std::ostream &stream) { + stream << "MODULE " << os_ << " " << architecture_ << " " + << id_ << " " << name_ << endl; + if (!stream.good()) return ReportError(); AssignSourceIds(); @@ -207,8 +213,8 @@ bool Module::Write(FILE *stream) { file_it != files_.end(); file_it++) { File *file = file_it->second; if (file->source_id >= 0) { - if (0 > fprintf(stream, "FILE %d %s\n", - file->source_id, file->name.c_str())) + stream << "FILE " << file->source_id << " " << file->name << endl; + if (!stream.good()) return ReportError(); } } @@ -217,29 +223,35 @@ bool Module::Write(FILE *stream) { for (FunctionSet::const_iterator func_it = functions_.begin(); func_it != functions_.end(); func_it++) { Function *func = *func_it; - if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n", - (unsigned long long) (func->address - load_address_), - (unsigned long long) func->size, - (unsigned long long) func->parameter_size, - func->name.c_str())) + stream << "FUNC " << hex + << (func->address - load_address_) << " " + << func->size << " " + << func->parameter_size << " " + << func->name << dec << endl; + + if (!stream.good()) return ReportError(); for (vector::iterator line_it = func->lines.begin(); - line_it != func->lines.end(); line_it++) - if (0 > fprintf(stream, "%llx %llx %d %d\n", - (unsigned long long) (line_it->address - load_address_), - (unsigned long long) line_it->size, - line_it->number, - line_it->file->source_id)) + line_it != func->lines.end(); line_it++) { + stream << hex + << (line_it->address - load_address_) << " " + << line_it->size << " " + << dec + << line_it->number << " " + << line_it->file->source_id << endl; + if (!stream.good()) return ReportError(); + } } // Write out 'PUBLIC' records. for (ExternSet::const_iterator extern_it = externs_.begin(); extern_it != externs_.end(); extern_it++) { Extern *ext = *extern_it; - if (0 > fprintf(stream, "PUBLIC %llx 0 %s\n", - (unsigned long long) (ext->address - load_address_), - ext->name.c_str())) + stream << "PUBLIC " << hex + << (ext->address - load_address_) << " 0 " + << ext->name << dec << endl; + if (!stream.good()) return ReportError(); } @@ -248,21 +260,25 @@ bool Module::Write(FILE *stream) { for (frame_it = stack_frame_entries_.begin(); frame_it != stack_frame_entries_.end(); frame_it++) { StackFrameEntry *entry = *frame_it; - if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ", - (unsigned long long) entry->address - load_address_, - (unsigned long long) entry->size) - || !WriteRuleMap(entry->initial_rules, stream) - || 0 > putc('\n', stream)) + stream << "STACK CFI INIT " << hex + << (entry->address - load_address_) << " " + << entry->size << " " << dec; + if (!stream.good() + || !WriteRuleMap(entry->initial_rules, stream)) return ReportError(); + stream << endl; + // Write out this entry's delta rules as 'STACK CFI' records. for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); delta_it != entry->rule_changes.end(); delta_it++) { - if (0 > fprintf(stream, "STACK CFI %llx ", - (unsigned long long) delta_it->first - load_address_) - || !WriteRuleMap(delta_it->second, stream) - || 0 > putc('\n', stream)) + stream << "STACK CFI " << hex + << (delta_it->first - load_address_) << " " << dec; + if (!stream.good() + || !WriteRuleMap(delta_it->second, stream)) return ReportError(); + + stream << endl; } } diff --git a/src/common/module.h b/src/common/module.h index 8e6a05e2..55f260f1 100644 --- a/src/common/module.h +++ b/src/common/module.h @@ -38,8 +38,7 @@ #ifndef COMMON_LINUX_MODULE_H__ #define COMMON_LINUX_MODULE_H__ -#include - +#include #include #include #include @@ -264,7 +263,7 @@ class Module { // - the functions added via AddFunctions, each with its lines. // Addresses in the output are all relative to the load address // established by SetLoadAddress. - bool Write(FILE *stream); + bool Write(std::ostream &stream); private: @@ -275,7 +274,7 @@ class Module { // Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI' // records, without a final newline. Return true if all goes well; // if an error occurs, return false, and leave errno set. - static bool WriteRuleMap(const RuleMap &rule_map, FILE *stream); + static bool WriteRuleMap(const RuleMap &rule_map, std::ostream &stream); // Module header entries. string name_, os_, architecture_, id_; diff --git a/src/common/module_unittest.cc b/src/common/module_unittest.cc index 63ad5056..4972bbe8 100644 --- a/src/common/module_unittest.cc +++ b/src/common/module_unittest.cc @@ -37,6 +37,7 @@ #include #include +#include #include #include "breakpad_googletest_includes.h" @@ -44,53 +45,10 @@ using google_breakpad::Module; using std::string; +using std::stringstream; using std::vector; using testing::ContainerEq; -// Return a FILE * referring to a temporary file that will be deleted -// automatically when the stream is closed or the program exits. -static FILE *checked_tmpfile() { - FILE *f = tmpfile(); - if (!f) { - fprintf(stderr, "error creating temporary file: %s\n", strerror(errno)); - exit(1); - } - return f; -} - -// Read from STREAM until end of file, and return the contents as a -// string. -static string checked_read(FILE *stream) { - string contents; - int c; - while ((c = getc(stream)) != EOF) - contents.push_back(c); - if (ferror(stream)) { - fprintf(stderr, "error reading temporary file contents: %s\n", - strerror(errno)); - exit(1); - } - return contents; -} - -// Apply 'fflush' to STREAM, and check for errors. -static void checked_fflush(FILE *stream) { - if (fflush(stream) == EOF) { - fprintf(stderr, "error flushing temporary file stream: %s\n", - strerror(errno)); - exit(1); - } -} - -// Apply 'fclose' to STREAM, and check for errors. -static void checked_fclose(FILE *stream) { - if (fclose(stream) == EOF) { - fprintf(stderr, "error closing temporary file stream: %s\n", - strerror(errno)); - exit(1); - } -} - static Module::Function *generate_duplicate_function(const string &name) { const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL; const Module::Address DUP_SIZE = 0x200b26e605f99071LL; @@ -110,19 +68,16 @@ static Module::Function *generate_duplicate_function(const string &name) { #define MODULE_ID "id-string" TEST(Write, Header) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n", contents.c_str()); } TEST(Write, OneLineFunc) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); Module::File *file = m.FindFile("file_name.cc"); @@ -136,11 +91,8 @@ TEST(Write, OneLineFunc) { function->lines.push_back(line); m.AddFunction(function); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FILE 0 file_name.cc\n" "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a" @@ -150,7 +102,7 @@ TEST(Write, OneLineFunc) { } TEST(Write, RelativeLoadAddress) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // Some source files. We will expect to see them in lexicographic order. @@ -189,11 +141,8 @@ TEST(Write, RelativeLoadAddress) { // the module must work fine. m.SetLoadAddress(0x2ab698b0b6407073LL); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FILE 0 filename-a.cc\n" "FILE 1 filename-b.cc\n" @@ -247,12 +196,9 @@ TEST(Write, OmitUnusedFiles) { EXPECT_STREQ("filename3", vec[2]->name.c_str()); EXPECT_NE(-1, vec[2]->source_id); - FILE *f = checked_tmpfile(); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + stringstream s; + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FILE 0 filename1\n" "FILE 1 filename3\n" @@ -264,7 +210,7 @@ TEST(Write, OmitUnusedFiles) { } TEST(Construct, AddFunctions) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // Two functions. @@ -287,11 +233,8 @@ TEST(Construct, AddFunctions) { m.AddFunctions(vec.begin(), vec.end()); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988" " _and_void\n" @@ -308,7 +251,7 @@ TEST(Construct, AddFunctions) { } TEST(Construct, AddFrames) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // First STACK CFI entry, with no initial rules or deltas. @@ -342,11 +285,8 @@ TEST(Construct, AddFrames) { m.AddStackFrameEntry(entry3); // Check that Write writes STACK CFI records properly. - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n" "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407" @@ -411,7 +351,7 @@ TEST(Construct, UniqueFiles) { } TEST(Construct, DuplicateFunctions) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // Two functions. @@ -421,11 +361,8 @@ TEST(Construct, DuplicateFunctions) { m.AddFunction(function1); m.AddFunction(function2); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" " _without_form\n", @@ -433,7 +370,7 @@ TEST(Construct, DuplicateFunctions) { } TEST(Construct, FunctionsWithSameAddress) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // Two functions. @@ -443,11 +380,8 @@ TEST(Construct, FunctionsWithSameAddress) { m.AddFunction(function1); m.AddFunction(function2); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" " _and_void\n" @@ -459,7 +393,7 @@ TEST(Construct, FunctionsWithSameAddress) { // Externs should be written out as PUBLIC records, sorted by // address. TEST(Construct, Externs) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // Two externs. @@ -473,11 +407,8 @@ TEST(Construct, Externs) { m.AddExtern(extern1); m.AddExtern(extern2); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " MODULE_ID " " MODULE_NAME "\n" @@ -489,7 +420,7 @@ TEST(Construct, Externs) { // Externs with the same address should only keep the first entry // added. TEST(Construct, DuplicateExterns) { - FILE *f = checked_tmpfile(); + stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); // Two externs. @@ -503,11 +434,8 @@ TEST(Construct, DuplicateExterns) { m.AddExtern(extern1); m.AddExtern(extern2); - m.Write(f); - checked_fflush(f); - rewind(f); - string contents = checked_read(f); - checked_fclose(f); + m.Write(s); + string contents = s.str(); EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " MODULE_ID " " MODULE_NAME "\n" diff --git a/src/common/stabs_to_module.cc b/src/common/stabs_to_module.cc index e694febc..67cd13bf 100644 --- a/src/common/stabs_to_module.cc +++ b/src/common/stabs_to_module.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include -- cgit v1.2.1