1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,533 @@ 1.4 +// -*- mode: c++ -*- 1.5 + 1.6 +// Copyright (c) 2011, Google Inc. 1.7 +// All rights reserved. 1.8 +// 1.9 +// Redistribution and use in source and binary forms, with or without 1.10 +// modification, are permitted provided that the following conditions are 1.11 +// met: 1.12 +// 1.13 +// * Redistributions of source code must retain the above copyright 1.14 +// notice, this list of conditions and the following disclaimer. 1.15 +// * Redistributions in binary form must reproduce the above 1.16 +// copyright notice, this list of conditions and the following disclaimer 1.17 +// in the documentation and/or other materials provided with the 1.18 +// distribution. 1.19 +// * Neither the name of Google Inc. nor the names of its 1.20 +// contributors may be used to endorse or promote products derived from 1.21 +// this software without specific prior written permission. 1.22 +// 1.23 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.24 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.25 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.26 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.27 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.28 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.29 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.30 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.31 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.32 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.33 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.34 + 1.35 +// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> 1.36 + 1.37 +// dump_syms.mm: Create a symbol file for use with minidumps 1.38 + 1.39 +#include "common/mac/dump_syms.h" 1.40 + 1.41 +#include <Foundation/Foundation.h> 1.42 +#include <mach-o/arch.h> 1.43 +#include <mach-o/fat.h> 1.44 +#include <stdio.h> 1.45 + 1.46 +#include <ostream> 1.47 +#include <string> 1.48 +#include <vector> 1.49 + 1.50 +#include "common/dwarf/bytereader-inl.h" 1.51 +#include "common/dwarf/dwarf2reader.h" 1.52 +#include "common/dwarf_cfi_to_module.h" 1.53 +#include "common/dwarf_cu_to_module.h" 1.54 +#include "common/dwarf_line_to_module.h" 1.55 +#include "common/mac/file_id.h" 1.56 +#include "common/mac/arch_utilities.h" 1.57 +#include "common/mac/macho_reader.h" 1.58 +#include "common/module.h" 1.59 +#include "common/scoped_ptr.h" 1.60 +#include "common/stabs_reader.h" 1.61 +#include "common/stabs_to_module.h" 1.62 +#include "common/symbol_data.h" 1.63 + 1.64 +#ifndef CPU_TYPE_ARM 1.65 +#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) 1.66 +#endif // CPU_TYPE_ARM 1.67 + 1.68 +using dwarf2reader::ByteReader; 1.69 +using google_breakpad::DwarfCUToModule; 1.70 +using google_breakpad::DwarfLineToModule; 1.71 +using google_breakpad::FileID; 1.72 +using google_breakpad::mach_o::FatReader; 1.73 +using google_breakpad::mach_o::Section; 1.74 +using google_breakpad::mach_o::Segment; 1.75 +using google_breakpad::Module; 1.76 +using google_breakpad::StabsReader; 1.77 +using google_breakpad::StabsToModule; 1.78 +using google_breakpad::scoped_ptr; 1.79 +using std::make_pair; 1.80 +using std::pair; 1.81 +using std::string; 1.82 +using std::vector; 1.83 + 1.84 +namespace google_breakpad { 1.85 + 1.86 +bool DumpSymbols::Read(NSString *filename) { 1.87 + if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { 1.88 + fprintf(stderr, "Object file does not exist: %s\n", 1.89 + [filename fileSystemRepresentation]); 1.90 + return false; 1.91 + } 1.92 + 1.93 + input_pathname_ = [filename retain]; 1.94 + 1.95 + // Does this filename refer to a dSYM bundle? 1.96 + NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; 1.97 + 1.98 + if (bundle) { 1.99 + // Filenames referring to bundles usually have names of the form 1.100 + // "<basename>.dSYM"; however, if the user has specified a wrapper 1.101 + // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), 1.102 + // then the name may have the form "<basename>.<extension>.dSYM". In 1.103 + // either case, the resource name for the file containing the DWARF 1.104 + // info within the bundle is <basename>. 1.105 + // 1.106 + // Since there's no way to tell how much to strip off, remove one 1.107 + // extension at a time, and use the first one that 1.108 + // pathForResource:ofType:inDirectory likes. 1.109 + NSString *base_name = [input_pathname_ lastPathComponent]; 1.110 + NSString *dwarf_resource; 1.111 + 1.112 + do { 1.113 + NSString *new_base_name = [base_name stringByDeletingPathExtension]; 1.114 + 1.115 + // If stringByDeletingPathExtension returned the name unchanged, then 1.116 + // there's nothing more for us to strip off --- lose. 1.117 + if ([new_base_name isEqualToString:base_name]) { 1.118 + fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", 1.119 + [input_pathname_ fileSystemRepresentation]); 1.120 + return false; 1.121 + } 1.122 + 1.123 + // Take the shortened result as our new base_name. 1.124 + base_name = new_base_name; 1.125 + 1.126 + // Try to find a DWARF resource in the bundle under the new base_name. 1.127 + dwarf_resource = [bundle pathForResource:base_name 1.128 + ofType:nil inDirectory:@"DWARF"]; 1.129 + } while (!dwarf_resource); 1.130 + 1.131 + object_filename_ = [dwarf_resource retain]; 1.132 + } else { 1.133 + object_filename_ = [input_pathname_ retain]; 1.134 + } 1.135 + 1.136 + // Read the file's contents into memory. 1.137 + // 1.138 + // The documentation for dataWithContentsOfMappedFile says: 1.139 + // 1.140 + // Because of file mapping restrictions, this method should only be 1.141 + // used if the file is guaranteed to exist for the duration of the 1.142 + // data object’s existence. It is generally safer to use the 1.143 + // dataWithContentsOfFile: method. 1.144 + // 1.145 + // I gather this means that OS X doesn't have (or at least, that method 1.146 + // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the 1.147 + // process appears to get its own copy of the data, and changes to the 1.148 + // file don't affect memory and vice versa). 1.149 + NSError *error; 1.150 + contents_ = [NSData dataWithContentsOfFile:object_filename_ 1.151 + options:0 1.152 + error:&error]; 1.153 + if (!contents_) { 1.154 + fprintf(stderr, "Error reading object file: %s: %s\n", 1.155 + [object_filename_ fileSystemRepresentation], 1.156 + [[error localizedDescription] UTF8String]); 1.157 + return false; 1.158 + } 1.159 + [contents_ retain]; 1.160 + 1.161 + // Get the list of object files present in the file. 1.162 + FatReader::Reporter fat_reporter([object_filename_ 1.163 + fileSystemRepresentation]); 1.164 + FatReader fat_reader(&fat_reporter); 1.165 + if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]), 1.166 + [contents_ length])) { 1.167 + return false; 1.168 + } 1.169 + 1.170 + // Get our own copy of fat_reader's object file list. 1.171 + size_t object_files_count; 1.172 + const struct fat_arch *object_files = 1.173 + fat_reader.object_files(&object_files_count); 1.174 + if (object_files_count == 0) { 1.175 + fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", 1.176 + [object_filename_ fileSystemRepresentation]); 1.177 + return false; 1.178 + } 1.179 + object_files_.resize(object_files_count); 1.180 + memcpy(&object_files_[0], object_files, 1.181 + sizeof(struct fat_arch) * object_files_count); 1.182 + 1.183 + return true; 1.184 +} 1.185 + 1.186 +bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, 1.187 + cpu_subtype_t cpu_subtype) { 1.188 + // Find the best match for the architecture the user requested. 1.189 + const struct fat_arch *best_match 1.190 + = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0], 1.191 + static_cast<uint32_t>(object_files_.size())); 1.192 + if (!best_match) return false; 1.193 + 1.194 + // Record the selected object file. 1.195 + selected_object_file_ = best_match; 1.196 + return true; 1.197 +} 1.198 + 1.199 +bool DumpSymbols::SetArchitecture(const std::string &arch_name) { 1.200 + bool arch_set = false; 1.201 + const NXArchInfo *arch_info = 1.202 + google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); 1.203 + if (arch_info) { 1.204 + arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); 1.205 + } 1.206 + return arch_set; 1.207 +} 1.208 + 1.209 +string DumpSymbols::Identifier() { 1.210 + FileID file_id([object_filename_ fileSystemRepresentation]); 1.211 + unsigned char identifier_bytes[16]; 1.212 + cpu_type_t cpu_type = selected_object_file_->cputype; 1.213 + cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; 1.214 + if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { 1.215 + fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", 1.216 + [object_filename_ fileSystemRepresentation]); 1.217 + return ""; 1.218 + } 1.219 + 1.220 + char identifier_string[40]; 1.221 + FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, 1.222 + sizeof(identifier_string)); 1.223 + 1.224 + string compacted(identifier_string); 1.225 + for(size_t i = compacted.find('-'); i != string::npos; 1.226 + i = compacted.find('-', i)) 1.227 + compacted.erase(i, 1); 1.228 + 1.229 + return compacted; 1.230 +} 1.231 + 1.232 +// A line-to-module loader that accepts line number info parsed by 1.233 +// dwarf2reader::LineInfo and populates a Module and a line vector 1.234 +// with the results. 1.235 +class DumpSymbols::DumperLineToModule: 1.236 + public DwarfCUToModule::LineToModuleHandler { 1.237 + public: 1.238 + // Create a line-to-module converter using BYTE_READER. 1.239 + DumperLineToModule(dwarf2reader::ByteReader *byte_reader) 1.240 + : byte_reader_(byte_reader) { } 1.241 + 1.242 + void StartCompilationUnit(const string& compilation_dir) { 1.243 + compilation_dir_ = compilation_dir; 1.244 + } 1.245 + 1.246 + void ReadProgram(const char *program, uint64 length, 1.247 + Module *module, vector<Module::Line> *lines) { 1.248 + DwarfLineToModule handler(module, compilation_dir_, lines); 1.249 + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); 1.250 + parser.Start(); 1.251 + } 1.252 + private: 1.253 + string compilation_dir_; 1.254 + dwarf2reader::ByteReader *byte_reader_; // WEAK 1.255 +}; 1.256 + 1.257 +bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, 1.258 + const mach_o::Reader &macho_reader, 1.259 + const mach_o::SectionMap &dwarf_sections) const { 1.260 + // Build a byte reader of the appropriate endianness. 1.261 + ByteReader byte_reader(macho_reader.big_endian() 1.262 + ? dwarf2reader::ENDIANNESS_BIG 1.263 + : dwarf2reader::ENDIANNESS_LITTLE); 1.264 + 1.265 + // Construct a context for this file. 1.266 + DwarfCUToModule::FileContext file_context(selected_object_name_, 1.267 + module); 1.268 + 1.269 + // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. 1.270 + for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); 1.271 + it != dwarf_sections.end(); it++) { 1.272 + file_context.section_map[it->first] = 1.273 + make_pair(reinterpret_cast<const char *>(it->second.contents.start), 1.274 + it->second.contents.Size()); 1.275 + } 1.276 + 1.277 + // Find the __debug_info section. 1.278 + std::pair<const char *, uint64> debug_info_section 1.279 + = file_context.section_map["__debug_info"]; 1.280 + // There had better be a __debug_info section! 1.281 + if (!debug_info_section.first) { 1.282 + fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", 1.283 + selected_object_name_.c_str()); 1.284 + return false; 1.285 + } 1.286 + 1.287 + // Build a line-to-module loader for the root handler to use. 1.288 + DumperLineToModule line_to_module(&byte_reader); 1.289 + 1.290 + // Walk the __debug_info section, one compilation unit at a time. 1.291 + uint64 debug_info_length = debug_info_section.second; 1.292 + for (uint64 offset = 0; offset < debug_info_length;) { 1.293 + // Make a handler for the root DIE that populates MODULE with the 1.294 + // debug info. 1.295 + DwarfCUToModule::WarningReporter reporter(selected_object_name_, 1.296 + offset); 1.297 + DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); 1.298 + // Make a Dwarf2Handler that drives our DIEHandler. 1.299 + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); 1.300 + // Make a DWARF parser for the compilation unit at OFFSET. 1.301 + dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map, 1.302 + offset, 1.303 + &byte_reader, 1.304 + &die_dispatcher); 1.305 + // Process the entire compilation unit; get the offset of the next. 1.306 + offset += dwarf_reader.Start(); 1.307 + } 1.308 + 1.309 + return true; 1.310 +} 1.311 + 1.312 +bool DumpSymbols::ReadCFI(google_breakpad::Module *module, 1.313 + const mach_o::Reader &macho_reader, 1.314 + const mach_o::Section §ion, 1.315 + bool eh_frame) const { 1.316 + // Find the appropriate set of register names for this file's 1.317 + // architecture. 1.318 + vector<const UniqueString*> register_names; 1.319 + switch (macho_reader.cpu_type()) { 1.320 + case CPU_TYPE_X86: 1.321 + register_names = DwarfCFIToModule::RegisterNames::I386(); 1.322 + break; 1.323 + case CPU_TYPE_X86_64: 1.324 + register_names = DwarfCFIToModule::RegisterNames::X86_64(); 1.325 + break; 1.326 + case CPU_TYPE_ARM: 1.327 + register_names = DwarfCFIToModule::RegisterNames::ARM(); 1.328 + break; 1.329 + default: { 1.330 + const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType( 1.331 + macho_reader.cpu_type(), macho_reader.cpu_subtype()); 1.332 + fprintf(stderr, "%s: cannot convert DWARF call frame information for ", 1.333 + selected_object_name_.c_str()); 1.334 + if (arch) 1.335 + fprintf(stderr, "architecture '%s'", arch->name); 1.336 + else 1.337 + fprintf(stderr, "architecture %d,%d", 1.338 + macho_reader.cpu_type(), macho_reader.cpu_subtype()); 1.339 + fprintf(stderr, " to Breakpad symbol file: no register name table\n"); 1.340 + return false; 1.341 + } 1.342 + } 1.343 + 1.344 + // Find the call frame information and its size. 1.345 + const char *cfi = reinterpret_cast<const char *>(section.contents.start); 1.346 + size_t cfi_size = section.contents.Size(); 1.347 + 1.348 + // Plug together the parser, handler, and their entourages. 1.349 + DwarfCFIToModule::Reporter module_reporter(selected_object_name_, 1.350 + section.section_name); 1.351 + DwarfCFIToModule handler(module, register_names, &module_reporter); 1.352 + dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? 1.353 + dwarf2reader::ENDIANNESS_BIG : 1.354 + dwarf2reader::ENDIANNESS_LITTLE); 1.355 + byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); 1.356 + // At the moment, according to folks at Apple and some cursory 1.357 + // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so 1.358 + // this is the only base address the CFI parser will need. 1.359 + byte_reader.SetCFIDataBase(section.address, cfi); 1.360 + 1.361 + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, 1.362 + section.section_name); 1.363 + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, 1.364 + &byte_reader, &handler, &dwarf_reporter, 1.365 + eh_frame); 1.366 + parser.Start(); 1.367 + return true; 1.368 +} 1.369 + 1.370 +// A LoadCommandHandler that loads whatever debugging data it finds into a 1.371 +// Module. 1.372 +class DumpSymbols::LoadCommandDumper: 1.373 + public mach_o::Reader::LoadCommandHandler { 1.374 + public: 1.375 + // Create a load command dumper handling load commands from READER's 1.376 + // file, and adding data to MODULE. 1.377 + LoadCommandDumper(const DumpSymbols &dumper, 1.378 + google_breakpad::Module *module, 1.379 + const mach_o::Reader &reader, 1.380 + SymbolData symbol_data) 1.381 + : dumper_(dumper), 1.382 + module_(module), 1.383 + reader_(reader), 1.384 + symbol_data_(symbol_data) { } 1.385 + 1.386 + bool SegmentCommand(const mach_o::Segment &segment); 1.387 + bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); 1.388 + 1.389 + private: 1.390 + const DumpSymbols &dumper_; 1.391 + google_breakpad::Module *module_; // WEAK 1.392 + const mach_o::Reader &reader_; 1.393 + const SymbolData symbol_data_; 1.394 +}; 1.395 + 1.396 +bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { 1.397 + mach_o::SectionMap section_map; 1.398 + if (!reader_.MapSegmentSections(segment, §ion_map)) 1.399 + return false; 1.400 + 1.401 + if (segment.name == "__TEXT" && symbol_data_ != NO_CFI) { 1.402 + module_->SetLoadAddress(segment.vmaddr); 1.403 + mach_o::SectionMap::const_iterator eh_frame = 1.404 + section_map.find("__eh_frame"); 1.405 + if (eh_frame != section_map.end()) { 1.406 + // If there is a problem reading this, don't treat it as a fatal error. 1.407 + dumper_.ReadCFI(module_, reader_, eh_frame->second, true); 1.408 + } 1.409 + return true; 1.410 + } 1.411 + 1.412 + if (segment.name == "__DWARF") { 1.413 + if (symbol_data_ != ONLY_CFI) { 1.414 + if (!dumper_.ReadDwarf(module_, reader_, section_map)) 1.415 + return false; 1.416 + } 1.417 + if (symbol_data_ != NO_CFI) { 1.418 + mach_o::SectionMap::const_iterator debug_frame 1.419 + = section_map.find("__debug_frame"); 1.420 + if (debug_frame != section_map.end()) { 1.421 + // If there is a problem reading this, don't treat it as a fatal error. 1.422 + dumper_.ReadCFI(module_, reader_, debug_frame->second, false); 1.423 + } 1.424 + } 1.425 + } 1.426 + 1.427 + return true; 1.428 +} 1.429 + 1.430 +bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, 1.431 + const ByteBuffer &strings) { 1.432 + StabsToModule stabs_to_module(module_); 1.433 + // Mac OS X STABS are never "unitized", and the size of the 'value' field 1.434 + // matches the address size of the executable. 1.435 + StabsReader stabs_reader(entries.start, entries.Size(), 1.436 + strings.start, strings.Size(), 1.437 + reader_.big_endian(), 1.438 + reader_.bits_64() ? 8 : 4, 1.439 + true, 1.440 + &stabs_to_module); 1.441 + if (!stabs_reader.Process()) 1.442 + return false; 1.443 + stabs_to_module.Finalize(); 1.444 + return true; 1.445 +} 1.446 + 1.447 +bool DumpSymbols::ReadSymbolData(Module** out_module) { 1.448 + // Select an object file, if SetArchitecture hasn't been called to set one 1.449 + // explicitly. 1.450 + if (!selected_object_file_) { 1.451 + // If there's only one architecture, that's the one. 1.452 + if (object_files_.size() == 1) 1.453 + selected_object_file_ = &object_files_[0]; 1.454 + else { 1.455 + // Look for an object file whose architecture matches our own. 1.456 + const NXArchInfo *local_arch = NXGetLocalArchInfo(); 1.457 + if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { 1.458 + fprintf(stderr, "%s: object file contains more than one" 1.459 + " architecture, none of which match the current" 1.460 + " architecture; specify an architecture explicitly" 1.461 + " with '-a ARCH' to resolve the ambiguity\n", 1.462 + [object_filename_ fileSystemRepresentation]); 1.463 + return false; 1.464 + } 1.465 + } 1.466 + } 1.467 + 1.468 + assert(selected_object_file_); 1.469 + 1.470 + // Find the name of the selected file's architecture, to appear in 1.471 + // the MODULE record and in error messages. 1.472 + const NXArchInfo *selected_arch_info = 1.473 + google_breakpad::BreakpadGetArchInfoFromCpuType( 1.474 + selected_object_file_->cputype, selected_object_file_->cpusubtype); 1.475 + 1.476 + const char *selected_arch_name = selected_arch_info->name; 1.477 + if (strcmp(selected_arch_name, "i386") == 0) 1.478 + selected_arch_name = "x86"; 1.479 + 1.480 + // Produce a name to use in error messages that includes the 1.481 + // filename, and the architecture, if there is more than one. 1.482 + selected_object_name_ = [object_filename_ UTF8String]; 1.483 + if (object_files_.size() > 1) { 1.484 + selected_object_name_ += ", architecture "; 1.485 + selected_object_name_ + selected_arch_name; 1.486 + } 1.487 + 1.488 + // Compute a module name, to appear in the MODULE record. 1.489 + NSString *module_name = [object_filename_ lastPathComponent]; 1.490 + 1.491 + // Choose an identifier string, to appear in the MODULE record. 1.492 + string identifier = Identifier(); 1.493 + if (identifier.empty()) 1.494 + return false; 1.495 + identifier += "0"; 1.496 + 1.497 + // Create a module to hold the debugging information. 1.498 + scoped_ptr<Module> module(new Module([module_name UTF8String], 1.499 + "mac", 1.500 + selected_arch_name, 1.501 + identifier)); 1.502 + 1.503 + // Parse the selected object file. 1.504 + mach_o::Reader::Reporter reporter(selected_object_name_); 1.505 + mach_o::Reader reader(&reporter); 1.506 + if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) 1.507 + + selected_object_file_->offset, 1.508 + selected_object_file_->size, 1.509 + selected_object_file_->cputype, 1.510 + selected_object_file_->cpusubtype)) 1.511 + return false; 1.512 + 1.513 + // Walk its load commands, and deal with whatever is there. 1.514 + LoadCommandDumper load_command_dumper(*this, module.get(), reader, 1.515 + symbol_data_); 1.516 + if (!reader.WalkLoadCommands(&load_command_dumper)) 1.517 + return false; 1.518 + 1.519 + *out_module = module.release(); 1.520 + 1.521 + return true; 1.522 +} 1.523 + 1.524 +bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { 1.525 + Module* module = NULL; 1.526 + 1.527 + if (ReadSymbolData(&module) && module) { 1.528 + bool res = module->Write(stream, symbol_data_); 1.529 + delete module; 1.530 + return res; 1.531 + } 1.532 + 1.533 + return false; 1.534 +} 1.535 + 1.536 +} // namespace google_breakpad