1// Copyright (c) 2006, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30// dump_syms.mm: Create a symbol file for use with minidumps 31 32#include <unistd.h> 33#include <signal.h> 34#include <cxxabi.h> 35#include <stdlib.h> 36 37#include <mach/machine.h> 38#include <mach-o/arch.h> 39#include <mach-o/fat.h> 40#include <mach-o/loader.h> 41#include <mach-o/nlist.h> 42#include <mach-o/stab.h> 43#include <fcntl.h> 44 45#import <Foundation/Foundation.h> 46 47#import "dump_syms.h" 48#import "common/mac/file_id.h" 49#import "common/mac/macho_utilities.h" 50#import "common/dwarf/dwarf2reader.h" 51#import "common/dwarf/functioninfo.h" 52#import "common/dwarf/bytereader.h" 53 54using google_breakpad::FileID; 55 56static NSString *kAddressSymbolKey = @"symbol"; 57static NSString *kAddressConvertedSymbolKey = @"converted_symbol"; 58static NSString *kAddressSourceLineKey = @"line"; 59static NSString *kFunctionSizeKey = @"size"; 60static NSString *kFunctionFileKey = @"source_file"; 61static NSString *kHeaderBaseAddressKey = @"baseAddr"; 62static NSString *kHeaderSizeKey = @"size"; 63static NSString *kHeaderOffsetKey = @"offset"; // Offset to the header 64static NSString *kHeaderIs64BitKey = @"is64"; 65static NSString *kHeaderCPUTypeKey = @"cpuType"; 66 67// The section for __TEXT, __text seems to be always 1. This is useful 68// for pruning out extraneous non-function symbols. 69static const int kTextSection = 1; 70 71// Dump FunctionMap to stdout. Print address, function name, file 72// name, line number, lowpc, and highpc if available. 73void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { 74 for (dwarf2reader::FunctionMap::const_iterator iter = function_map.begin(); 75 iter != function_map.end(); ++iter) { 76 if (iter->second->name.empty()) { 77 continue; 78 } 79 printf("%08llx: %s", iter->first, 80 iter->second->name.data()); 81 if (!iter->second->file.empty()) { 82 printf(" - %s", iter->second->file.data()); 83 if (iter->second->line != 0) { 84 printf(":%u", iter->second->line); 85 } 86 } 87 if (iter->second->lowpc != 0 && iter->second->highpc != 0) { 88 printf(" (%08llx - %08llx)\n", 89 iter->second->lowpc, 90 iter->second->highpc); 91 } 92 } 93} 94 95 96@interface DumpSymbols(PrivateMethods) 97- (NSString *)convertCPlusPlusSymbol:(NSString *)symbol; 98- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address section:(int)section; 99- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table; 100- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset; 101- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset; 102- (BOOL)loadSymbolInfoForArchitecture; 103- (BOOL)loadDWARFSymbolInfo:(void *)base offset:(uint32_t)offset; 104- (BOOL)loadSTABSSymbolInfo:(void *)base offset:(uint32_t)offset; 105- (void)generateSectionDictionary:(struct mach_header*)header; 106- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset; 107- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset; 108- (BOOL)loadModuleInfo; 109- (void)processDWARFLineNumberInfo:(dwarf2reader::LineMap*)line_map; 110- (void)processDWARFFunctionInfo:(dwarf2reader::FunctionMap*)address_to_funcinfo; 111- (void)processDWARFSourceFileInfo:(vector<dwarf2reader::SourceFileInfo>*) files; 112- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset; 113- (dwarf2reader::SectionMap*)getSectionMapForArchitecture:(NSString*)architecture; 114@end 115 116@implementation DumpSymbols 117//============================================================================= 118- (NSString *)convertCPlusPlusSymbol:(NSString *)symbol { 119 // __cxa_demangle will realloc this if needed 120 char *buffer = (char *)malloc(1024); 121 size_t buffer_size = 1024; 122 int result; 123 124 const char *sym = [symbol UTF8String]; 125 NSString *demangled = nil; 126 buffer = abi::__cxa_demangle(sym, buffer, &buffer_size, &result); 127 if (result == 0) { 128 demangled = [NSString stringWithUTF8String:buffer]; 129 } 130 free(buffer); 131 return demangled; 132} 133 134//============================================================================= 135- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address section:(int)section { 136 NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:address]; 137 138 if (!address) 139 return; 140 141 // If the function starts with "_Z" or "__Z" then demangle it. 142 BOOL isCPP = NO; 143 144 if ([name hasPrefix:@"__Z"]) { 145 // Remove the leading underscore 146 name = [name substringFromIndex:1]; 147 isCPP = YES; 148 } else if ([name hasPrefix:@"_Z"]) { 149 isCPP = YES; 150 } 151 152 // Filter out non-functions 153 if ([name hasSuffix:@".eh"]) 154 return; 155 156 if ([name hasSuffix:@"__func__"]) 157 return; 158 159 if ([name hasSuffix:@"GCC_except_table"]) 160 return; 161 162 if (isCPP) { 163 // OBJCPP_MANGLING_HACK 164 // There are cases where ObjC++ mangles up an ObjC name using quasi-C++ 165 // mangling: 166 // @implementation Foozles + (void)barzles { 167 // static int Baz = 0; 168 // } @end 169 // gives you _ZZ18+[Foozles barzles]E3Baz 170 // c++filt won't parse this properly, and will crash in certain cases. 171 // Logged as radar: 172 // 5129938: c++filt does not deal with ObjC++ symbols 173 // If 5129938 ever gets fixed, we can remove this, but for now this prevents 174 // c++filt from attempting to demangle names it doesn't know how to handle. 175 // This is with c++filt 2.16 176 NSCharacterSet *objcppCharSet = [NSCharacterSet characterSetWithCharactersInString:@"-+[]: "]; 177 NSRange emptyRange = { NSNotFound, 0 }; 178 NSRange objcppRange = [name rangeOfCharacterFromSet:objcppCharSet]; 179 isCPP = NSEqualRanges(objcppRange, emptyRange); 180 } else if ([name characterAtIndex:0] == '_') { 181 // Remove the leading underscore 182 name = [name substringFromIndex:1]; 183 } 184 185 // If there's already an entry for this address, check and see if we can add 186 // either the symbol, or a missing line # 187 NSMutableDictionary *dict = [addresses_ objectForKey:addressNum]; 188 189 if (!dict) { 190 dict = [[NSMutableDictionary alloc] init]; 191 [addresses_ setObject:dict forKey:addressNum]; 192 [dict release]; 193 } 194 195 if (name && ![dict objectForKey:kAddressSymbolKey]) { 196 [dict setObject:name forKey:kAddressSymbolKey]; 197 198 // only functions, not line number addresses 199 [functionAddresses_ addObject:addressNum]; 200 } 201 202 if (isCPP) { 203 // try demangling 204 NSString *demangled = [self convertCPlusPlusSymbol:name]; 205 if (demangled != nil) 206 [dict setObject:demangled forKey:kAddressConvertedSymbolKey]; 207 } 208 209 if (line && ![dict objectForKey:kAddressSourceLineKey]) 210 [dict setObject:[NSNumber numberWithUnsignedInt:line] 211 forKey:kAddressSourceLineKey]; 212 213} 214 215//============================================================================= 216- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table { 217 uint32_t n_strx = list->n_un.n_strx; 218 BOOL result = NO; 219 220 // We don't care about non-section specific information except function length 221 if (list->n_sect == 0 && list->n_type != N_FUN ) 222 return NO; 223 224 if (list->n_type == N_FUN) { 225 if (list->n_sect != 0) { 226 // we get the function address from the first N_FUN 227 lastStartAddress_ = list->n_value; 228 } 229 else { 230 // an N_FUN from section 0 may follow the initial N_FUN 231 // giving us function length information 232 NSMutableDictionary *dict = [addresses_ objectForKey: 233 [NSNumber numberWithUnsignedLong:lastStartAddress_]]; 234 235 assert(dict); 236 237 // only set the function size the first time 238 // (sometimes multiple section 0 N_FUN entries appear!) 239 if (![dict objectForKey:kFunctionSizeKey]) { 240 [dict setObject:[NSNumber numberWithUnsignedLongLong:list->n_value] 241 forKey:kFunctionSizeKey]; 242 } 243 } 244 } 245 246 int line = list->n_desc; 247 248 // __TEXT __text section 249 NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_]; 250 251 uint32_t mainSection = [[archSections objectForKey:@"__TEXT__text" ] sectionNumber]; 252 253 // Extract debugging information: 254 // Doc: http://developer.apple.com/documentation/DeveloperTools/gdb/stabs/stabs_toc.html 255 // Header: /usr/include/mach-o/stab.h: 256 if (list->n_type == N_SO) { 257 NSString *src = [NSString stringWithUTF8String:&table[n_strx]]; 258 NSString *ext = [src pathExtension]; 259 NSNumber *address = [NSNumber numberWithUnsignedLongLong:list->n_value]; 260 261 // Leopard puts .c files with no code as an offset of 0, but a 262 // crash can't happen here and it throws off our code that matches 263 // symbols to line numbers so we ignore them.. 264 // Return YES because this isn't an error, just something we don't 265 // care to handle. 266 if ([address unsignedLongValue] == 0) { 267 return YES; 268 } 269 // TODO(waylonis):Ensure that we get the full path for the source file 270 // from the first N_SO record 271 // If there is an extension, we'll consider it source code 272 if ([ext length]) { 273 if (!sources_) 274 sources_ = [[NSMutableDictionary alloc] init]; 275 // Save the source associated with an address 276 [sources_ setObject:src forKey:address]; 277 result = YES; 278 } 279 } else if (list->n_type == N_FUN) { 280 NSString *fn = [NSString stringWithUTF8String:&table[n_strx]]; 281 NSRange range = [fn rangeOfString:@":" options:NSBackwardsSearch]; 282 283 if (![fn length]) 284 return NO; 285 286 if (range.length > 0) { 287 // The function has a ":" followed by some stuff, so strip it off 288 fn = [fn substringToIndex:range.location]; 289 } 290 291 [self addFunction:fn line:line address:list->n_value section:list->n_sect ]; 292 293 result = YES; 294 } else if (list->n_type == N_SLINE && list->n_sect == mainSection) { 295 [self addFunction:nil line:line address:list->n_value section:list->n_sect ]; 296 result = YES; 297 } else if (((list->n_type & N_TYPE) == N_SECT) && !(list->n_type & N_STAB)) { 298 // Regular symbols or ones that are external 299 NSString *fn = [NSString stringWithUTF8String:&table[n_strx]]; 300 301 [self addFunction:fn line:0 address:list->n_value section:list->n_sect ]; 302 result = YES; 303 } 304 305 return result; 306} 307 308#define SwapLongLongIfNeeded(a) (swap ? NXSwapLongLong(a) : (a)) 309#define SwapLongIfNeeded(a) (swap ? NXSwapLong(a) : (a)) 310#define SwapIntIfNeeded(a) (swap ? NXSwapInt(a) : (a)) 311#define SwapShortIfNeeded(a) (swap ? NXSwapShort(a) : (a)) 312 313//============================================================================= 314- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset { 315 BOOL loadedStabs = [self loadSTABSSymbolInfo:base offset:offset]; 316 317 NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_]; 318 BOOL loadedDWARF = NO; 319 if ([archSections objectForKey:@"__DWARF__debug_info"]) { 320 // Treat this this as debug information 321 loadedDWARF = [self loadDWARFSymbolInfo:base offset:offset]; 322 } 323 324 return loadedDWARF || loadedStabs; 325} 326 327//============================================================================= 328- (BOOL)loadDWARFSymbolInfo:(void *)base offset:(uint32_t)offset { 329 330 struct mach_header *header = (struct mach_header *) 331 ((uint32_t)base + offset); 332 BOOL swap = (header->magic == MH_CIGAM); 333 334 NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_]; 335 assert (archSections != nil); 336 section *dbgInfoSection = [[archSections objectForKey:@"__DWARF__debug_info"] sectionPointer]; 337 uint32_t debugInfoSize = SwapLongIfNeeded(dbgInfoSection->size); 338 339#if __BIG_ENDIAN__ 340 dwarf2reader::ByteReader byte_reader(swap ? 341 dwarf2reader::ENDIANNESS_LITTLE : 342 dwarf2reader::ENDIANNESS_BIG); 343#elif __LITTLE_ENDIAN__ 344 dwarf2reader::ByteReader byte_reader(swap ? 345 dwarf2reader::ENDIANNESS_BIG : 346 dwarf2reader::ENDIANNESS_LITTLE); 347#endif 348 uint64_t dbgOffset = 0; 349 350 dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:architecture_]; 351 352 while (dbgOffset < debugInfoSize) { 353 // Prepare necessary objects. 354 dwarf2reader::FunctionMap off_to_funcinfo; 355 dwarf2reader::FunctionMap address_to_funcinfo; 356 dwarf2reader::LineMap line_map; 357 vector<dwarf2reader::SourceFileInfo> files; 358 vector<string> dirs; 359 360 dwarf2reader::CULineInfoHandler line_info_handler(&files, &dirs, 361 &line_map); 362 363 dwarf2reader::CUFunctionInfoHandler function_info_handler(&files, &dirs, 364 &line_map, 365 &off_to_funcinfo, 366 &address_to_funcinfo, 367 &line_info_handler, 368 *oneArchitectureSectionMap, 369 &byte_reader); 370 371 dwarf2reader::CompilationUnit compilation_unit(*oneArchitectureSectionMap, 372 dbgOffset, 373 &byte_reader, 374 &function_info_handler); 375 376 dbgOffset += compilation_unit.Start(); 377 378 // The next 3 functions take the info that the dwarf reader 379 // gives and massages them into the data structures that 380 // dump_syms uses 381 [self processDWARFSourceFileInfo:&files]; 382 [self processDWARFFunctionInfo:&address_to_funcinfo]; 383 [self processDWARFLineNumberInfo:&line_map]; 384 } 385 386 return YES; 387} 388 389- (void)processDWARFSourceFileInfo:(vector<dwarf2reader::SourceFileInfo>*) files { 390 if (!sources_) 391 sources_ = [[NSMutableDictionary alloc] init]; 392 // Save the source associated with an address 393 vector<dwarf2reader::SourceFileInfo>::const_iterator iter = files->begin(); 394 for (; iter != files->end(); iter++) { 395 NSString *sourceFile = [NSString stringWithUTF8String:(*iter).name.c_str()]; 396 if ((*iter).lowpc != ULLONG_MAX) { 397 NSNumber *address = [NSNumber numberWithUnsignedLongLong:(*iter).lowpc]; 398 if ([address unsignedLongLongValue] == 0) { 399 continue; 400 } 401 [sources_ setObject:sourceFile forKey:address]; 402 } 403 } 404} 405 406- (void)processDWARFFunctionInfo:(dwarf2reader::FunctionMap*)address_to_funcinfo { 407 for (dwarf2reader::FunctionMap::const_iterator iter = address_to_funcinfo->begin(); 408 iter != address_to_funcinfo->end(); ++iter) { 409 if (iter->second->name.empty()) { 410 continue; 411 } 412 413 if (!addresses_) 414 addresses_ = [[NSMutableDictionary alloc] init]; 415 416 NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:(*iter).second->lowpc]; 417 418 [functionAddresses_ addObject:addressNum]; 419 420 NSMutableDictionary *dict = [addresses_ objectForKey:addressNum]; 421 422 if (!dict) { 423 dict = [[NSMutableDictionary alloc] init]; 424 [addresses_ setObject:dict forKey:addressNum]; 425 [dict release]; 426 } 427 428 // set name of function if it isn't already set 429 if (![dict objectForKey:kAddressSymbolKey]) { 430 NSString *symbolName = [NSString stringWithUTF8String:iter->second->name.c_str()]; 431 [dict setObject:symbolName forKey:kAddressSymbolKey]; 432 } 433 434 // try demangling function name if we have a mangled name 435 if (![dict objectForKey:kAddressConvertedSymbolKey] && 436 !iter->second->mangled_name.empty()) { 437 NSString *mangled = [NSString stringWithUTF8String:iter->second->mangled_name.c_str()]; 438 NSString *demangled = [self convertCPlusPlusSymbol:mangled]; 439 if (demangled != nil) 440 [dict setObject:demangled forKey:kAddressConvertedSymbolKey]; 441 } 442 443 // set line number for beginning of function 444 if (iter->second->line && ![dict objectForKey:kAddressSourceLineKey]) 445 [dict setObject:[NSNumber numberWithUnsignedInt:iter->second->line] 446 forKey:kAddressSourceLineKey]; 447 448 // set function size by subtracting low PC from high PC 449 if (![dict objectForKey:kFunctionSizeKey]) { 450 [dict setObject:[NSNumber numberWithUnsignedLongLong:iter->second->highpc - iter->second->lowpc] 451 forKey:kFunctionSizeKey]; 452 } 453 454 // Set the file that the function is in 455 if (![dict objectForKey:kFunctionFileKey]) { 456 [dict setObject:[NSString stringWithUTF8String:iter->second->file.c_str()] 457 forKey:kFunctionFileKey]; 458 } 459 } 460} 461 462- (void)processDWARFLineNumberInfo:(dwarf2reader::LineMap*)line_map { 463 for (dwarf2reader::LineMap::const_iterator iter = line_map->begin(); 464 iter != line_map->end(); 465 ++iter) { 466 467 NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:iter->first]; 468 NSMutableDictionary *dict = [addresses_ objectForKey:addressNum]; 469 470 if (!dict) { 471 dict = [[NSMutableDictionary alloc] init]; 472 [addresses_ setObject:dict forKey:addressNum]; 473 [dict release]; 474 } 475 476 if (iter->second.second && ![dict objectForKey:kAddressSourceLineKey]) { 477 [dict setObject:[NSNumber numberWithUnsignedInt:iter->second.second] 478 forKey:kAddressSourceLineKey]; 479 } 480 481 // Set the file that the function's address is in 482 if (![dict objectForKey:kFunctionFileKey]) { 483 [dict setObject:[NSString stringWithUTF8String:iter->second.first.c_str()] 484 forKey:kFunctionFileKey]; 485 } 486 } 487} 488 489//============================================================================= 490- (BOOL)loadSTABSSymbolInfo:(void *)base offset:(uint32_t)offset { 491 struct mach_header *header = (struct mach_header *)((uint32_t)base + offset); 492 BOOL swap = (header->magic == MH_CIGAM); 493 uint32_t count = SwapLongIfNeeded(header->ncmds); 494 struct load_command *cmd = 495 (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); 496 uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB); 497 BOOL result = NO; 498 499 if (!addresses_) 500 addresses_ = [[NSMutableDictionary alloc] init]; 501 502 for (uint32_t i = 0; cmd && (i < count); ++i) { 503 if (cmd->cmd == symbolTableCommand) { 504 struct symtab_command *symtab = (struct symtab_command *)cmd; 505 uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms); 506 uint32_t symoff = SwapLongIfNeeded(symtab->symoff); 507 uint32_t stroff = SwapLongIfNeeded(symtab->stroff); 508 struct nlist *list = (struct nlist *)((uint32_t)base + symoff + offset); 509 char *strtab = ((char *)header + stroff); 510 511 // Process each command, looking for debugging stuff 512 for (uint32_t j = 0; j < ncmds; ++j, ++list) { 513 // Fill in an nlist_64 structure and process with that 514 struct nlist_64 nlist64; 515 nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx); 516 nlist64.n_type = list->n_type; 517 nlist64.n_sect = list->n_sect; 518 nlist64.n_desc = SwapShortIfNeeded(list->n_desc); 519 nlist64.n_value = (uint64_t)SwapLongIfNeeded(list->n_value); 520 521 // TODO(nealsid): is this broken? we get NO if one symbol fails 522 // but then we lose that information if another suceeeds 523 if ([self processSymbolItem:&nlist64 stringTable:strtab]) 524 result = YES; 525 } 526 } 527 528 uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); 529 cmd = (struct load_command *)((uint32_t)cmd + cmdSize); 530 } 531 532 return result; 533} 534 535//============================================================================= 536- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset { 537 struct mach_header_64 *header = (struct mach_header_64 *) 538 ((uint32_t)base + offset); 539 BOOL swap = (header->magic == MH_CIGAM_64); 540 uint32_t count = SwapLongIfNeeded(header->ncmds); 541 struct load_command *cmd = 542 (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); 543 uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB); 544 BOOL result = NO; 545 546 for (uint32_t i = 0; cmd && (i < count); i++) { 547 if (cmd->cmd == symbolTableCommand) { 548 struct symtab_command *symtab = (struct symtab_command *)cmd; 549 uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms); 550 uint32_t symoff = SwapLongIfNeeded(symtab->symoff); 551 uint32_t stroff = SwapLongIfNeeded(symtab->stroff); 552 struct nlist_64 *list = (struct nlist_64 *)((uint32_t)base + symoff); 553 char *strtab = ((char *)header + stroff); 554 555 // Process each command, looking for debugging stuff 556 for (uint32_t j = 0; j < ncmds; ++j, ++list) { 557 if (!(list->n_type & (N_STAB | N_TYPE))) 558 continue; 559 560 // Fill in an nlist_64 structure and process with that 561 struct nlist_64 nlist64; 562 nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx); 563 nlist64.n_type = list->n_type; 564 nlist64.n_sect = list->n_sect; 565 nlist64.n_desc = SwapShortIfNeeded(list->n_desc); 566 nlist64.n_value = SwapLongLongIfNeeded(list->n_value); 567 568 if ([self processSymbolItem:&nlist64 stringTable:strtab]) 569 result = YES; 570 } 571 } 572 573 uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); 574 cmd = (struct load_command *)((uint32_t)cmd + cmdSize); 575 } 576 577 return result; 578} 579 580//============================================================================= 581- (BOOL)loadSymbolInfoForArchitecture { 582 NSMutableData *data = [[NSMutableData alloc] 583 initWithContentsOfMappedFile:sourcePath_]; 584 585 NSDictionary *headerInfo = [headers_ objectForKey:architecture_]; 586 void *base = [data mutableBytes]; 587 uint32_t offset = 588 [[headerInfo objectForKey:kHeaderOffsetKey] unsignedLongValue]; 589 BOOL is64 = [[headerInfo objectForKey:kHeaderIs64BitKey] boolValue]; 590 BOOL result = is64 ? [self loadSymbolInfo64:base offset:offset] : 591 [self loadSymbolInfo:base offset:offset]; 592 593 [data release]; 594 return result; 595} 596 597- (dwarf2reader::SectionMap*)getSectionMapForArchitecture:(NSString*)architecture { 598 599 string currentArch([architecture UTF8String]); 600 dwarf2reader::SectionMap *oneArchitectureSectionMap; 601 602 ArchSectionMap::const_iterator iter = sectionsForArch_->find(currentArch); 603 604 if (iter == sectionsForArch_->end()) { 605 oneArchitectureSectionMap = new dwarf2reader::SectionMap(); 606 sectionsForArch_->insert(make_pair(currentArch, oneArchitectureSectionMap)); 607 } else { 608 oneArchitectureSectionMap = iter->second; 609 } 610 611 return oneArchitectureSectionMap; 612} 613 614//============================================================================= 615// build a dictionary of section numbers keyed off a string 616// which is the concatenation of the segment name and the section name 617- (void)generateSectionDictionary:(struct mach_header*)header { 618 619 BOOL swap = (header->magic == MH_CIGAM); 620 uint32_t count = SwapLongIfNeeded(header->ncmds); 621 struct load_command *cmd = 622 (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); 623 uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT); 624 uint32_t sectionNumber = 1; // section numbers are counted from 1 625 626 cpu_type_t cpu = SwapIntIfNeeded(header->cputype); 627 628 NSString *arch; 629 630 if (cpu & CPU_ARCH_ABI64) 631 arch = ((cpu & ~CPU_ARCH_ABI64) == CPU_TYPE_X86) ? 632 @"x86_64" : @"ppc64"; 633 else 634 arch = (cpu == CPU_TYPE_X86) ? @"x86" : @"ppc"; 635 636 NSMutableDictionary *archSections; 637 638 if (!sectionData_) { 639 sectionData_ = [[NSMutableDictionary alloc] init]; 640 } 641 642 if (![sectionData_ objectForKey:architecture_]) { 643 [sectionData_ setObject:[[NSMutableDictionary alloc] init] forKey:arch]; 644 } 645 646 archSections = [sectionData_ objectForKey:arch]; 647 648 dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:arch]; 649 650 // loop through every segment command, then through every section 651 // contained inside each of them 652 for (uint32_t i = 0; cmd && (i < count); ++i) { 653 if (cmd->cmd == segmentCommand) { 654 struct segment_command *seg = (struct segment_command *)cmd; 655 section *sect = (section *)((uint32_t)cmd + sizeof(segment_command)); 656 uint32_t nsects = SwapLongIfNeeded(seg->nsects); 657 658 for (uint32_t j = 0; j < nsects; ++j) { 659 NSString *segSectName = [NSString stringWithFormat:@"%s%s", 660 seg->segname, sect->sectname]; 661 662 [archSections setObject:[[MachSection alloc] initWithMachSection:sect andNumber:sectionNumber] 663 forKey:segSectName]; 664 665 // filter out sections with size 0, offset 0 666 if (sect->offset != 0 && sect->size != 0) { 667 // fill sectionmap for dwarf reader 668 oneArchitectureSectionMap->insert(make_pair(sect->sectname,make_pair(((const char*)header) + SwapLongIfNeeded(sect->offset), (size_t)SwapLongIfNeeded(sect->size)))); 669 } 670 671 ++sect; 672 ++sectionNumber; 673 } 674 } 675 676 uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); 677 cmd = (struct load_command *)((uint32_t)cmd + cmdSize); 678 } 679} 680 681//============================================================================= 682- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset { 683 struct mach_header *header = (struct mach_header *)((uint32_t)base + offset); 684 BOOL swap = (header->magic == MH_CIGAM); 685 uint32_t count = SwapLongIfNeeded(header->ncmds); 686 struct load_command *cmd = 687 (struct load_command *)((uint32_t)header + sizeof(struct mach_header)); 688 uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT); 689 690 [self generateSectionDictionary:header]; 691 692 for (uint32_t i = 0; cmd && (i < count); ++i) { 693 if (cmd->cmd == segmentCommand) { 694 struct segment_command *seg = (struct segment_command *)cmd; 695 696 if (!strcmp(seg->segname, "__TEXT")) { 697 uint32_t addr = SwapLongIfNeeded(seg->vmaddr); 698 uint32_t size = SwapLongIfNeeded(seg->vmsize); 699 cpu_type_t cpu = SwapIntIfNeeded(header->cputype); 700 NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86" : @"ppc"; 701 702 [headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: 703 [NSNumber numberWithUnsignedLongLong:(uint64_t)addr], 704 kHeaderBaseAddressKey, 705 [NSNumber numberWithUnsignedLongLong:(uint64_t)size], kHeaderSizeKey, 706 [NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey, 707 [NSNumber numberWithBool:NO], kHeaderIs64BitKey, 708 [NSNumber numberWithUnsignedLong:cpu], kHeaderCPUTypeKey, 709 nil] forKey:cpuStr]; 710 711 return YES; 712 } 713 } 714 715 uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); 716 cmd = (struct load_command *)((uint32_t)cmd + cmdSize); 717 } 718 719 return NO; 720} 721 722//============================================================================= 723- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset { 724 struct mach_header_64 *header = 725 (struct mach_header_64 *)((uint32_t)base + offset); 726 BOOL swap = (header->magic == MH_CIGAM_64); 727 uint32_t count = SwapLongIfNeeded(header->ncmds); 728 struct load_command *cmd = 729 (struct load_command *)((uint32_t)header + sizeof(struct mach_header_64)); 730 731 for (uint32_t i = 0; cmd && (i < count); ++i) { 732 uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT_64); 733 if (cmd->cmd == segmentCommand) { 734 struct segment_command_64 *seg = (struct segment_command_64 *)cmd; 735 if (!strcmp(seg->segname, "__TEXT")) { 736 uint64_t addr = SwapLongLongIfNeeded(seg->vmaddr); 737 uint64_t size = SwapLongLongIfNeeded(seg->vmsize); 738 cpu_type_t cpu = SwapIntIfNeeded(header->cputype); 739 cpu &= (~CPU_ARCH_ABI64); 740 NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86_64" : @"ppc64"; 741 742 [headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: 743 [NSNumber numberWithUnsignedLongLong:addr], kHeaderBaseAddressKey, 744 [NSNumber numberWithUnsignedLongLong:size], kHeaderSizeKey, 745 [NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey, 746 [NSNumber numberWithBool:YES], kHeaderIs64BitKey, 747 [NSNumber numberWithUnsignedLong:cpu], kHeaderCPUTypeKey, 748 nil] forKey:cpuStr]; 749 return YES; 750 } 751 } 752 753 uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize); 754 cmd = (struct load_command *)((uint32_t)cmd + cmdSize); 755 } 756 757 return NO; 758} 759 760//============================================================================= 761- (BOOL)loadModuleInfo { 762 uint64_t result = 0; 763 NSMutableData *data = [[NSMutableData alloc] 764 initWithContentsOfMappedFile:sourcePath_]; 765 void *bytes = [data mutableBytes]; 766 struct fat_header *fat = (struct fat_header *)bytes; 767 768 if (!fat) { 769 [data release]; 770 return 0; 771 } 772 773 // Gather some information based on the header 774 BOOL isFat = fat->magic == FAT_MAGIC || fat->magic == FAT_CIGAM; 775 BOOL is64 = fat->magic == MH_MAGIC_64 || fat->magic == MH_CIGAM_64; 776 BOOL is32 = fat->magic == MH_MAGIC || fat->magic == MH_CIGAM; 777 BOOL swap = fat->magic == FAT_CIGAM || fat->magic == MH_CIGAM_64 || 778 fat->magic == MH_CIGAM; 779 780 if (!is64 && !is32 && !isFat) { 781 [data release]; 782 return 0; 783 } 784 785 // Load any available architectures and save the information 786 headers_ = [[NSMutableDictionary alloc] init]; 787 788 if (isFat) { 789 struct fat_arch *archs = 790 (struct fat_arch *)((uint32_t)fat + sizeof(struct fat_header)); 791 uint32_t count = SwapLongIfNeeded(fat->nfat_arch); 792 793 for (uint32_t i = 0; i < count; ++i) { 794 archs[i].cputype = SwapIntIfNeeded(archs[i].cputype); 795 archs[i].cpusubtype = SwapIntIfNeeded(archs[i].cpusubtype); 796 archs[i].offset = SwapLongIfNeeded(archs[i].offset); 797 archs[i].size = SwapLongIfNeeded(archs[i].size); 798 archs[i].align = SwapLongIfNeeded(archs[i].align); 799 800 if (archs[i].cputype & CPU_ARCH_ABI64) 801 result = [self loadHeader64:bytes offset:archs[i].offset]; 802 else 803 result = [self loadHeader:bytes offset:archs[i].offset]; 804 } 805 } else if (is32) { 806 result = [self loadHeader:bytes offset:0]; 807 } else { 808 result = [self loadHeader64:bytes offset:0]; 809 } 810 811 [data release]; 812 return result; 813} 814 815//============================================================================= 816static BOOL WriteFormat(int fd, const char *fmt, ...) { 817 va_list list; 818 char buffer[4096]; 819 ssize_t expected, written; 820 821 va_start(list, fmt); 822 vsnprintf(buffer, sizeof(buffer), fmt, list); 823 expected = strlen(buffer); 824 written = write(fd, buffer, strlen(buffer)); 825 va_end(list); 826 827 return expected == written; 828} 829 830//============================================================================= 831- (BOOL)outputSymbolFile:(int)fd { 832 // Get the baseAddress for this architecture 833 NSDictionary *archDict = [headers_ objectForKey:architecture_]; 834 NSNumber *baseAddressNum = [archDict objectForKey:kHeaderBaseAddressKey]; 835 uint64_t baseAddress = 836 baseAddressNum ? [baseAddressNum unsignedLongLongValue] : 0; 837 NSNumber *moduleSizeNum = [archDict objectForKey:kHeaderSizeKey]; 838 uint64_t moduleSize = 839 moduleSizeNum ? [moduleSizeNum unsignedLongLongValue] : 0; 840 841 // UUID 842 FileID file_id([sourcePath_ fileSystemRepresentation]); 843 unsigned char identifier[16]; 844 char identifierStr[40]; 845 const char *moduleName = [[sourcePath_ lastPathComponent] UTF8String]; 846 int cpu_type = [[archDict objectForKey:kHeaderCPUTypeKey] unsignedLongValue]; 847 if (file_id.MachoIdentifier(cpu_type, identifier)) { 848 FileID::ConvertIdentifierToString(identifier, identifierStr, 849 sizeof(identifierStr)); 850 } 851 else { 852 fprintf(stderr, "Unable to calculate UUID of mach-o binary!\n"); 853 return NO; 854 } 855 856 // keep track exclusively of function addresses 857 // for sanity checking function lengths 858 functionAddresses_ = [[NSMutableSet alloc] init]; 859 860 // Gather the information 861 [self loadSymbolInfoForArchitecture]; 862 863 NSArray *sortedAddresses = [[addresses_ allKeys] 864 sortedArrayUsingSelector:@selector(compare:)]; 865 866 NSArray *sortedFunctionAddresses = [[functionAddresses_ allObjects] 867 sortedArrayUsingSelector:@selector(compare:)]; 868 869 // position ourselves at the 2nd function 870 unsigned int funcIndex = 1; 871 872 // Remove the dashes from the string 873 NSMutableString *compactedStr = 874 [NSMutableString stringWithCString:identifierStr encoding:NSASCIIStringEncoding]; 875 [compactedStr replaceOccurrencesOfString:@"-" withString:@"" options:0 876 range:NSMakeRange(0, [compactedStr length])]; 877 878 if (!WriteFormat(fd, "MODULE mac %s %s0 %s\n", [architecture_ UTF8String], 879 [compactedStr UTF8String], moduleName)) { 880 return NO; 881 } 882 883 // Sources ordered by address 884 NSArray *sources = [[sources_ allKeys] 885 sortedArrayUsingSelector:@selector(compare:)]; 886 NSMutableDictionary *fileNameToFileIndex = [[NSMutableDictionary alloc] init]; 887 unsigned int sourceCount = [sources count]; 888 for (unsigned int i = 0; i < sourceCount; ++i) { 889 NSString *file = [sources_ objectForKey:[sources objectAtIndex:i]]; 890 if (!WriteFormat(fd, "FILE %d %s\n", i + 1, [file UTF8String])) 891 return NO; 892 893 [fileNameToFileIndex setObject:[NSNumber numberWithUnsignedInt:i+1] 894 forKey:file]; 895 } 896 897 // Symbols 898 char terminatingChar = '\n'; 899 uint32_t fileIdx = 0, nextFileIdx = 0; 900 uint64_t nextSourceFileAddress = 0; 901 NSNumber *nextAddress; 902 uint64_t nextAddressVal; 903 unsigned int addressCount = [sortedAddresses count]; 904 905 bool insideFunction = false; 906 907 for (unsigned int i = 0; i < addressCount; ++i) { 908 NSNumber *address = [sortedAddresses objectAtIndex:i]; 909 // skip sources that have a starting address of 0 910 if ([address unsignedLongValue] == 0) { 911 continue; 912 } 913 914 uint64_t addressVal = [address unsignedLongLongValue] - baseAddress; 915 916 // Get the next address to calculate the length 917 if (i + 1 < addressCount) { 918 nextAddress = [sortedAddresses objectAtIndex:i + 1]; 919 nextAddressVal = [nextAddress unsignedLongLongValue] - baseAddress; 920 } else { 921 nextAddressVal = baseAddress + moduleSize; 922 // The symbol reader doesn't want a trailing newline 923 terminatingChar = '\0'; 924 } 925 926 NSDictionary *dict = [addresses_ objectForKey:address]; 927 NSNumber *line = [dict objectForKey:kAddressSourceLineKey]; 928 NSString *symbol = [dict objectForKey:kAddressConvertedSymbolKey]; 929 930 if (!symbol) 931 symbol = [dict objectForKey:kAddressSymbolKey]; 932 933 // sanity check the function length by making sure it doesn't 934 // run beyond the next function entry 935 uint64_t nextFunctionAddress = 0; 936 if (symbol && funcIndex < [sortedFunctionAddresses count]) { 937 nextFunctionAddress = [[sortedFunctionAddresses objectAtIndex:funcIndex] 938 unsignedLongLongValue] - baseAddress; 939 ++funcIndex; 940 } 941 942 // Skip some symbols 943 if ([symbol hasPrefix:@"vtable for"]) 944 continue; 945 946 if ([symbol hasPrefix:@"__static_initialization_and_destruction_0"]) 947 continue; 948 949 if ([symbol hasPrefix:@"_GLOBAL__I_"]) 950 continue; 951 952 if ([symbol hasPrefix:@"__func__."]) 953 continue; 954 955 if ([symbol hasPrefix:@"__gnu"]) 956 continue; 957 958 if ([symbol hasPrefix:@"typeinfo "]) 959 continue; 960 961 if ([symbol hasPrefix:@"EH_frame"]) 962 continue; 963 964 if ([symbol hasPrefix:@"GCC_except_table"]) 965 continue; 966 967 if ([symbol hasPrefix:@"__tcf"]) 968 continue; 969 970 if ([symbol hasPrefix:@"non-virtual thunk"]) 971 continue; 972 973 // Find the source file (if any) that contains this address 974 while (sourceCount && (addressVal >= nextSourceFileAddress)) { 975 fileIdx = nextFileIdx; 976 977 if (nextFileIdx < sourceCount) { 978 NSNumber *addr = [sources objectAtIndex:nextFileIdx]; 979 ++nextFileIdx; 980 nextSourceFileAddress = [addr unsignedLongLongValue] - baseAddress; 981 } else { 982 nextSourceFileAddress = baseAddress + moduleSize; 983 break; 984 } 985 } 986 987 NSNumber *functionLength = [dict objectForKey:kFunctionSizeKey]; 988 989 if (line) { 990 if (symbol && functionLength) { 991 992 uint64_t functionLengthVal = [functionLength unsignedLongLongValue]; 993 994 insideFunction = true; 995 // sanity check to make sure the length we were told does not exceed 996 // the space between this function and the next 997 if (nextFunctionAddress != 0) { 998 uint64_t functionLengthVal2 = nextFunctionAddress - addressVal; 999 1000 if(functionLengthVal > functionLengthVal2 ) { 1001 functionLengthVal = functionLengthVal2; 1002 } 1003 } 1004 1005 // Function 1006 if (!WriteFormat(fd, "FUNC %llx %llx 0 %s\n", addressVal, 1007 functionLengthVal, [symbol UTF8String])) 1008 return NO; 1009 } 1010 1011 // Throw out line number information that doesn't correspond to 1012 // any function 1013 if (insideFunction) { 1014 // Source line 1015 uint64_t length = nextAddressVal - addressVal; 1016 1017 // if fileNameToFileIndex/dict has an entry for the 1018 // file/kFunctionFileKey, we're processing DWARF and have stored 1019 // files for each program counter. If there is no entry, we're 1020 // processing STABS and can use the old method of mapping 1021 // addresses to files(which was basically iterating over a set 1022 // of addresses until we reached one that was greater than the 1023 // high PC of the current file, then moving on to the next file) 1024 NSNumber *fileIndex = [fileNameToFileIndex objectForKey:[dict objectForKey:kFunctionFileKey]]; 1025 if (!WriteFormat(fd, "%llx %llx %d %d\n", addressVal, length, 1026 [line unsignedIntValue], fileIndex ? [fileIndex unsignedIntValue] : fileIdx)) 1027 return NO; 1028 } 1029 } else { 1030 // PUBLIC <address> <stack-size> <name> 1031 if (!WriteFormat(fd, "PUBLIC %llx 0 %s\n", addressVal, 1032 [symbol UTF8String])) 1033 return NO; 1034 insideFunction = false; 1035 } 1036 } 1037 1038 return YES; 1039} 1040 1041//============================================================================= 1042- (id)initWithContentsOfFile:(NSString *)path { 1043 if ((self = [super init])) { 1044 1045 if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { 1046 [self autorelease]; 1047 return nil; 1048 } 1049 1050 sourcePath_ = [path copy]; 1051 1052 // Test for .DSYM bundle 1053 NSBundle *dsymBundle = [NSBundle bundleWithPath:sourcePath_]; 1054 1055 if (dsymBundle) { 1056 1057 // we need to take the DSYM bundle path and remove it's 1058 // extension to get the name of the file inside the resources 1059 // directory of the bundle that actually has the DWARF 1060 // information 1061 // But, Xcode supports something called "Wrapper extension"(see 1062 // build settings), which would make the bundle name 1063 // /tmp/foo/test.kext.dSYM, but the dwarf binary name would 1064 // still be "test". so, now we loop through until deleting the 1065 // extension doesn't change the string 1066 1067 // e.g. suppose sourcepath_ is /tmp/foo/test.dSYM 1068 1069 NSString *dwarfBinName = [sourcePath_ lastPathComponent]; 1070 NSString *dwarfBinPath; 1071 1072 // We use a do/while loop so we can handle files without an extension 1073 do { 1074 dwarfBinName = [dwarfBinName stringByDeletingPathExtension]; 1075 // now, dwarfBinName is "test" 1076 dwarfBinPath = [dsymBundle pathForResource:dwarfBinName ofType:nil inDirectory:@"DWARF"]; 1077 if (dwarfBinPath != nil) 1078 break; 1079 } while (![[dwarfBinName stringByDeletingPathExtension] isEqualToString:dwarfBinName]); 1080 1081 if (dwarfBinPath == nil) { 1082 NSLog(@"The bundle passed on the command line does not appear to be a DWARF dSYM bundle"); 1083 [self autorelease]; 1084 return nil; 1085 } 1086 1087 // otherwise we're good to go 1088 [sourcePath_ release]; 1089 1090 sourcePath_ = [dwarfBinPath copy]; 1091 NSLog(@"Loading DWARF dSYM file from %@", sourcePath_); 1092 } 1093 1094 sectionsForArch_ = new ArchSectionMap(); 1095 1096 if (![self loadModuleInfo]) { 1097 [self autorelease]; 1098 return nil; 1099 } 1100 1101 // If there's more than one, use the native one 1102 if ([headers_ count] > 1) { 1103 const NXArchInfo *localArchInfo = NXGetLocalArchInfo(); 1104 1105 if (localArchInfo) { 1106 cpu_type_t cpu = localArchInfo->cputype; 1107 NSString *arch; 1108 1109 if (cpu & CPU_ARCH_ABI64) 1110 arch = ((cpu & ~CPU_ARCH_ABI64) == CPU_TYPE_X86) ? 1111 @"x86_64" : @"ppc64"; 1112 else 1113 arch = (cpu == CPU_TYPE_X86) ? @"x86" : @"ppc"; 1114 1115 [self setArchitecture:arch]; 1116 } 1117 } else { 1118 // Specify the default architecture 1119 [self setArchitecture:[[headers_ allKeys] objectAtIndex:0]]; 1120 } 1121 } 1122 1123 return self; 1124} 1125 1126//============================================================================= 1127- (NSArray *)availableArchitectures { 1128 return [headers_ allKeys]; 1129} 1130 1131//============================================================================= 1132- (void)dealloc { 1133 [sourcePath_ release]; 1134 [architecture_ release]; 1135 [addresses_ release]; 1136 [functionAddresses_ release]; 1137 [sources_ release]; 1138 [headers_ release]; 1139 delete sectionsForArch_; 1140 1141 [super dealloc]; 1142} 1143 1144//============================================================================= 1145- (BOOL)setArchitecture:(NSString *)architecture { 1146 NSString *normalized = [architecture lowercaseString]; 1147 BOOL isValid = NO; 1148 1149 if ([normalized isEqualToString:@"ppc"]) { 1150 isValid = YES; 1151 } 1152 else if ([normalized isEqualToString:@"i386"]) { 1153 normalized = @"x86"; 1154 isValid = YES; 1155 } 1156 else if ([normalized isEqualToString:@"x86"]) { 1157 isValid = YES; 1158 } 1159 else if ([normalized isEqualToString:@"ppc64"]) { 1160 isValid = YES; 1161 } 1162 else if ([normalized isEqualToString:@"x86_64"]) { 1163 isValid = YES; 1164 } 1165 1166 if (isValid) { 1167 if (![headers_ objectForKey:normalized]) 1168 return NO; 1169 1170 [architecture_ autorelease]; 1171 architecture_ = [normalized copy]; 1172 } 1173 1174 return isValid; 1175} 1176 1177//============================================================================= 1178- (NSString *)architecture { 1179 return architecture_; 1180} 1181 1182//============================================================================= 1183- (BOOL)writeSymbolFile:(NSString *)destinationPath { 1184 const char *dest = [destinationPath fileSystemRepresentation]; 1185 int fd; 1186 1187 if ([[destinationPath substringToIndex:1] isEqualToString:@"-"]) 1188 fd = STDOUT_FILENO; 1189 else 1190 fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666); 1191 1192 if (fd == -1) 1193 return NO; 1194 1195 BOOL result = [self outputSymbolFile:fd]; 1196 1197 close(fd); 1198 1199 return result; 1200} 1201 1202@end 1203 1204@implementation MachSection 1205 1206- (id)initWithMachSection:(section *)sect andNumber:(uint32_t)sectionNumber { 1207 if ((self = [super init])) { 1208 sect_ = sect; 1209 sectionNumber_ = sectionNumber; 1210 } 1211 1212 return self; 1213} 1214 1215- (section*)sectionPointer { 1216 return sect_; 1217} 1218 1219- (uint32_t)sectionNumber { 1220 return sectionNumber_; 1221} 1222@end 1223