toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial