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