From d31c8b02925a1b20c09ee9ab771322353aea6267 Mon Sep 17 00:00:00 2001 From: waylonis Date: Tue, 23 Jan 2007 19:17:03 +0000 Subject: Changes to support patch #108: - Calculate unique file id for mach-o files - Add file id support to dump_syms and symupload tools - Fix return values of tools to indicate success or failure - Change dump_syms class to be Objective-C++ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@109 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/mac/handler/minidump_generator.cc | 33 +- src/client/mac/handler/minidump_generator.h | 3 +- src/common/mac/dump_syms.h | 6 - src/common/mac/dump_syms.m | 808 -------------------- src/common/mac/dump_syms.mm | 847 +++++++++++++++++++++ src/common/mac/macho_walker.cc | 5 +- src/tools/mac/crash_report/crash_report.mm | 1 + .../crash_report.xcodeproj/project.pbxproj | 42 +- .../dump_syms/dump_syms.xcodeproj/project.pbxproj | 40 +- src/tools/mac/dump_syms/dump_syms_tool.m | 38 +- src/tools/mac/symupload/symupload.m | 42 +- .../symupload/symupload.xcodeproj/project.pbxproj | 2 +- 12 files changed, 989 insertions(+), 878 deletions(-) delete mode 100644 src/common/mac/dump_syms.m create mode 100644 src/common/mac/dump_syms.mm (limited to 'src') diff --git a/src/client/mac/handler/minidump_generator.cc b/src/client/mac/handler/minidump_generator.cc index 65b4e6f2..3446785e 100644 --- a/src/client/mac/handler/minidump_generator.cc +++ b/src/client/mac/handler/minidump_generator.cc @@ -39,6 +39,7 @@ #include "client/mac/handler/minidump_generator.h" #include "client/minidump_file_writer-inl.h" +#include "common/mac/file_id.h" #include "common/mac/string_utilities.h" using MacStringUtils::ConvertToString; @@ -492,6 +493,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index, if (!header) return false; + int cpu_type = header->cputype; unsigned long slide = _dyld_get_image_vmaddr_slide(index); const char* name = _dyld_get_image_name(index); const struct load_command *cmd = @@ -513,7 +515,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index, module->size_of_image = seg->vmsize; module->module_name_rva = string_location.rva; - if (!WriteCVRecord(module, name)) + if (!WriteCVRecord(module, cpu_type, name)) return false; return true; @@ -540,7 +542,7 @@ static int FindExecutableModule() { return 0; } -bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, +bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, const char *module_path) { TypedMDRVA cv(&writer_); @@ -566,14 +568,25 @@ bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; cv_ptr->age = 0; - // Convert the string to MDGUID - // TODO(waylonis): - // MacOS doesn't currently have a uuid string, so we'll just write a - // placeholder here. - cv_ptr->signature.data1 = 0; - cv_ptr->signature.data2 = 0; - cv_ptr->signature.data3 = 0; - memset(cv_ptr->signature.data4, 0, sizeof(cv_ptr->signature.data4)); + // Get the module identifier + FileID file_id(module_path); + unsigned char identifier[16]; + + if (file_id.MachoIdentifier(cpu_type, identifier)) { + cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | + (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | + (uint32_t)identifier[3]; + cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; + cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; + cv_ptr->signature.data4[0] = identifier[8]; + cv_ptr->signature.data4[1] = identifier[9]; + cv_ptr->signature.data4[2] = identifier[10]; + cv_ptr->signature.data4[3] = identifier[11]; + cv_ptr->signature.data4[4] = identifier[12]; + cv_ptr->signature.data4[5] = identifier[13]; + cv_ptr->signature.data4[6] = identifier[14]; + cv_ptr->signature.data4[7] = identifier[15]; + } return true; } diff --git a/src/client/mac/handler/minidump_generator.h b/src/client/mac/handler/minidump_generator.h index 53c76041..f1be0603 100644 --- a/src/client/mac/handler/minidump_generator.h +++ b/src/client/mac/handler/minidump_generator.h @@ -95,7 +95,8 @@ class MinidumpGenerator { bool WriteContext(thread_state_data_t state, MDLocationDescriptor *register_location); bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread); - bool WriteCVRecord(MDRawModule *module, const char *module_path); + bool WriteCVRecord(MDRawModule *module, int cpu_type, + const char *module_path); bool WriteModuleStream(unsigned int index, MDRawModule *module); // disallow copy ctor and operator= diff --git a/src/common/mac/dump_syms.h b/src/common/mac/dump_syms.h index 38c7d3ce..bfc96374 100644 --- a/src/common/mac/dump_syms.h +++ b/src/common/mac/dump_syms.h @@ -38,7 +38,6 @@ @protected NSString *sourcePath_; // Source of symbols (STRONG) NSString *architecture_; // Architecture to extract (STRONG) - NSString *uuidStr_; // Module UUID (STRONG) NSMutableDictionary *addresses_; // Addresses and symbols (STRONG) NSMutableDictionary *sources_; // Address and Source file paths (STRONG) NSMutableArray *cppAddresses_; // Addresses of C++ symbols (STRONG) @@ -56,11 +55,6 @@ - (BOOL)setArchitecture:(NSString *)architecture; - (NSString *)architecture; -// Specify a UUID to output with the symbol file. -// If none specified, a new one will be created -- (void)setUUID:(NSString *)uuidStr; -- (NSString *)uuid; - // Write the symbols to |symbolFilePath|. Return YES if successful. - (BOOL)writeSymbolFile:(NSString *)symbolFilePath; diff --git a/src/common/mac/dump_syms.m b/src/common/mac/dump_syms.m deleted file mode 100644 index d133e120..00000000 --- a/src/common/mac/dump_syms.m +++ /dev/null @@ -1,808 +0,0 @@ -// -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// dump_syms.m: Create a symbol file for use with minidumps - -#include -#include - -#include -#include -#include -#include -#include -#include - -#import - -#import "dump_syms.h" - -static NSString *kAddressSymbolKey = @"symbol"; -static NSString *kAddressConvertedSymbolKey = @"converted_symbol"; -static NSString *kAddressSourceLineKey = @"line"; -static NSString *kFunctionSizeKey = @"size"; -static NSString *kHeaderBaseAddressKey = @"baseAddr"; -static NSString *kHeaderSizeKey = @"size"; -static NSString *kHeaderOffsetKey = @"offset"; // Offset to the header -static NSString *kHeaderIs64BitKey = @"is64"; -static NSString *kUnknownSymbol = @"???"; - -@interface DumpSymbols(PrivateMethods) -- (NSString *)stringFromTask:(NSString *)action args:(NSArray *)args - standardIn:(NSFileHandle *)standardIn; -- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols; -- (void)convertSymbols; -- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address; -- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table; -- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset; -- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset; -- (BOOL)loadSymbolInfoForArchitecture; -- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset; -- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset; -- (BOOL)loadModuleInfo; -- (NSMutableString *)generateSymbolFileString; -@end - -@implementation DumpSymbols -//============================================================================= -- (NSString *)stringFromTask:(NSString *)action args:(NSArray *)args - standardIn:(NSFileHandle *)standardIn { - NSTask *task = [[NSTask alloc] init]; - [task setLaunchPath:action]; - NSPipe *pipe = [NSPipe pipe]; - [task setStandardOutput:pipe]; - NSFileHandle *output = [pipe fileHandleForReading]; - - if (standardIn) - [task setStandardInput:standardIn]; - - if (args) - [task setArguments:args]; - - [task launch]; - - // This seems a bit strange, but when using [task waitUntilExit], it hangs - // waiting for data, but there isn't any. So, we'll poll for data, - // take a short nap, and then ask again - BOOL done = NO; - NSMutableData *allData = [NSMutableData data]; - NSData *data = nil; - int exceptionCount = 0; - - while (!done) { - data = nil; - // If there's a communications problem with the task, this might throw - // an exception. We'll catch and keep trying. - @try { - data = [output availableData]; - } - @catch (NSException *e) { - ++exceptionCount; - } - - [allData appendData:data]; - - // Loop over the data until we're no longer returning data. If we're - // still receiving data, sleep for 1/2 second and let the task - // continue. If we keep receiving exceptions, bail out - if (![data length] && data || exceptionCount > 10) - done = YES; - else - usleep(500); - } - - // Gather any remaining data - [task waitUntilExit]; - data = [output availableData]; - [allData appendData:data]; - [task release]; - - return [[[NSString alloc] initWithData:allData - encoding:NSUTF8StringEncoding] autorelease]; -} - -//============================================================================= -- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols { - NSString *action = @"/usr/bin/c++filt"; - int count = [symbols count]; - - // It's possible that we have too many symbols on the command line. - // Unfortunately, c++filt doesn't take a file containing names, so we'll - // copy the symbols to a temporary file and use that as stdin. - char buffer[PATH_MAX]; - snprintf(buffer, sizeof(buffer), "/tmp/dump_syms_filtXXXXX"); - int fd = mkstemp(buffer); - char nl = '\n'; - for (unsigned int i = 0; i < count; ++i) { - const char *symbol = [[symbols objectAtIndex:i] UTF8String]; - write(fd, symbol, strlen(symbol)); - write(fd, &nl, 1); - } - - // Reset to the beginning and wrap up with a file handle - lseek(fd, 0, SEEK_SET); - NSArray *args = [NSArray arrayWithObject:@"-n"]; - NSFileHandle *fh = [[NSFileHandle alloc] initWithFileDescriptor:fd - closeOnDealloc:YES]; - NSArray *result = [[self stringFromTask:action args:args standardIn:fh] - componentsSeparatedByString:@"\n"]; - - [fh release]; - - return result; -} - -//============================================================================= -- (void)convertSymbols { - unsigned int count = [cppAddresses_ count]; - NSMutableArray *symbols = [[NSMutableArray alloc] initWithCapacity:count]; - - // Sort addresses for processing - NSArray *addresses = [cppAddresses_ sortedArrayUsingSelector: - @selector(compare:)]; - - for (int i = 0; i < count; ++i) { - NSMutableDictionary *dict = [addresses_ objectForKey: - [addresses objectAtIndex:i]]; - NSString *symbol = [dict objectForKey:kAddressSymbolKey]; - - // Make sure that the symbol is valid - if ([symbol length] < 1) - symbol = kUnknownSymbol; - - [symbols addObject:symbol]; - } - - NSArray *converted = [self convertCPlusPlusSymbols:symbols]; - [symbols release]; - - for (int i = 0; i < count; ++i) { - NSMutableDictionary *dict = [addresses_ objectForKey: - [addresses objectAtIndex:i]]; - NSString *symbol = [converted objectAtIndex:i]; - - // Only add if this is a non-zero length symbol - if ([symbol length]) - [dict setObject:symbol forKey:kAddressConvertedSymbolKey]; - } -} - -//============================================================================= -- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address { - NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:address]; - - if (!address) - return; - - // If the function starts with "_Z" or "__Z" then add it to the list of - // addresses to run through the c++filt - BOOL isCPP = NO; - if ([name length] > 3) { - unichar buffer[3]; - [name getCharacters:buffer range:NSMakeRange(0, 3)]; - if (buffer[0] == '_') { - if (buffer[1] == '_' && buffer[2] == 'Z') { - // Remove the leading underscore - name = [name substringFromIndex:1]; - isCPP = YES; - } else if (buffer[1] == 'Z') { - isCPP = YES; - } - } - } - - if (isCPP) { - // Check if this is some debug symbol that ends in ".eh" I appears as - // though it's got to do with exception handling, but it's not the - // actual address of the function. So we'll ignore them - if ([[name substringFromIndex:[name length] - 3] isEqualToString:@".eh"]) - return; - - if (!cppAddresses_) - cppAddresses_ = [[NSMutableArray alloc] init]; - [cppAddresses_ addObject:addressNum]; - } else if ([name characterAtIndex:0] == '_') { - // Remove the leading underscore - name = [name substringFromIndex:1]; - } - - // If there's already an entry for this address, check and see if we can add - // either the symbol, or a missing line # - NSMutableDictionary *dict = [addresses_ objectForKey:addressNum]; - - if (!dict) { - dict = [[NSMutableDictionary alloc] init]; - [addresses_ setObject:dict forKey:addressNum]; - [dict release]; - } - - if (name && ![dict objectForKey:kAddressSymbolKey]) - [dict setObject:name forKey:kAddressSymbolKey]; - - if (line && ![dict objectForKey:kAddressSourceLineKey]) - [dict setObject:[NSNumber numberWithUnsignedInt:line] - forKey:kAddressSourceLineKey]; - - // Save the function name so that we can add the end of function address - if ([name length]) - lastFunctionStart_ = addressNum; -} - -//============================================================================= -- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table { - uint32_t n_strx = list->n_un.n_strx; - BOOL result = NO; - - // Extract debugging information: - // Doc: http://developer.apple.com/documentation/DeveloperTools/gdb/stabs/stabs_toc.html - // Header: /usr/include/mach-o/stab.h: - if (list->n_type == N_SO) { - NSString *src = [NSString stringWithUTF8String:&table[n_strx]]; - NSString *ext = [src pathExtension]; - NSNumber *address = [NSNumber numberWithUnsignedLongLong:list->n_value]; - - // TODO(waylonis):Ensure that we get the full path for the source file - // from the first N_SO record - // If there is an extension, we'll consider it source code - if ([ext length]) { - if (!sources_) - sources_ = [[NSMutableDictionary alloc] init]; - // Save the source associated with an address - [sources_ setObject:src forKey:address]; - - result = YES; - } - } else if (list->n_type == N_FUN) { - NSString *fn = [NSString stringWithUTF8String:&table[n_strx]]; - NSRange range = [fn rangeOfString:@":" options:NSBackwardsSearch]; - - if (![fn length] || !range.length) - return NO; - - // The function has a ":" followed by some stuff - fn = [fn substringToIndex:range.location]; - [self addFunction:fn line:list->n_desc address:list->n_value]; - result = YES; - } else if (list->n_type == N_SLINE) { - [self addFunction:nil line:list->n_desc address:list->n_value]; - result = YES; - } else if ((list->n_type & N_TYPE) == N_SECT && - !(list->n_type & N_STAB)) { - // Regular symbols or ones that are external - NSString *fn = [NSString stringWithUTF8String:&table[n_strx]]; - [self addFunction:fn line:0 address:list->n_value]; - result = YES; - } else if (list->n_type == N_ENSYM) { - // End of symbols for current function - if (lastFunctionStart_) { - unsigned long long start = [lastFunctionStart_ unsignedLongLongValue]; - unsigned long long size = list->n_value - start; - NSMutableDictionary *dict = [addresses_ objectForKey:lastFunctionStart_]; - assert(dict); - [dict setObject:[NSNumber numberWithUnsignedLongLong:size] - forKey:kFunctionSizeKey]; - } - } - - return result; -} - -#define SwapLongLongIfNeeded(a) (swap ? NXSwapLongLong(a) : (a)) -#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 { - struct mach_header *header = (struct mach_header *)(base + offset); - BOOL swap = (header->magic == MH_CIGAM); - uint32_t count = SwapLongIfNeeded(header->ncmds); - struct load_command *cmd = (void *)header + sizeof(struct mach_header); - uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB); - BOOL result = NO; - - if (!addresses_) - addresses_ = [[NSMutableDictionary alloc] init]; - - for (uint32_t i = 0; cmd && (i < count); i++) { - if (cmd->cmd == symbolTableCommand) { - struct symtab_command *symtab = (struct symtab_command *)cmd; - uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms); - uint32_t symoff = SwapLongIfNeeded(symtab->symoff); - uint32_t stroff = SwapLongIfNeeded(symtab->stroff); - struct nlist *list = (struct nlist *)(base + symoff + offset); - char *strtab = ((char *)header + stroff); - - // Process each command, looking for debugging stuff - for (uint32_t j = 0; j < ncmds; ++j, ++list) { - // Fill in an nlist_64 structure and process with that - struct nlist_64 nlist64 = { - .n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx), - .n_type = list->n_type, - .n_sect = list->n_sect, - .n_desc = SwapIntIfNeeded(list->n_desc), - .n_value = (uint64_t)SwapLongIfNeeded(list->n_value) - }; - - if ([self processSymbolItem:&nlist64 stringTable:strtab]) - result = YES; - } - } - - uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); - cmd = (struct load_command *)((char *)cmd + cmdSize); - } - - return result; -} - -//============================================================================= -- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset { - struct mach_header_64 *header = (struct mach_header_64 *)(base + offset); - BOOL swap = (header->magic == MH_CIGAM_64); - uint32_t count = SwapLongIfNeeded(header->ncmds); - struct load_command *cmd = (void *)header + sizeof(struct mach_header); - uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB); - BOOL result = NO; - - for (uint32_t i = 0; cmd && (i < count); i++) { - if (cmd->cmd == symbolTableCommand) { - struct symtab_command *symtab = (struct symtab_command *)cmd; - uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms); - uint32_t symoff = SwapLongIfNeeded(symtab->symoff); - uint32_t stroff = SwapLongIfNeeded(symtab->stroff); - struct nlist_64 *list = (struct nlist_64 *)(base + symoff); - char *strtab = ((char *)header + stroff); - - // Process each command, looking for debugging stuff - for (uint32_t j = 0; j < ncmds; ++j, ++list) { - if (!(list->n_type & (N_STAB | N_TYPE))) - continue; - - // Fill in an nlist_64 structure and process with that - struct nlist_64 nlist64 = { - .n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx), - .n_type = list->n_type, - .n_sect = list->n_sect, - .n_desc = SwapIntIfNeeded(list->n_desc), - .n_value = SwapLongLongIfNeeded(list->n_value) - }; - - if ([self processSymbolItem:&nlist64 stringTable:strtab]) - result = YES; - } - } - - uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); - cmd = (struct load_command *)((char *)cmd + cmdSize); - } - - return result; -} - -//============================================================================= -- (BOOL)loadSymbolInfoForArchitecture { - NSMutableData *data = [[NSMutableData alloc] - initWithContentsOfMappedFile:sourcePath_]; - NSDictionary *headerInfo = [headers_ objectForKey:architecture_]; - char *base = [data mutableBytes]; - uint32_t offset = - [[headerInfo objectForKey:kHeaderOffsetKey] unsignedLongValue]; - BOOL is64 = [[headerInfo objectForKey:kHeaderIs64BitKey] boolValue]; - BOOL result = is64 ? [self loadSymbolInfo64:base offset:offset] : - [self loadSymbolInfo:base offset:offset]; - - [data release]; - return result; -} - -//============================================================================= -- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset { - struct mach_header *header = (struct mach_header *)(base + offset); - BOOL swap = (header->magic == MH_CIGAM); - uint32_t count = SwapLongIfNeeded(header->ncmds); - struct load_command *cmd = (void *)header + sizeof(struct mach_header); - - for (uint32_t i = 0; cmd && (i < count); i++) { - uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT); - if (cmd->cmd == segmentCommand) { - struct segment_command *seg = (struct segment_command *)cmd; - if (!strcmp(seg->segname, "__TEXT")) { - uint32_t addr = SwapLongIfNeeded(seg->vmaddr); - uint32_t size = SwapLongIfNeeded(seg->vmsize); - cpu_type_t cpu = SwapIntIfNeeded(header->cputype); - NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86" : @"ppc"; - - [headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithUnsignedLongLong:(uint64_t)addr], - kHeaderBaseAddressKey, - [NSNumber numberWithUnsignedLongLong:(uint64_t)size], kHeaderSizeKey, - [NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey, - [NSNumber numberWithBool:NO], kHeaderIs64BitKey, - nil] forKey:cpuStr]; - - return YES; - } - } - - uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); - cmd = (struct load_command *)((char *)cmd + cmdSize); - } - - return NO; -} - -//============================================================================= -- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset { - struct mach_header_64 *header = (struct mach_header_64 *)(base + offset); - BOOL swap = (header->magic == MH_CIGAM_64); - uint32_t count = SwapLongIfNeeded(header->ncmds); - struct load_command *cmd = (void *)header + sizeof(struct mach_header_64); - - for (uint32_t i = 0; cmd && (i < count); i++) { - uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT_64); - if (cmd->cmd == segmentCommand) { - struct segment_command_64 *seg = (struct segment_command_64 *)cmd; - if (!strcmp(seg->segname, "__TEXT")) { - uint64_t addr = SwapLongLongIfNeeded(seg->vmaddr); - uint64_t size = SwapLongLongIfNeeded(seg->vmsize); - cpu_type_t cpu = SwapIntIfNeeded(header->cputype); - cpu &= (~CPU_ARCH_ABI64); - NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86_64" : @"ppc64"; - - [headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithUnsignedLongLong:addr], kHeaderBaseAddressKey, - [NSNumber numberWithUnsignedLongLong:size], kHeaderSizeKey, - [NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey, - [NSNumber numberWithBool:YES], kHeaderIs64BitKey, - nil] forKey:cpuStr]; - return YES; - } - } - - uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); - cmd = (struct load_command *)((char *)cmd + cmdSize); - } - - return NO; -} - -//============================================================================= -- (BOOL)loadModuleInfo { - uint64_t result = 0; - NSMutableData *data = [[NSMutableData alloc] - initWithContentsOfMappedFile:sourcePath_]; - char *bytes = [data mutableBytes]; - struct fat_header *fat = (struct fat_header *)bytes; - - if (!fat) { - [data release]; - return 0; - } - - // Gather some information based on the header - BOOL isFat = fat->magic == FAT_MAGIC || fat->magic == FAT_CIGAM; - BOOL is64 = fat->magic == MH_MAGIC_64 || fat->magic == MH_CIGAM_64; - BOOL is32 = fat->magic == MH_MAGIC || fat->magic == MH_CIGAM; - BOOL swap = fat->magic == FAT_CIGAM || fat->magic == MH_CIGAM_64 || - fat->magic == MH_CIGAM; - - if (!is64 && !is32 && !isFat) { - [data release]; - return 0; - } - - // Load any available architectures and save the information - headers_ = [[NSMutableDictionary alloc] init]; - - if (isFat) { - struct fat_arch *archs = - (struct fat_arch *)((void *)fat + sizeof(struct fat_header)); - uint32_t count = SwapLongIfNeeded(fat->nfat_arch); - - for (uint32_t i = 0; i < count; ++i) { - archs[i].cputype = SwapIntIfNeeded(archs[i].cputype); - archs[i].cpusubtype = SwapIntIfNeeded(archs[i].cpusubtype); - archs[i].offset = SwapLongIfNeeded(archs[i].offset); - archs[i].size = SwapLongIfNeeded(archs[i].size); - archs[i].align = SwapLongIfNeeded(archs[i].align); - - if (archs[i].cputype & CPU_ARCH_ABI64) - result = [self loadHeader64:bytes offset:archs[i].offset]; - else - result = [self loadHeader:bytes offset:archs[i].offset]; - } - } else if (is32) { - result = [self loadHeader:bytes offset:0]; - } else { - result = [self loadHeader64:bytes offset:0]; - } - - [data release]; - return result; -} - -//============================================================================= -static BOOL WriteFormat(int fd, const char *fmt, ...) { - va_list list; - char buffer[4096]; - ssize_t expected, written; - - va_start(list, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, list); - expected = strlen(buffer); - written = write(fd, buffer, strlen(buffer)); - va_end(list); - - return expected == written; -} - -//============================================================================= -- (BOOL)outputSymbolFile:(int)fd { - // Get the baseAddress for this architecture - NSDictionary *archDict = [headers_ objectForKey:architecture_]; - NSNumber *baseAddressNum = [archDict objectForKey:kHeaderBaseAddressKey]; - uint64_t baseAddress = - baseAddressNum ? [baseAddressNum unsignedLongLongValue] : 0; - NSNumber *moduleSizeNum = [archDict objectForKey:kHeaderSizeKey]; - uint64_t moduleSize = - moduleSizeNum ? [moduleSizeNum unsignedLongLongValue] : 0; - - lastFunctionStart_ = nil; - - // Gather the information - [self loadSymbolInfoForArchitecture]; - [self convertSymbols]; - - NSArray *sortedAddresses = [[addresses_ allKeys] - sortedArrayUsingSelector:@selector(compare:)]; - - // Module - if (!WriteFormat(fd, "MODULE mac %s %s 1 %s\n", [architecture_ UTF8String], - [uuidStr_ UTF8String], - [[sourcePath_ lastPathComponent] UTF8String])) - return NO; - - // Sources ordered by address - NSArray *sources = [[sources_ allKeys] - sortedArrayUsingSelector:@selector(compare:)]; - int sourceCount = [sources count]; - for (int i = 0; i < sourceCount; ++i) { - NSString *file = [sources_ objectForKey:[sources objectAtIndex:i]]; - if (!WriteFormat(fd, "FILE %d %s\n", i + 1, [file UTF8String])) - return NO; - } - - // Symbols - char terminatingChar = '\n'; - uint32_t fileIdx = 0, nextFileIdx = 0; - uint64_t nextSourceFileAddress = 0; - NSNumber *nextAddress; - uint64_t nextAddressVal; - int addressCount = [sortedAddresses count]; - - for (int i = 0; i < addressCount; ++i) { - NSNumber *address = [sortedAddresses objectAtIndex:i]; - uint64_t addressVal = [address unsignedLongLongValue] - baseAddress; - - // Get the next address to calculate the length - if (i + 1 < addressCount) { - nextAddress = [sortedAddresses objectAtIndex:i + 1]; - nextAddressVal = [nextAddress unsignedLongLongValue] - baseAddress; - } else { - nextAddressVal = baseAddress + moduleSize; - // The symbol reader doesn't want a trailing newline - terminatingChar = '\0'; - } - - NSDictionary *dict = [addresses_ objectForKey:address]; - NSNumber *line = [dict objectForKey:kAddressSourceLineKey]; - NSString *symbol = [dict objectForKey:kAddressConvertedSymbolKey]; - - // Find the source file (if any) that contains this address - while (sourceCount && (addressVal >= nextSourceFileAddress)) { - fileIdx = nextFileIdx; - - if (nextFileIdx < sourceCount) { - NSNumber *addr = [sources objectAtIndex:nextFileIdx]; - ++nextFileIdx; - nextSourceFileAddress = [addr unsignedLongLongValue] - baseAddress; - } else { - nextSourceFileAddress = baseAddress + moduleSize; - } - } - - if (!symbol) - symbol = [dict objectForKey:kAddressSymbolKey]; - - if (line) { - if (symbol) { - NSNumber *functionLength = [dict objectForKey:kFunctionSizeKey]; - uint64_t functionLengthVal = [functionLength unsignedLongLongValue]; - - // Function - if (!WriteFormat(fd, "FUNC %llx %llx 0 %s%c", addressVal, - functionLengthVal, [symbol UTF8String], - terminatingChar)) - return NO; - } - - // Source line - uint64_t length = nextAddressVal - addressVal; - if (!WriteFormat(fd, "%llx %llx %d %d%c", addressVal, length, - [line unsignedIntValue], fileIdx, terminatingChar)) - return NO; - } else { - // PUBLIC
- if (!WriteFormat(fd, "PUBLIC %llx 0 %s%c", addressVal, - [symbol UTF8String], terminatingChar)) - return NO; - } - } - - return YES; -} - -//============================================================================= -- (id)initWithContentsOfFile:(NSString *)path { - if (self = [super init]) { - - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { - NSLog(@"Missing source file: %@", path); - [self autorelease]; - return nil; - } - - sourcePath_ = [path copy]; - - if (![self loadModuleInfo]) { - NSLog(@"Not a valid Mach-o file: %@", path); - [self autorelease]; - return nil; - } - - // If there's more than one, use the native one - if ([headers_ count] > 1) { - const NXArchInfo *localArchInfo = NXGetLocalArchInfo(); - - if (localArchInfo) { - cpu_type_t cpu = localArchInfo->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"; - - [self setArchitecture:arch]; - } - } else { - // Specify the default architecture - [self setArchitecture:[[headers_ allKeys] objectAtIndex:0]]; - } - - [self setUUID:nil]; - } - - return self; -} - -//============================================================================= -- (NSArray *)availableArchitectures { - return [headers_ allKeys]; -} - -//============================================================================= -- (void)dealloc { - [sourcePath_ release]; - [architecture_ release]; - [uuidStr_ release]; - [addresses_ release]; - [sources_ release]; - [headers_ release]; - - [super dealloc]; -} - -//============================================================================= -- (BOOL)setArchitecture:(NSString *)architecture { - NSString *normalized = [architecture lowercaseString]; - BOOL isValid = NO; - - if ([normalized isEqualToString:@"ppc"]) { - isValid = YES; - } - else if ([normalized isEqualToString:@"i386"]) { - normalized = @"x86"; - isValid = YES; - } - else if ([normalized isEqualToString:@"x86"]) { - isValid = YES; - } - else if ([normalized isEqualToString:@"ppc64"]) { - isValid = YES; - } - else if ([normalized isEqualToString:@"x86_64"]) { - isValid = YES; - } - - if (isValid) { - if (![headers_ objectForKey:normalized]) - return NO; - - [architecture_ autorelease]; - architecture_ = [architecture copy]; - } - - return isValid; -} - -//============================================================================= -- (NSString *)architecture { - return architecture_; -} - -//============================================================================= -- (void)setUUID:(NSString *)uuidStr { - if (!uuidStr) { - CFUUIDRef uuid = CFUUIDCreate(NULL); - uuidStr = [(NSString *)CFUUIDCreateString(NULL, uuid) autorelease]; - } - - [uuidStr_ autorelease]; - uuidStr_ = [uuidStr copy]; -} - -//============================================================================= -- (NSString *)uuid { - return uuidStr_; -} - -//============================================================================= -- (BOOL)writeSymbolFile:(NSString *)destinationPath { - const char *dest = [destinationPath fileSystemRepresentation]; - int fd; - - if ([[destinationPath substringToIndex:1] isEqualToString:@"-"]) - fd = STDOUT_FILENO; - else - fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if (fd == -1) - return NO; - - BOOL result = [self outputSymbolFile:fd]; - - close(fd); - - return result; -} - -@end diff --git a/src/common/mac/dump_syms.mm b/src/common/mac/dump_syms.mm new file mode 100644 index 00000000..86410cbe --- /dev/null +++ b/src/common/mac/dump_syms.mm @@ -0,0 +1,847 @@ +// +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// dump_syms.mm: Create a symbol file for use with minidumps + +#include +#include + +#include +#include +#include +#include +#include +#include + +#import + +#import "dump_syms.h" +#import "common/mac/file_id.h" + +using google_airbag::FileID; + +static NSString *kAddressSymbolKey = @"symbol"; +static NSString *kAddressConvertedSymbolKey = @"converted_symbol"; +static NSString *kAddressSourceLineKey = @"line"; +static NSString *kFunctionSizeKey = @"size"; +static NSString *kHeaderBaseAddressKey = @"baseAddr"; +static NSString *kHeaderSizeKey = @"size"; +static NSString *kHeaderOffsetKey = @"offset"; // Offset to the header +static NSString *kHeaderIs64BitKey = @"is64"; +static NSString *kHeaderCPUTypeKey = @"cpuType"; +static NSString *kUnknownSymbol = @"???"; + +@interface DumpSymbols(PrivateMethods) +- (NSString *)stringFromTask:(NSString *)action args:(NSArray *)args + standardIn:(NSFileHandle *)standardIn; +- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols; +- (void)convertSymbols; +- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address; +- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table; +- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset; +- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset; +- (BOOL)loadSymbolInfoForArchitecture; +- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset; +- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset; +- (BOOL)loadModuleInfo; +- (NSMutableString *)generateSymbolFileString; +@end + +static BOOL StringHeadMatches(NSString *str, NSString *head) { + int headLen = [head length]; + int strLen = [str length]; + + if (headLen > strLen) + return NO; + + return [[str substringToIndex:headLen] isEqualToString:head]; +} + +static BOOL StringTailMatches(NSString *str, NSString *tail) { + int tailLen = [tail length]; + int strLen = [str length]; + + if (tailLen > strLen) + return NO; + + return [[str substringFromIndex:strLen - tailLen] isEqualToString:tail]; +} + +@implementation DumpSymbols +//============================================================================= +- (NSString *)stringFromTask:(NSString *)action args:(NSArray *)args + standardIn:(NSFileHandle *)standardIn { + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:action]; + NSPipe *pipe = [NSPipe pipe]; + [task setStandardOutput:pipe]; + NSFileHandle *output = [pipe fileHandleForReading]; + + if (standardIn) + [task setStandardInput:standardIn]; + + if (args) + [task setArguments:args]; + + [task launch]; + + // This seems a bit strange, but when using [task waitUntilExit], it hangs + // waiting for data, but there isn't any. So, we'll poll for data, + // take a short nap, and then ask again + BOOL done = NO; + NSMutableData *allData = [NSMutableData data]; + NSData *data = nil; + int exceptionCount = 0; + + while (!done) { + data = nil; + // If there's a communications problem with the task, this might throw + // an exception. We'll catch and keep trying. + @try { + data = [output availableData]; + } + @catch (NSException *e) { + ++exceptionCount; + } + + [allData appendData:data]; + + // Loop over the data until we're no longer returning data. If we're + // still receiving data, sleep for 1/2 second and let the task + // continue. If we keep receiving exceptions, bail out + if (![data length] && data || exceptionCount > 10) + done = YES; + else + usleep(500); + } + + // Gather any remaining data + [task waitUntilExit]; + data = [output availableData]; + [allData appendData:data]; + [task release]; + + return [[[NSString alloc] initWithData:allData + encoding:NSUTF8StringEncoding] autorelease]; +} + +//============================================================================= +- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols { + NSString *action = @"/usr/bin/c++filt"; + unsigned int count = [symbols count]; + + // It's possible that we have too many symbols on the command line. + // Unfortunately, c++filt doesn't take a file containing names, so we'll + // copy the symbols to a temporary file and use that as stdin. + char buffer[PATH_MAX]; + snprintf(buffer, sizeof(buffer), "/tmp/dump_syms_filtXXXXX"); + int fd = mkstemp(buffer); + char nl = '\n'; + for (unsigned int i = 0; i < count; ++i) { + const char *symbol = [[symbols objectAtIndex:i] UTF8String]; + write(fd, symbol, strlen(symbol)); + write(fd, &nl, 1); + } + + // Reset to the beginning and wrap up with a file handle + lseek(fd, 0, SEEK_SET); + NSArray *args = [NSArray arrayWithObject:@"-n"]; + NSFileHandle *fh = [[NSFileHandle alloc] initWithFileDescriptor:fd + closeOnDealloc:YES]; + NSArray *result = [[self stringFromTask:action args:args standardIn:fh] + componentsSeparatedByString:@"\n"]; + + [fh release]; + + return result; +} + +//============================================================================= +- (void)convertSymbols { + unsigned int count = [cppAddresses_ count]; + NSMutableArray *symbols = [[NSMutableArray alloc] initWithCapacity:count]; + + // Sort addresses for processing + NSArray *addresses = [cppAddresses_ sortedArrayUsingSelector: + @selector(compare:)]; + + for (unsigned int i = 0; i < count; ++i) { + NSMutableDictionary *dict = [addresses_ objectForKey: + [addresses objectAtIndex:i]]; + NSString *symbol = [dict objectForKey:kAddressSymbolKey]; + + // Make sure that the symbol is valid + if ([symbol length] < 1) + symbol = kUnknownSymbol; + + [symbols addObject:symbol]; + } + + NSArray *converted = [self convertCPlusPlusSymbols:symbols]; + [symbols release]; + + for (unsigned int i = 0; i < count; ++i) { + NSMutableDictionary *dict = [addresses_ objectForKey: + [addresses objectAtIndex:i]]; + NSString *symbol = [converted objectAtIndex:i]; + + // Only add if this is a non-zero length symbol + if ([symbol length]) + [dict setObject:symbol forKey:kAddressConvertedSymbolKey]; + } +} + +//============================================================================= +- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address { + NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:address]; + + if (!address) + return; + + // If the function starts with "_Z" or "__Z" then add it to the list of + // addresses to run through the c++filt + BOOL isCPP = NO; + + if (StringHeadMatches(name, @"__Z")) { + // Remove the leading underscore + name = [name substringFromIndex:1]; + isCPP = YES; + } else if (StringHeadMatches(name, @"_Z")) { + isCPP = YES; + } + + // Filter out non-functions + if (StringTailMatches(name, @".eh")) + return; + + if (StringTailMatches(name, @"__func__")) + return; + + if (StringTailMatches(name, @"GCC_except_table")) + return; + + if (isCPP) { + if (!cppAddresses_) + cppAddresses_ = [[NSMutableArray alloc] init]; + [cppAddresses_ addObject:addressNum]; + } else if ([name characterAtIndex:0] == '_') { + // Remove the leading underscore + name = [name substringFromIndex:1]; + } + + // If there's already an entry for this address, check and see if we can add + // either the symbol, or a missing line # + NSMutableDictionary *dict = [addresses_ objectForKey:addressNum]; + + if (!dict) { + dict = [[NSMutableDictionary alloc] init]; + [addresses_ setObject:dict forKey:addressNum]; + [dict release]; + } + + if (name && ![dict objectForKey:kAddressSymbolKey]) + [dict setObject:name forKey:kAddressSymbolKey]; + + if (line && ![dict objectForKey:kAddressSourceLineKey]) + [dict setObject:[NSNumber numberWithUnsignedInt:line] + forKey:kAddressSourceLineKey]; + + // Save the function name so that we can add the end of function address + if ([name length]) + lastFunctionStart_ = addressNum; +} + +//============================================================================= +- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table { + uint32_t n_strx = list->n_un.n_strx; + BOOL result = NO; + + // Extract debugging information: + // Doc: http://developer.apple.com/documentation/DeveloperTools/gdb/stabs/stabs_toc.html + // Header: /usr/include/mach-o/stab.h: + if (list->n_type == N_SO) { + NSString *src = [NSString stringWithUTF8String:&table[n_strx]]; + NSString *ext = [src pathExtension]; + NSNumber *address = [NSNumber numberWithUnsignedLongLong:list->n_value]; + + // TODO(waylonis):Ensure that we get the full path for the source file + // from the first N_SO record + // If there is an extension, we'll consider it source code + if ([ext length]) { + if (!sources_) + sources_ = [[NSMutableDictionary alloc] init]; + // Save the source associated with an address + [sources_ setObject:src forKey:address]; + + result = YES; + } + } else if (list->n_type == N_FUN) { + NSString *fn = [NSString stringWithUTF8String:&table[n_strx]]; + NSRange range = [fn rangeOfString:@":" options:NSBackwardsSearch]; + + if (![fn length] || !range.length) + return NO; + + // The function has a ":" followed by some stuff + fn = [fn substringToIndex:range.location]; + [self addFunction:fn line:list->n_desc address:list->n_value]; + result = YES; + } else if (list->n_type == N_SLINE) { + [self addFunction:nil line:list->n_desc address:list->n_value]; + result = YES; + } else if ((list->n_type & N_TYPE) == N_SECT && + !(list->n_type & N_STAB)) { + // Regular symbols or ones that are external + NSString *fn = [NSString stringWithUTF8String:&table[n_strx]]; + [self addFunction:fn line:0 address:list->n_value]; + result = YES; + } else if (list->n_type == N_ENSYM) { + // End of symbols for current function + if (lastFunctionStart_) { + unsigned long long start = [lastFunctionStart_ unsignedLongLongValue]; + unsigned long long size = list->n_value - start; + NSMutableDictionary *dict = [addresses_ objectForKey:lastFunctionStart_]; + assert(dict); + [dict setObject:[NSNumber numberWithUnsignedLongLong:size] + forKey:kFunctionSizeKey]; + } + } + + return result; +} + +#define SwapLongLongIfNeeded(a) (swap ? NXSwapLongLong(a) : (a)) +#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 { + struct mach_header *header = (struct mach_header *)((uint32_t)base + offset); + BOOL swap = (header->magic == MH_CIGAM); + uint32_t count = SwapLongIfNeeded(header->ncmds); + struct load_command *cmd = + (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); + uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB); + BOOL result = NO; + + if (!addresses_) + addresses_ = [[NSMutableDictionary alloc] init]; + + for (uint32_t i = 0; cmd && (i < count); i++) { + if (cmd->cmd == symbolTableCommand) { + struct symtab_command *symtab = (struct symtab_command *)cmd; + uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms); + uint32_t symoff = SwapLongIfNeeded(symtab->symoff); + uint32_t stroff = SwapLongIfNeeded(symtab->stroff); + struct nlist *list = (struct nlist *)((uint32_t)base + symoff + offset); + char *strtab = ((char *)header + stroff); + + // Process each command, looking for debugging stuff + for (uint32_t j = 0; j < ncmds; ++j, ++list) { + // Fill in an nlist_64 structure and process with that + struct nlist_64 nlist64; + nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx); + nlist64.n_type = list->n_type; + nlist64.n_sect = list->n_sect; + nlist64.n_desc = SwapIntIfNeeded(list->n_desc); + nlist64.n_value = (uint64_t)SwapLongIfNeeded(list->n_value); + + if ([self processSymbolItem:&nlist64 stringTable:strtab]) + result = YES; + } + } + + uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); + cmd = (struct load_command *)((uint32_t)cmd + cmdSize); + } + + return result; +} + +//============================================================================= +- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset { + struct mach_header_64 *header = (struct mach_header_64 *) + ((uint32_t)base + offset); + BOOL swap = (header->magic == MH_CIGAM_64); + uint32_t count = SwapLongIfNeeded(header->ncmds); + struct load_command *cmd = + (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); + uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB); + BOOL result = NO; + + for (uint32_t i = 0; cmd && (i < count); i++) { + if (cmd->cmd == symbolTableCommand) { + struct symtab_command *symtab = (struct symtab_command *)cmd; + uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms); + uint32_t symoff = SwapLongIfNeeded(symtab->symoff); + uint32_t stroff = SwapLongIfNeeded(symtab->stroff); + struct nlist_64 *list = (struct nlist_64 *)((uint32_t)base + symoff); + char *strtab = ((char *)header + stroff); + + // Process each command, looking for debugging stuff + for (uint32_t j = 0; j < ncmds; ++j, ++list) { + if (!(list->n_type & (N_STAB | N_TYPE))) + continue; + + // Fill in an nlist_64 structure and process with that + struct nlist_64 nlist64; + nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx); + nlist64.n_type = list->n_type; + nlist64.n_sect = list->n_sect; + nlist64.n_desc = SwapIntIfNeeded(list->n_desc); + nlist64.n_value = SwapLongLongIfNeeded(list->n_value); + + if ([self processSymbolItem:&nlist64 stringTable:strtab]) + result = YES; + } + } + + uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); + cmd = (struct load_command *)((uint32_t)cmd + cmdSize); + } + + return result; +} + +//============================================================================= +- (BOOL)loadSymbolInfoForArchitecture { + NSMutableData *data = [[NSMutableData alloc] + initWithContentsOfMappedFile:sourcePath_]; + NSDictionary *headerInfo = [headers_ objectForKey:architecture_]; + void *base = [data mutableBytes]; + uint32_t offset = + [[headerInfo objectForKey:kHeaderOffsetKey] unsignedLongValue]; + BOOL is64 = [[headerInfo objectForKey:kHeaderIs64BitKey] boolValue]; + BOOL result = is64 ? [self loadSymbolInfo64:base offset:offset] : + [self loadSymbolInfo:base offset:offset]; + + [data release]; + return result; +} + +//============================================================================= +- (BOOL)loadHeader:(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); + struct load_command *cmd = + (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); + + for (uint32_t i = 0; cmd && (i < count); i++) { + uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT); + if (cmd->cmd == segmentCommand) { + struct segment_command *seg = (struct segment_command *)cmd; + if (!strcmp(seg->segname, "__TEXT")) { + uint32_t addr = SwapLongIfNeeded(seg->vmaddr); + uint32_t size = SwapLongIfNeeded(seg->vmsize); + cpu_type_t cpu = SwapIntIfNeeded(header->cputype); + NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86" : @"ppc"; + + [headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedLongLong:(uint64_t)addr], + kHeaderBaseAddressKey, + [NSNumber numberWithUnsignedLongLong:(uint64_t)size], kHeaderSizeKey, + [NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey, + [NSNumber numberWithBool:NO], kHeaderIs64BitKey, + [NSNumber numberWithUnsignedLong:cpu], kHeaderCPUTypeKey, + nil] forKey:cpuStr]; + + return YES; + } + } + + uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); + cmd = (struct load_command *)((uint32_t)cmd + cmdSize); + } + + return NO; +} + +//============================================================================= +- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset { + struct mach_header_64 *header = + (struct mach_header_64 *)((uint32_t)base + offset); + BOOL swap = (header->magic == MH_CIGAM_64); + uint32_t count = SwapLongIfNeeded(header->ncmds); + struct load_command *cmd = + (struct load_command *)((uint32_t)header + sizeof(struct mach_header_64)); + + for (uint32_t i = 0; cmd && (i < count); i++) { + uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT_64); + if (cmd->cmd == segmentCommand) { + struct segment_command_64 *seg = (struct segment_command_64 *)cmd; + if (!strcmp(seg->segname, "__TEXT")) { + uint64_t addr = SwapLongLongIfNeeded(seg->vmaddr); + uint64_t size = SwapLongLongIfNeeded(seg->vmsize); + cpu_type_t cpu = SwapIntIfNeeded(header->cputype); + cpu &= (~CPU_ARCH_ABI64); + NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86_64" : @"ppc64"; + + [headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedLongLong:addr], kHeaderBaseAddressKey, + [NSNumber numberWithUnsignedLongLong:size], kHeaderSizeKey, + [NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey, + [NSNumber numberWithBool:YES], kHeaderIs64BitKey, + nil] forKey:cpuStr]; + return YES; + } + } + + uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); + cmd = (struct load_command *)((uint32_t)cmd + cmdSize); + } + + return NO; +} + +//============================================================================= +- (BOOL)loadModuleInfo { + uint64_t result = 0; + NSMutableData *data = [[NSMutableData alloc] + initWithContentsOfMappedFile:sourcePath_]; + void *bytes = [data mutableBytes]; + struct fat_header *fat = (struct fat_header *)bytes; + + if (!fat) { + [data release]; + return 0; + } + + // Gather some information based on the header + BOOL isFat = fat->magic == FAT_MAGIC || fat->magic == FAT_CIGAM; + BOOL is64 = fat->magic == MH_MAGIC_64 || fat->magic == MH_CIGAM_64; + BOOL is32 = fat->magic == MH_MAGIC || fat->magic == MH_CIGAM; + BOOL swap = fat->magic == FAT_CIGAM || fat->magic == MH_CIGAM_64 || + fat->magic == MH_CIGAM; + + if (!is64 && !is32 && !isFat) { + [data release]; + return 0; + } + + // Load any available architectures and save the information + headers_ = [[NSMutableDictionary alloc] init]; + + if (isFat) { + struct fat_arch *archs = + (struct fat_arch *)((uint32_t)fat + sizeof(struct fat_header)); + uint32_t count = SwapLongIfNeeded(fat->nfat_arch); + + for (uint32_t i = 0; i < count; ++i) { + archs[i].cputype = SwapIntIfNeeded(archs[i].cputype); + archs[i].cpusubtype = SwapIntIfNeeded(archs[i].cpusubtype); + archs[i].offset = SwapLongIfNeeded(archs[i].offset); + archs[i].size = SwapLongIfNeeded(archs[i].size); + archs[i].align = SwapLongIfNeeded(archs[i].align); + + if (archs[i].cputype & CPU_ARCH_ABI64) + result = [self loadHeader64:bytes offset:archs[i].offset]; + else + result = [self loadHeader:bytes offset:archs[i].offset]; + } + } else if (is32) { + result = [self loadHeader:bytes offset:0]; + } else { + result = [self loadHeader64:bytes offset:0]; + } + + [data release]; + return result; +} + +//============================================================================= +static BOOL WriteFormat(int fd, const char *fmt, ...) { + va_list list; + char buffer[4096]; + ssize_t expected, written; + + va_start(list, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, list); + expected = strlen(buffer); + written = write(fd, buffer, strlen(buffer)); + va_end(list); + + return expected == written; +} + +//============================================================================= +- (BOOL)outputSymbolFile:(int)fd { + // Get the baseAddress for this architecture + NSDictionary *archDict = [headers_ objectForKey:architecture_]; + NSNumber *baseAddressNum = [archDict objectForKey:kHeaderBaseAddressKey]; + uint64_t baseAddress = + baseAddressNum ? [baseAddressNum unsignedLongLongValue] : 0; + NSNumber *moduleSizeNum = [archDict objectForKey:kHeaderSizeKey]; + uint64_t moduleSize = + moduleSizeNum ? [moduleSizeNum unsignedLongLongValue] : 0; + + lastFunctionStart_ = nil; + + // Gather the information + [self loadSymbolInfoForArchitecture]; + [self convertSymbols]; + + NSArray *sortedAddresses = [[addresses_ allKeys] + sortedArrayUsingSelector:@selector(compare:)]; + + // UUID + FileID file_id([sourcePath_ fileSystemRepresentation]); + unsigned char identifier[16]; + char identifierStr[40]; + const char *moduleName = [[sourcePath_ lastPathComponent] UTF8String]; + int cpu_type = [[archDict objectForKey:kHeaderCPUTypeKey] unsignedLongValue]; + if (file_id.MachoIdentifier(cpu_type, identifier)) { + FileID::ConvertIdentifierToString(identifier, identifierStr, + sizeof(identifierStr)); + } + else { + strlcpy(identifierStr, moduleName, sizeof(identifierStr)); + } + + // Remove the dashes from the string + NSMutableString *compactedStr = + [NSMutableString stringWithCString:identifierStr encoding:NSASCIIStringEncoding]; + [compactedStr replaceOccurrencesOfString:@"-" withString:@"" options:0 + range:NSMakeRange(0, [compactedStr length])]; + + if (!WriteFormat(fd, "MODULE mac %s %s0 %s\n", [architecture_ UTF8String], + [compactedStr UTF8String], moduleName)) { + return NO; + } + + // Sources ordered by address + NSArray *sources = [[sources_ allKeys] + sortedArrayUsingSelector:@selector(compare:)]; + unsigned int sourceCount = [sources count]; + for (unsigned int i = 0; i < sourceCount; ++i) { + NSString *file = [sources_ objectForKey:[sources objectAtIndex:i]]; + if (!WriteFormat(fd, "FILE %d %s\n", i + 1, [file UTF8String])) + return NO; + } + + // Symbols + char terminatingChar = '\n'; + uint32_t fileIdx = 0, nextFileIdx = 0; + uint64_t nextSourceFileAddress = 0; + NSNumber *nextAddress; + uint64_t nextAddressVal; + unsigned int addressCount = [sortedAddresses count]; + + for (unsigned int i = 0; i < addressCount; ++i) { + NSNumber *address = [sortedAddresses objectAtIndex:i]; + uint64_t addressVal = [address unsignedLongLongValue] - baseAddress; + + // Get the next address to calculate the length + if (i + 1 < addressCount) { + nextAddress = [sortedAddresses objectAtIndex:i + 1]; + nextAddressVal = [nextAddress unsignedLongLongValue] - baseAddress; + } else { + nextAddressVal = baseAddress + moduleSize; + // The symbol reader doesn't want a trailing newline + terminatingChar = '\0'; + } + + NSDictionary *dict = [addresses_ objectForKey:address]; + NSNumber *line = [dict objectForKey:kAddressSourceLineKey]; + NSString *symbol = [dict objectForKey:kAddressConvertedSymbolKey]; + + if (!symbol) + symbol = [dict objectForKey:kAddressSymbolKey]; + + // Skip some symbols + if (StringHeadMatches(symbol, @"__gnu")) + continue; + + if (StringHeadMatches(symbol, @"typeinfo ")) + continue; + + if (StringHeadMatches(symbol, @"EH_frame")) + continue; + + if (StringHeadMatches(symbol, @"GCC_except_table")) + continue; + + // Find the source file (if any) that contains this address + while (sourceCount && (addressVal >= nextSourceFileAddress)) { + fileIdx = nextFileIdx; + + if (nextFileIdx < sourceCount) { + NSNumber *addr = [sources objectAtIndex:nextFileIdx]; + ++nextFileIdx; + nextSourceFileAddress = [addr unsignedLongLongValue] - baseAddress; + } else { + nextSourceFileAddress = baseAddress + moduleSize; + break; + } + } + + if (line) { + if (symbol) { + NSNumber *functionLength = [dict objectForKey:kFunctionSizeKey]; + uint64_t functionLengthVal = [functionLength unsignedLongLongValue]; + + // Function + if (!WriteFormat(fd, "FUNC %llx %llx 0 %s\n", addressVal, + functionLengthVal, [symbol UTF8String])) + return NO; + } + + // Source line + uint64_t length = nextAddressVal - addressVal; + if (!WriteFormat(fd, "%llx %llx %d %d\n", addressVal, length, + [line unsignedIntValue], fileIdx)) + return NO; + } else { + // PUBLIC
+ if (!WriteFormat(fd, "PUBLIC %llx 0 %s\n", addressVal, + [symbol UTF8String])) + return NO; + } + } + + return YES; +} + +//============================================================================= +- (id)initWithContentsOfFile:(NSString *)path { + if ((self = [super init])) { + + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { + [self autorelease]; + return nil; + } + + sourcePath_ = [path copy]; + + if (![self loadModuleInfo]) { + [self autorelease]; + return nil; + } + + // If there's more than one, use the native one + if ([headers_ count] > 1) { + const NXArchInfo *localArchInfo = NXGetLocalArchInfo(); + + if (localArchInfo) { + cpu_type_t cpu = localArchInfo->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"; + + [self setArchitecture:arch]; + } + } else { + // Specify the default architecture + [self setArchitecture:[[headers_ allKeys] objectAtIndex:0]]; + } + } + + return self; +} + +//============================================================================= +- (NSArray *)availableArchitectures { + return [headers_ allKeys]; +} + +//============================================================================= +- (void)dealloc { + [sourcePath_ release]; + [architecture_ release]; + [addresses_ release]; + [sources_ release]; + [headers_ release]; + + [super dealloc]; +} + +//============================================================================= +- (BOOL)setArchitecture:(NSString *)architecture { + NSString *normalized = [architecture lowercaseString]; + BOOL isValid = NO; + + if ([normalized isEqualToString:@"ppc"]) { + isValid = YES; + } + else if ([normalized isEqualToString:@"i386"]) { + normalized = @"x86"; + isValid = YES; + } + else if ([normalized isEqualToString:@"x86"]) { + isValid = YES; + } + else if ([normalized isEqualToString:@"ppc64"]) { + isValid = YES; + } + else if ([normalized isEqualToString:@"x86_64"]) { + isValid = YES; + } + + if (isValid) { + if (![headers_ objectForKey:normalized]) + return NO; + + [architecture_ autorelease]; + architecture_ = [architecture copy]; + } + + return isValid; +} + +//============================================================================= +- (NSString *)architecture { + return architecture_; +} + +//============================================================================= +- (BOOL)writeSymbolFile:(NSString *)destinationPath { + const char *dest = [destinationPath fileSystemRepresentation]; + int fd; + + if ([[destinationPath substringToIndex:1] isEqualToString:@"-"]) + fd = STDOUT_FILENO; + else + fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if (fd == -1) + return NO; + + BOOL result = [self outputSymbolFile:fd]; + + close(fd); + + return result; +} + +@end diff --git a/src/common/mac/macho_walker.cc b/src/common/mac/macho_walker.cc index 96dca406..5e9de5f4 100644 --- a/src/common/mac/macho_walker.cc +++ b/src/common/mac/macho_walker.cc @@ -103,7 +103,7 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) { offset = sizeof(magic); // Figure out what type of file we've got - bool is_fat; + bool is_fat = false; if (magic == FAT_MAGIC || magic == FAT_CIGAM) { is_fat = true; } @@ -119,9 +119,6 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) { if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset)) return false; - if (NXHostByteOrder() != NX_BigEndian) - header_cpu_type = NXSwapLong(header_cpu_type); - if (valid_cpu_type != header_cpu_type) return false; diff --git a/src/tools/mac/crash_report/crash_report.mm b/src/tools/mac/crash_report/crash_report.mm index bd026cf1..1a42fc03 100644 --- a/src/tools/mac/crash_report/crash_report.mm +++ b/src/tools/mac/crash_report/crash_report.mm @@ -309,6 +309,7 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { if ((argc - optind) != 1) { fprintf(stderr, "%s: Missing minidump file\n", argv[0]); Usage(argc, argv); + exit(1); } options->minidumpPath = [[NSFileManager defaultManager] diff --git a/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj b/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj index 2c1eccc1..eb43b3a8 100644 --- a/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj +++ b/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj @@ -32,7 +32,13 @@ 9BDF1AFC0B1BEB6300F8391B /* address_map-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF1AFA0B1BEB6300F8391B /* address_map-inl.h */; }; 9BDF1AFD0B1BEB6300F8391B /* address_map.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF1AFB0B1BEB6300F8391B /* address_map.h */; }; 9BDF21A60B1E825400F8391B /* dump_syms.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF192D0B1BC15D00F8391B /* dump_syms.h */; }; - 9BDF21A70B1E825400F8391B /* dump_syms.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF192E0B1BC15D00F8391B /* dump_syms.m */; }; + 9BDF21A70B1E825400F8391B /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF192E0B1BC15D00F8391B /* dump_syms.mm */; }; + 9BE650B20B52FE3000611104 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650AC0B52FE3000611104 /* file_id.cc */; }; + 9BE650B30B52FE3000611104 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE650AD0B52FE3000611104 /* file_id.h */; }; + 9BE650B40B52FE3000611104 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650AE0B52FE3000611104 /* macho_id.cc */; }; + 9BE650B50B52FE3000611104 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE650AF0B52FE3000611104 /* macho_id.h */; }; + 9BE650B60B52FE3000611104 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650B00B52FE3000611104 /* macho_walker.cc */; }; + 9BE650B70B52FE3000611104 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE650B10B52FE3000611104 /* macho_walker.h */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -54,6 +60,9 @@ 9B35FEEB0B26761C008DE8C7 /* basic_code_modules.h in CopyFiles */, 9B3904960B2E52D90059FABE /* basic_source_line_resolver.h in CopyFiles */, 9B3904970B2E52D90059FABE /* source_line_resolver_interface.h in CopyFiles */, + 9BE650B30B52FE3000611104 /* file_id.h in CopyFiles */, + 9BE650B50B52FE3000611104 /* macho_id.h in CopyFiles */, + 9BE650B70B52FE3000611104 /* macho_walker.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -92,12 +101,18 @@ 9BDF176B0B1B8CB100F8391B /* on_demand_symbol_supplier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = on_demand_symbol_supplier.h; sourceTree = ""; }; 9BDF176C0B1B8CB100F8391B /* on_demand_symbol_supplier.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = on_demand_symbol_supplier.mm; sourceTree = ""; }; 9BDF192D0B1BC15D00F8391B /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; }; - 9BDF192E0B1BC15D00F8391B /* dump_syms.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = dump_syms.m; path = ../../../common/mac/dump_syms.m; sourceTree = SOURCE_ROOT; }; + 9BDF192E0B1BC15D00F8391B /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; }; 9BDF1A270B1BD58200F8391B /* pathname_stripper.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = pathname_stripper.cc; path = ../../../processor/pathname_stripper.cc; sourceTree = SOURCE_ROOT; }; 9BDF1A7A0B1BE30100F8391B /* range_map-inl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "range_map-inl.h"; path = "../../../processor/range_map-inl.h"; sourceTree = SOURCE_ROOT; }; 9BDF1A7B0B1BE30100F8391B /* range_map.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = range_map.h; path = ../../../processor/range_map.h; sourceTree = SOURCE_ROOT; }; 9BDF1AFA0B1BEB6300F8391B /* address_map-inl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "address_map-inl.h"; path = "../../../processor/address_map-inl.h"; sourceTree = SOURCE_ROOT; }; 9BDF1AFB0B1BEB6300F8391B /* address_map.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = address_map.h; path = ../../../processor/address_map.h; sourceTree = SOURCE_ROOT; }; + 9BE650AC0B52FE3000611104 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + 9BE650AD0B52FE3000611104 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + 9BE650AE0B52FE3000611104 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + 9BE650AF0B52FE3000611104 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + 9BE650B00B52FE3000611104 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + 9BE650B10B52FE3000611104 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -116,7 +131,7 @@ isa = PBXGroup; children = ( 9BDF192D0B1BC15D00F8391B /* dump_syms.h */, - 9BDF192E0B1BC15D00F8391B /* dump_syms.m */, + 9BDF192E0B1BC15D00F8391B /* dump_syms.mm */, 08FB7796FE84155DC02AAC07 /* crash_report.mm */, 9BDF176B0B1B8CB100F8391B /* on_demand_symbol_supplier.h */, 9BDF176C0B1B8CB100F8391B /* on_demand_symbol_supplier.mm */, @@ -130,6 +145,7 @@ 08FB7795FE84155DC02AAC07 /* airbag */ = { isa = PBXGroup; children = ( + 9BE650AB0B52FE1A00611104 /* common */, 9BDF17280B1B8B0200F8391B /* processor */, 9BDF16F70B1B8ACD00F8391B /* google_airbag */, ); @@ -214,6 +230,19 @@ name = processor; sourceTree = ""; }; + 9BE650AB0B52FE1A00611104 /* common */ = { + isa = PBXGroup; + children = ( + 9BE650AC0B52FE3000611104 /* file_id.cc */, + 9BE650AD0B52FE3000611104 /* file_id.h */, + 9BE650AE0B52FE3000611104 /* macho_id.cc */, + 9BE650AF0B52FE3000611104 /* macho_id.h */, + 9BE650B00B52FE3000611104 /* macho_walker.cc */, + 9BE650B10B52FE3000611104 /* macho_walker.h */, + ); + name = common; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -265,9 +294,12 @@ 9BDF175D0B1B8C1B00F8391B /* process_state.cc in Sources */, 9BDF176E0B1B8CB100F8391B /* on_demand_symbol_supplier.mm in Sources */, 9BDF1A280B1BD58200F8391B /* pathname_stripper.cc in Sources */, - 9BDF21A70B1E825400F8391B /* dump_syms.m in Sources */, + 9BDF21A70B1E825400F8391B /* dump_syms.mm in Sources */, 9B35FEEA0B26761C008DE8C7 /* basic_code_modules.cc in Sources */, 9B3904990B2E52FD0059FABE /* basic_source_line_resolver.cc in Sources */, + 9BE650B20B52FE3000611104 /* file_id.cc in Sources */, + 9BE650B40B52FE3000611104 /* macho_id.cc in Sources */, + 9BE650B60B52FE3000611104 /* macho_walker.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -286,6 +318,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-lcrypto"; PRODUCT_NAME = crash_report; USER_HEADER_SEARCH_PATHS = "../../../../** $(inherited)"; ZERO_LINK = NO; @@ -305,6 +338,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-lcrypto"; PRODUCT_NAME = crash_report; USER_HEADER_SEARCH_PATHS = "../../../../** $(inherited)"; ZERO_LINK = NO; diff --git a/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj index b51a3f19..80f94097 100644 --- a/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj +++ b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj @@ -7,10 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 8DD76F9A0486AA7600D96B5E /* dump_syms.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* dump_syms.m */; settings = {ATTRIBUTES = (); }; }; + 8DD76F9A0486AA7600D96B5E /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* dump_syms.mm */; settings = {ATTRIBUTES = (); }; }; 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; 9BDF186F0B1BB43700F8391B /* dump_syms.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF186D0B1BB43700F8391B /* dump_syms.h */; }; 9BDF18700B1BB43700F8391B /* dump_syms_tool.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF186E0B1BB43700F8391B /* dump_syms_tool.m */; }; + 9BE650470B52F6D800611104 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650410B52F6D800611104 /* file_id.cc */; }; + 9BE650480B52F6D800611104 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE650420B52F6D800611104 /* file_id.h */; }; + 9BE650490B52F6D800611104 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650430B52F6D800611104 /* macho_id.cc */; }; + 9BE6504A0B52F6D800611104 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE650440B52F6D800611104 /* macho_id.h */; }; + 9BE6504B0B52F6D800611104 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650450B52F6D800611104 /* macho_walker.cc */; }; + 9BE6504C0B52F6D800611104 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE650460B52F6D800611104 /* macho_walker.h */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -21,17 +27,26 @@ dstSubfolderSpec = 0; files = ( 9BDF186F0B1BB43700F8391B /* dump_syms.h in CopyFiles */, + 9BE650480B52F6D800611104 /* file_id.h in CopyFiles */, + 9BE6504A0B52F6D800611104 /* macho_id.h in CopyFiles */, + 9BE6504C0B52F6D800611104 /* macho_walker.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 08FB7796FE84155DC02AAC07 /* dump_syms.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = dump_syms.m; path = ../../../common/mac/dump_syms.m; sourceTree = ""; }; + 08FB7796FE84155DC02AAC07 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = ""; }; 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 8DD76FA10486AA7600D96B5E /* dump_syms */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dump_syms; sourceTree = BUILT_PRODUCTS_DIR; }; 9BDF186D0B1BB43700F8391B /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = ""; }; 9BDF186E0B1BB43700F8391B /* dump_syms_tool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = dump_syms_tool.m; sourceTree = ""; }; + 9BE650410B52F6D800611104 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + 9BE650420B52F6D800611104 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + 9BE650430B52F6D800611104 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + 9BE650440B52F6D800611104 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + 9BE650450B52F6D800611104 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + 9BE650460B52F6D800611104 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,8 +64,14 @@ 08FB7794FE84155DC02AAC07 /* dump_syms */ = { isa = PBXGroup; children = ( + 9BE650410B52F6D800611104 /* file_id.cc */, + 9BE650420B52F6D800611104 /* file_id.h */, + 9BE650430B52F6D800611104 /* macho_id.cc */, + 9BE650440B52F6D800611104 /* macho_id.h */, + 9BE650450B52F6D800611104 /* macho_walker.cc */, + 9BE650460B52F6D800611104 /* macho_walker.h */, 9BDF186D0B1BB43700F8391B /* dump_syms.h */, - 08FB7796FE84155DC02AAC07 /* dump_syms.m */, + 08FB7796FE84155DC02AAC07 /* dump_syms.mm */, 9BDF186E0B1BB43700F8391B /* dump_syms_tool.m */, 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, 1AB674ADFE9D54B511CA2CBB /* Products */, @@ -115,8 +136,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8DD76F9A0486AA7600D96B5E /* dump_syms.m in Sources */, + 8DD76F9A0486AA7600D96B5E /* dump_syms.mm in Sources */, 9BDF18700B1BB43700F8391B /* dump_syms_tool.m in Sources */, + 9BE650470B52F6D800611104 /* file_id.cc in Sources */, + 9BE650490B52F6D800611104 /* macho_id.cc in Sources */, + 9BE6504B0B52F6D800611104 /* macho_walker.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -127,7 +151,7 @@ isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = c99; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -140,7 +164,9 @@ "$(inherited)", ); INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-lcrypto"; PRODUCT_NAME = dump_syms; + WARNING_CFLAGS = "-Wall"; ZERO_LINK = NO; }; name = Debug; @@ -152,7 +178,7 @@ ppc, i386, ); - GCC_C_LANGUAGE_STANDARD = c99; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; @@ -163,7 +189,9 @@ "$(inherited)", ); INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-lcrypto"; PRODUCT_NAME = dump_syms; + WARNING_CFLAGS = "-Wall"; ZERO_LINK = NO; }; name = Release; diff --git a/src/tools/mac/dump_syms/dump_syms_tool.m b/src/tools/mac/dump_syms/dump_syms_tool.m index 60499a88..147989bf 100644 --- a/src/tools/mac/dump_syms/dump_syms_tool.m +++ b/src/tools/mac/dump_syms/dump_syms_tool.m @@ -46,21 +46,31 @@ typedef struct { static void Start(Options *options) { DumpSymbols *dump = [[DumpSymbols alloc] initWithContentsOfFile:options->srcPath]; + + if (!dump) { + fprintf(stderr, "%s is not a valid Mach-o file\n", + [options->srcPath fileSystemRepresentation]); + options->result = NO; + return; + } - [dump setArchitecture:options->arch]; - [dump setUUID:options->uuidStr]; + if (![dump setArchitecture:options->arch]) { + fprintf(stderr, "Architecture: %s not available in %s\n", + [options->arch UTF8String], + [options->srcPath fileSystemRepresentation]); + options->result = NO; + return; + } + options->result = [dump writeSymbolFile:@"-"]; } //============================================================================= static void Usage(int argc, const char *argv[]) { - fprintf(stderr, "Output airbag symbol file. If a UUID is not specified,\n" - "the program will try to locate one in the mach-o-file. If one is\n" - "not found, one will be created.\n"); - fprintf(stderr, "Usage: %s [-a ppc|i386|x86][-u uuid] \n", + fprintf(stderr, "Output an Airbag symbol file from a Mach-o file.\n"); + fprintf(stderr, "Usage: %s [-a ppc|i386|x86] \n", argv[0]); fprintf(stderr, "\t-a: Architecture type [default: native]\n"); - fprintf(stderr, "\t-u: Specify a UUID\n"); fprintf(stderr, "\t-h: Usage\n"); fprintf(stderr, "\t-?: Usage\n"); } @@ -97,9 +107,6 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { exit(1); } break; - case 'u': - options->uuidStr = [NSString stringWithUTF8String:optarg]; - break; case '?': case 'h': Usage(argc, argv); @@ -107,14 +114,9 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { break; } } - - if (!options->uuidStr) { - CFUUIDRef uuid = CFUUIDCreate(NULL); - options->uuidStr = (NSString *)CFUUIDCreateString(NULL, uuid); - } - + if ((argc - optind) != 1) { - fprintf(stderr, "%s: Missing executable\n", argv[0]); + fprintf(stderr, "Must specify Mach-o file\n"); Usage(argc, argv); exit(1); } @@ -135,5 +137,5 @@ int main (int argc, const char * argv[]) { [pool release]; - return options.result; + return !options.result; } diff --git a/src/tools/mac/symupload/symupload.m b/src/tools/mac/symupload/symupload.m index 6f7db19f..15e6ad80 100644 --- a/src/tools/mac/symupload/symupload.m +++ b/src/tools/mac/symupload/symupload.m @@ -34,7 +34,6 @@ // debug_identifier: the debug file's identifier, usually consisting of // the guid and age embedded in the pdb, e.g. // "11111111BBBB3333DDDD555555555555F" -// version: the file version of the module, e.g. "1.2.3.4" // os: the operating system that the module was built for // cpu: the CPU that the module was built for (x86 or ppc) // symbol_file: the contents of the airbag-format symbol file @@ -47,7 +46,6 @@ typedef struct { NSString *symbolsPath; NSString *uploadURLStr; - NSString *version; BOOL success; } Options; @@ -69,11 +67,11 @@ static NSArray *ModuleDataForSymbolFile(NSString *file) { } //============================================================================= -static NSString *CompactIdentifier(NSString *uuid, NSString *age) { +static NSString *CompactIdentifier(NSString *uuid) { NSMutableString *str = [NSMutableString stringWithString:uuid]; [str replaceOccurrencesOfString:@"-" withString:@"" options:0 range:NSMakeRange(0, [str length])]; - [str appendString:age]; + return str; } @@ -83,22 +81,30 @@ static void Start(Options *options) { HTTPMultipartUpload *ul = [[HTTPMultipartUpload alloc] initWithURL:url]; NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; NSArray *moduleParts = ModuleDataForSymbolFile(options->symbolsPath); - NSString *compactedID = CompactIdentifier([moduleParts objectAtIndex:3], - [moduleParts objectAtIndex:4]); + NSMutableString *compactedID = + [NSMutableString stringWithString:[moduleParts objectAtIndex:3]]; + [compactedID replaceOccurrencesOfString:@"-" withString:@"" options:0 + range:NSMakeRange(0, [compactedID length])]; // Add parameters - if (options->version) - [parameters setObject:options->version forKey:@"version"]; + [parameters setObject:compactedID forKey:@"debug_identifier"]; - // MODULE - // 0 1 2 3 4 5 - [parameters setObject:@"1" forKey:@"age"]; + // MODULE + // 0 1 2 3 4 [parameters setObject:[moduleParts objectAtIndex:1] forKey:@"os"]; [parameters setObject:[moduleParts objectAtIndex:2] forKey:@"cpu"]; - [parameters setObject:[moduleParts objectAtIndex:5] forKey:@"debug_file"]; - [parameters setObject:[moduleParts objectAtIndex:5] forKey:@"code_file"]; - [parameters setObject:compactedID forKey:@"debug_identifier"]; + [parameters setObject:[moduleParts objectAtIndex:4] forKey:@"debug_file"]; + [parameters setObject:[moduleParts objectAtIndex:4] forKey:@"code_file"]; [ul setParameters:parameters]; + + NSArray *keys = [parameters allKeys]; + int count = [keys count]; + for (int i = 0; i < count; ++i) { + NSString *key = [keys objectAtIndex:i]; + NSString *value = [parameters objectForKey:key]; + fprintf(stdout, "'%s' = '%s'\n", [key UTF8String], + [value UTF8String]); + } // Add file [ul addFileAtPath:options->symbolsPath name:@"symbol_file"]; @@ -123,10 +129,9 @@ static void Start(Options *options) { static void Usage(int argc, const char *argv[]) { fprintf(stderr, "Submit symbol information.\n"); - fprintf(stderr, "Usage: %s [-v version] \n", argv[0]); + fprintf(stderr, "Usage: %s \n", argv[0]); fprintf(stderr, " should be created by using the dump_syms tool.\n"); fprintf(stderr, " is the destination for the upload\n"); - fprintf(stderr, "\t-v: Version information (e.g., 1.2.3.4)\n"); fprintf(stderr, "\t-h: Usage\n"); fprintf(stderr, "\t-?: Usage\n"); } @@ -137,11 +142,8 @@ SetupOptions(int argc, const char *argv[], Options *options) { extern int optind; char ch; - while ((ch = getopt(argc, (char * const *)argv, "v:h?")) != -1) { + while ((ch = getopt(argc, (char * const *)argv, "h?")) != -1) { switch (ch) { - case 'v': - options->version = [NSString stringWithCString:optarg]; - break; default: Usage(argc, argv); exit(0); diff --git a/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj b/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj index d2f4a1ca..f9550364 100644 --- a/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj +++ b/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj @@ -31,7 +31,7 @@ /* Begin PBXFileReference section */ 08FB7796FE84155DC02AAC07 /* symupload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = symupload.m; sourceTree = ""; }; 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 8DD76FA10486AA7600D96B5E /* symupload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = symupload; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DD76FA10486AA7600D96B5E /* symupload */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "compiled.mach-o.executable"; path = symupload; sourceTree = BUILT_PRODUCTS_DIR; }; 9BD833680B03E4080055103E /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../../common/mac/HTTPMultipartUpload.h; sourceTree = ""; }; 9BD833690B03E4080055103E /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../../common/mac/HTTPMultipartUpload.m; sourceTree = ""; }; 9BD835FB0B0544950055103E /* minidump_upload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_upload; sourceTree = BUILT_PRODUCTS_DIR; }; -- cgit v1.2.1