aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorwaylonis <waylonis@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-12-19 20:28:32 +0000
committerwaylonis <waylonis@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-12-19 20:28:32 +0000
commit9ddb3ae9499634c2ec61b09d6358d4dc7ddf9f32 (patch)
tree421eaec1df7ef72d0a86a13b8b104b914b199e9e /src
parentAdd crash_report tool that will: (diff)
downloadbreakpad-9ddb3ae9499634c2ec61b09d6358d4dc7ddf9f32.tar.xz
Add Mac dump_syms tool and support class. Fixes issue #73.
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@94 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src')
-rw-r--r--src/common/mac/dump_syms.h67
-rw-r--r--src/common/mac/dump_syms.m808
-rw-r--r--src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj215
-rw-r--r--src/tools/mac/dump_syms/dump_syms_tool.m139
4 files changed, 1229 insertions, 0 deletions
diff --git a/src/common/mac/dump_syms.h b/src/common/mac/dump_syms.h
new file mode 100644
index 00000000..38c7d3ce
--- /dev/null
+++ b/src/common/mac/dump_syms.h
@@ -0,0 +1,67 @@
+// 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.h: Interface for DumpSymbols. This class will take a mach-o file
+// and extract the symbol information and write it to a file using the
+// airbag symbol file format.
+// NOTE: Only Stabs format is currently supported -- not DWARF.
+
+#import <Foundation/Foundation.h>
+
+@interface DumpSymbols : NSObject {
+ @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)
+ NSMutableDictionary *headers_; // Mach-o header information (STRONG)
+ NSNumber *lastFunctionStart_; // Address of last function symbol parsed
+}
+
+- (id)initWithContentsOfFile:(NSString *)machoFile;
+
+- (NSArray *)availableArchitectures;
+
+// One of ppc, x86, i386, ppc64, x86_64
+// If the architecture is not available, it will return NO
+// If not set, the native architecture will be used
+- (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;
+
+@end
diff --git a/src/common/mac/dump_syms.m b/src/common/mac/dump_syms.m
new file mode 100644
index 00000000..d133e120
--- /dev/null
+++ b/src/common/mac/dump_syms.m
@@ -0,0 +1,808 @@
+//
+// 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 <unistd.h>
+#include <signal.h>
+
+#include <mach/machine.h>
+#include <mach-o/arch.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/stab.h>
+
+#import <Foundation/Foundation.h>
+
+#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 <address> <stack-size> <name>
+ 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/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..b51a3f19
--- /dev/null
+++ b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
@@ -0,0 +1,215 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 42;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 8DD76F9A0486AA7600D96B5E /* dump_syms.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* dump_syms.m */; 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 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ 9BDF186F0B1BB43700F8391B /* dump_syms.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 = "<group>"; };
+ 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 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 = "<group>"; };
+ 9BDF186E0B1BB43700F8391B /* dump_syms_tool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = dump_syms_tool.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 08FB7794FE84155DC02AAC07 /* dump_syms */ = {
+ isa = PBXGroup;
+ children = (
+ 9BDF186D0B1BB43700F8391B /* dump_syms.h */,
+ 08FB7796FE84155DC02AAC07 /* dump_syms.m */,
+ 9BDF186E0B1BB43700F8391B /* dump_syms_tool.m */,
+ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
+ 1AB674ADFE9D54B511CA2CBB /* Products */,
+ );
+ name = dump_syms;
+ sourceTree = "<group>";
+ };
+ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 08FB779EFE84155DC02AAC07 /* Foundation.framework */,
+ );
+ name = "External Frameworks and Libraries";
+ sourceTree = "<group>";
+ };
+ 1AB674ADFE9D54B511CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8DD76FA10486AA7600D96B5E /* dump_syms */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8DD76F960486AA7600D96B5E /* dump_syms */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "dump_syms" */;
+ buildPhases = (
+ 8DD76F990486AA7600D96B5E /* Sources */,
+ 8DD76F9B0486AA7600D96B5E /* Frameworks */,
+ 8DD76F9E0486AA7600D96B5E /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dump_syms;
+ productInstallPath = "$(HOME)/bin";
+ productName = dump_syms;
+ productReference = 8DD76FA10486AA7600D96B5E /* dump_syms */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 08FB7793FE84155DC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "dump_syms" */;
+ hasScannedForEncodings = 1;
+ mainGroup = 08FB7794FE84155DC02AAC07 /* dump_syms */;
+ projectDirPath = "";
+ targets = (
+ 8DD76F960486AA7600D96B5E /* dump_syms */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8DD76F990486AA7600D96B5E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8DD76F9A0486AA7600D96B5E /* dump_syms.m in Sources */,
+ 9BDF18700B1BB43700F8391B /* dump_syms_tool.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB927508733DD40010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "";
+ HEADER_SEARCH_PATHS = (
+ "../../../**",
+ "$(inherited)",
+ );
+ INSTALL_PATH = "$(HOME)/bin";
+ PRODUCT_NAME = dump_syms;
+ ZERO_LINK = NO;
+ };
+ name = Debug;
+ };
+ 1DEB927608733DD40010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ ppc,
+ i386,
+ );
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "";
+ HEADER_SEARCH_PATHS = (
+ "../../../**",
+ "$(inherited)",
+ );
+ INSTALL_PATH = "$(HOME)/bin";
+ PRODUCT_NAME = dump_syms;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 1DEB927908733DD40010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+ };
+ name = Debug;
+ };
+ 1DEB927A08733DD40010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "dump_syms" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB927508733DD40010E9CD /* Debug */,
+ 1DEB927608733DD40010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "dump_syms" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB927908733DD40010E9CD /* Debug */,
+ 1DEB927A08733DD40010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/src/tools/mac/dump_syms/dump_syms_tool.m b/src/tools/mac/dump_syms/dump_syms_tool.m
new file mode 100644
index 00000000..60499a88
--- /dev/null
+++ b/src/tools/mac/dump_syms/dump_syms_tool.m
@@ -0,0 +1,139 @@
+// 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_tool.m: Command line tool that uses the DumpSymbols class.
+// TODO(waylonis): accept stdin
+
+#include <unistd.h>
+#include <mach-o/arch.h>
+
+#include "dump_syms.h"
+
+typedef struct {
+ NSString *srcPath;
+ NSString *arch;
+ NSString *uuidStr;
+ BOOL result;
+} Options;
+
+//=============================================================================
+static void Start(Options *options) {
+ DumpSymbols *dump = [[DumpSymbols alloc]
+ initWithContentsOfFile:options->srcPath];
+
+ [dump setArchitecture:options->arch];
+ [dump setUUID:options->uuidStr];
+ 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] <mach-o-file>\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");
+}
+
+//=============================================================================
+static void SetupOptions(int argc, const char *argv[], Options *options) {
+ extern int optind;
+ const NXArchInfo *localArchInfo = NXGetLocalArchInfo();
+ char ch;
+
+ if (localArchInfo) {
+ if (localArchInfo->cputype & CPU_ARCH_ABI64)
+ options->arch = (localArchInfo->cputype == CPU_TYPE_POWERPC64) ? @"ppc64":
+ @"x86_64";
+ else
+ options->arch = (localArchInfo->cputype == CPU_TYPE_POWERPC) ? @"ppc" :
+ @"x86";
+ }
+
+ while ((ch = getopt(argc, (char * const *)argv, "a:h?")) != -1) {
+ switch (ch) {
+ case 'a':
+ if (strcmp("ppc", optarg) == 0)
+ options->arch = @"ppc";
+ else if (strcmp("x86", optarg) == 0 || strcmp("i386", optarg) == 0)
+ options->arch = @"x86";
+ else if (strcmp("ppc64", optarg) == 0)
+ options->arch = @"ppc64";
+ else if (strcmp("x86_64", optarg) == 0)
+ options->arch = @"x86_64";
+ else {
+ fprintf(stderr, "%s: Invalid architecture: %s\n", argv[0], optarg);
+ Usage(argc, argv);
+ exit(1);
+ }
+ break;
+ case 'u':
+ options->uuidStr = [NSString stringWithUTF8String:optarg];
+ break;
+ case '?':
+ case 'h':
+ Usage(argc, argv);
+ exit(0);
+ 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]);
+ Usage(argc, argv);
+ exit(1);
+ }
+
+ options->srcPath = [[NSFileManager defaultManager]
+ stringWithFileSystemRepresentation:argv[optind]
+ length:strlen(argv[optind])];
+}
+
+//=============================================================================
+int main (int argc, const char * argv[]) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ Options options;
+
+ bzero(&options, sizeof(Options));
+ SetupOptions(argc, argv, &options);
+ Start(&options);
+
+ [pool release];
+
+ return options.result;
+}