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:
nealsid
2008-10-08 23:56:02 +00:00
parent a509f4ec4b
commit cb4aa6b804
21 changed files with 8525 additions and 31 deletions

View File

@@ -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