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