dump_syms.cc
1 // -*- mode: c++ -*- 2 3 // Copyright 2011 Google LLC 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google LLC nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 // Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> 32 33 // dump_syms.cc: Create a symbol file for use with minidumps 34 35 #ifdef HAVE_CONFIG_H 36 #include <config.h> // Must come first 37 #endif 38 39 #include "common/mac/dump_syms.h" 40 41 #include <assert.h> 42 #include <dirent.h> 43 #include <errno.h> 44 #include <mach-o/arch.h> 45 #include <mach-o/fat.h> 46 #include <stdint.h> 47 #include <stdio.h> 48 #include <sys/stat.h> 49 #include <sys/types.h> 50 #include <unistd.h> 51 52 #include <ostream> 53 #include <string> 54 #include <vector> 55 56 #include "common/dwarf/bytereader-inl.h" 57 #include "common/dwarf/dwarf2reader.h" 58 #include "common/dwarf_cfi_to_module.h" 59 #include "common/dwarf_cu_to_module.h" 60 #include "common/dwarf_line_to_module.h" 61 #include "common/dwarf_range_list_handler.h" 62 #include "common/mac/file_id.h" 63 #include "common/mac/arch_utilities.h" 64 #include "common/mac/macho_reader.h" 65 #include "common/module.h" 66 #include "common/path_helper.h" 67 #include "common/scoped_ptr.h" 68 #include "common/stabs_reader.h" 69 #include "common/stabs_to_module.h" 70 #include "common/symbol_data.h" 71 72 #ifndef CPU_TYPE_ARM 73 #define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) 74 #endif // CPU_TYPE_ARM 75 76 #ifndef CPU_TYPE_ARM64 77 #define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228)) 78 #endif // CPU_TYPE_ARM64 79 80 using google_breakpad::ByteReader; 81 using google_breakpad::DwarfCUToModule; 82 using google_breakpad::DwarfLineToModule; 83 using google_breakpad::DwarfRangeListHandler; 84 using google_breakpad::mach_o::FatReader; 85 using google_breakpad::mach_o::FileID; 86 using google_breakpad::mach_o::Section; 87 using google_breakpad::mach_o::Segment; 88 using google_breakpad::Module; 89 using google_breakpad::StabsReader; 90 using google_breakpad::StabsToModule; 91 using google_breakpad::scoped_ptr; 92 using std::make_pair; 93 using std::pair; 94 using std::string; 95 using std::vector; 96 97 namespace { 98 // Return a vector<string> with absolute paths to all the entries 99 // in directory (excluding . and ..). 100 vector<string> list_directory(const string& directory) { 101 vector<string> entries; 102 DIR* dir = opendir(directory.c_str()); 103 if (!dir) { 104 return entries; 105 } 106 107 string path = directory; 108 if (path[path.length() - 1] != '/') { 109 path += '/'; 110 } 111 112 struct dirent* entry = NULL; 113 while ((entry = readdir(dir))) { 114 if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { 115 entries.push_back(path + entry->d_name); 116 } 117 } 118 119 closedir(dir); 120 return entries; 121 } 122 } 123 124 namespace google_breakpad { 125 126 bool DumpSymbols::Read(const string& filename) { 127 selected_object_file_ = nullptr; 128 struct stat st; 129 if (stat(filename.c_str(), &st) == -1) { 130 fprintf(stderr, "Could not access object file %s: %s\n", 131 filename.c_str(), strerror(errno)); 132 return false; 133 } 134 135 from_disk_ = true; 136 137 // Does this filename refer to a dSYM bundle? 138 string contents_path = filename + "/Contents/Resources/DWARF"; 139 string object_filename; 140 if (S_ISDIR(st.st_mode) && 141 access(contents_path.c_str(), F_OK) == 0) { 142 // If there's one file under Contents/Resources/DWARF then use that, 143 // otherwise bail out. 144 const vector<string> entries = list_directory(contents_path); 145 if (entries.size() == 0) { 146 fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", 147 filename.c_str()); 148 return false; 149 } 150 if (entries.size() > 1) { 151 fprintf(stderr, "Too many DWARF files in bundle: %s\n", 152 filename.c_str()); 153 return false; 154 } 155 156 object_filename = entries[0]; 157 } else { 158 object_filename = filename; 159 } 160 161 // Read the file's contents into memory. 162 bool read_ok = true; 163 string error; 164 scoped_array<uint8_t> contents; 165 off_t total = 0; 166 if (stat(object_filename.c_str(), &st) != -1) { 167 FILE* f = fopen(object_filename.c_str(), "rb"); 168 if (f) { 169 contents.reset(new uint8_t[st.st_size]); 170 while (total < st.st_size && !feof(f)) { 171 size_t read = fread(&contents[0] + total, 1, st.st_size - total, f); 172 if (read == 0) { 173 if (ferror(f)) { 174 read_ok = false; 175 error = strerror(errno); 176 } 177 break; 178 } 179 total += read; 180 } 181 fclose(f); 182 } else { 183 error = strerror(errno); 184 } 185 } 186 187 if (!read_ok) { 188 fprintf(stderr, "Error reading object file: %s: %s\n", 189 object_filename.c_str(), error.c_str()); 190 return false; 191 } 192 return ReadData(contents.release(), total, object_filename); 193 } 194 195 bool DumpSymbols::ReadData(uint8_t* contents, size_t size, 196 const std::string& filename) { 197 contents_.reset(contents); 198 size_ = size; 199 object_filename_ = filename; 200 201 // Get the list of object files present in the file. 202 FatReader::Reporter fat_reporter(object_filename_); 203 FatReader fat_reader(&fat_reporter); 204 if (!fat_reader.Read(contents_.get(), size)) { 205 return false; 206 } 207 208 // Get our own copy of fat_reader's object file list. 209 size_t object_files_count; 210 const SuperFatArch* object_files = 211 fat_reader.object_files(&object_files_count); 212 if (object_files_count == 0) { 213 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", 214 object_filename_.c_str()); 215 return false; 216 } 217 object_files_.resize(object_files_count); 218 memcpy(&object_files_[0], object_files, 219 sizeof(SuperFatArch) * object_files_count); 220 221 return true; 222 } 223 224 bool DumpSymbols::SetArchitecture(const ArchInfo& info) { 225 // Find the best match for the architecture the user requested. 226 const SuperFatArch* best_match = 227 FindBestMatchForArchitecture(info.cputype, info.cpusubtype); 228 if (!best_match) return false; 229 230 // Record the selected object file. 231 selected_object_file_ = best_match; 232 return true; 233 } 234 235 236 SuperFatArch* DumpSymbols::FindBestMatchForArchitecture( 237 cpu_type_t cpu_type, 238 cpu_subtype_t cpu_subtype) { 239 SuperFatArch* closest_match = nullptr; 240 for (auto& object_file : object_files_) { 241 if (static_cast<cpu_type_t>(object_file.cputype) == cpu_type) { 242 // If there's an exact match, return it directly. 243 if ((static_cast<cpu_subtype_t>(object_file.cpusubtype) & 244 ~CPU_SUBTYPE_MASK) == (cpu_subtype & ~CPU_SUBTYPE_MASK)) { 245 return &object_file; 246 } 247 // Otherwise, hold on to this as the closest match since at least the CPU 248 // type matches. 249 if (!closest_match) { 250 closest_match = &object_file; 251 } 252 } 253 } 254 // No exact match found. 255 fprintf(stderr, 256 "Failed to find an exact match for an object file with cpu " 257 "type: %d and cpu subtype: %d.\n", 258 cpu_type, cpu_subtype); 259 if (closest_match) { 260 fprintf(stderr, "Using %s as the closest match.\n", 261 GetNameFromCPUType(closest_match->cputype, 262 closest_match->cpusubtype)); 263 return closest_match; 264 } 265 return nullptr; 266 } 267 268 string DumpSymbols::Identifier() { 269 scoped_ptr<FileID> file_id; 270 271 if (from_disk_) { 272 file_id.reset(new FileID(object_filename_.c_str())); 273 } else { 274 file_id.reset(new FileID(contents_.get(), size_)); 275 } 276 unsigned char identifier_bytes[16]; 277 scoped_ptr<Module> module; 278 if (!selected_object_file_) { 279 if (!CreateEmptyModule(module)) 280 return string(); 281 } 282 cpu_type_t cpu_type = selected_object_file_->cputype; 283 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; 284 if (!file_id->MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { 285 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", 286 object_filename_.c_str()); 287 return ""; 288 } 289 290 char identifier_string[40]; 291 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, 292 sizeof(identifier_string)); 293 294 string compacted(identifier_string); 295 for(size_t i = compacted.find('-'); i != string::npos; 296 i = compacted.find('-', i)) 297 compacted.erase(i, 1); 298 299 // The pdb for these IDs has an extra byte, so to make everything uniform put 300 // a 0 on the end of mac IDs. 301 compacted += "0"; 302 303 return compacted; 304 } 305 306 // A range handler that accepts rangelist data parsed by 307 // RangeListReader and populates a range vector (typically 308 // owned by a function) with the results. 309 class DumpSymbols::DumperRangesHandler: 310 public DwarfCUToModule::RangesHandler { 311 public: 312 DumperRangesHandler(ByteReader* reader) : 313 reader_(reader) { } 314 315 bool ReadRanges( 316 enum DwarfForm form, uint64_t data, 317 RangeListReader::CURangesInfo* cu_info, 318 vector<Module::Range>* ranges) { 319 DwarfRangeListHandler handler(ranges); 320 RangeListReader range_list_reader(reader_, cu_info, 321 &handler); 322 return range_list_reader.ReadRanges(form, data); 323 } 324 325 private: 326 ByteReader* reader_; 327 }; 328 329 // A line-to-module loader that accepts line number info parsed by 330 // LineInfo and populates a Module and a line vector 331 // with the results. 332 class DumpSymbols::DumperLineToModule: 333 public DwarfCUToModule::LineToModuleHandler { 334 public: 335 // Create a line-to-module converter using BYTE_READER. 336 DumperLineToModule(ByteReader* byte_reader) 337 : byte_reader_(byte_reader) { } 338 339 void StartCompilationUnit(const string& compilation_dir) { 340 compilation_dir_ = compilation_dir; 341 } 342 343 void ReadProgram(const uint8_t* program, 344 uint64_t length, 345 const uint8_t* string_section, 346 uint64_t string_section_length, 347 const uint8_t* line_string_section, 348 uint64_t line_string_section_length, 349 Module* module, 350 vector<Module::Line>* lines, 351 std::map<uint32_t, Module::File*>* files) { 352 DwarfLineToModule handler(module, compilation_dir_, lines, files); 353 LineInfo parser(program, length, byte_reader_, nullptr, 0, 354 nullptr, 0, &handler); 355 parser.Start(); 356 } 357 private: 358 string compilation_dir_; 359 ByteReader* byte_reader_; // WEAK 360 }; 361 362 bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { 363 // Select an object file, if SetArchitecture hasn't been called to set one 364 // explicitly. 365 if (!selected_object_file_) { 366 // If there's only one architecture, that's the one. 367 if (object_files_.size() == 1) 368 selected_object_file_ = &object_files_[0]; 369 else { 370 // Look for an object file whose architecture matches our own. 371 ArchInfo local_arch = GetLocalArchInfo(); 372 if (!SetArchitecture(local_arch)) { 373 fprintf(stderr, "%s: object file contains more than one" 374 " architecture, none of which match the current" 375 " architecture; specify an architecture explicitly" 376 " with '-a ARCH' to resolve the ambiguity\n", 377 object_filename_.c_str()); 378 return false; 379 } 380 } 381 } 382 383 assert(selected_object_file_); 384 385 // Find the name of the selected file's architecture, to appear in 386 // the MODULE record and in error messages. 387 const char* selected_arch_name = GetNameFromCPUType( 388 selected_object_file_->cputype, selected_object_file_->cpusubtype); 389 390 // In certain cases, it is possible that architecture info can't be reliably 391 // determined, e.g. new architectures that breakpad is unware of. In that 392 // case, avoid crashing and return false instead. 393 if (selected_arch_name == kUnknownArchName) { 394 return false; 395 } 396 397 if (strcmp(selected_arch_name, "i386") == 0) 398 selected_arch_name = "x86"; 399 400 // Produce a name to use in error messages that includes the 401 // filename, and the architecture, if there is more than one. 402 selected_object_name_ = object_filename_; 403 if (object_files_.size() > 1) { 404 selected_object_name_ += ", architecture "; 405 selected_object_name_ + selected_arch_name; 406 } 407 408 // Compute a module name, to appear in the MODULE record. 409 string module_name; 410 if (!module_name_.empty()) { 411 module_name = module_name_; 412 } else { 413 module_name = google_breakpad::BaseName(object_filename_); 414 } 415 416 // Choose an identifier string, to appear in the MODULE record. 417 string identifier = Identifier(); 418 if (identifier.empty()) 419 return false; 420 421 // Create a module to hold the debugging information. 422 module.reset(new Module(module_name, "mac", selected_arch_name, identifier, 423 "", enable_multiple_, prefer_extern_name_)); 424 return true; 425 } 426 427 void DumpSymbols::StartProcessSplitDwarf( 428 google_breakpad::CompilationUnit* reader, 429 Module* module, 430 google_breakpad::Endianness endianness, 431 bool handle_inter_cu_refs, 432 bool handle_inline) const { 433 std::string split_file; 434 google_breakpad::SectionMap split_sections; 435 google_breakpad::ByteReader split_byte_reader(endianness); 436 uint64_t cu_offset = 0; 437 if (reader->ProcessSplitDwarf(split_file, split_sections, split_byte_reader, 438 cu_offset)) 439 return; 440 DwarfCUToModule::FileContext file_context(split_file, module, 441 handle_inter_cu_refs); 442 for (auto section : split_sections) 443 file_context.AddSectionToSectionMap(section.first, section.second.first, 444 section.second.second); 445 // Because DWP/DWO file doesn't have .debug_addr/.debug_line/.debug_line_str, 446 // its debug info will refer to .debug_addr/.debug_line in the main binary. 447 if (file_context.section_map().find(".debug_addr") == 448 file_context.section_map().end()) 449 file_context.AddSectionToSectionMap(".debug_addr", reader->GetAddrBuffer(), 450 reader->GetAddrBufferLen()); 451 if (file_context.section_map().find(".debug_line") == 452 file_context.section_map().end()) 453 file_context.AddSectionToSectionMap(".debug_line", reader->GetLineBuffer(), 454 reader->GetLineBufferLen()); 455 if (file_context.section_map().find(".debug_line_str") == 456 file_context.section_map().end()) 457 file_context.AddSectionToSectionMap(".debug_line_str", 458 reader->GetLineStrBuffer(), 459 reader->GetLineStrBufferLen()); 460 DumperRangesHandler ranges_handler(&split_byte_reader); 461 DumperLineToModule line_to_module(&split_byte_reader); 462 DwarfCUToModule::WarningReporter reporter(split_file, cu_offset); 463 DwarfCUToModule root_handler( 464 &file_context, &line_to_module, &ranges_handler, &reporter, handle_inline, 465 reader->GetLowPC(), reader->GetAddrBase(), reader->HasSourceLineInfo(), 466 reader->GetSourceLineOffset()); 467 google_breakpad::DIEDispatcher die_dispatcher(&root_handler); 468 google_breakpad::CompilationUnit split_reader( 469 split_file, file_context.section_map(), cu_offset, &split_byte_reader, 470 &die_dispatcher); 471 split_reader.SetSplitDwarf(reader->GetAddrBase(), reader->GetDWOID()); 472 split_reader.Start(); 473 // Normally, it won't happen unless we have transitive reference. 474 if (split_reader.ShouldProcessSplitDwarf()) { 475 StartProcessSplitDwarf(&split_reader, module, endianness, 476 handle_inter_cu_refs, handle_inline); 477 } 478 } 479 480 void DumpSymbols::ReadDwarf(google_breakpad::Module* module, 481 const mach_o::Reader& macho_reader, 482 const mach_o::SectionMap& dwarf_sections, 483 bool handle_inter_cu_refs) const { 484 // Build a byte reader of the appropriate endianness. 485 google_breakpad::Endianness endianness = 486 macho_reader.big_endian() ? ENDIANNESS_BIG : ENDIANNESS_LITTLE; 487 ByteReader byte_reader(endianness); 488 489 // Construct a context for this file. 490 DwarfCUToModule::FileContext file_context(selected_object_name_, 491 module, 492 handle_inter_cu_refs); 493 494 // Build a SectionMap from our mach_o::SectionMap. 495 for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); 496 it != dwarf_sections.end(); ++it) { 497 file_context.AddSectionToSectionMap( 498 it->first, 499 it->second.contents.start, 500 it->second.contents.Size()); 501 } 502 503 // Find the __debug_info section. 504 SectionMap::const_iterator debug_info_entry = 505 file_context.section_map().find("__debug_info"); 506 // There had better be a __debug_info section! 507 if (debug_info_entry == file_context.section_map().end()) { 508 fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", 509 selected_object_name_.c_str()); 510 return; 511 } 512 const std::pair<const uint8_t*, uint64_t>& debug_info_section = 513 debug_info_entry->second; 514 515 // Build a line-to-module loader for the root handler to use. 516 DumperLineToModule line_to_module(&byte_reader); 517 518 // .debug_ranges and .debug_rngslists reader 519 DumperRangesHandler ranges_handler(&byte_reader); 520 521 // Walk the __debug_info section, one compilation unit at a time. 522 uint64_t debug_info_length = debug_info_section.second; 523 bool handle_inline = symbol_data_ & INLINES; 524 for (uint64_t offset = 0; offset < debug_info_length;) { 525 // Make a handler for the root DIE that populates MODULE with the 526 // debug info. 527 DwarfCUToModule::WarningReporter reporter(selected_object_name_, 528 offset); 529 DwarfCUToModule root_handler(&file_context, &line_to_module, 530 &ranges_handler, &reporter, handle_inline); 531 // Make a Dwarf2Handler that drives our DIEHandler. 532 DIEDispatcher die_dispatcher(&root_handler); 533 // Make a DWARF parser for the compilation unit at OFFSET. 534 CompilationUnit dwarf_reader(selected_object_name_, 535 file_context.section_map(), 536 offset, 537 &byte_reader, 538 &die_dispatcher); 539 // Process the entire compilation unit; get the offset of the next. 540 offset += dwarf_reader.Start(); 541 // Start to process split dwarf file. 542 if (dwarf_reader.ShouldProcessSplitDwarf()) { 543 StartProcessSplitDwarf(&dwarf_reader, module, endianness, 544 handle_inter_cu_refs, handle_inline); 545 } 546 } 547 } 548 549 bool DumpSymbols::ReadCFI(google_breakpad::Module* module, 550 const mach_o::Reader& macho_reader, 551 const mach_o::Section& section, 552 bool eh_frame) const { 553 // Find the appropriate set of register names for this file's 554 // architecture. 555 vector<string> register_names; 556 switch (macho_reader.cpu_type()) { 557 case CPU_TYPE_X86: 558 register_names = DwarfCFIToModule::RegisterNames::I386(); 559 break; 560 case CPU_TYPE_X86_64: 561 register_names = DwarfCFIToModule::RegisterNames::X86_64(); 562 break; 563 case CPU_TYPE_ARM: 564 register_names = DwarfCFIToModule::RegisterNames::ARM(); 565 break; 566 case CPU_TYPE_ARM64: 567 register_names = DwarfCFIToModule::RegisterNames::ARM64(); 568 break; 569 default: { 570 const char* arch_name = GetNameFromCPUType(macho_reader.cpu_type(), 571 macho_reader.cpu_subtype()); 572 fprintf( 573 stderr, 574 "%s: cannot convert DWARF call frame information for architecture " 575 "'%s' (%d, %d) to Breakpad symbol file: no register name table\n", 576 selected_object_name_.c_str(), arch_name, macho_reader.cpu_type(), 577 macho_reader.cpu_subtype()); 578 return false; 579 } 580 } 581 582 // Find the call frame information and its size. 583 const uint8_t* cfi = section.contents.start; 584 size_t cfi_size = section.contents.Size(); 585 586 // Plug together the parser, handler, and their entourages. 587 DwarfCFIToModule::Reporter module_reporter(selected_object_name_, 588 section.section_name); 589 DwarfCFIToModule handler(module, register_names, &module_reporter); 590 ByteReader byte_reader(macho_reader.big_endian() ? 591 ENDIANNESS_BIG : 592 ENDIANNESS_LITTLE); 593 byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); 594 // At the moment, according to folks at Apple and some cursory 595 // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so 596 // this is the only base address the CFI parser will need. 597 byte_reader.SetCFIDataBase(section.address, cfi); 598 599 CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, 600 section.section_name); 601 CallFrameInfo parser(cfi, cfi_size, 602 &byte_reader, &handler, &dwarf_reporter, 603 eh_frame); 604 parser.Start(); 605 return true; 606 } 607 608 // A LoadCommandHandler that loads whatever debugging data it finds into a 609 // Module. 610 class DumpSymbols::LoadCommandDumper: 611 public mach_o::Reader::LoadCommandHandler { 612 public: 613 // Create a load command dumper handling load commands from READER's 614 // file, and adding data to MODULE. 615 LoadCommandDumper(const DumpSymbols& dumper, 616 google_breakpad::Module* module, 617 const mach_o::Reader& reader, 618 SymbolData symbol_data, 619 bool handle_inter_cu_refs) 620 : dumper_(dumper), 621 module_(module), 622 reader_(reader), 623 symbol_data_(symbol_data), 624 handle_inter_cu_refs_(handle_inter_cu_refs) { } 625 626 bool SegmentCommand(const mach_o::Segment& segment); 627 bool SymtabCommand(const ByteBuffer& entries, const ByteBuffer& strings); 628 629 private: 630 const DumpSymbols& dumper_; 631 google_breakpad::Module* module_; // WEAK 632 const mach_o::Reader& reader_; 633 const SymbolData symbol_data_; 634 const bool handle_inter_cu_refs_; 635 }; 636 637 bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment& segment) { 638 mach_o::SectionMap section_map; 639 if (!reader_.MapSegmentSections(segment, §ion_map)) 640 return false; 641 642 if (segment.name == "__TEXT") { 643 module_->SetLoadAddress(segment.vmaddr); 644 if (symbol_data_ & CFI) { 645 mach_o::SectionMap::const_iterator eh_frame = 646 section_map.find("__eh_frame"); 647 if (eh_frame != section_map.end()) { 648 // If there is a problem reading this, don't treat it as a fatal error. 649 dumper_.ReadCFI(module_, reader_, eh_frame->second, true); 650 } 651 } 652 return true; 653 } 654 655 if (segment.name == "__DWARF") { 656 if ((symbol_data_ & SYMBOLS_AND_FILES) || (symbol_data_ & INLINES)) { 657 dumper_.ReadDwarf(module_, reader_, section_map, handle_inter_cu_refs_); 658 } 659 if (symbol_data_ & CFI) { 660 mach_o::SectionMap::const_iterator debug_frame 661 = section_map.find("__debug_frame"); 662 if (debug_frame != section_map.end()) { 663 // If there is a problem reading this, don't treat it as a fatal error. 664 dumper_.ReadCFI(module_, reader_, debug_frame->second, false); 665 } 666 } 667 } 668 669 return true; 670 } 671 672 bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer& entries, 673 const ByteBuffer& strings) { 674 StabsToModule stabs_to_module(module_); 675 // Mac OS X STABS are never "unitized", and the size of the 'value' field 676 // matches the address size of the executable. 677 StabsReader stabs_reader(entries.start, entries.Size(), 678 strings.start, strings.Size(), 679 reader_.big_endian(), 680 reader_.bits_64() ? 8 : 4, 681 true, 682 &stabs_to_module); 683 if (!stabs_reader.Process()) 684 return false; 685 stabs_to_module.Finalize(); 686 return true; 687 } 688 689 bool DumpSymbols::ReadSymbolData(Module** out_module) { 690 scoped_ptr<Module> module; 691 if (!CreateEmptyModule(module)) 692 return false; 693 694 // Parse the selected object file. 695 mach_o::Reader::Reporter reporter(selected_object_name_); 696 mach_o::Reader reader(&reporter); 697 if (!reader.Read(&contents_[0] 698 + selected_object_file_->offset, 699 selected_object_file_->size, 700 selected_object_file_->cputype, 701 selected_object_file_->cpusubtype)) 702 return false; 703 704 // Walk its load commands, and deal with whatever is there. 705 LoadCommandDumper load_command_dumper(*this, module.get(), reader, 706 symbol_data_, handle_inter_cu_refs_); 707 if (!reader.WalkLoadCommands(&load_command_dumper)) 708 return false; 709 710 *out_module = module.release(); 711 712 return true; 713 } 714 715 // Read the selected object file's debugging information, and write out the 716 // header only to |stream|. Return true on success; if an error occurs, report 717 // it and return false. 718 bool DumpSymbols::WriteSymbolFileHeader(std::ostream& stream) { 719 scoped_ptr<Module> module; 720 if (!CreateEmptyModule(module)) 721 return false; 722 723 return module->Write(stream, symbol_data_); 724 } 725 726 } // namespace google_breakpad