aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/linux/dump_symbols.cc20
-rw-r--r--src/common/stabs_reader.cc128
-rw-r--r--src/common/stabs_reader.h127
-rw-r--r--src/common/stabs_reader_unittest.cc727
-rw-r--r--src/common/test_assembler.cc1
-rw-r--r--src/common/testdata/stabs_reader_unittest.input119
-rw-r--r--src/common/testdata/stabs_reader_unittest.input21
-rw-r--r--src/common/testdata/stabs_reader_unittest.input32
-rw-r--r--src/common/testdata/stabs_reader_unittest.input41
-rw-r--r--src/common/testdata/stabs_reader_unittest.input52
-rw-r--r--src/common/testdata/stabs_reader_unittest.input68
-rw-r--r--src/tools/linux/dump_syms/Makefile5
12 files changed, 446 insertions, 595 deletions
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index c3648e9a..da4263f7 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -123,17 +123,31 @@ static const ElfW(Shdr) *FindSectionByName(const char *name,
return NULL;
}
-static bool LoadStabs(const ElfW(Shdr) *stab_section,
+static bool LoadStabs(const ElfW(Ehdr) *elf_header,
+ const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section,
Module *module) {
+ // Figure out what endianness this file is.
+ bool big_endian;
+ if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
+ big_endian = false;
+ else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
+ big_endian = true;
+ else {
+ fprintf(stderr, "bad data encoding in ELF header: %d\n",
+ elf_header->e_ident[EI_DATA]);
+ return false;
+ }
// A callback object to handle data from the STABS reader.
DumpStabsHandler handler(module);
// Find the addresses of the STABS data, and create a STABS reader object.
+ // On Linux, STABS entries always have 32-bit values, regardless of the
+ // address size of the architecture whose code they're describing.
uint8_t *stabs = reinterpret_cast<uint8_t *>(stab_section->sh_offset);
uint8_t *stabstr = reinterpret_cast<uint8_t *>(stabstr_section->sh_offset);
google_breakpad::StabsReader reader(stabs, stab_section->sh_size,
stabstr, stabstr_section->sh_size,
- &handler);
+ big_endian, 4, &handler);
// Read the STABS data, and do post-processing.
if (!reader.Process())
return false;
@@ -330,7 +344,7 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
if (stabstr_section) {
found_debug_info_section = true;
- if (!LoadStabs(stab_section, stabstr_section, module))
+ if (!LoadStabs(elf_header, stab_section, stabstr_section, module))
fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS"
" debugging information\n", obj_file.c_str());
}
diff --git a/src/common/stabs_reader.cc b/src/common/stabs_reader.cc
index 7c2b1cc4..add2a8dd 100644
--- a/src/common/stabs_reader.cc
+++ b/src/common/stabs_reader.cc
@@ -31,48 +31,64 @@
// This file implements the google_breakpad::StabsReader class.
// See stabs_reader.h.
-#include <a.out.h>
-#include <stab.h>
-#include <cstring>
-#include <cassert>
-
#include "common/stabs_reader.h"
+#include <assert.h>
+#include <stab.h>
+#include <string.h>
+
namespace google_breakpad {
+StabsReader::EntryIterator::EntryIterator(const ByteBuffer *buffer,
+ bool big_endian, size_t value_size)
+ : value_size_(value_size), cursor_(buffer, big_endian) {
+ // Actually, we could handle weird sizes just fine, but they're
+ // probably mistakes --- expressed in bits, say.
+ assert(value_size == 4 || value_size == 8);
+ entry_.index = 0;
+ Fetch();
+}
+
+void StabsReader::EntryIterator::Fetch() {
+ cursor_
+ .Read(4, false, &entry_.name_offset)
+ .Read(1, false, &entry_.type)
+ .Read(1, false, &entry_.other)
+ .Read(2, false, &entry_.descriptor)
+ .Read(value_size_, false, &entry_.value);
+ entry_.at_end = !cursor_;
+}
+
StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
const uint8_t *stabstr, size_t stabstr_size,
- StabsHandler *handler) :
- stabstr_(stabstr),
- stabstr_size_(stabstr_size),
- handler_(handler),
- string_offset_(0),
- next_cu_string_offset_(0),
- symbol_(NULL),
- current_source_file_(NULL) {
- symbols_ = reinterpret_cast<const struct nlist *>(stab);
- symbols_end_ = symbols_ + (stab_size / sizeof (*symbols_));
-}
+ bool big_endian, size_t value_size,
+ StabsHandler *handler)
+ : entries_(stab, stab_size),
+ strings_(stabstr, stabstr_size),
+ iterator_(&entries_, big_endian, value_size),
+ handler_(handler),
+ string_offset_(0),
+ next_cu_string_offset_(0),
+ current_source_file_(NULL) { }
const char *StabsReader::SymbolString() {
- ptrdiff_t offset = string_offset_ + symbol_->n_un.n_strx;
- if (offset < 0 || (size_t) offset >= stabstr_size_) {
+ ptrdiff_t offset = string_offset_ + iterator_->name_offset;
+ if (offset < 0 || (size_t) offset >= strings_.Size()) {
handler_->Warning("symbol %d: name offset outside the string section\n",
- symbol_ - symbols_);
+ iterator_->index);
// Return our null string, to keep our promise about all names being
// taken from the string section.
offset = 0;
}
- return reinterpret_cast<const char *>(stabstr_ + offset);
+ return reinterpret_cast<const char *>(strings_.start + offset);
}
bool StabsReader::Process() {
- symbol_ = symbols_;
- while (symbol_ < symbols_end_) {
- if (symbol_->n_type == N_SO) {
+ while (!iterator_->at_end) {
+ if (iterator_->type == N_SO) {
if (! ProcessCompilationUnit())
return false;
- } else if (symbol_->n_type == N_UNDF) {
+ } else if (iterator_->type == N_UNDF) {
// At the head of each compilation unit's entries there is an
// N_UNDF stab giving the number of symbols in the compilation
// unit, and the number of bytes that compilation unit's strings
@@ -85,16 +101,16 @@ bool StabsReader::Process() {
// beginning. However, other linkers, like Gold, do not perform
// this optimization.
string_offset_ = next_cu_string_offset_;
- next_cu_string_offset_ = SymbolValue();
- symbol_++;
+ next_cu_string_offset_ = iterator_->value;
+ ++iterator_;
} else
- symbol_++;
+ ++iterator_;
}
return true;
}
bool StabsReader::ProcessCompilationUnit() {
- assert(symbol_ < symbols_end_ && symbol_->n_type == N_SO);
+ assert(!iterator_->at_end && iterator_->type == N_SO);
// There may be an N_SO entry whose name ends with a slash,
// indicating the directory in which the compilation occurred.
@@ -104,32 +120,32 @@ bool StabsReader::ProcessCompilationUnit() {
const char *name = SymbolString();
if (name[0] && name[strlen(name) - 1] == '/') {
build_directory = name;
- symbol_++;
+ ++iterator_;
}
}
// We expect to see an N_SO entry with a filename next, indicating
// the start of the compilation unit.
{
- if (symbol_ >= symbols_end_ || symbol_->n_type != N_SO)
+ if (iterator_->at_end || iterator_->type != N_SO)
return true;
const char *name = SymbolString();
if (name[0] == '\0') {
// This seems to be a stray end-of-compilation-unit marker;
// consume it, but don't report the end, since we didn't see a
// beginning.
- symbol_++;
+ ++iterator_;
return true;
}
current_source_file_ = name;
}
if (! handler_->StartCompilationUnit(current_source_file_,
- SymbolValue(),
+ iterator_->value,
build_directory))
return false;
- symbol_++;
+ ++iterator_;
// The STABS documentation says that some compilers may emit
// additional N_SO entries with names immediately following the
@@ -137,24 +153,24 @@ bool StabsReader::ProcessCompilationUnit() {
// Breakpad STABS reader doesn't ignore them, so we won't either.
// Process the body of the compilation unit, up to the next N_SO.
- while (symbol_ < symbols_end_ && symbol_->n_type != N_SO) {
- if (symbol_->n_type == N_FUN) {
+ while (!iterator_->at_end && iterator_->type != N_SO) {
+ if (iterator_->type == N_FUN) {
if (! ProcessFunction())
return false;
} else
// Ignore anything else.
- symbol_++;
+ ++iterator_;
}
// An N_SO with an empty name indicates the end of the compilation
// unit. Default to zero.
uint64_t ending_address = 0;
- if (symbol_ < symbols_end_) {
- assert(symbol_->n_type == N_SO);
+ if (!iterator_->at_end) {
+ assert(iterator_->type == N_SO);
const char *name = SymbolString();
if (name[0] == '\0') {
- ending_address = SymbolValue();
- symbol_++;
+ ending_address = iterator_->value;
+ ++iterator_;
}
}
@@ -165,9 +181,9 @@ bool StabsReader::ProcessCompilationUnit() {
}
bool StabsReader::ProcessFunction() {
- assert(symbol_ < symbols_end_ && symbol_->n_type == N_FUN);
+ assert(!iterator_->at_end && iterator_->type == N_FUN);
- uint64_t function_address = SymbolValue();
+ uint64_t function_address = iterator_->value;
// The STABS string for an N_FUN entry is the name of the function,
// followed by a colon, followed by type information for the
// function. We want to pass the name alone to StartFunction.
@@ -178,37 +194,37 @@ bool StabsReader::ProcessFunction() {
std::string name(stab_string, name_end - stab_string);
if (! handler_->StartFunction(name, function_address))
return false;
- symbol_++;
+ ++iterator_;
- while (symbol_ < symbols_end_) {
- if (symbol_->n_type == N_SO || symbol_->n_type == N_FUN)
+ while (!iterator_->at_end) {
+ if (iterator_->type == N_SO || iterator_->type == N_FUN)
break;
- else if (symbol_->n_type == N_SLINE) {
+ else if (iterator_->type == N_SLINE) {
// The value of an N_SLINE entry is the offset of the line from
// the function's start address.
- uint64_t line_address = function_address + SymbolValue();
+ uint64_t line_address = function_address + iterator_->value;
// The n_desc of a N_SLINE entry is the line number. It's a
// signed 16-bit field; line numbers from 32768 to 65535 are
// stored as n-65536.
- uint16_t line_number = symbol_->n_desc;
+ uint16_t line_number = iterator_->descriptor;
if (! handler_->Line(line_address, current_source_file_, line_number))
return false;
- symbol_++;
- } else if (symbol_->n_type == N_SOL) {
+ ++iterator_;
+ } else if (iterator_->type == N_SOL) {
current_source_file_ = SymbolString();
- symbol_++;
+ ++iterator_;
} else
// Ignore anything else.
- symbol_++;
+ ++iterator_;
}
// If there is a subsequent N_SO or N_FUN entry, its address is our
// end address.
uint64_t ending_address = 0;
- if (symbol_ < symbols_end_) {
- assert(symbol_->n_type == N_SO || symbol_->n_type == N_FUN);
- ending_address = SymbolValue();
- // Note: we do not increment symbol_ here, since we haven't consumed it.
+ if (!iterator_->at_end) {
+ assert(iterator_->type == N_SO || iterator_->type == N_FUN);
+ ending_address = iterator_->value;
+ // Note: we do not advance iterator_ here, since we haven't consumed it.
}
if (! handler_->EndFunction(ending_address))
diff --git a/src/common/stabs_reader.h b/src/common/stabs_reader.h
index 5cfcdfb5..9f954650 100644
--- a/src/common/stabs_reader.h
+++ b/src/common/stabs_reader.h
@@ -38,36 +38,47 @@
//
// The comments here assume you understand the format.
//
-// This parser assumes that the system's <a.out.h> and <stab.h>
-// headers accurately describe the layout of the STABS data; this code
-// will not parse STABS data for a system with a different address
-// size or endianness.
+// This parser can handle big-endian and little-endian data, and the symbol
+// values may be either 32 or 64 bits long. It handles both STABS in
+// sections (as used on Linux) and STABS appearing directly in an
+// a.out-like symbol table (as used in Darwin OS X Mach-O files).
-#ifndef COMMON_LINUX_STABS_READER_H__
-#define COMMON_LINUX_STABS_READER_H__
+#ifndef COMMON_STABS_READER_H__
+#define COMMON_STABS_READER_H__
+#include <stddef.h>
#include <stdint.h>
-#include <cstddef>
+
+#ifdef HAVE_A_OUT_H
#include <a.out.h>
+#endif
#include <string>
+#include "common/byte_cursor.h"
+
namespace google_breakpad {
class StabsHandler;
class StabsReader {
public:
- // Create a reader for the STABS debug information whose .stab
- // section is the STAB_SIZE bytes at STAB, and whose .stabstr
- // section is the STABSTR_SIZE bytes at STABSTR. The reader will
- // call the member functions of HANDLER to report the information it
- // finds, when the reader's 'Process' member function is called.
+ // Create a reader for the STABS debug information whose .stab section is
+ // being traversed by ITERATOR, and whose .stabstr section is referred to
+ // by STRINGS. The reader will call the member functions of HANDLER to
+ // report the information it finds, when the reader's 'Process' member
+ // function is called.
+ //
+ // BIG_ENDIAN should be true if the entries in the .stab section are in
+ // big-endian form, or false if they are in little-endian form.
+ // VALUE_SIZE should be either 4 or 8, indicating the size of the 'value'
+ // field in each entry in bytes.
//
// Note that, in ELF, the .stabstr section should be found using the
// 'sh_link' field of the .stab section header, not by name.
StabsReader(const uint8_t *stab, size_t stab_size,
const uint8_t *stabstr, size_t stabstr_size,
+ bool big_endian, size_t value_size,
StabsHandler *handler);
// Process the STABS data, calling the handler's member functions to
@@ -75,17 +86,82 @@ class StabsReader {
// continue to process until we reach the end of the section. If we
// processed the entire section and all handlers returned true,
// return true. If any handler returned false, return false.
+ //
+ // This is only meant to be called once per StabsReader instance;
+ // resuming a prior processing pass that stopped abruptly isn't supported.
bool Process();
private:
+
+ // An class for walking arrays of STABS entries. This isolates the main
+ // STABS reader from the exact format (size; endianness) of the entries
+ // themselves.
+ class EntryIterator {
+ public:
+ // The contents of a STABS entry, adjusted for the host's endianness,
+ // word size, 'struct nlist' layout, and so on.
+ struct Entry {
+ // True if this iterator has reached the end of the entry array. When
+ // this is set, the other members of this structure are not valid.
+ bool at_end;
+
+ // The number of this entry within the list.
+ size_t index;
+
+ // The current entry's name offset. This is the offset within the
+ // current compilation unit's strings, as establish by the N_UNDF entries.
+ size_t name_offset;
+
+ // The current entry's type, 'other' field, descriptor, and value.
+ unsigned char type;
+ unsigned char other;
+ short descriptor;
+ uint64_t value;
+ };
+
+ // Create a EntryIterator walking the entries in BUFFER. Treat the
+ // entries as big-endian if BIG_ENDIAN is true, as little-endian
+ // otherwise. Assume each entry has a 'value' field whose size is
+ // VALUE_SIZE.
+ //
+ // This would not be terribly clean to extend to other format variations,
+ // but it's enough to handle Linux and Mac, and we'd like STABS to die
+ // anyway.
+ //
+ // For the record: on Linux, STABS entry values are always 32 bits,
+ // regardless of the architecture address size (don't ask me why); on
+ // Mac, they are 32 or 64 bits long. Oddly, the section header's entry
+ // size for a Linux ELF .stab section varies according to the ELF class
+ // from 12 to 20 even as the actual entries remain unchanged.
+ EntryIterator(const ByteBuffer *buffer, bool big_endian, size_t value_size);
+
+ // Move to the next entry. This function's behavior is undefined if
+ // at_end() is true when it is called.
+ EntryIterator &operator++() { Fetch(); entry_.index++; return *this; }
+
+ // Dereferencing this iterator produces a reference to an Entry structure
+ // that holds the current entry's values. The entry is owned by this
+ // EntryIterator, and will be invalidated at the next call to operator++.
+ const Entry &operator*() const { return entry_; }
+ const Entry *operator->() const { return &entry_; }
+
+ private:
+ // Read the STABS entry at cursor_, and set entry_ appropriately.
+ void Fetch();
+
+ // The size of entries' value field, in bytes.
+ size_t value_size_;
+
+ // A byte cursor traversing buffer_.
+ ByteCursor cursor_;
+
+ // Values for the entry this iterator refers to.
+ Entry entry_;
+ };
+
// Return the name of the current symbol.
const char *SymbolString();
- // Return the value of the current symbol.
- const uint64_t SymbolValue() {
- return symbol_->n_value;
- }
-
// Process a compilation unit starting at symbol_. Return true
// to continue processing, or false to abort.
bool ProcessCompilationUnit();
@@ -94,10 +170,14 @@ class StabsReader {
// Return true to continue processing, or false to abort.
bool ProcessFunction();
- // The debugging information we're reading.
- const struct nlist *symbols_, *symbols_end_;
- const uint8_t *stabstr_;
- size_t stabstr_size_;
+ // The STABS entries we're parsing.
+ ByteBuffer entries_;
+
+ // The string section to which the entries refer.
+ ByteBuffer strings_;
+
+ // The iterator walking the STABS entries.
+ EntryIterator iterator_;
StabsHandler *handler_;
@@ -108,9 +188,6 @@ class StabsReader {
// as established by N_UNDF entries.
size_t next_cu_string_offset_;
- // The current symbol we're processing.
- const struct nlist *symbol_;
-
// The current source file name.
const char *current_source_file_;
};
@@ -203,4 +280,4 @@ class StabsHandler {
} // namespace google_breakpad
-#endif // COMMON_LINUX_STABS_READER_H__
+#endif // COMMON_STABS_READER_H__
diff --git a/src/common/stabs_reader_unittest.cc b/src/common/stabs_reader_unittest.cc
index a22d9d0e..74897c46 100644
--- a/src/common/stabs_reader_unittest.cc
+++ b/src/common/stabs_reader_unittest.cc
@@ -31,7 +31,6 @@
// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader.
-#include <a.out.h>
#include <cassert>
#include <cerrno>
#include <cstdarg>
@@ -46,444 +45,171 @@
#include "breakpad_googletest_includes.h"
#include "common/stabs_reader.h"
+#include "common/test_assembler.h"
-using std::istream;
-using std::istringstream;
-using std::map;
-using std::ostream;
-using std::ostringstream;
-using std::string;
-
-using ::testing::_;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
-using ::testing::Sequence;
using ::testing::StrEq;
-
+using ::testing::_;
using google_breakpad::StabsHandler;
using google_breakpad::StabsReader;
+using google_breakpad::TestAssembler::Label;
+using google_breakpad::TestAssembler::Section;
+using google_breakpad::TestAssembler::kBigEndian;
+using google_breakpad::TestAssembler::kLittleEndian;
+using std::map;
+using std::string;
namespace {
-// Mock stabs file parser
-//
-// In order to test StabsReader, we parse a human-readable input file
-// describing STABS entries into in-memory .stab and .stabstr
-// sections, and then pass those to StabsReader to look at. The
-// human-readable file is called a "mock stabs file".
-//
-// Except for compilation unit boundary lines (described below), each
-// line of a mock stabs file should have the following form:
-//
-// TYPE OTHER DESC VALUE NAME
-//
-// where all data is Latin-1 bytes and fields are separated by single
-// space characters, except for NAME, which may contain spaces and
-// continues to the end of the line. The fields have the following
-// meanings:
-//
-// - TYPE: the name of the stabs symbol type; like SO or FUN. These are
-// the names from /usr/include/bits/stab.def, without the leading N_.
-//
-// - OTHER, DESC, VALUE: numeric values for the n_other, n_desc, and
-// n_value fields of the stab. These can be decimal or hex,
-// using C++ notation (10, 0x10)
-//
-// - NAME: textual data for the entry. STABS packs all kinds of
-// interesting data into entries' NAME fields, so calling it a NAME
-// is misleading, but that's how it is. For SO, this may be a
-// filename; for FUN, this is the function name, plus type data; and
-// so on.
-//
-// A compilation unit boundary line has the form:
-//
-// cu-boundary FILENAME
-
-// I don't know if the whole parser/handler pattern is really worth
-// the bureaucracy in this case. But just writing it out as
-// old-fashioned functions wasn't astonishingly clear either, so it
-// seemed worth a try.
-
-// A handler class for mock stabs data.
-class MockStabsHandler {
+// A StringAssembler is a class for generating .stabstr sections to present
+// as input to the STABS parser.
+class StringAssembler: public Section {
public:
- MockStabsHandler() { }
- virtual ~MockStabsHandler() { }
- // The mock stabs parser calls this member function for each entry
- // it parses, passing it the contents of the entry. If this function
- // returns true, the parser continues; if it returns false, the parser
- // stops, and its Process member function returns false.
- virtual bool Entry(enum __stab_debug_code type, char other, short desc,
- unsigned long value, const string &name) { return true; }
- // Report a compilation unit boundary whose filename is FILENAME. As
- // for the Entry function, this should return true to continue
- // parsing, or false to stop processing.
- virtual bool CUBoundary(const string &filename) { return true; }
-
- // Report an error in parsing the mock stabs data. If this returns true,
- // the parser continues; if it returns false, the parser stops and
- // its Process member function returns false.
- virtual bool Error(const char *format, ...) = 0;
-};
-
-// A class for parsing mock stabs files.
-class MockStabsParser {
- public:
- // Create a parser reading input from STREAM and passing data to HANDLER.
- // Use FILENAME when reporting errors.
- MockStabsParser(const string &filename, istream *stream,
- MockStabsHandler *handler);
- // Parse data from the STREAM, invoking HANDLER->Entry for each
- // entry we get. Return true if we parsed all the data succesfully,
- // or false if we stopped early because Entry returned false, or if
- // there were any errors during parsing.
- bool Process();
- private:
- // A type for maps from stab type names ("SO", "SLINE", etc.) to
- // n_type values.
- typedef map<string, unsigned char> StabTypeNameTable;
-
- // Initialize the table mapping STAB type names to n_type values.
- void InitializeTypeNames();
-
- // Parse LINE, one line of input from a mock stabs file, and pass
- // its contents to handler_->Entry and return the boolean value that
- // returns. If we encounter an error parsing the line, report it
- // using handler->Error.
- bool ParseLine(const string &line);
-
- const string &filename_;
- istream *stream_;
- MockStabsHandler *handler_;
- int line_number_;
- StabTypeNameTable type_names_;
-};
-
-MockStabsParser::MockStabsParser(const string &filename, istream *stream,
- MockStabsHandler *handler):
- filename_(filename), stream_(stream), handler_(handler),
- line_number_(0) {
- InitializeTypeNames();
-}
-
-bool MockStabsParser::Process() {
- // Iterate once per line, including a line at EOF without a
- // terminating newline.
- for(;;) {
- string line;
- getline(*stream_, line, '\n');
- if (line.empty() && stream_->eof())
- break;
- line_number_++;
- if (! ParseLine(line))
- return false;
+ StringAssembler() : in_cu_(false) { StartCU(); }
+
+ // Add the string S to this StringAssembler, and return the string's
+ // offset within this compilation unit's strings. If S has been added
+ // already, this returns the offset of its first instance.
+ size_t Add(const string &s) {
+ map<string, size_t>::iterator it = added_.find(s);
+ if (it != added_.end())
+ return it->second;
+ size_t offset = Size() - cu_start_;
+ AppendCString(s);
+ added_[s] = offset;
+ return offset;
}
- return true;
-}
-void MockStabsParser::InitializeTypeNames() {
- // On GLIBC-based systems, <bits/stab.def> is a file containing a
- // call to an unspecified macro __define_stab for each stab type.
- // <stab.h> uses it to define the __stab_debug_code enum type. We
- // use it here to initialize our mapping from type names to enum
- // values.
- //
- // This isn't portable to non-GLIBC systems. Feel free to just
- // hard-code the values if this becomes a problem.
-# define __define_stab(name, code, str) type_names_[string(str)] = code;
-# include <bits/stab.def>
-# undef __define_stab
-}
+ // Start a fresh compilation unit string collection.
+ void StartCU() {
+ // Ignore duplicate calls to StartCU. Our test data don't always call
+ // StartCU at all, meaning that our constructor has to take care of it,
+ // meaning that tests that *do* call StartCU call it twice at the
+ // beginning. This is not worth smoothing out.
+ if (in_cu_) return;
-bool MockStabsParser::ParseLine(const string &line) {
- istringstream linestream(line);
- // Allow "0x" prefix for hex, and so on.
- linestream.unsetf(istringstream::basefield);
- // Parse and validate the stabs type.
- string typeName;
- linestream >> typeName;
- if (typeName == "cu-boundary") {
- if (linestream.peek() == ' ')
- linestream.get();
- string filename;
- getline(linestream, filename, '\n');
- return handler_->CUBoundary(filename);
- } else {
- StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName);
- if (typeIt == type_names_.end())
- return handler_->Error("%s:%d: unrecognized stab type: %s\n",
- filename_.c_str(), line_number_, typeName.c_str());
- // These are int, not char and unsigned char, to ensure they're parsed
- // as decimal numbers, not characters.
- int otherInt, descInt;
- unsigned long value;
- linestream >> otherInt >> descInt >> value;
- if (linestream.fail())
- return handler_->Error("%s:%d: malformed mock stabs input line\n",
- filename_.c_str(), line_number_);
- if (linestream.peek() == ' ')
- linestream.get();
- string name;
- getline(linestream, name, '\n');
- return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second),
- otherInt, descInt, value, name);
- }
-}
+ added_.clear();
+ cu_start_ = Size();
-// A class for constructing .stab sections.
-//
-// A .stab section is an array of struct nlist entries. These
-// entries' n_un.n_strx fields are indices into an accompanying
-// .stabstr section.
-class StabSection {
- public:
- StabSection(): used_(0), size_(1) {
- entries_ = (struct nlist *) malloc(sizeof(*entries_) * size_);
- }
- ~StabSection() { free(entries_); }
-
- // Append a new 'struct nlist' entry to the end of the section, and
- // return a pointer to it. This pointer is valid until the next
- // call to Append. The caller should initialize the returned entry
- // as needed.
- struct nlist *Append();
- // Set the first entry's n_desc field to COUNT, and set its n_value field
- // to STRING_SIZE.
- void SetHeader(short count, unsigned long string_size);
- // Set SECTION to the contents of a .stab section holding the
- // accumulated list of entries added with Append.
- void GetSection(string *section);
- // Clear the array, and prepare this StabSection to accumulate a fresh
- // set of entries.
- void Clear();
+ // Each compilation unit's strings start with an empty string.
+ AppendCString("");
+ added_[""] = 0;
- private:
- // The array of stabs entries,
- struct nlist *entries_;
- // The number of elements of entries_ that are used, and the allocated size
- // of the array.
- size_t used_, size_;
-};
-
-struct nlist *StabSection::Append() {
- if (used_ == size_) {
- size_ *= 2;
- entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
+ in_cu_ = true;
+ }
+
+ // Finish off the current CU's strings.
+ size_t EndCU() {
+ assert(in_cu_);
+ in_cu_ = false;
+ return Size() - cu_start_;
}
- assert(used_ < size_);
- return &entries_[used_++];
-}
-
-void StabSection::SetHeader(short count, unsigned long string_size) {
- assert(used_ >= 1);
- entries_[0].n_desc = count;
- entries_[0].n_value = string_size;
-}
-
-void StabSection::GetSection(string *section) {
- section->assign(reinterpret_cast<char *>(entries_),
- sizeof(*entries_) * used_);
-}
-
-void StabSection::Clear() {
- used_ = 0;
- size_ = 1;
- entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
-}
-// A class for building .stabstr sections.
-//
-// A .stabstr section is an array of characters containing a bunch of
-// null-terminated strings. A string is identified by the index of
-// its initial character in the array. The array always starts with a
-// null byte, so that an index of zero refers to the empty string.
-//
-// This implementation also ensures that if two strings are equal, we
-// assign them the same indices; most linkers do this, and some
-// clients may rely upon it. (Note that this is not quite the same as
-// ensuring that a string only appears once in the section; you could
-// share space when one string is a suffix of another, but we don't.)
-class StabstrSection {
- public:
- StabstrSection(): next_byte_(1) { string_indices_[""] = 0; }
- // Ensure STR is present in the string section, and return its index.
- size_t Insert(const string &str);
- // Set SECTION to the contents of a .stabstr section in which the
- // strings passed to Insert appear at the indices we promised.
- void GetSection(string *section);
- // Clear the contents of this StabstrSection, and prepare it to
- // accumulate a new set of strings.
- void Clear();
private:
- // Maps from strings to .stabstr indices and back.
- typedef map<string, size_t> StringToIndex;
- typedef map<size_t, const string *> IndexToString;
+ // The offset of the start of this compilation unit's strings.
+ size_t cu_start_;
- // A map from strings to the indices we've assigned them.
- StringToIndex string_indices_;
+ // True if we're in a CU.
+ bool in_cu_;
- // The next unused byte in the section. The next string we add
- // will get this index.
- size_t next_byte_;
+ // A map from the strings that have been added to this section to
+ // their starting indices within their compilation unit.
+ map<string, size_t> added_;
};
-size_t StabstrSection::Insert(const string &str) {
- StringToIndex::iterator it = string_indices_.find(str);
- size_t index;
- if (it != string_indices_.end()) {
- index = it->second;
- } else {
- // This is the first time we've seen STR; add it to the table.
- string_indices_[str] = next_byte_;
- index = next_byte_;
- next_byte_ += str.size() + 1;
+// A StabsAssembler is a class for generating .stab sections to present as
+// test input for the STABS parser.
+class StabsAssembler: public Section {
+ public:
+ // Create a StabsAssembler that uses StringAssembler for its strings.
+ StabsAssembler(StringAssembler *string_assembler)
+ : Section(string_assembler->endianness()),
+ string_assembler_(string_assembler),
+ value_size_(0),
+ entry_count_(0),
+ cu_header_(NULL) { }
+ ~StabsAssembler() { assert(!cu_header_); }
+
+ // Accessor and setter for value_size_.
+ size_t value_size() const { return value_size_; }
+ StabsAssembler &set_value_size(size_t value_size) {
+ value_size_ = value_size;
+ return *this;
}
- return index;
-}
-void StabstrSection::GetSection(string *section) {
- // First we have to invert the map.
- IndexToString byIndex;
- for (StringToIndex::const_iterator it = string_indices_.begin();
- it != string_indices_.end(); it++)
- byIndex[it->second] = &it->first;
- // Now we build the .stabstr section.
- section->clear();
- for (IndexToString::const_iterator it = byIndex.begin();
- it != byIndex.end(); it++) {
- // Make sure we're actually assigning it the index we claim to be.
- assert(it->first == section->size());
- *section += *(it->second);
- *section += '\0';
+ // Append a STAB entry to the end of this section with the given
+ // characteristics. NAME is the offset of this entry's name string within
+ // its compilation unit's portion of the .stabstr section; this can be a
+ // value generated by a StringAssembler. Return a reference to this
+ // StabsAssembler.
+ StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor,
+ Label value, Label name) {
+ D32(name);
+ D8(type);
+ D8(other);
+ D16(descriptor);
+ Append(endianness(), value_size_, value);
+ entry_count_++;
+ return *this;
}
-}
-void StabstrSection::Clear() {
- string_indices_.clear();
- string_indices_[""] = 0;
- next_byte_ = 1;
-}
+ // As above, but automatically add NAME to our StringAssembler.
+ StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor,
+ Label value, const string &name) {
+ return Stab(type, other, descriptor, value, string_assembler_->Add(name));
+ }
-// A mock stabs parser handler class that builds .stab and .stabstr
-// sections.
-class StabsSectionsBuilder: public MockStabsHandler {
- public:
- // Construct a handler that will receive data from a MockStabsParser
- // and construct .stab and .stabstr sections. FILENAME should be
- // the name of the mock stabs input file; we use it in error
- // messages.
- StabsSectionsBuilder(const string &filename)
- : filename_(filename), error_count_(0), has_header_(false),
- entry_count_(0) { }
-
- // Overridden virtual member functions.
- bool Entry(enum __stab_debug_code type, char other, short desc,
- unsigned long value, const string &name);
- bool CUBoundary(const string &filename);
- bool Error(const char *format, ...);
-
- // Set SECTION to the contents of a .stab or .stabstr section
- // reflecting the entries that have been passed to us via Entry.
- void GetStab(string *section);
- void GetStabstr(string *section);
+ // Start a compilation unit named NAME, with an N_UNDF symbol to start
+ // it, and its own portion of the string section. Return a reference to
+ // this StabsAssembler.
+ StabsAssembler &StartCU(const string &name) {
+ assert(!cu_header_);
+ cu_header_ = new CUHeader;
+ string_assembler_->StartCU();
+ entry_count_ = 0;
+ return Stab(N_UNDF, 0,
+ cu_header_->final_entry_count,
+ cu_header_->final_string_size,
+ string_assembler_->Add(name));
+ }
+ // Close off the current compilation unit. Return a reference to this
+ // StabsAssembler.
+ StabsAssembler &EndCU() {
+ assert(cu_header_);
+ cu_header_->final_entry_count = entry_count_;
+ cu_header_->final_string_size = string_assembler_->EndCU();
+ delete cu_header_;
+ cu_header_ = NULL;
+ return *this;
+ }
+
private:
- // Finish a compilation unit. If there are any entries accumulated in
- // stab_ and stabstr_, add them as a new compilation unit to
- // finished_cu_stabs_ and finished_cu_stabstr_, and then clear stab_ and
- // stabstr_.
- void FinishCU();
-
- const string &filename_; // input filename, for error messages
- int error_count_; // number of errors we've seen so far
-
- // The following members accumulate the contents of a single compilation
- // unit, possibly headed by an N_UNDF stab.
- bool has_header_; // true if we have an N_UNDF header
- int entry_count_; // the number of entries we've seen
- StabSection stab_; // 'struct nlist' entries
- StabstrSection stabstr_; // and the strings they love
-
- // Accumulated .stab and .stabstr content for all compilation units.
- string finished_cu_stab_, finished_cu_stabstr_;
-};
-
-bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other,
- short desc, unsigned long value,
- const string &name) {
- struct nlist *entry = stab_.Append();
- entry->n_type = type;
- entry->n_other = other;
- entry->n_desc = desc;
- entry->n_value = value;
- entry->n_un.n_strx = stabstr_.Insert(name);
- entry_count_++;
- return true;
-}
-
-bool StabsSectionsBuilder::CUBoundary(const string &filename) {
- FinishCU();
- // Add a header for the compilation unit.
- assert(!has_header_);
- assert(entry_count_ == 0);
- struct nlist *entry = stab_.Append();
- entry->n_type = N_UNDF;
- entry->n_other = 0;
- entry->n_desc = 0; // will be set to number of entries
- entry->n_value = 0; // will be set to size of .stabstr data
- entry->n_un.n_strx = stabstr_.Insert(filename);
- has_header_ = true;
- // The N_UNDF header isn't included in the symbol count, so we
- // shouldn't bump entry_count_ here.
- return true;
-}
+ // Data used in a compilation unit header STAB that we won't know until
+ // we've finished the compilation unit.
+ struct CUHeader {
+ // The final number of entries this compilation unit will hold.
+ Label final_entry_count;
-void StabsSectionsBuilder::FinishCU() {
- if (entry_count_ > 0) {
- // Get the strings first, so we can record their size in the header.
- string stabstr;
- stabstr_.GetSection(&stabstr);
- finished_cu_stabstr_ += stabstr;
-
- // Initialize our header, if we have one, and extract the .stab data.
- if (has_header_)
- stab_.SetHeader(entry_count_, stabstr.size());
- string stab;
- stab_.GetSection(&stab);
- finished_cu_stab_ += stab;
- }
+ // The final size of this compilation unit's strings.
+ Label final_string_size;
+ };
- stab_.Clear();
- stabstr_.Clear();
- has_header_ = false;
- entry_count_ = 0;
-}
+ // The strings for our STABS entries.
+ StringAssembler *string_assembler_;
-bool StabsSectionsBuilder::Error(const char *format, ...) {
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- error_count_++;
- if (error_count_ >= 20) {
- fprintf(stderr,
- "%s: lots of errors; is this really a mock stabs file?\n",
- filename_.c_str());
- return false;
- }
- return true;
-}
+ // The size of the 'value' field of stabs entries in this section.
+ size_t value_size_;
-void StabsSectionsBuilder::GetStab(string *section) {
- FinishCU();
- *section = finished_cu_stab_;
-}
+ // The number of entries in this compilation unit so far.
+ size_t entry_count_;
-void StabsSectionsBuilder::GetStabstr(string *section) {
- FinishCU();
- *section = finished_cu_stabstr_;
-}
+ // Header labels for this compilation unit, if we've started one but not
+ // finished it.
+ CUHeader *cu_header_;
+};
class MockStabsReaderHandler: public StabsHandler {
public:
@@ -497,109 +223,138 @@ class MockStabsReaderHandler: public StabsHandler {
MOCK_METHOD1(MockWarning, void(const char *));
};
-// Create a StabsReader to parse the mock stabs data in INPUT_FILE,
-// passing the parsed information to HANDLER. If all goes well, return
-// the result of calling the reader's Process member function.
-// Otherwise, return false. INPUT_FILE should be relative to the top
-// of the source tree.
-static bool ApplyHandlerToMockStabsData(StabsHandler *handler,
- const string &input_file) {
- string full_input_file
- = string(getenv("srcdir") ? getenv("srcdir") : ".") + "/" + input_file;
-
- // Open the input file.
- std::ifstream stream(full_input_file.c_str());
- if (stream.fail()) {
- fprintf(stderr, "error opening mock stabs input file %s: %s\n",
- full_input_file.c_str(), strerror(errno));
- return false;
- }
-
- // Parse the mock stabs data, and produce stabs sections to use as
- // test input to the reader.
- StabsSectionsBuilder builder(full_input_file);
- MockStabsParser mock_parser(full_input_file, &stream, &builder);
- if (!mock_parser.Process())
- return false;
+// Create a StabsReader to parse the mock stabs data in STABS_ASSEMBLER and
+// STRINGS_ASSEMBLER, and pass the parsed information to HANDLER. Use the
+// endianness and value size of STABS_ASSEMBLER to parse the data. If all
+// goes well, return the result of calling the reader's Process member
+// function. Otherwise, return false.
+static bool ApplyHandlerToMockStabsData(StabsAssembler *stabs_assembler,
+ StringAssembler *strings_assembler,
+ StabsHandler *handler) {
string stab, stabstr;
- builder.GetStab(&stab);
- builder.GetStabstr(&stabstr);
+ if (!stabs_assembler->GetContents(&stab) ||
+ !strings_assembler->GetContents(&stabstr))
+ return false;
// Run the parser on the test input, passing whatever we find to HANDLER.
StabsReader reader(
reinterpret_cast<const uint8_t *>(stab.data()), stab.size(),
reinterpret_cast<const uint8_t *>(stabstr.data()), stabstr.size(),
+ stabs_assembler->endianness() == kBigEndian,
+ stabs_assembler->value_size(),
handler);
return reader.Process();
}
TEST(StabsReader, MockStabsInput) {
+ StringAssembler strings;
+ StabsAssembler stabs(&strings);
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(4);
+ stabs
+ .Stab(N_SO, 149, 40232, 0x18a2a72bU, "builddir/")
+ .Stab(N_FUN, 83, 50010, 0x91a5353fU,
+ "not the SO with source file name we expected ")
+ .Stab(N_SO, 165, 24791, 0xfe69d23cU, "")
+ .Stab(N_SO, 184, 34178, 0xca4d883aU, "builddir1/")
+ .Stab(N_SO, 83, 40859, 0xd2fe5df3U, "file1.c")
+ .Stab(N_LSYM, 147, 39565, 0x60d4bb8aU, "not the FUN we're looking for")
+ .Stab(N_FUN, 120, 50271, 0xa049f4b1U, "fun1")
+ .Stab(N_BINCL, 150, 15694, 0xef65c659U,
+ "something to ignore in a FUN body")
+ .Stab(N_SLINE, 147, 4967, 0xd904b3f, "")
+ .Stab(N_SOL, 177, 56135, 0xbd97b1dcU, "header.h")
+ .Stab(N_SLINE, 130, 24610, 0x90f145b, "")
+ .Stab(N_FUN, 45, 32441, 0xbf27cf93U,
+ "fun2:some stabs type info here:to trim from the name")
+ .Stab(N_SLINE, 138, 39002, 0x8148b87, "")
+ .Stab(N_SOL, 60, 49318, 0x1d06e025U, "file1.c")
+ .Stab(N_SLINE, 29, 52163, 0x6eebbb7, "")
+ .Stab(N_SO, 167, 4647, 0xd04b7448U, "")
+ .Stab(N_LSYM, 58, 37837, 0xe6b14d37U, "")
+ .Stab(N_SO, 152, 7810, 0x11759f10U, "file3.c")
+ .Stab(N_SO, 218, 12447, 0x11cfe4b5U, "");
+
MockStabsReaderHandler mock_handler;
{
InSequence s;
- EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file1.c"),
- 0x42, StrEq("builddir1/")))
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("file1.c"), 0xd2fe5df3U,
+ StrEq("builddir1/")))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0x62))
+ EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0xa049f4b1U))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, Line(0xe4, StrEq("file1.c"), 91))
+ EXPECT_CALL(mock_handler,
+ Line(0xa049f4b1U + 0xd904b3f, StrEq("file1.c"), 4967))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, Line(0x164, StrEq("header.h"), 111))
+ EXPECT_CALL(mock_handler,
+ Line(0xa049f4b1U + 0x90f145b, StrEq("header.h"), 24610))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndFunction(0x112))
+ EXPECT_CALL(mock_handler, EndFunction(0xbf27cf93U))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0x112))
+ EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0xbf27cf93U))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, Line(0x234, StrEq("header.h"), 131))
+ EXPECT_CALL(mock_handler,
+ Line(0xbf27cf93U + 0x8148b87, StrEq("header.h"), 39002))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, Line(0x254, StrEq("file1.c"), 151))
+ EXPECT_CALL(mock_handler,
+ Line(0xbf27cf93U + 0x6eebbb7, StrEq("file1.c"), 52163))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndFunction(0x152))
+ EXPECT_CALL(mock_handler, EndFunction(0xd04b7448U))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndCompilationUnit(0x152))
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0xd04b7448U))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"),
- 0x182, NULL))
+ 0x11759f10U, NULL))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndCompilationUnit(0x192))
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x11cfe4b5U))
.WillOnce(Return(true));
}
- ASSERT_TRUE(ApplyHandlerToMockStabsData(
- &mock_handler,
- "common/testdata/stabs_reader_unittest.input1"));
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(&stabs, &strings, &mock_handler));
}
TEST(StabsReader, AbruptCU) {
+ StringAssembler strings;
+ StabsAssembler stabs(&strings);
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(4);
+ stabs.Stab(N_SO, 177, 23446, 0xbf10d5e4, "file2-1.c");
+
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
- StartCompilationUnit(StrEq("file2-1.c"), 0x12, NULL))
+ StartCompilationUnit(StrEq("file2-1.c"), 0xbf10d5e4, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
}
- ASSERT_TRUE(ApplyHandlerToMockStabsData(
- &mock_handler,
- "common/testdata/stabs_reader_unittest.input2"));
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(&stabs, &strings, &mock_handler));
}
TEST(StabsReader, AbruptFunction) {
MockStabsReaderHandler mock_handler;
+ StringAssembler strings;
+ StabsAssembler stabs(&strings);
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(8);
+ stabs
+ .Stab(N_SO, 218, 26631, 0xb83ddf10U, "file3-1.c")
+ .Stab(N_FUN, 113, 24765, 0xbbd4a145U, "fun3_1");
{
InSequence s;
EXPECT_CALL(mock_handler,
- StartCompilationUnit(StrEq("file3-1.c"), 0x12, NULL))
+ StartCompilationUnit(StrEq("file3-1.c"), 0xb83ddf10U, NULL))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0x22))
+ EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0xbbd4a145U))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndFunction(NULL))
.WillOnce(Return(true));
@@ -607,12 +362,16 @@ TEST(StabsReader, AbruptFunction) {
.WillOnce(Return(true));
}
- ASSERT_TRUE(ApplyHandlerToMockStabsData(
- &mock_handler,
- "common/testdata/stabs_reader_unittest.input3"));
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(&stabs, &strings, &mock_handler));
}
TEST(StabsReader, NoCU) {
+ StringAssembler strings;
+ StabsAssembler stabs(&strings);
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(8);
+ stabs.Stab(N_SO, 161, 25673, 0x8f676e7bU, "build-directory/");
+
MockStabsReaderHandler mock_handler;
EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
@@ -620,64 +379,80 @@ TEST(StabsReader, NoCU) {
EXPECT_CALL(mock_handler, StartFunction(_, _))
.Times(0);
- ASSERT_TRUE(ApplyHandlerToMockStabsData(
- &mock_handler,
- "common/testdata/stabs_reader_unittest.input4"));
-
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(&stabs, &strings, &mock_handler));
}
TEST(StabsReader, NoCUEnd) {
+ StringAssembler strings;
+ StabsAssembler stabs(&strings);
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(8);
+ stabs
+ .Stab(N_SO, 116, 58280, 0x2f7493c9U, "file5-1.c")
+ .Stab(N_SO, 224, 23057, 0xf9f1d50fU, "file5-2.c");
+
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
- StartCompilationUnit(StrEq("file5-1.c"), 0x12, NULL))
+ StartCompilationUnit(StrEq("file5-1.c"), 0x2f7493c9U, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler,
- StartCompilationUnit(StrEq("file5-2.c"), 0x22, NULL))
+ StartCompilationUnit(StrEq("file5-2.c"), 0xf9f1d50fU, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
}
- ASSERT_TRUE(ApplyHandlerToMockStabsData(
- &mock_handler,
- "common/testdata/stabs_reader_unittest.input5"));
-
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(&stabs, &strings, &mock_handler));
}
TEST(StabsReader, MultipleCUs) {
+ StringAssembler strings;
+ StabsAssembler stabs(&strings);
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(4);
+ stabs
+ .StartCU("antimony")
+ .Stab(N_SO, 49, 26043, 0x7e259f1aU, "antimony")
+ .Stab(N_FUN, 101, 63253, 0x7fbcccaeU, "arsenic")
+ .Stab(N_SO, 124, 37175, 0x80b0014cU, "")
+ .EndCU()
+ .StartCU("aluminum")
+ .Stab(N_SO, 72, 23084, 0x86756839U, "aluminum")
+ .Stab(N_FUN, 59, 3305, 0xa8e120b0U, "selenium")
+ .Stab(N_SO, 178, 56949, 0xbffff983U, "")
+ .EndCU();
+
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
- StartCompilationUnit(StrEq("antimony"), 0x12, NULL))
+ StartCompilationUnit(StrEq("antimony"), 0x7e259f1aU, NULL))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22))
+ EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x7fbcccaeU))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndFunction(0x32))
+ EXPECT_CALL(mock_handler, EndFunction(0x80b0014cU))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndCompilationUnit(0x32))
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x80b0014cU))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler,
- StartCompilationUnit(StrEq("aluminum"), 0x42, NULL))
+ StartCompilationUnit(StrEq("aluminum"), 0x86756839U, NULL))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52))
+ EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0xa8e120b0U))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndFunction(0x62))
+ EXPECT_CALL(mock_handler, EndFunction(0xbffff983U))
.WillOnce(Return(true));
- EXPECT_CALL(mock_handler, EndCompilationUnit(0x62))
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0xbffff983U))
.WillOnce(Return(true));
}
- ASSERT_TRUE(ApplyHandlerToMockStabsData(
- &mock_handler,
- "common/testdata/stabs_reader_unittest.input6"));
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(&stabs, &strings, &mock_handler));
}
// name duplication
diff --git a/src/common/test_assembler.cc b/src/common/test_assembler.cc
index 3fd7cad0..54092f37 100644
--- a/src/common/test_assembler.cc
+++ b/src/common/test_assembler.cc
@@ -203,6 +203,7 @@ void Label::Binding::Get(Binding **base, u_int64_t *addend) {
template<typename Inserter>
static inline void InsertEndian(TestAssembler::Endianness endianness,
size_t size, u_int64_t number, Inserter dest) {
+ assert(size > 0);
if (endianness == kLittleEndian) {
for (size_t i = 0; i < size; i++) {
*dest++ = (char) (number & 0xff);
diff --git a/src/common/testdata/stabs_reader_unittest.input1 b/src/common/testdata/stabs_reader_unittest.input1
deleted file mode 100644
index 44b3d0a3..00000000
--- a/src/common/testdata/stabs_reader_unittest.input1
+++ /dev/null
@@ -1,19 +0,0 @@
-SO 10 11 0x02 builddir/
-FUN 20 21 0x12 not the SO with source file name we expected
-SO 30 31 0x22
-SO 40 41 0x32 builddir1/
-SO 50 51 0x42 file1.c
-LSYM 60 61 0x52 not the FUN we're looking for
-FUN 70 71 0x62 fun1
-BINCL 80 81 0x72 something to ignore in a FUN body
-SLINE 90 91 0x82
-SOL 100 101 0x92 header.h
-SLINE 110 111 0x102
-FUN 120 121 0x112 fun2:some stabs type info here, to trim from the name
-SLINE 130 131 0x122
-SOL 140 141 0x132 file1.c
-SLINE 150 151 0x142
-SO 160 161 0x152
-LSYM 170 171 0x162
-SO 180 181 0x182 file3.c
-SO 190 191 0x192
diff --git a/src/common/testdata/stabs_reader_unittest.input2 b/src/common/testdata/stabs_reader_unittest.input2
deleted file mode 100644
index 82efeb92..00000000
--- a/src/common/testdata/stabs_reader_unittest.input2
+++ /dev/null
@@ -1 +0,0 @@
-SO 10 11 0x12 file2-1.c
diff --git a/src/common/testdata/stabs_reader_unittest.input3 b/src/common/testdata/stabs_reader_unittest.input3
deleted file mode 100644
index 73e6a302..00000000
--- a/src/common/testdata/stabs_reader_unittest.input3
+++ /dev/null
@@ -1,2 +0,0 @@
-SO 10 11 0x12 file3-1.c
-FUN 20 21 0x22 fun3_1
diff --git a/src/common/testdata/stabs_reader_unittest.input4 b/src/common/testdata/stabs_reader_unittest.input4
deleted file mode 100644
index dfdbf677..00000000
--- a/src/common/testdata/stabs_reader_unittest.input4
+++ /dev/null
@@ -1 +0,0 @@
-SO 10 11 0x12 build-directory/
diff --git a/src/common/testdata/stabs_reader_unittest.input5 b/src/common/testdata/stabs_reader_unittest.input5
deleted file mode 100644
index 7a447c88..00000000
--- a/src/common/testdata/stabs_reader_unittest.input5
+++ /dev/null
@@ -1,2 +0,0 @@
-SO 10 11 0x12 file5-1.c
-SO 20 21 0x22 file5-2.c
diff --git a/src/common/testdata/stabs_reader_unittest.input6 b/src/common/testdata/stabs_reader_unittest.input6
deleted file mode 100644
index 54c2c9d4..00000000
--- a/src/common/testdata/stabs_reader_unittest.input6
+++ /dev/null
@@ -1,8 +0,0 @@
-cu-boundary antimony
-SO 10 11 0x12 antimony
-FUN 20 21 0x22 arsenic
-SO 30 31 0x32
-cu-boundary aluminum
-SO 40 41 0x42 aluminum
-FUN 50 51 0x52 selenium
-SO 60 61 0x62
diff --git a/src/tools/linux/dump_syms/Makefile b/src/tools/linux/dump_syms/Makefile
index 4d50c3aa..800e3add 100644
--- a/src/tools/linux/dump_syms/Makefile
+++ b/src/tools/linux/dump_syms/Makefile
@@ -154,6 +154,7 @@ stabs_reader_unittest: \
gtest-all.o \
gtest_main.o \
stabs_reader.o \
+ test_assembler.o \
$(empty)
CPP_EXECUTABLES += stabs_reader_unittest
stabs_reader_unittest.o: stabs_reader_unittest.cc
@@ -360,7 +361,7 @@ $(CPP_EXECUTABLES): %: %.o
# Allow #include directives to refer to files below 'src'; generate
# dependency files automatically; and I doubt _REENTRANT is needed at all.
-BREAKPAD_CPPFLAGS = -I$(SRC) -MMD -D_REENTRANT
+BREAKPAD_CPPFLAGS = -I$(SRC) -MMD -D_REENTRANT -DHAVE_A_OUT_H
# Bring in whatever dependency files we have generated by compiling with -MMD.
-include *.d
@@ -383,7 +384,7 @@ clean::
### appropriate for Google C++ Testing Framework test programs. But
### you can provide your own commands.
check-%: %
- srcdir=$(SRC) $(TEST_WRAPPER) ./$< $(TEST_ARGS)
+ $(TEST_WRAPPER) ./$< $(TEST_ARGS)
### Generic coverage reporting rules.