mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-03-01 19:38:01 +00:00
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
This commit is contained in:
parent
b65dce60f1
commit
9ddb3ae949
67
src/common/mac/dump_syms.h
Normal file
67
src/common/mac/dump_syms.h
Normal file
|
@ -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
|
808
src/common/mac/dump_syms.m
Normal file
808
src/common/mac/dump_syms.m
Normal file
|
@ -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
|
215
src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
Normal file
215
src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
Normal file
|
@ -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 */;
|
||||||
|
}
|
139
src/tools/mac/dump_syms/dump_syms_tool.m
Normal file
139
src/tools/mac/dump_syms/dump_syms_tool.m
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in a new issue