aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2011-07-06 17:05:59 +0000
committerted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2011-07-06 17:05:59 +0000
commit3ca4a120de8ec3f35e972e4b23f527bb8f65c479 (patch)
tree0186b36bf01d54c7700c00c3c2cec21fbb382268 /src/common
parentDump PUBLIC + CFI records from libraries without debug info on Linux, use .dy... (diff)
downloadbreakpad-3ca4a120de8ec3f35e972e4b23f527bb8f65c479.tar.xz
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
Diffstat (limited to 'src/common')
-rw-r--r--src/common/dwarf_cu_to_module.cc1
-rw-r--r--src/common/dwarf_line_to_module.cc2
-rw-r--r--src/common/linux/dump_symbols.cc48
-rw-r--r--src/common/linux/dump_symbols.h8
-rw-r--r--src/common/linux/dump_symbols_unittest.cc162
-rw-r--r--src/common/linux/elf_symbols_to_module_unittest.cc67
-rw-r--r--src/common/linux/synth_elf.cc174
-rw-r--r--src/common/linux/synth_elf.h155
-rw-r--r--src/common/linux/synth_elf_unittest.cc265
-rw-r--r--src/common/module.cc88
-rw-r--r--src/common/module.h7
-rw-r--r--src/common/module_unittest.cc136
-rw-r--r--src/common/stabs_to_module.cc1
13 files changed, 887 insertions, 227 deletions
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 <assert.h>
#include <inttypes.h>
+#include <stdio.h>
#include <algorithm>
#include <set>
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 <stdio.h>
+
// 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 <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
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 <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
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 <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
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 <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(&section_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 <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_
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 <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__)
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 <assert.h>
#include <errno.h>
+#include <stdio.h>
#include <string.h>
+#include <iostream>
+
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<Line>::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 <stdio.h>
-
+#include <iostream>
#include <map>
#include <set>
#include <string>
@@ -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 <string.h>
#include <algorithm>
+#include <sstream>
#include <string>
#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 <assert.h>
#include <cxxabi.h>
#include <stdarg.h>
+#include <stdio.h>
#include <algorithm>