1// -*- mode: c++ -*- 2 3// Copyright (c) 2011, Google Inc. 4// All rights reserved. 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions are 8// met: 9// 10// * Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// * Redistributions in binary form must reproduce the above 13// copyright notice, this list of conditions and the following disclaimer 14// in the documentation and/or other materials provided with the 15// distribution. 16// * Neither the name of Google Inc. nor the names of its 17// contributors may be used to endorse or promote products derived from 18// this software without specific prior written permission. 19// 20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32// dump_syms_tool.mm: Command line tool that uses the DumpSymbols class. 33// TODO(waylonis): accept stdin 34 35#include <mach-o/arch.h> 36#include <unistd.h> 37 38#include <iostream> 39#include <vector> 40 41#include "common/mac/dump_syms.h" 42#include "common/mac/arch_utilities.h" 43#include "common/mac/macho_utilities.h" 44#include "common/scoped_ptr.h" 45 46using google_breakpad::DumpSymbols; 47using google_breakpad::Module; 48using google_breakpad::scoped_ptr; 49using std::vector; 50 51struct Options { 52 Options() 53 : srcPath(), dsymPath(), arch(), cfi(true), handle_inter_cu_refs(true) {} 54 NSString *srcPath; 55 NSString *dsymPath; 56 const NXArchInfo *arch; 57 bool cfi; 58 bool handle_inter_cu_refs; 59}; 60 61static bool StackFrameEntryComparator(const Module::StackFrameEntry* a, 62 const Module::StackFrameEntry* b) { 63 return a->address < b->address; 64} 65 66// Copy the CFI data from |from_module| into |to_module|, for any non- 67// overlapping ranges. 68static void CopyCFIDataBetweenModules(Module* to_module, 69 const Module* from_module) { 70 typedef vector<Module::StackFrameEntry*>::const_iterator Iterator; 71 72 // Get the CFI data from both the source and destination modules and ensure 73 // it is sorted by start address. 74 vector<Module::StackFrameEntry*> from_data; 75 from_module->GetStackFrameEntries(&from_data); 76 std::sort(from_data.begin(), from_data.end(), &StackFrameEntryComparator); 77 78 vector<Module::StackFrameEntry*> to_data; 79 to_module->GetStackFrameEntries(&to_data); 80 std::sort(to_data.begin(), to_data.end(), &StackFrameEntryComparator); 81 82 Iterator to_it = to_data.begin(); 83 84 for (Iterator it = from_data.begin(); it != from_data.end(); ++it) { 85 Module::StackFrameEntry* from_entry = *it; 86 Module::Address from_entry_end = from_entry->address + from_entry->size; 87 88 // Find the first CFI record in the |to_module| that does not have an 89 // address less than the entry to be copied. 90 while (to_it != to_data.end()) { 91 if (from_entry->address > (*to_it)->address) 92 ++to_it; 93 else 94 break; 95 } 96 97 // If the entry does not overlap, then it is safe to copy to |to_module|. 98 if (to_it == to_data.end() || (from_entry->address < (*to_it)->address && 99 from_entry_end < (*to_it)->address)) { 100 to_module->AddStackFrameEntry(new Module::StackFrameEntry(*from_entry)); 101 } 102 } 103} 104 105static bool Start(const Options &options) { 106 SymbolData symbol_data = options.cfi ? ALL_SYMBOL_DATA : NO_CFI; 107 DumpSymbols dump_symbols(symbol_data, options.handle_inter_cu_refs); 108 109 // For x86_64 binaries, the CFI data is in the __TEXT,__eh_frame of the 110 // Mach-O file, which is not copied into the dSYM. Whereas in i386, the CFI 111 // data is in the __DWARF,__debug_frame section, which is moved into the 112 // dSYM. Therefore, to get x86_64 CFI data, dump_syms needs to look at both 113 // the dSYM and the Mach-O file. If both paths are present and CFI was 114 // requested, then consider the Module as "split" and dump all the debug data 115 // from the primary debug info file, the dSYM, and then dump additional CFI 116 // data from the source Mach-O file. 117 bool split_module = options.dsymPath && options.srcPath && options.cfi; 118 NSString* primary_file = split_module ? options.dsymPath : options.srcPath; 119 120 if (!dump_symbols.Read(primary_file)) 121 return false; 122 123 if (options.arch) { 124 if (!dump_symbols.SetArchitecture(options.arch->cputype, 125 options.arch->cpusubtype)) { 126 fprintf(stderr, "%s: no architecture '%s' is present in file.\n", 127 [primary_file fileSystemRepresentation], options.arch->name); 128 size_t available_size; 129 const struct fat_arch *available = 130 dump_symbols.AvailableArchitectures(&available_size); 131 if (available_size == 1) 132 fprintf(stderr, "the file's architecture is: "); 133 else 134 fprintf(stderr, "architectures present in the file are:\n"); 135 for (size_t i = 0; i < available_size; i++) { 136 const struct fat_arch *arch = &available[i]; 137 const NXArchInfo *arch_info = 138 google_breakpad::BreakpadGetArchInfoFromCpuType( 139 arch->cputype, arch->cpusubtype); 140 if (arch_info) 141 fprintf(stderr, "%s (%s)\n", arch_info->name, arch_info->description); 142 else 143 fprintf(stderr, "unrecognized cpu type 0x%x, subtype 0x%x\n", 144 arch->cputype, arch->cpusubtype); 145 } 146 return false; 147 } 148 } 149 150 // Read the primary file into a Breakpad Module. 151 Module* module = NULL; 152 if (!dump_symbols.ReadSymbolData(&module)) 153 return false; 154 scoped_ptr<Module> scoped_module(module); 155 156 // If this is a split module, read the secondary Mach-O file, from which the 157 // CFI data will be extracted. 158 if (split_module && primary_file == options.dsymPath) { 159 if (!dump_symbols.Read(options.srcPath)) 160 return false; 161 162 Module* cfi_module = NULL; 163 if (!dump_symbols.ReadSymbolData(&cfi_module)) 164 return false; 165 scoped_ptr<Module> scoped_cfi_module(cfi_module); 166 167 // Ensure that the modules are for the same debug code file. 168 if (cfi_module->name() != module->name() || 169 cfi_module->os() != module->os() || 170 cfi_module->architecture() != module->architecture() || 171 cfi_module->identifier() != module->identifier()) { 172 fprintf(stderr, "Cannot generate a symbol file from split sources that do" 173 " not match.\n"); 174 return false; 175 } 176 177 CopyCFIDataBetweenModules(module, cfi_module); 178 } 179 180 return module->Write(std::cout, symbol_data); 181} 182 183//============================================================================= 184static void Usage(int argc, const char *argv[]) { 185 fprintf(stderr, "Output a Breakpad symbol file from a Mach-o file.\n"); 186 fprintf(stderr, "Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] " 187 "<Mach-o file>\n", argv[0]); 188 fprintf(stderr, "\t-a: Architecture type [default: native, or whatever is\n"); 189 fprintf(stderr, "\t in the file, if it contains only one architecture]\n"); 190 fprintf(stderr, "\t-g: Debug symbol file (dSYM) to dump in addition to the " 191 "Mach-o file\n"); 192 fprintf(stderr, "\t-c: Do not generate CFI section\n"); 193 fprintf(stderr, "\t-r: Do not handle inter-compilation unit references\n"); 194 fprintf(stderr, "\t-h: Usage\n"); 195 fprintf(stderr, "\t-?: Usage\n"); 196} 197 198//============================================================================= 199static void SetupOptions(int argc, const char *argv[], Options *options) { 200 extern int optind; 201 signed char ch; 202 203 while ((ch = getopt(argc, (char * const *)argv, "a:g:chr?")) != -1) { 204 switch (ch) { 205 case 'a': { 206 const NXArchInfo *arch_info = 207 google_breakpad::BreakpadGetArchInfoFromName(optarg); 208 if (!arch_info) { 209 fprintf(stderr, "%s: Invalid architecture: %s\n", argv[0], optarg); 210 Usage(argc, argv); 211 exit(1); 212 } 213 options->arch = arch_info; 214 break; 215 } 216 case 'g': 217 options->dsymPath = [[NSFileManager defaultManager] 218 stringWithFileSystemRepresentation:optarg length:strlen(optarg)]; 219 break; 220 case 'c': 221 options->cfi = false; 222 break; 223 case 'r': 224 options->handle_inter_cu_refs = false; 225 break; 226 case '?': 227 case 'h': 228 Usage(argc, argv); 229 exit(0); 230 break; 231 } 232 } 233 234 if ((argc - optind) != 1) { 235 fprintf(stderr, "Must specify Mach-o file\n"); 236 Usage(argc, argv); 237 exit(1); 238 } 239 240 options->srcPath = [[NSFileManager defaultManager] 241 stringWithFileSystemRepresentation:argv[optind] 242 length:strlen(argv[optind])]; 243} 244 245//============================================================================= 246int main (int argc, const char * argv[]) { 247 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 248 Options options; 249 bool result; 250 251 SetupOptions(argc, argv, &options); 252 result = Start(options); 253 254 [pool release]; 255 256 return !result; 257} 258