aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/linux/stabs_reader.cc19
-rw-r--r--src/common/linux/stabs_reader.h7
-rw-r--r--src/common/linux/stabs_reader_unittest.cc203
3 files changed, 194 insertions, 35 deletions
diff --git a/src/common/linux/stabs_reader.cc b/src/common/linux/stabs_reader.cc
index 23b6edbc..67956930 100644
--- a/src/common/linux/stabs_reader.cc
+++ b/src/common/linux/stabs_reader.cc
@@ -43,6 +43,8 @@ StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
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);
@@ -50,7 +52,7 @@ StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
}
const char *StabsReader::SymbolString() {
- ptrdiff_t offset = symbol_->n_un.n_strx;
+ ptrdiff_t offset = string_offset_ + symbol_->n_un.n_strx;
if (offset < 0 || (size_t) offset >= stabstr_size_) {
handler_->Warning("symbol %d: name offset outside the string section",
symbol_ - symbols_);
@@ -67,6 +69,21 @@ bool StabsReader::Process() {
if (symbol_->n_type == N_SO) {
if (! ProcessCompilationUnit())
return false;
+ } else if (symbol_->n_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
+ // take up in the .stabstr section. Each CU's strings are
+ // separate; the n_strx values are offsets within the current
+ // CU's portion of the .stabstr section.
+ //
+ // As an optimization, the GNU linker combines all the
+ // compilation units into one, with a single N_UNDF at the
+ // beginning. However, other linkers, like Gold, do not perform
+ // this optimization.
+ string_offset_ = next_cu_string_offset_;
+ next_cu_string_offset_ = SymbolValue();
+ symbol_++;
} else
symbol_++;
}
diff --git a/src/common/linux/stabs_reader.h b/src/common/linux/stabs_reader.h
index 2465942b..06bd115e 100644
--- a/src/common/linux/stabs_reader.h
+++ b/src/common/linux/stabs_reader.h
@@ -96,6 +96,13 @@ class StabsReader {
StabsHandler *handler_;
+ // The offset of the current compilation unit's strings within stabstr_.
+ size_t string_offset_;
+
+ // The value string_offset_ should have for the next compilation unit,
+ // as established by N_UNDF entries.
+ size_t next_cu_string_offset_;
+
// The current symbol we're processing.
const struct nlist *symbol_;
diff --git a/src/common/linux/stabs_reader_unittest.cc b/src/common/linux/stabs_reader_unittest.cc
index 836b3a4d..ad5f90a3 100644
--- a/src/common/linux/stabs_reader_unittest.cc
+++ b/src/common/linux/stabs_reader_unittest.cc
@@ -53,6 +53,7 @@ using std::ostringstream;
using std::string;
using ::testing::_;
+using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
@@ -70,7 +71,8 @@ namespace {
// sections, and then pass those to StabsReader to look at. The
// human-readable file is called a "mock stabs file".
//
-// Each line of a mock stabs file should have the following form:
+// 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
//
@@ -91,6 +93,10 @@ namespace {
// 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
@@ -108,6 +114,11 @@ class MockStabsHandler {
// 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.
@@ -159,7 +170,7 @@ bool MockStabsParser::Process() {
// terminating newline.
for(;;) {
string line;
- std::getline(*stream_, line, '\n');
+ getline(*stream_, line, '\n');
if (line.empty() && stream_->eof())
break;
line_number_++;
@@ -190,24 +201,32 @@ bool MockStabsParser::ParseLine(const string &line) {
// Parse and validate the stabs type.
string typeName;
linestream >> typeName;
- 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);
+ 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);
+ }
}
// A class for constructing .stab sections.
@@ -227,9 +246,15 @@ class StabSection {
// 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();
private:
// The array of stabs entries,
@@ -248,11 +273,23 @@ struct nlist *StabSection::Append() {
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
@@ -273,6 +310,9 @@ class StabstrSection {
// 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;
@@ -317,6 +357,12 @@ void StabstrSection::GetSection(string *section) {
}
}
+void StabstrSection::Clear() {
+ string_indices_.clear();
+ string_indices_[""] = 0;
+ next_byte_ = 1;
+}
+
// A mock stabs parser handler class that builds .stab and .stabstr
// sections.
class StabsSectionsBuilder: public MockStabsHandler {
@@ -325,13 +371,15 @@ class StabsSectionsBuilder: public MockStabsHandler {
// 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) { }
+ 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);
- virtual bool Error(const char *format, ...);
+ 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.
@@ -339,10 +387,24 @@ class StabsSectionsBuilder: public MockStabsHandler {
void GetStabstr(string *section);
private:
- StabSection stab_; // stabs entries we've seen
- StabstrSection stabstr_; // and the strings they love
- const string &filename_; // input filename, for error messages
- int error_count_; // number of errors we've seen so far
+ // 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,
@@ -354,9 +416,48 @@ bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char 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;
}
+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;
+ }
+
+ stab_.Clear();
+ stabstr_.Clear();
+ has_header_ = false;
+ entry_count_ = 0;
+}
+
bool StabsSectionsBuilder::Error(const char *format, ...) {
va_list args;
va_start(args, format);
@@ -373,11 +474,13 @@ bool StabsSectionsBuilder::Error(const char *format, ...) {
}
void StabsSectionsBuilder::GetStab(string *section) {
- stab_.GetSection(section);
+ FinishCU();
+ *section = finished_cu_stab_;
}
void StabsSectionsBuilder::GetStabstr(string *section) {
- stabstr_.GetSection(section);
+ FinishCU();
+ *section = finished_cu_stabstr_;
}
class MockStabsReaderHandler: public StabsHandler {
@@ -428,7 +531,7 @@ static bool ApplyHandlerToMockStabsData(StabsHandler *handler,
return reader.Process();
}
-TEST(StabsReaderTestCase, MockStabsInput) {
+TEST(StabsReader, MockStabsInput) {
MockStabsReaderHandler mock_handler;
{
@@ -467,7 +570,7 @@ TEST(StabsReaderTestCase, MockStabsInput) {
"common/linux/testdata/stabs_reader_unittest.input1"));
}
-TEST(StabsReaderTestCase, AbruptCU) {
+TEST(StabsReader, AbruptCU) {
MockStabsReaderHandler mock_handler;
{
@@ -485,7 +588,7 @@ TEST(StabsReaderTestCase, AbruptCU) {
"common/linux/testdata/stabs_reader_unittest.input2"));
}
-TEST(StabsReaderTestCase, AbruptFunction) {
+TEST(StabsReader, AbruptFunction) {
MockStabsReaderHandler mock_handler;
{
@@ -507,7 +610,7 @@ TEST(StabsReaderTestCase, AbruptFunction) {
"common/linux/testdata/stabs_reader_unittest.input3"));
}
-TEST(StabsReaderTestCase, NoCU) {
+TEST(StabsReader, NoCU) {
MockStabsReaderHandler mock_handler;
EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
@@ -521,7 +624,7 @@ TEST(StabsReaderTestCase, NoCU) {
}
-TEST(StabsReaderTestCase, NoCUEnd) {
+TEST(StabsReader, NoCUEnd) {
MockStabsReaderHandler mock_handler;
{
@@ -545,4 +648,36 @@ TEST(StabsReaderTestCase, NoCUEnd) {
}
+TEST(StabsReader, MultipleCUs) {
+ MockStabsReaderHandler mock_handler;
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("antimony"), 0x12, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0x32))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x32))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("aluminum"), 0x42, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0x62))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x62))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData(
+ &mock_handler,
+ "common/linux/testdata/stabs_reader_unittest.input6"));
+}
+
+// name duplication
+
} // anonymous namespace