| OLD | NEW |
| (Empty) |
| 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 // Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | |
| 33 | |
| 34 // dump_syms.mm: Create a symbol file for use with minidumps | |
| 35 | |
| 36 #include "common/mac/dump_syms.h" | |
| 37 | |
| 38 #include <assert.h> | |
| 39 #include <Foundation/Foundation.h> | |
| 40 #include <mach-o/arch.h> | |
| 41 #include <mach-o/fat.h> | |
| 42 #include <stdio.h> | |
| 43 | |
| 44 #include <ostream> | |
| 45 #include <string> | |
| 46 #include <vector> | |
| 47 | |
| 48 #include "common/dwarf/bytereader-inl.h" | |
| 49 #include "common/dwarf/dwarf2reader.h" | |
| 50 #include "common/dwarf_cfi_to_module.h" | |
| 51 #include "common/dwarf_cu_to_module.h" | |
| 52 #include "common/dwarf_line_to_module.h" | |
| 53 #include "common/mac/file_id.h" | |
| 54 #include "common/mac/arch_utilities.h" | |
| 55 #include "common/mac/macho_reader.h" | |
| 56 #include "common/module.h" | |
| 57 #include "common/scoped_ptr.h" | |
| 58 #include "common/stabs_reader.h" | |
| 59 #include "common/stabs_to_module.h" | |
| 60 #include "common/symbol_data.h" | |
| 61 | |
| 62 #ifndef CPU_TYPE_ARM | |
| 63 #define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) | |
| 64 #endif // CPU_TYPE_ARM | |
| 65 | |
| 66 #ifndef CPU_TYPE_ARM64 | |
| 67 #define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228)) | |
| 68 #endif // CPU_TYPE_ARM64 | |
| 69 | |
| 70 using dwarf2reader::ByteReader; | |
| 71 using google_breakpad::DwarfCUToModule; | |
| 72 using google_breakpad::DwarfLineToModule; | |
| 73 using google_breakpad::FileID; | |
| 74 using google_breakpad::mach_o::FatReader; | |
| 75 using google_breakpad::mach_o::Section; | |
| 76 using google_breakpad::mach_o::Segment; | |
| 77 using google_breakpad::Module; | |
| 78 using google_breakpad::StabsReader; | |
| 79 using google_breakpad::StabsToModule; | |
| 80 using google_breakpad::scoped_ptr; | |
| 81 using std::make_pair; | |
| 82 using std::pair; | |
| 83 using std::string; | |
| 84 using std::vector; | |
| 85 | |
| 86 namespace google_breakpad { | |
| 87 | |
| 88 bool DumpSymbols::Read(NSString *filename) { | |
| 89 if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { | |
| 90 fprintf(stderr, "Object file does not exist: %s\n", | |
| 91 [filename fileSystemRepresentation]); | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 input_pathname_ = [filename retain]; | |
| 96 | |
| 97 // Does this filename refer to a dSYM bundle? | |
| 98 NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; | |
| 99 | |
| 100 if (bundle) { | |
| 101 // Filenames referring to bundles usually have names of the form | |
| 102 // "<basename>.dSYM"; however, if the user has specified a wrapper | |
| 103 // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), | |
| 104 // then the name may have the form "<basename>.<extension>.dSYM". In | |
| 105 // either case, the resource name for the file containing the DWARF | |
| 106 // info within the bundle is <basename>. | |
| 107 // | |
| 108 // Since there's no way to tell how much to strip off, remove one | |
| 109 // extension at a time, and use the first one that | |
| 110 // pathForResource:ofType:inDirectory likes. | |
| 111 NSString *base_name = [input_pathname_ lastPathComponent]; | |
| 112 NSString *dwarf_resource; | |
| 113 | |
| 114 do { | |
| 115 NSString *new_base_name = [base_name stringByDeletingPathExtension]; | |
| 116 | |
| 117 // If stringByDeletingPathExtension returned the name unchanged, then | |
| 118 // there's nothing more for us to strip off --- lose. | |
| 119 if ([new_base_name isEqualToString:base_name]) { | |
| 120 fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", | |
| 121 [input_pathname_ fileSystemRepresentation]); | |
| 122 return false; | |
| 123 } | |
| 124 | |
| 125 // Take the shortened result as our new base_name. | |
| 126 base_name = new_base_name; | |
| 127 | |
| 128 // Try to find a DWARF resource in the bundle under the new base_name. | |
| 129 dwarf_resource = [bundle pathForResource:base_name | |
| 130 ofType:nil inDirectory:@"DWARF"]; | |
| 131 } while (!dwarf_resource); | |
| 132 | |
| 133 object_filename_ = [dwarf_resource retain]; | |
| 134 } else { | |
| 135 object_filename_ = [input_pathname_ retain]; | |
| 136 } | |
| 137 | |
| 138 // Read the file's contents into memory. | |
| 139 // | |
| 140 // The documentation for dataWithContentsOfMappedFile says: | |
| 141 // | |
| 142 // Because of file mapping restrictions, this method should only be | |
| 143 // used if the file is guaranteed to exist for the duration of the | |
| 144 // data object’s existence. It is generally safer to use the | |
| 145 // dataWithContentsOfFile: method. | |
| 146 // | |
| 147 // I gather this means that OS X doesn't have (or at least, that method | |
| 148 // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the | |
| 149 // process appears to get its own copy of the data, and changes to the | |
| 150 // file don't affect memory and vice versa). | |
| 151 NSError *error; | |
| 152 contents_ = [NSData dataWithContentsOfFile:object_filename_ | |
| 153 options:0 | |
| 154 error:&error]; | |
| 155 if (!contents_) { | |
| 156 fprintf(stderr, "Error reading object file: %s: %s\n", | |
| 157 [object_filename_ fileSystemRepresentation], | |
| 158 [[error localizedDescription] UTF8String]); | |
| 159 return false; | |
| 160 } | |
| 161 [contents_ retain]; | |
| 162 | |
| 163 // Get the list of object files present in the file. | |
| 164 FatReader::Reporter fat_reporter([object_filename_ | |
| 165 fileSystemRepresentation]); | |
| 166 FatReader fat_reader(&fat_reporter); | |
| 167 if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]), | |
| 168 [contents_ length])) { | |
| 169 return false; | |
| 170 } | |
| 171 | |
| 172 // Get our own copy of fat_reader's object file list. | |
| 173 size_t object_files_count; | |
| 174 const SuperFatArch *object_files = | |
| 175 fat_reader.object_files(&object_files_count); | |
| 176 if (object_files_count == 0) { | |
| 177 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", | |
| 178 [object_filename_ fileSystemRepresentation]); | |
| 179 return false; | |
| 180 } | |
| 181 object_files_.resize(object_files_count); | |
| 182 memcpy(&object_files_[0], object_files, | |
| 183 sizeof(SuperFatArch) * object_files_count); | |
| 184 | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, | |
| 189 cpu_subtype_t cpu_subtype) { | |
| 190 // Find the best match for the architecture the user requested. | |
| 191 const SuperFatArch *best_match = FindBestMatchForArchitecture( | |
| 192 cpu_type, cpu_subtype); | |
| 193 if (!best_match) return false; | |
| 194 | |
| 195 // Record the selected object file. | |
| 196 selected_object_file_ = best_match; | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 bool DumpSymbols::SetArchitecture(const std::string &arch_name) { | |
| 201 bool arch_set = false; | |
| 202 const NXArchInfo *arch_info = | |
| 203 google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); | |
| 204 if (arch_info) { | |
| 205 arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); | |
| 206 } | |
| 207 return arch_set; | |
| 208 } | |
| 209 | |
| 210 SuperFatArch* DumpSymbols::FindBestMatchForArchitecture( | |
| 211 cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { | |
| 212 // Check if all the object files can be converted to struct fat_arch. | |
| 213 bool can_convert_to_fat_arch = true; | |
| 214 vector<struct fat_arch> fat_arch_vector; | |
| 215 for (vector<SuperFatArch>::const_iterator it = object_files_.begin(); | |
| 216 it != object_files_.end(); | |
| 217 ++it) { | |
| 218 struct fat_arch arch; | |
| 219 bool success = it->ConvertToFatArch(&arch); | |
| 220 if (!success) { | |
| 221 can_convert_to_fat_arch = false; | |
| 222 break; | |
| 223 } | |
| 224 fat_arch_vector.push_back(arch); | |
| 225 } | |
| 226 | |
| 227 // If all the object files can be converted to struct fat_arch, use | |
| 228 // NXFindBestFatArch. | |
| 229 if (can_convert_to_fat_arch) { | |
| 230 const struct fat_arch *best_match | |
| 231 = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0], | |
| 232 static_cast<uint32_t>(fat_arch_vector.size())); | |
| 233 | |
| 234 for (size_t i = 0; i < fat_arch_vector.size(); ++i) { | |
| 235 if (best_match == &fat_arch_vector[i]) | |
| 236 return &object_files_[i]; | |
| 237 } | |
| 238 assert(best_match == NULL); | |
| 239 return NULL; | |
| 240 } | |
| 241 | |
| 242 // Check for an exact match with cpu_type and cpu_subtype. | |
| 243 for (vector<SuperFatArch>::iterator it = object_files_.begin(); | |
| 244 it != object_files_.end(); | |
| 245 ++it) { | |
| 246 if (it->cputype == cpu_type && it->cpusubtype == cpu_subtype) | |
| 247 return &*it; | |
| 248 } | |
| 249 | |
| 250 // No exact match found. | |
| 251 // TODO(erikchen): If it becomes necessary, we can copy the implementation of | |
| 252 // NXFindBestFatArch, located at | |
| 253 // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. | |
| 254 fprintf(stderr, "Failed to find an exact match for an object file with cpu " | |
| 255 "type: %d and cpu subtype: %d. Furthermore, at least one object file is " | |
| 256 "larger than 2**32.\n", cpu_type, cpu_subtype); | |
| 257 return NULL; | |
| 258 } | |
| 259 | |
| 260 string DumpSymbols::Identifier() { | |
| 261 FileID file_id([object_filename_ fileSystemRepresentation]); | |
| 262 unsigned char identifier_bytes[16]; | |
| 263 cpu_type_t cpu_type = selected_object_file_->cputype; | |
| 264 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; | |
| 265 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { | |
| 266 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", | |
| 267 [object_filename_ fileSystemRepresentation]); | |
| 268 return ""; | |
| 269 } | |
| 270 | |
| 271 char identifier_string[40]; | |
| 272 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, | |
| 273 sizeof(identifier_string)); | |
| 274 | |
| 275 string compacted(identifier_string); | |
| 276 for(size_t i = compacted.find('-'); i != string::npos; | |
| 277 i = compacted.find('-', i)) | |
| 278 compacted.erase(i, 1); | |
| 279 | |
| 280 return compacted; | |
| 281 } | |
| 282 | |
| 283 // A line-to-module loader that accepts line number info parsed by | |
| 284 // dwarf2reader::LineInfo and populates a Module and a line vector | |
| 285 // with the results. | |
| 286 class DumpSymbols::DumperLineToModule: | |
| 287 public DwarfCUToModule::LineToModuleHandler { | |
| 288 public: | |
| 289 // Create a line-to-module converter using BYTE_READER. | |
| 290 DumperLineToModule(dwarf2reader::ByteReader *byte_reader) | |
| 291 : byte_reader_(byte_reader) { } | |
| 292 | |
| 293 void StartCompilationUnit(const string& compilation_dir) { | |
| 294 compilation_dir_ = compilation_dir; | |
| 295 } | |
| 296 | |
| 297 void ReadProgram(const char *program, uint64 length, | |
| 298 Module *module, vector<Module::Line> *lines) { | |
| 299 DwarfLineToModule handler(module, compilation_dir_, lines); | |
| 300 dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); | |
| 301 parser.Start(); | |
| 302 } | |
| 303 private: | |
| 304 string compilation_dir_; | |
| 305 dwarf2reader::ByteReader *byte_reader_; // WEAK | |
| 306 }; | |
| 307 | |
| 308 bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, | |
| 309 const mach_o::Reader &macho_reader, | |
| 310 const mach_o::SectionMap &dwarf_sections, | |
| 311 bool handle_inter_cu_refs) const { | |
| 312 // Build a byte reader of the appropriate endianness. | |
| 313 ByteReader byte_reader(macho_reader.big_endian() | |
| 314 ? dwarf2reader::ENDIANNESS_BIG | |
| 315 : dwarf2reader::ENDIANNESS_LITTLE); | |
| 316 | |
| 317 // Construct a context for this file. | |
| 318 DwarfCUToModule::FileContext file_context(selected_object_name_, | |
| 319 module, | |
| 320 handle_inter_cu_refs); | |
| 321 | |
| 322 // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. | |
| 323 for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); | |
| 324 it != dwarf_sections.end(); ++it) { | |
| 325 file_context.AddSectionToSectionMap( | |
| 326 it->first, | |
| 327 reinterpret_cast<const char *>(it->second.contents.start), | |
| 328 it->second.contents.Size()); | |
| 329 } | |
| 330 | |
| 331 // Find the __debug_info section. | |
| 332 dwarf2reader::SectionMap::const_iterator debug_info_entry = | |
| 333 file_context.section_map().find("__debug_info"); | |
| 334 assert(debug_info_entry != file_context.section_map().end()); | |
| 335 const std::pair<const char*, uint64>& debug_info_section = | |
| 336 debug_info_entry->second; | |
| 337 // There had better be a __debug_info section! | |
| 338 if (!debug_info_section.first) { | |
| 339 fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", | |
| 340 selected_object_name_.c_str()); | |
| 341 return false; | |
| 342 } | |
| 343 | |
| 344 // Build a line-to-module loader for the root handler to use. | |
| 345 DumperLineToModule line_to_module(&byte_reader); | |
| 346 | |
| 347 // Walk the __debug_info section, one compilation unit at a time. | |
| 348 uint64 debug_info_length = debug_info_section.second; | |
| 349 for (uint64 offset = 0; offset < debug_info_length;) { | |
| 350 // Make a handler for the root DIE that populates MODULE with the | |
| 351 // debug info. | |
| 352 DwarfCUToModule::WarningReporter reporter(selected_object_name_, | |
| 353 offset); | |
| 354 DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); | |
| 355 // Make a Dwarf2Handler that drives our DIEHandler. | |
| 356 dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); | |
| 357 // Make a DWARF parser for the compilation unit at OFFSET. | |
| 358 dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map(), | |
| 359 offset, | |
| 360 &byte_reader, | |
| 361 &die_dispatcher); | |
| 362 // Process the entire compilation unit; get the offset of the next. | |
| 363 offset += dwarf_reader.Start(); | |
| 364 } | |
| 365 | |
| 366 return true; | |
| 367 } | |
| 368 | |
| 369 bool DumpSymbols::ReadCFI(google_breakpad::Module *module, | |
| 370 const mach_o::Reader &macho_reader, | |
| 371 const mach_o::Section §ion, | |
| 372 bool eh_frame) const { | |
| 373 // Find the appropriate set of register names for this file's | |
| 374 // architecture. | |
| 375 vector<string> register_names; | |
| 376 switch (macho_reader.cpu_type()) { | |
| 377 case CPU_TYPE_X86: | |
| 378 register_names = DwarfCFIToModule::RegisterNames::I386(); | |
| 379 break; | |
| 380 case CPU_TYPE_X86_64: | |
| 381 register_names = DwarfCFIToModule::RegisterNames::X86_64(); | |
| 382 break; | |
| 383 case CPU_TYPE_ARM: | |
| 384 register_names = DwarfCFIToModule::RegisterNames::ARM(); | |
| 385 break; | |
| 386 case CPU_TYPE_ARM64: | |
| 387 register_names = DwarfCFIToModule::RegisterNames::ARM64(); | |
| 388 break; | |
| 389 default: { | |
| 390 const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType( | |
| 391 macho_reader.cpu_type(), macho_reader.cpu_subtype()); | |
| 392 fprintf(stderr, "%s: cannot convert DWARF call frame information for ", | |
| 393 selected_object_name_.c_str()); | |
| 394 if (arch) | |
| 395 fprintf(stderr, "architecture '%s'", arch->name); | |
| 396 else | |
| 397 fprintf(stderr, "architecture %d,%d", | |
| 398 macho_reader.cpu_type(), macho_reader.cpu_subtype()); | |
| 399 fprintf(stderr, " to Breakpad symbol file: no register name table\n"); | |
| 400 return false; | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 // Find the call frame information and its size. | |
| 405 const char *cfi = reinterpret_cast<const char *>(section.contents.start); | |
| 406 size_t cfi_size = section.contents.Size(); | |
| 407 | |
| 408 // Plug together the parser, handler, and their entourages. | |
| 409 DwarfCFIToModule::Reporter module_reporter(selected_object_name_, | |
| 410 section.section_name); | |
| 411 DwarfCFIToModule handler(module, register_names, &module_reporter); | |
| 412 dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? | |
| 413 dwarf2reader::ENDIANNESS_BIG : | |
| 414 dwarf2reader::ENDIANNESS_LITTLE); | |
| 415 byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); | |
| 416 // At the moment, according to folks at Apple and some cursory | |
| 417 // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so | |
| 418 // this is the only base address the CFI parser will need. | |
| 419 byte_reader.SetCFIDataBase(section.address, cfi); | |
| 420 | |
| 421 dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, | |
| 422 section.section_name); | |
| 423 dwarf2reader::CallFrameInfo parser(cfi, cfi_size, | |
| 424 &byte_reader, &handler, &dwarf_reporter, | |
| 425 eh_frame); | |
| 426 parser.Start(); | |
| 427 return true; | |
| 428 } | |
| 429 | |
| 430 // A LoadCommandHandler that loads whatever debugging data it finds into a | |
| 431 // Module. | |
| 432 class DumpSymbols::LoadCommandDumper: | |
| 433 public mach_o::Reader::LoadCommandHandler { | |
| 434 public: | |
| 435 // Create a load command dumper handling load commands from READER's | |
| 436 // file, and adding data to MODULE. | |
| 437 LoadCommandDumper(const DumpSymbols &dumper, | |
| 438 google_breakpad::Module *module, | |
| 439 const mach_o::Reader &reader, | |
| 440 SymbolData symbol_data, | |
| 441 bool handle_inter_cu_refs) | |
| 442 : dumper_(dumper), | |
| 443 module_(module), | |
| 444 reader_(reader), | |
| 445 symbol_data_(symbol_data), | |
| 446 handle_inter_cu_refs_(handle_inter_cu_refs) { } | |
| 447 | |
| 448 bool SegmentCommand(const mach_o::Segment &segment); | |
| 449 bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); | |
| 450 | |
| 451 private: | |
| 452 const DumpSymbols &dumper_; | |
| 453 google_breakpad::Module *module_; // WEAK | |
| 454 const mach_o::Reader &reader_; | |
| 455 const SymbolData symbol_data_; | |
| 456 const bool handle_inter_cu_refs_; | |
| 457 }; | |
| 458 | |
| 459 bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { | |
| 460 mach_o::SectionMap section_map; | |
| 461 if (!reader_.MapSegmentSections(segment, §ion_map)) | |
| 462 return false; | |
| 463 | |
| 464 if (segment.name == "__TEXT") { | |
| 465 module_->SetLoadAddress(segment.vmaddr); | |
| 466 if (symbol_data_ != NO_CFI) { | |
| 467 mach_o::SectionMap::const_iterator eh_frame = | |
| 468 section_map.find("__eh_frame"); | |
| 469 if (eh_frame != section_map.end()) { | |
| 470 // If there is a problem reading this, don't treat it as a fatal error. | |
| 471 dumper_.ReadCFI(module_, reader_, eh_frame->second, true); | |
| 472 } | |
| 473 } | |
| 474 return true; | |
| 475 } | |
| 476 | |
| 477 if (segment.name == "__DWARF") { | |
| 478 if (symbol_data_ != ONLY_CFI) { | |
| 479 if (!dumper_.ReadDwarf(module_, reader_, section_map, | |
| 480 handle_inter_cu_refs_)) { | |
| 481 return false; | |
| 482 } | |
| 483 } | |
| 484 if (symbol_data_ != NO_CFI) { | |
| 485 mach_o::SectionMap::const_iterator debug_frame | |
| 486 = section_map.find("__debug_frame"); | |
| 487 if (debug_frame != section_map.end()) { | |
| 488 // If there is a problem reading this, don't treat it as a fatal error. | |
| 489 dumper_.ReadCFI(module_, reader_, debug_frame->second, false); | |
| 490 } | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 return true; | |
| 495 } | |
| 496 | |
| 497 bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, | |
| 498 const ByteBuffer &strings) { | |
| 499 StabsToModule stabs_to_module(module_); | |
| 500 // Mac OS X STABS are never "unitized", and the size of the 'value' field | |
| 501 // matches the address size of the executable. | |
| 502 StabsReader stabs_reader(entries.start, entries.Size(), | |
| 503 strings.start, strings.Size(), | |
| 504 reader_.big_endian(), | |
| 505 reader_.bits_64() ? 8 : 4, | |
| 506 true, | |
| 507 &stabs_to_module); | |
| 508 if (!stabs_reader.Process()) | |
| 509 return false; | |
| 510 stabs_to_module.Finalize(); | |
| 511 return true; | |
| 512 } | |
| 513 | |
| 514 bool DumpSymbols::ReadSymbolData(Module** out_module) { | |
| 515 // Select an object file, if SetArchitecture hasn't been called to set one | |
| 516 // explicitly. | |
| 517 if (!selected_object_file_) { | |
| 518 // If there's only one architecture, that's the one. | |
| 519 if (object_files_.size() == 1) | |
| 520 selected_object_file_ = &object_files_[0]; | |
| 521 else { | |
| 522 // Look for an object file whose architecture matches our own. | |
| 523 const NXArchInfo *local_arch = NXGetLocalArchInfo(); | |
| 524 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { | |
| 525 fprintf(stderr, "%s: object file contains more than one" | |
| 526 " architecture, none of which match the current" | |
| 527 " architecture; specify an architecture explicitly" | |
| 528 " with '-a ARCH' to resolve the ambiguity\n", | |
| 529 [object_filename_ fileSystemRepresentation]); | |
| 530 return false; | |
| 531 } | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 assert(selected_object_file_); | |
| 536 | |
| 537 // Find the name of the selected file's architecture, to appear in | |
| 538 // the MODULE record and in error messages. | |
| 539 const NXArchInfo *selected_arch_info = | |
| 540 google_breakpad::BreakpadGetArchInfoFromCpuType( | |
| 541 selected_object_file_->cputype, selected_object_file_->cpusubtype); | |
| 542 | |
| 543 const char *selected_arch_name = selected_arch_info->name; | |
| 544 if (strcmp(selected_arch_name, "i386") == 0) | |
| 545 selected_arch_name = "x86"; | |
| 546 | |
| 547 // Produce a name to use in error messages that includes the | |
| 548 // filename, and the architecture, if there is more than one. | |
| 549 selected_object_name_ = [object_filename_ UTF8String]; | |
| 550 if (object_files_.size() > 1) { | |
| 551 selected_object_name_ += ", architecture "; | |
| 552 selected_object_name_ + selected_arch_name; | |
| 553 } | |
| 554 | |
| 555 // Compute a module name, to appear in the MODULE record. | |
| 556 NSString *module_name = [object_filename_ lastPathComponent]; | |
| 557 | |
| 558 // Choose an identifier string, to appear in the MODULE record. | |
| 559 string identifier = Identifier(); | |
| 560 if (identifier.empty()) | |
| 561 return false; | |
| 562 identifier += "0"; | |
| 563 | |
| 564 // Create a module to hold the debugging information. | |
| 565 scoped_ptr<Module> module(new Module([module_name UTF8String], | |
| 566 "mac", | |
| 567 selected_arch_name, | |
| 568 identifier)); | |
| 569 | |
| 570 // Parse the selected object file. | |
| 571 mach_o::Reader::Reporter reporter(selected_object_name_); | |
| 572 mach_o::Reader reader(&reporter); | |
| 573 if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) | |
| 574 + selected_object_file_->offset, | |
| 575 selected_object_file_->size, | |
| 576 selected_object_file_->cputype, | |
| 577 selected_object_file_->cpusubtype)) | |
| 578 return false; | |
| 579 | |
| 580 // Walk its load commands, and deal with whatever is there. | |
| 581 LoadCommandDumper load_command_dumper(*this, module.get(), reader, | |
| 582 symbol_data_, handle_inter_cu_refs_); | |
| 583 if (!reader.WalkLoadCommands(&load_command_dumper)) | |
| 584 return false; | |
| 585 | |
| 586 *out_module = module.release(); | |
| 587 | |
| 588 return true; | |
| 589 } | |
| 590 | |
| 591 bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { | |
| 592 Module* module = NULL; | |
| 593 | |
| 594 if (ReadSymbolData(&module) && module) { | |
| 595 bool res = module->Write(stream, symbol_data_); | |
| 596 delete module; | |
| 597 return res; | |
| 598 } | |
| 599 | |
| 600 return false; | |
| 601 } | |
| 602 | |
| 603 } // namespace google_breakpad | |
| OLD | NEW |