mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-03-12 13:56:27 +00:00
Added DWARF support to Breakpad client. Thanks to Google for open sourcing their DWARF code!
Modified dump_syms to detect dSYM bundles or a binary with DWARF data appropriately, and convert data from DWARF reader to dump_syms native structures R=danny.berlin (original writer of DWARF code) git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@286 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
@@ -47,6 +47,9 @@
|
||||
#import "dump_syms.h"
|
||||
#import "common/mac/file_id.h"
|
||||
#import "common/mac/macho_utilities.h"
|
||||
#import "common/mac/dwarf/dwarf2reader.h"
|
||||
#import "common/mac/dwarf/functioninfo.h"
|
||||
#import "common/mac/dwarf/bytereader.h"
|
||||
|
||||
using google_breakpad::FileID;
|
||||
|
||||
@@ -65,6 +68,40 @@ static NSString *kUnknownSymbol = @"???";
|
||||
// for pruning out extraneous non-function symbols.
|
||||
static const int kTextSection = 1;
|
||||
|
||||
namespace __gnu_cxx {
|
||||
template<>
|
||||
struct hash<std::string> {
|
||||
size_t operator()(const std::string& k) const {
|
||||
return hash< const char* >()( k.c_str() );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Dump FunctionMap to stdout. Print address, function name, file
|
||||
// name, line number, lowpc, and highpc if available.
|
||||
void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) {
|
||||
for (dwarf2reader::FunctionMap::const_iterator iter = function_map.begin();
|
||||
iter != function_map.end(); ++iter) {
|
||||
if (iter->second->name.empty()) {
|
||||
continue;
|
||||
}
|
||||
printf("%08llx: %s", iter->first,
|
||||
iter->second->name.data());
|
||||
if (!iter->second->file.empty()) {
|
||||
printf(" - %s", iter->second->file.data());
|
||||
if (iter->second->line != 0) {
|
||||
printf(":%u", iter->second->line);
|
||||
}
|
||||
}
|
||||
if (iter->second->lowpc != 0 && iter->second->highpc != 0) {
|
||||
printf(" (%08llx - %08llx)\n",
|
||||
iter->second->lowpc,
|
||||
iter->second->highpc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@interface DumpSymbols(PrivateMethods)
|
||||
- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols;
|
||||
- (void)convertSymbols;
|
||||
@@ -73,10 +110,17 @@ static const int kTextSection = 1;
|
||||
- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadSymbolInfoForArchitecture;
|
||||
- (BOOL)loadDWARFSymbolInfo:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadSTABSSymbolInfo:(void *)base offset:(uint32_t)offset;
|
||||
- (void)generateSectionDictionary:(struct mach_header*)header;
|
||||
- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadModuleInfo;
|
||||
- (void)processDWARFLineNumberInfo:(dwarf2reader::LineMap*)line_map;
|
||||
- (void)processDWARFFunctionInfo:(dwarf2reader::FunctionMap*)address_to_funcinfo;
|
||||
- (void)processDWARFSourceFileInfo:(vector<dwarf2reader::SourceFileInfo>*) files;
|
||||
- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset;
|
||||
- (dwarf2reader::SectionMap*)getSectionMapForArchitecture:(NSString*)architecture;
|
||||
@end
|
||||
|
||||
@implementation DumpSymbols
|
||||
@@ -241,6 +285,7 @@ static const int kTextSection = 1;
|
||||
if (line && ![dict objectForKey:kAddressSourceLineKey])
|
||||
[dict setObject:[NSNumber numberWithUnsignedInt:line]
|
||||
forKey:kAddressSourceLineKey];
|
||||
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
@@ -277,7 +322,9 @@ static const int kTextSection = 1;
|
||||
int line = list->n_desc;
|
||||
|
||||
// __TEXT __text section
|
||||
uint32_t mainSection = [[sectionNumbers_ objectForKey:@"__TEXT__text" ] unsignedLongValue];
|
||||
NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_];
|
||||
|
||||
uint32_t mainSection = [[archSections objectForKey:@"__TEXT__text" ] sectionNumber];
|
||||
|
||||
// Extract debugging information:
|
||||
// Doc: http://developer.apple.com/documentation/DeveloperTools/gdb/stabs/stabs_toc.html
|
||||
@@ -303,7 +350,7 @@ static const int kTextSection = 1;
|
||||
sources_ = [[NSMutableDictionary alloc] init];
|
||||
// Save the source associated with an address
|
||||
[sources_ setObject:src forKey:address];
|
||||
|
||||
NSLog(@"Setting source %@ for %@", src, address);
|
||||
result = YES;
|
||||
}
|
||||
} else if (list->n_type == N_FUN) {
|
||||
@@ -321,7 +368,7 @@ static const int kTextSection = 1;
|
||||
[self addFunction:fn line:line address:list->n_value section:list->n_sect ];
|
||||
|
||||
result = YES;
|
||||
} else if (list->n_type == N_SLINE && list->n_sect == mainSection ) {
|
||||
} else if (list->n_type == N_SLINE && list->n_sect == mainSection) {
|
||||
[self addFunction:nil line:line address:list->n_value section:list->n_sect ];
|
||||
result = YES;
|
||||
} else if (((list->n_type & N_TYPE) == N_SECT) && !(list->n_type & N_STAB)) {
|
||||
@@ -339,8 +386,155 @@ static const int kTextSection = 1;
|
||||
#define SwapLongIfNeeded(a) (swap ? NXSwapLong(a) : (a))
|
||||
#define SwapIntIfNeeded(a) (swap ? NXSwapInt(a) : (a))
|
||||
#define SwapShortIfNeeded(a) (swap ? NXSwapShort(a) : (a))
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset {
|
||||
NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_];
|
||||
if ([archSections objectForKey:@"__DWARF__debug_info"]) {
|
||||
// Treat this this as debug information
|
||||
return [self loadDWARFSymbolInfo:base offset:offset];
|
||||
}
|
||||
|
||||
return [self loadSTABSSymbolInfo:base offset:offset];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadDWARFSymbolInfo:(void *)base offset:(uint32_t)offset {
|
||||
|
||||
struct mach_header *header = (struct mach_header *)
|
||||
((uint32_t)base + offset);
|
||||
BOOL swap = (header->magic == MH_CIGAM);
|
||||
|
||||
NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_];
|
||||
assert (archSections != nil);
|
||||
section *dbgInfoSection = [[archSections objectForKey:@"__DWARF__debug_info"] sectionPointer];
|
||||
uint32_t debugInfoSize = SwapLongIfNeeded(dbgInfoSection->size);
|
||||
|
||||
// i think this will break if run on a big-endian machine
|
||||
dwarf2reader::ByteReader byte_reader(swap ?
|
||||
dwarf2reader::ENDIANNESS_BIG :
|
||||
dwarf2reader::ENDIANNESS_LITTLE);
|
||||
|
||||
uint64_t dbgOffset = 0;
|
||||
|
||||
dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:architecture_];
|
||||
|
||||
while (dbgOffset < debugInfoSize) {
|
||||
// Prepare necessary objects.
|
||||
dwarf2reader::FunctionMap off_to_funcinfo;
|
||||
dwarf2reader::FunctionMap address_to_funcinfo;
|
||||
dwarf2reader::LineMap line_map;
|
||||
vector<dwarf2reader::SourceFileInfo> files;
|
||||
vector<string> dirs;
|
||||
|
||||
dwarf2reader::CULineInfoHandler line_info_handler(&files, &dirs,
|
||||
&line_map);
|
||||
|
||||
dwarf2reader::CUFunctionInfoHandler function_info_handler(&files, &dirs,
|
||||
&line_map,
|
||||
&off_to_funcinfo,
|
||||
&address_to_funcinfo,
|
||||
&line_info_handler,
|
||||
*oneArchitectureSectionMap,
|
||||
&byte_reader);
|
||||
|
||||
dwarf2reader::CompilationUnit compilation_unit(*oneArchitectureSectionMap,
|
||||
dbgOffset,
|
||||
&byte_reader,
|
||||
&function_info_handler);
|
||||
|
||||
dbgOffset += compilation_unit.Start();
|
||||
|
||||
// The next 3 functions take the info that the dwarf reader
|
||||
// gives and massages them into the data structures that
|
||||
// dump_syms uses
|
||||
[self processDWARFSourceFileInfo:&files];
|
||||
[self processDWARFFunctionInfo:&address_to_funcinfo];
|
||||
[self processDWARFLineNumberInfo:&line_map];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)processDWARFSourceFileInfo:(vector<dwarf2reader::SourceFileInfo>*) files {
|
||||
if (!sources_)
|
||||
sources_ = [[NSMutableDictionary alloc] init];
|
||||
// Save the source associated with an address
|
||||
vector<dwarf2reader::SourceFileInfo>::const_iterator iter = files->begin();
|
||||
for (; iter != files->end(); iter++) {
|
||||
NSString *sourceFile = [NSString stringWithUTF8String:(*iter).name.c_str()];
|
||||
if ((*iter).lowpc != ULLONG_MAX) {
|
||||
NSNumber *address = [NSNumber numberWithUnsignedLongLong:(*iter).lowpc];
|
||||
[sources_ setObject:sourceFile forKey:address];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processDWARFFunctionInfo:(dwarf2reader::FunctionMap*)address_to_funcinfo {
|
||||
for (dwarf2reader::FunctionMap::const_iterator iter = address_to_funcinfo->begin();
|
||||
iter != address_to_funcinfo->end(); ++iter) {
|
||||
if (iter->second->name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!addresses_)
|
||||
addresses_ = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:(*iter).second->lowpc];
|
||||
|
||||
[functionAddresses_ addObject:addressNum];
|
||||
|
||||
NSMutableDictionary *dict = [addresses_ objectForKey:addressNum];
|
||||
|
||||
if (!dict) {
|
||||
dict = [[NSMutableDictionary alloc] init];
|
||||
[addresses_ setObject:dict forKey:addressNum];
|
||||
[dict release];
|
||||
}
|
||||
|
||||
// set name of function if it isn't already set
|
||||
if (![dict objectForKey:kAddressSymbolKey]) {
|
||||
NSString *symbolName = [NSString stringWithUTF8String:iter->second->name.c_str()];
|
||||
[dict setObject:symbolName forKey:kAddressSymbolKey];
|
||||
}
|
||||
|
||||
// set line number for beginning of function
|
||||
if (![dict objectForKey:kAddressSourceLineKey])
|
||||
[dict setObject:[NSNumber numberWithUnsignedInt:iter->second->line]
|
||||
forKey:kAddressSourceLineKey];
|
||||
|
||||
// set function size by subtracting low PC from high PC
|
||||
if (![dict objectForKey:kFunctionSizeKey]) {
|
||||
[dict setObject:[NSNumber numberWithUnsignedLongLong:iter->second->highpc - iter->second->lowpc]
|
||||
forKey:kFunctionSizeKey];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processDWARFLineNumberInfo:(dwarf2reader::LineMap*)line_map {
|
||||
for (dwarf2reader::LineMap::const_iterator iter = line_map->begin();
|
||||
iter != line_map->end();
|
||||
++iter) {
|
||||
|
||||
NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:iter->first];
|
||||
NSMutableDictionary *dict = [addresses_ objectForKey:addressNum];
|
||||
|
||||
if (!dict) {
|
||||
dict = [[NSMutableDictionary alloc] init];
|
||||
[addresses_ setObject:dict forKey:addressNum];
|
||||
[dict release];
|
||||
}
|
||||
|
||||
if (![dict objectForKey:kAddressSourceLineKey]) {
|
||||
[dict setObject:[NSNumber numberWithUnsignedInt:iter->second.second]
|
||||
forKey:kAddressSourceLineKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadSTABSSymbolInfo:(void *)base offset:(uint32_t)offset {
|
||||
struct mach_header *header = (struct mach_header *)((uint32_t)base + offset);
|
||||
BOOL swap = (header->magic == MH_CIGAM);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
@@ -434,6 +628,7 @@ static const int kTextSection = 1;
|
||||
- (BOOL)loadSymbolInfoForArchitecture {
|
||||
NSMutableData *data = [[NSMutableData alloc]
|
||||
initWithContentsOfMappedFile:sourcePath_];
|
||||
|
||||
NSDictionary *headerInfo = [headers_ objectForKey:architecture_];
|
||||
void *base = [data mutableBytes];
|
||||
uint32_t offset =
|
||||
@@ -446,10 +641,28 @@ static const int kTextSection = 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (dwarf2reader::SectionMap*)getSectionMapForArchitecture:(NSString*)architecture {
|
||||
|
||||
string currentArch([architecture UTF8String]);
|
||||
dwarf2reader::SectionMap *oneArchitectureSectionMap;
|
||||
|
||||
ArchSectionMap::const_iterator iter = sectionsForArch_->find(currentArch);
|
||||
|
||||
if (iter == sectionsForArch_->end()) {
|
||||
oneArchitectureSectionMap = new dwarf2reader::SectionMap();
|
||||
sectionsForArch_->insert(make_pair(currentArch, oneArchitectureSectionMap));
|
||||
} else {
|
||||
oneArchitectureSectionMap = iter->second;
|
||||
}
|
||||
|
||||
return oneArchitectureSectionMap;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// build a dictionary of section numbers keyed off a string
|
||||
// which is the concatenation of the segment name and the section name
|
||||
- (void)generateSectionDictionary:(struct mach_header*)header {
|
||||
|
||||
BOOL swap = (header->magic == MH_CIGAM);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
struct load_command *cmd =
|
||||
@@ -457,8 +670,29 @@ static const int kTextSection = 1;
|
||||
uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT);
|
||||
uint32_t sectionNumber = 1; // section numbers are counted from 1
|
||||
|
||||
if (!sectionNumbers_)
|
||||
sectionNumbers_ = [[NSMutableDictionary alloc] init];
|
||||
cpu_type_t cpu = SwapIntIfNeeded(header->cputype);
|
||||
|
||||
NSString *arch;
|
||||
|
||||
if (cpu & CPU_ARCH_ABI64)
|
||||
arch = ((cpu & ~CPU_ARCH_ABI64) == CPU_TYPE_X86) ?
|
||||
@"x86_64" : @"ppc64";
|
||||
else
|
||||
arch = (cpu == CPU_TYPE_X86) ? @"x86" : @"ppc";
|
||||
|
||||
NSMutableDictionary *archSections;
|
||||
|
||||
if (!sectionData_) {
|
||||
sectionData_ = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
if (![sectionData_ objectForKey:architecture_]) {
|
||||
[sectionData_ setObject:[[NSMutableDictionary alloc] init] forKey:arch];
|
||||
}
|
||||
|
||||
archSections = [sectionData_ objectForKey:arch];
|
||||
|
||||
dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:arch];
|
||||
|
||||
// loop through every segment command, then through every section
|
||||
// contained inside each of them
|
||||
@@ -469,13 +703,18 @@ static const int kTextSection = 1;
|
||||
uint32_t nsects = SwapLongIfNeeded(seg->nsects);
|
||||
|
||||
for (uint32_t j = 0; j < nsects; ++j) {
|
||||
//printf("%d: %s %s\n", sectionNumber, seg->segname, sect->sectname );
|
||||
NSString *segSectName = [NSString stringWithFormat:@"%s%s",
|
||||
seg->segname, sect->sectname ];
|
||||
|
||||
[sectionNumbers_ setValue:[NSNumber numberWithUnsignedLong:sectionNumber]
|
||||
forKey:segSectName ];
|
||||
seg->segname, sect->sectname];
|
||||
|
||||
[archSections setObject:[[MachSection alloc] initWithMachSection:sect andNumber:sectionNumber]
|
||||
forKey:segSectName];
|
||||
|
||||
// filter out sections with size 0, offset 0
|
||||
if (sect->offset != 0 && sect->size != 0) {
|
||||
// fill sectionmap for dwarf reader
|
||||
oneArchitectureSectionMap->insert(make_pair(sect->sectname,make_pair(((const char*)header) + SwapLongIfNeeded(sect->offset), (size_t)SwapLongIfNeeded(sect->size))));
|
||||
}
|
||||
|
||||
++sect;
|
||||
++sectionNumber;
|
||||
}
|
||||
@@ -825,6 +1064,49 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
|
||||
|
||||
sourcePath_ = [path copy];
|
||||
|
||||
// Test for .DSYM bundle
|
||||
NSBundle *dsymBundle = [NSBundle bundleWithPath:sourcePath_];
|
||||
|
||||
if (dsymBundle) {
|
||||
|
||||
// we need to take the DSYM bundle path and remove it's
|
||||
// extension to get the name of the file inside the resources
|
||||
// directory of the bundle that actually has the DWARF
|
||||
// information
|
||||
// But, Xcode supports something called "Wrapper extension"(see
|
||||
// build settings), which would make the bundle name
|
||||
// /tmp/foo/test.kext.dSYM, but the dwarf binary name would
|
||||
// still be "test". so, now we loop through until deleting the
|
||||
// extension doesn't change the string
|
||||
|
||||
// e.g. suppose sourcepath_ is /tmp/foo/test.dSYM
|
||||
|
||||
NSString *dwarfBinName = [[sourcePath_ lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
// now, dwarfBinName is "test"
|
||||
|
||||
while (![[dwarfBinName stringByDeletingPathExtension] isEqualToString:dwarfBinName]) {
|
||||
dwarfBinName = [dwarfBinName stringByDeletingPathExtension];
|
||||
}
|
||||
|
||||
NSString *dwarfBinPath;
|
||||
dwarfBinPath = [dsymBundle pathForResource:dwarfBinName ofType:nil inDirectory:@"DWARF"];
|
||||
|
||||
if (dwarfBinPath == nil) {
|
||||
NSLog(@"The bundle passed on the command line does not appear to be a DWARF dSYM bundle");
|
||||
[self autorelease];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// otherwise we're good to go
|
||||
[sourcePath_ release];
|
||||
|
||||
sourcePath_ = [dwarfBinPath copy];
|
||||
NSLog(@"Loading DWARF dSYM file from %@", sourcePath_);
|
||||
}
|
||||
|
||||
sectionsForArch_ = new ArchSectionMap();
|
||||
|
||||
if (![self loadModuleInfo]) {
|
||||
[self autorelease];
|
||||
return nil;
|
||||
@@ -868,7 +1150,8 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
|
||||
[functionAddresses_ release];
|
||||
[sources_ release];
|
||||
[headers_ release];
|
||||
|
||||
delete sectionsForArch_;
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -899,7 +1182,7 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
|
||||
return NO;
|
||||
|
||||
[architecture_ autorelease];
|
||||
architecture_ = [architecture copy];
|
||||
architecture_ = [normalized copy];
|
||||
}
|
||||
|
||||
return isValid;
|
||||
@@ -931,3 +1214,23 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MachSection
|
||||
|
||||
- (id)initWithMachSection:(section *)sect andNumber:(uint32_t)sectionNumber {
|
||||
if ((self = [super init])) {
|
||||
sect_ = sect;
|
||||
sectionNumber_ = sectionNumber;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (section*)sectionPointer {
|
||||
return sect_;
|
||||
}
|
||||
|
||||
- (uint32_t)sectionNumber {
|
||||
return sectionNumber_;
|
||||
}
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user