/ src / common / mac / dump_syms.cc
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, &section_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