aboutsummaryrefslogtreecommitdiff
path: root/src/common/mac/dump_syms.mm
diff options
context:
space:
mode:
authornealsid <nealsid@4c0a9323-5329-0410-9bdc-e9ce6186880e>2008-10-08 23:56:02 +0000
committernealsid <nealsid@4c0a9323-5329-0410-9bdc-e9ce6186880e>2008-10-08 23:56:02 +0000
commitcb4aa6b804d9c4a5d43aa50bfbc46257bc55c001 (patch)
tree43e29af9b6c3a85db4964e3b53b4e8ad64f2ca6b /src/common/mac/dump_syms.mm
parentGenerate Windows full memory dumps as a separate file. Patch by Huan Ren (diff)
downloadbreakpad-cb4aa6b804d9c4a5d43aa50bfbc46257bc55c001.tar.xz
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
Diffstat (limited to 'src/common/mac/dump_syms.mm')
-rw-r--r--src/common/mac/dump_syms.mm327
1 files changed, 315 insertions, 12 deletions
diff --git a/src/common/mac/dump_syms.mm b/src/common/mac/dump_syms.mm
index 133c6ab2..d5ae55f8 100644
--- a/src/common/mac/dump_syms.mm
+++ b/src/common/mac/dump_syms.mm
@@ -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