mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-08-04 07:21:11 +00:00
Put PUBLIC lines in Mac symbol files.
Exported symbols on Mach-O binaries are defined in a STABS section. This patch makes stabs_reader handle them, adds support for Extern symbols in the Module class (which are output as PUBLIC lines in symbol files), and the proper processing in stabs_to_module to hook it all up. A=mark R=jimb at http://breakpad.appspot.com/163001 and http://breakpad.appspot.com/267001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@778 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
68b256aed3
commit
bf25801d83
|
@ -55,6 +55,9 @@ Module::~Module() {
|
||||||
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
|
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
|
||||||
it != stack_frame_entries_.end(); it++)
|
it != stack_frame_entries_.end(); it++)
|
||||||
delete *it;
|
delete *it;
|
||||||
|
for (ExternSet::iterator it = externs_.begin();
|
||||||
|
it != externs_.end(); it++)
|
||||||
|
delete *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::SetLoadAddress(Address address) {
|
void Module::SetLoadAddress(Address address) {
|
||||||
|
@ -64,7 +67,8 @@ void Module::SetLoadAddress(Address address) {
|
||||||
void Module::AddFunction(Function *function) {
|
void Module::AddFunction(Function *function) {
|
||||||
std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function);
|
std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function);
|
||||||
if (!ret.second) {
|
if (!ret.second) {
|
||||||
// Free the duplicate we failed to insert because we own it.
|
// Free the duplicate that was not inserted because this Module
|
||||||
|
// now owns it.
|
||||||
delete function;
|
delete function;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,11 +83,25 @@ void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
|
||||||
stack_frame_entries_.push_back(stack_frame_entry);
|
stack_frame_entries_.push_back(stack_frame_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::AddExtern(Extern *ext) {
|
||||||
|
std::pair<ExternSet::iterator,bool> ret = externs_.insert(ext);
|
||||||
|
if (!ret.second) {
|
||||||
|
// Free the duplicate that was not inserted because this Module
|
||||||
|
// now owns it.
|
||||||
|
delete ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Module::GetFunctions(vector<Function *> *vec,
|
void Module::GetFunctions(vector<Function *> *vec,
|
||||||
vector<Function *>::iterator i) {
|
vector<Function *>::iterator i) {
|
||||||
vec->insert(i, functions_.begin(), functions_.end());
|
vec->insert(i, functions_.begin(), functions_.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::GetExterns(vector<Extern *> *vec,
|
||||||
|
vector<Extern *>::iterator i) {
|
||||||
|
vec->insert(i, externs_.begin(), externs_.end());
|
||||||
|
}
|
||||||
|
|
||||||
Module::File *Module::FindFile(const string &name) {
|
Module::File *Module::FindFile(const string &name) {
|
||||||
// A tricky bit here. The key of each map entry needs to be a
|
// A tricky bit here. The key of each map entry needs to be a
|
||||||
// pointer to the entry's File's name string. This means that we
|
// pointer to the entry's File's name string. This means that we
|
||||||
|
@ -211,6 +229,16 @@ bool Module::Write(FILE *stream) {
|
||||||
return ReportError();
|
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()))
|
||||||
|
return ReportError();
|
||||||
|
}
|
||||||
|
|
||||||
// Write out 'STACK CFI INIT' and 'STACK CFI' records.
|
// Write out 'STACK CFI INIT' and 'STACK CFI' records.
|
||||||
vector<StackFrameEntry *>::const_iterator frame_it;
|
vector<StackFrameEntry *>::const_iterator frame_it;
|
||||||
for (frame_it = stack_frame_entries_.begin();
|
for (frame_it = stack_frame_entries_.begin();
|
||||||
|
|
|
@ -65,6 +65,7 @@ class Module {
|
||||||
struct File;
|
struct File;
|
||||||
struct Function;
|
struct Function;
|
||||||
struct Line;
|
struct Line;
|
||||||
|
struct Extern;
|
||||||
|
|
||||||
// Addresses appearing in File, Function, and Line structures are
|
// Addresses appearing in File, Function, and Line structures are
|
||||||
// absolute, not relative to the the module's load address. That
|
// absolute, not relative to the the module's load address. That
|
||||||
|
@ -117,6 +118,12 @@ class Module {
|
||||||
int number; // The source line number.
|
int number; // The source line number.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An exported symbol.
|
||||||
|
struct Extern {
|
||||||
|
Address address;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
// A map from register names to postfix expressions that recover
|
// A map from register names to postfix expressions that recover
|
||||||
// their their values. This can represent a complete set of rules to
|
// their their values. This can represent a complete set of rules to
|
||||||
// follow at some address, or a set of changes to be applied to an
|
// follow at some address, or a set of changes to be applied to an
|
||||||
|
@ -155,6 +162,13 @@ class Module {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExternCompare {
|
||||||
|
bool operator() (const Extern *lhs,
|
||||||
|
const Extern *rhs) const {
|
||||||
|
return lhs->address < rhs->address;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Create a new module with the given name, operating system,
|
// Create a new module with the given name, operating system,
|
||||||
// architecture, and ID string.
|
// architecture, and ID string.
|
||||||
Module(const string &name, const string &os, const string &architecture,
|
Module(const string &name, const string &os, const string &architecture,
|
||||||
|
@ -187,11 +201,15 @@ class Module {
|
||||||
vector<Function *>::iterator end);
|
vector<Function *>::iterator end);
|
||||||
|
|
||||||
// Add STACK_FRAME_ENTRY to the module.
|
// Add STACK_FRAME_ENTRY to the module.
|
||||||
//
|
|
||||||
// This module owns all StackFrameEntry objects added with this
|
// This module owns all StackFrameEntry objects added with this
|
||||||
// function: destroying the module destroys them as well.
|
// function: destroying the module destroys them as well.
|
||||||
void AddStackFrameEntry(StackFrameEntry *stack_frame_entry);
|
void AddStackFrameEntry(StackFrameEntry *stack_frame_entry);
|
||||||
|
|
||||||
|
// Add PUBLIC to the module.
|
||||||
|
// This module owns all Extern objects added with this function:
|
||||||
|
// destroying the module destroys them as well.
|
||||||
|
void AddExtern(Extern *ext);
|
||||||
|
|
||||||
// If this module has a file named NAME, return a pointer to it. If
|
// If this module has a file named NAME, return a pointer to it. If
|
||||||
// it has none, then create one and return a pointer to the new
|
// it has none, then create one and return a pointer to the new
|
||||||
// file. This module owns all File objects created using these
|
// file. This module owns all File objects created using these
|
||||||
|
@ -210,6 +228,13 @@ class Module {
|
||||||
// appropriate interface.)
|
// appropriate interface.)
|
||||||
void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
|
void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
|
||||||
|
|
||||||
|
// Insert pointers to the externs added to this module at I in
|
||||||
|
// VEC. The pointed-to Externs are still owned by this module.
|
||||||
|
// (Since this is effectively a copy of the extern list, this is
|
||||||
|
// mostly useful for testing; other uses should probably get a more
|
||||||
|
// appropriate interface.)
|
||||||
|
void GetExterns(vector<Extern *> *vec, vector<Extern *>::iterator i);
|
||||||
|
|
||||||
// Clear VEC and fill it with pointers to the Files added to this
|
// Clear VEC and fill it with pointers to the Files added to this
|
||||||
// module, sorted by name. The pointed-to Files are still owned by
|
// module, sorted by name. The pointed-to Files are still owned by
|
||||||
// this module. (Since this is effectively a copy of the file list,
|
// this module. (Since this is effectively a copy of the file list,
|
||||||
|
@ -269,8 +294,13 @@ class Module {
|
||||||
// A map from filenames to File structures. The map's keys are
|
// A map from filenames to File structures. The map's keys are
|
||||||
// pointers to the Files' names.
|
// pointers to the Files' names.
|
||||||
typedef map<const string *, File *, CompareStringPtrs> FileByNameMap;
|
typedef map<const string *, File *, CompareStringPtrs> FileByNameMap;
|
||||||
|
|
||||||
|
// A set containing Function structures, sorted by address.
|
||||||
typedef set<Function *, FunctionCompare> FunctionSet;
|
typedef set<Function *, FunctionCompare> FunctionSet;
|
||||||
|
|
||||||
|
// A set containing Extern structures, sorted by address.
|
||||||
|
typedef set<Extern *, ExternCompare> ExternSet;
|
||||||
|
|
||||||
// The module owns all the files and functions that have been added
|
// The module owns all the files and functions that have been added
|
||||||
// to it; destroying the module frees the Files and Functions these
|
// to it; destroying the module frees the Files and Functions these
|
||||||
// point to.
|
// point to.
|
||||||
|
@ -280,6 +310,10 @@ class Module {
|
||||||
// The module owns all the call frame info entries that have been
|
// The module owns all the call frame info entries that have been
|
||||||
// added to it.
|
// added to it.
|
||||||
vector<StackFrameEntry *> stack_frame_entries_;
|
vector<StackFrameEntry *> stack_frame_entries_;
|
||||||
|
|
||||||
|
// The module owns all the externs that have been added to it;
|
||||||
|
// destroying the module frees the Externs these point to.
|
||||||
|
ExternSet externs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -455,3 +455,62 @@ TEST(Construct, FunctionsWithSameAddress) {
|
||||||
" _without_form\n",
|
" _without_form\n",
|
||||||
contents.c_str());
|
contents.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Externs should be written out as PUBLIC records, sorted by
|
||||||
|
// address.
|
||||||
|
TEST(Construct, Externs) {
|
||||||
|
FILE *f = checked_tmpfile();
|
||||||
|
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||||
|
|
||||||
|
// Two externs.
|
||||||
|
Module::Extern *extern1 = new(Module::Extern);
|
||||||
|
extern1->address = 0xffff;
|
||||||
|
extern1->name = "_abc";
|
||||||
|
Module::Extern *extern2 = new(Module::Extern);
|
||||||
|
extern2->address = 0xaaaa;
|
||||||
|
extern2->name = "_xyz";
|
||||||
|
|
||||||
|
m.AddExtern(extern1);
|
||||||
|
m.AddExtern(extern2);
|
||||||
|
|
||||||
|
m.Write(f);
|
||||||
|
checked_fflush(f);
|
||||||
|
rewind(f);
|
||||||
|
string contents = checked_read(f);
|
||||||
|
checked_fclose(f);
|
||||||
|
|
||||||
|
EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
|
||||||
|
MODULE_ID " " MODULE_NAME "\n"
|
||||||
|
"PUBLIC aaaa 0 _xyz\n"
|
||||||
|
"PUBLIC ffff 0 _abc\n",
|
||||||
|
contents.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Externs with the same address should only keep the first entry
|
||||||
|
// added.
|
||||||
|
TEST(Construct, DuplicateExterns) {
|
||||||
|
FILE *f = checked_tmpfile();
|
||||||
|
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||||
|
|
||||||
|
// Two externs.
|
||||||
|
Module::Extern *extern1 = new(Module::Extern);
|
||||||
|
extern1->address = 0xffff;
|
||||||
|
extern1->name = "_xyz";
|
||||||
|
Module::Extern *extern2 = new(Module::Extern);
|
||||||
|
extern2->address = 0xffff;
|
||||||
|
extern2->name = "_abc";
|
||||||
|
|
||||||
|
m.AddExtern(extern1);
|
||||||
|
m.AddExtern(extern2);
|
||||||
|
|
||||||
|
m.Write(f);
|
||||||
|
checked_fflush(f);
|
||||||
|
rewind(f);
|
||||||
|
string contents = checked_read(f);
|
||||||
|
checked_fclose(f);
|
||||||
|
|
||||||
|
EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
|
||||||
|
MODULE_ID " " MODULE_NAME "\n"
|
||||||
|
"PUBLIC ffff 0 _xyz\n",
|
||||||
|
contents.c_str());
|
||||||
|
}
|
||||||
|
|
|
@ -107,9 +107,20 @@ bool StabsReader::Process() {
|
||||||
string_offset_ = next_cu_string_offset_;
|
string_offset_ = next_cu_string_offset_;
|
||||||
next_cu_string_offset_ = iterator_->value;
|
next_cu_string_offset_ = iterator_->value;
|
||||||
++iterator_;
|
++iterator_;
|
||||||
} else
|
}
|
||||||
|
#if defined(HAVE_MACH_O_NLIST_H)
|
||||||
|
// Export symbols in Mach-O binaries look like this.
|
||||||
|
// This is necessary in order to be able to dump symbols
|
||||||
|
// from OS X system libraries.
|
||||||
|
else if ((iterator_->type & N_STAB) == 0 &&
|
||||||
|
(iterator_->type & N_TYPE) == N_SECT) {
|
||||||
|
ProcessExtern();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
++iterator_;
|
++iterator_;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,4 +293,19 @@ bool StabsReader::ProcessFunction() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StabsReader::ProcessExtern() {
|
||||||
|
#if defined(HAVE_MACH_O_NLIST_H)
|
||||||
|
assert(!iterator_->at_end &&
|
||||||
|
(iterator_->type & N_STAB) == 0 &&
|
||||||
|
(iterator_->type & N_TYPE) == N_SECT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO(mark): only do symbols in the text section?
|
||||||
|
if (!handler_->Extern(SymbolString(), iterator_->value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
++iterator_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -193,7 +193,11 @@ class StabsReader {
|
||||||
// Return true to continue processing, or false to abort.
|
// Return true to continue processing, or false to abort.
|
||||||
bool ProcessFunction();
|
bool ProcessFunction();
|
||||||
|
|
||||||
// The STABS entries we're parsing.
|
// Process an exported function symbol.
|
||||||
|
// Return true to continue processing, or false to abort.
|
||||||
|
bool ProcessExtern();
|
||||||
|
|
||||||
|
// The STABS entries being parsed.
|
||||||
ByteBuffer entries_;
|
ByteBuffer entries_;
|
||||||
|
|
||||||
// The string section to which the entries refer.
|
// The string section to which the entries refer.
|
||||||
|
@ -305,6 +309,12 @@ class StabsHandler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Report that an exported function NAME is present at ADDRESS.
|
||||||
|
// The size of the function is unknown.
|
||||||
|
virtual bool Extern(const std::string &name, uint64_t address) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Report a warning. FORMAT is a printf-like format string,
|
// Report a warning. FORMAT is a printf-like format string,
|
||||||
// specifying how to format the subsequent arguments.
|
// specifying how to format the subsequent arguments.
|
||||||
virtual void Warning(const char *format, ...) = 0;
|
virtual void Warning(const char *format, ...) = 0;
|
||||||
|
|
|
@ -221,6 +221,7 @@ class MockStabsReaderHandler: public StabsHandler {
|
||||||
MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t));
|
MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t));
|
||||||
MOCK_METHOD1(EndFunction, bool(uint64_t));
|
MOCK_METHOD1(EndFunction, bool(uint64_t));
|
||||||
MOCK_METHOD3(Line, bool(uint64_t, const char *, int));
|
MOCK_METHOD3(Line, bool(uint64_t, const char *, int));
|
||||||
|
MOCK_METHOD2(Extern, bool(const std::string &, uint64_t));
|
||||||
void Warning(const char *format, ...) { MockWarning(format); }
|
void Warning(const char *format, ...) { MockWarning(format); }
|
||||||
MOCK_METHOD1(MockWarning, void(const char *));
|
MOCK_METHOD1(MockWarning, void(const char *));
|
||||||
};
|
};
|
||||||
|
@ -555,6 +556,55 @@ TEST_F(Stabs, LeadingLine) {
|
||||||
ASSERT_TRUE(ApplyHandlerToMockStabsData());
|
ASSERT_TRUE(ApplyHandlerToMockStabsData());
|
||||||
}
|
}
|
||||||
|
|
||||||
// name duplication
|
|
||||||
|
#if defined(HAVE_MACH_O_NLIST_H)
|
||||||
|
// These tests have no meaning on non-Mach-O-based systems, as
|
||||||
|
// only Mach-O uses N_SECT to represent public symbols.
|
||||||
|
TEST_F(Stabs, OnePublicSymbol) {
|
||||||
|
stabs.set_endianness(kLittleEndian);
|
||||||
|
stabs.set_value_size(4);
|
||||||
|
|
||||||
|
const u_int32_t kExpectedAddress = 0x9000;
|
||||||
|
const string kExpectedFunctionName("public_function");
|
||||||
|
stabs
|
||||||
|
.Stab(N_SECT, 1, 0, kExpectedAddress, kExpectedFunctionName);
|
||||||
|
|
||||||
|
{
|
||||||
|
InSequence s;
|
||||||
|
EXPECT_CALL(mock_handler,
|
||||||
|
Extern(StrEq(kExpectedFunctionName),
|
||||||
|
kExpectedAddress))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(ApplyHandlerToMockStabsData());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Stabs, TwoPublicSymbols) {
|
||||||
|
stabs.set_endianness(kLittleEndian);
|
||||||
|
stabs.set_value_size(4);
|
||||||
|
|
||||||
|
const u_int32_t kExpectedAddress1 = 0xB0B0B0B0;
|
||||||
|
const string kExpectedFunctionName1("public_function");
|
||||||
|
const u_int32_t kExpectedAddress2 = 0xF0F0F0F0;
|
||||||
|
const string kExpectedFunctionName2("something else");
|
||||||
|
stabs
|
||||||
|
.Stab(N_SECT, 1, 0, kExpectedAddress1, kExpectedFunctionName1)
|
||||||
|
.Stab(N_SECT, 1, 0, kExpectedAddress2, kExpectedFunctionName2);
|
||||||
|
|
||||||
|
{
|
||||||
|
InSequence s;
|
||||||
|
EXPECT_CALL(mock_handler,
|
||||||
|
Extern(StrEq(kExpectedFunctionName1),
|
||||||
|
kExpectedAddress1))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(mock_handler,
|
||||||
|
Extern(StrEq(kExpectedFunctionName2),
|
||||||
|
kExpectedAddress2))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(ApplyHandlerToMockStabsData());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
|
@ -132,6 +132,22 @@ bool StabsToModule::Line(uint64_t address, const char *name, int number) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StabsToModule::Extern(const string &name, uint64_t address) {
|
||||||
|
Module::Extern *ext = new Module::Extern;
|
||||||
|
// Older libstdc++ demangle implementations can crash on unexpected
|
||||||
|
// input, so be careful about what gets passed in.
|
||||||
|
if (name.compare(0, 3, "__Z") == 0) {
|
||||||
|
ext->name = Demangle(name.substr(1));
|
||||||
|
} else if (name[0] == '_') {
|
||||||
|
ext->name = name.substr(1);
|
||||||
|
} else {
|
||||||
|
ext->name = name;
|
||||||
|
}
|
||||||
|
ext->address = address;
|
||||||
|
module_->AddExtern(ext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void StabsToModule::Warning(const char *format, ...) {
|
void StabsToModule::Warning(const char *format, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
|
|
@ -35,8 +35,8 @@
|
||||||
// STABS debugging information from a parser and adds it to a Breakpad
|
// STABS debugging information from a parser and adds it to a Breakpad
|
||||||
// symbol file.
|
// symbol file.
|
||||||
|
|
||||||
#ifndef COMMON_LINUX_DUMP_STABS_H__
|
#ifndef BREAKPAD_COMMON_STABS_TO_MODULE_H_
|
||||||
#define COMMON_LINUX_DUMP_STABS_H__
|
#define BREAKPAD_COMMON_STABS_TO_MODULE_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -51,12 +51,14 @@ namespace google_breakpad {
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
// A StabsToModule is a handler that receives parsed STABS
|
// A StabsToModule is a handler that receives parsed STABS debugging
|
||||||
// debugging information from a StabsReader, and uses that to populate
|
// information from a StabsReader, and uses that to populate
|
||||||
// a Module. (All classes are in the google_breakpad namespace.) A
|
// a Module. (All classes are in the google_breakpad namespace.) A
|
||||||
// Module represents the contents of a Breakpad symbol file, and knows
|
// Module represents the contents of a Breakpad symbol file, and knows
|
||||||
// how to write itself out as such. A StabsToModule thus acts as
|
// how to write itself out as such. A StabsToModule thus acts as
|
||||||
// the bridge between STABS and Breakpad data.
|
// the bridge between STABS and Breakpad data.
|
||||||
|
// When processing Darwin Mach-O files, this also receives public linker
|
||||||
|
// symbols, like those found in system libraries.
|
||||||
class StabsToModule: public google_breakpad::StabsHandler {
|
class StabsToModule: public google_breakpad::StabsHandler {
|
||||||
public:
|
public:
|
||||||
// Receive parsed debugging information from a StabsReader, and
|
// Receive parsed debugging information from a StabsReader, and
|
||||||
|
@ -77,6 +79,7 @@ class StabsToModule: public google_breakpad::StabsHandler {
|
||||||
bool StartFunction(const string &name, uint64_t address);
|
bool StartFunction(const string &name, uint64_t address);
|
||||||
bool EndFunction(uint64_t address);
|
bool EndFunction(uint64_t address);
|
||||||
bool Line(uint64_t address, const char *name, int number);
|
bool Line(uint64_t address, const char *name, int number);
|
||||||
|
bool Extern(const string &name, uint64_t address);
|
||||||
void Warning(const char *format, ...);
|
void Warning(const char *format, ...);
|
||||||
|
|
||||||
// Do any final processing necessary to make module_ contain all the
|
// Do any final processing necessary to make module_ contain all the
|
||||||
|
@ -137,4 +140,4 @@ class StabsToModule: public google_breakpad::StabsHandler {
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
#endif // COMMON_LINUX_DUMP_STABS_H__
|
#endif // BREAKPAD_COMMON_STABS_TO_MODULE_H_
|
||||||
|
|
|
@ -74,6 +74,35 @@ TEST(StabsToModule, SimpleCU) {
|
||||||
EXPECT_EQ(174823314, line->number);
|
EXPECT_EQ(174823314, line->number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
// Function name mangling can vary by compiler, so only run mangled-name
|
||||||
|
// tests on GCC for simplicity's sake.
|
||||||
|
TEST(StabsToModule, Externs) {
|
||||||
|
Module m("name", "os", "arch", "id");
|
||||||
|
StabsToModule h(&m);
|
||||||
|
|
||||||
|
// Feed in a few Extern symbols.
|
||||||
|
EXPECT_TRUE(h.Extern("_foo", 0xffff));
|
||||||
|
EXPECT_TRUE(h.Extern("__Z21dyldGlobalLockAcquirev", 0xaaaa));
|
||||||
|
EXPECT_TRUE(h.Extern("_MorphTableGetNextMorphChain", 0x1111));
|
||||||
|
h.Finalize();
|
||||||
|
|
||||||
|
// Now check to see what has been added to the Module.
|
||||||
|
vector<Module::Extern *> externs;
|
||||||
|
m.GetExterns(&externs, externs.end());
|
||||||
|
ASSERT_EQ((size_t) 3, externs.size());
|
||||||
|
Module::Extern *extern1 = externs[0];
|
||||||
|
EXPECT_STREQ("MorphTableGetNextMorphChain", extern1->name.c_str());
|
||||||
|
EXPECT_EQ((Module::Address)0x1111, extern1->address);
|
||||||
|
Module::Extern *extern2 = externs[1];
|
||||||
|
EXPECT_STREQ("dyldGlobalLockAcquire()", extern2->name.c_str());
|
||||||
|
EXPECT_EQ((Module::Address)0xaaaa, extern2->address);
|
||||||
|
Module::Extern *extern3 = externs[2];
|
||||||
|
EXPECT_STREQ("foo", extern3->name.c_str());
|
||||||
|
EXPECT_EQ((Module::Address)0xffff, extern3->address);
|
||||||
|
}
|
||||||
|
#endif // __GNUC__
|
||||||
|
|
||||||
TEST(StabsToModule, DuplicateFunctionNames) {
|
TEST(StabsToModule, DuplicateFunctionNames) {
|
||||||
Module m("name", "os", "arch", "id");
|
Module m("name", "os", "arch", "id");
|
||||||
StabsToModule h(&m);
|
StabsToModule h(&m);
|
||||||
|
@ -154,6 +183,9 @@ TEST(InferSizes, LineSize) {
|
||||||
EXPECT_EQ(87660088, line2->number);
|
EXPECT_EQ(87660088, line2->number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
// Function name mangling can vary by compiler, so only run mangled-name
|
||||||
|
// tests on GCC for simplicity's sake.
|
||||||
TEST(FunctionNames, Mangled) {
|
TEST(FunctionNames, Mangled) {
|
||||||
Module m("name", "os", "arch", "id");
|
Module m("name", "os", "arch", "id");
|
||||||
StabsToModule h(&m);
|
StabsToModule h(&m);
|
||||||
|
@ -188,6 +220,7 @@ TEST(FunctionNames, Mangled) {
|
||||||
EXPECT_EQ(0U, function->parameter_size);
|
EXPECT_EQ(0U, function->parameter_size);
|
||||||
ASSERT_EQ(0U, function->lines.size());
|
ASSERT_EQ(0U, function->lines.size());
|
||||||
}
|
}
|
||||||
|
#endif // __GNUC__
|
||||||
|
|
||||||
// The GNU toolchain can omit functions that are not used; however,
|
// The GNU toolchain can omit functions that are not used; however,
|
||||||
// when it does so, it doesn't clean up the debugging information that
|
// when it does so, it doesn't clean up the debugging information that
|
||||||
|
|
Loading…
Reference in a new issue