aboutsummaryrefslogtreecommitdiff
path: root/externals/breakpad/src/common/mac/dump_syms.cc
diff options
context:
space:
mode:
Diffstat (limited to 'externals/breakpad/src/common/mac/dump_syms.cc')
-rw-r--r--externals/breakpad/src/common/mac/dump_syms.cc726
1 files changed, 726 insertions, 0 deletions
diff --git a/externals/breakpad/src/common/mac/dump_syms.cc b/externals/breakpad/src/common/mac/dump_syms.cc
new file mode 100644
index 0000000000..c06945e483
--- /dev/null
+++ b/externals/breakpad/src/common/mac/dump_syms.cc
@@ -0,0 +1,726 @@
+// -*- mode: c++ -*-
+
+// Copyright 2011 Google LLC
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dump_syms.cc: Create a symbol file for use with minidumps
+
+#ifdef HAVE_CONFIG_H
+#include <config.h> // Must come first
+#endif
+
+#include "common/mac/dump_syms.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <mach-o/arch.h>
+#include <mach-o/fat.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "common/dwarf/bytereader-inl.h"
+#include "common/dwarf/dwarf2reader.h"
+#include "common/dwarf_cfi_to_module.h"
+#include "common/dwarf_cu_to_module.h"
+#include "common/dwarf_line_to_module.h"
+#include "common/dwarf_range_list_handler.h"
+#include "common/mac/file_id.h"
+#include "common/mac/arch_utilities.h"
+#include "common/mac/macho_reader.h"
+#include "common/module.h"
+#include "common/path_helper.h"
+#include "common/scoped_ptr.h"
+#include "common/stabs_reader.h"
+#include "common/stabs_to_module.h"
+#include "common/symbol_data.h"
+
+#ifndef CPU_TYPE_ARM
+#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
+#endif // CPU_TYPE_ARM
+
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228))
+#endif // CPU_TYPE_ARM64
+
+using google_breakpad::ByteReader;
+using google_breakpad::DwarfCUToModule;
+using google_breakpad::DwarfLineToModule;
+using google_breakpad::DwarfRangeListHandler;
+using google_breakpad::mach_o::FatReader;
+using google_breakpad::mach_o::FileID;
+using google_breakpad::mach_o::Section;
+using google_breakpad::mach_o::Segment;
+using google_breakpad::Module;
+using google_breakpad::StabsReader;
+using google_breakpad::StabsToModule;
+using google_breakpad::scoped_ptr;
+using std::make_pair;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace {
+// Return a vector<string> with absolute paths to all the entries
+// in directory (excluding . and ..).
+vector<string> list_directory(const string& directory) {
+ vector<string> entries;
+ DIR* dir = opendir(directory.c_str());
+ if (!dir) {
+ return entries;
+ }
+
+ string path = directory;
+ if (path[path.length() - 1] != '/') {
+ path += '/';
+ }
+
+ struct dirent* entry = NULL;
+ while ((entry = readdir(dir))) {
+ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
+ entries.push_back(path + entry->d_name);
+ }
+ }
+
+ closedir(dir);
+ return entries;
+}
+}
+
+namespace google_breakpad {
+
+bool DumpSymbols::Read(const string& filename) {
+ selected_object_file_ = nullptr;
+ struct stat st;
+ if (stat(filename.c_str(), &st) == -1) {
+ fprintf(stderr, "Could not access object file %s: %s\n",
+ filename.c_str(), strerror(errno));
+ return false;
+ }
+
+ from_disk_ = true;
+
+ // Does this filename refer to a dSYM bundle?
+ string contents_path = filename + "/Contents/Resources/DWARF";
+ string object_filename;
+ if (S_ISDIR(st.st_mode) &&
+ access(contents_path.c_str(), F_OK) == 0) {
+ // If there's one file under Contents/Resources/DWARF then use that,
+ // otherwise bail out.
+ const vector<string> entries = list_directory(contents_path);
+ if (entries.size() == 0) {
+ fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
+ filename.c_str());
+ return false;
+ }
+ if (entries.size() > 1) {
+ fprintf(stderr, "Too many DWARF files in bundle: %s\n",
+ filename.c_str());
+ return false;
+ }
+
+ object_filename = entries[0];
+ } else {
+ object_filename = filename;
+ }
+
+ // Read the file's contents into memory.
+ bool read_ok = true;
+ string error;
+ scoped_array<uint8_t> contents;
+ off_t total = 0;
+ if (stat(object_filename.c_str(), &st) != -1) {
+ FILE* f = fopen(object_filename.c_str(), "rb");
+ if (f) {
+ contents.reset(new uint8_t[st.st_size]);
+ while (total < st.st_size && !feof(f)) {
+ size_t read = fread(&contents[0] + total, 1, st.st_size - total, f);
+ if (read == 0) {
+ if (ferror(f)) {
+ read_ok = false;
+ error = strerror(errno);
+ }
+ break;
+ }
+ total += read;
+ }
+ fclose(f);
+ } else {
+ error = strerror(errno);
+ }
+ }
+
+ if (!read_ok) {
+ fprintf(stderr, "Error reading object file: %s: %s\n",
+ object_filename.c_str(), error.c_str());
+ return false;
+ }
+ return ReadData(contents.release(), total, object_filename);
+}
+
+bool DumpSymbols::ReadData(uint8_t* contents, size_t size,
+ const std::string& filename) {
+ contents_.reset(contents);
+ size_ = size;
+ object_filename_ = filename;
+
+ // Get the list of object files present in the file.
+ FatReader::Reporter fat_reporter(object_filename_);
+ FatReader fat_reader(&fat_reporter);
+ if (!fat_reader.Read(contents_.get(), size)) {
+ return false;
+ }
+
+ // Get our own copy of fat_reader's object file list.
+ size_t object_files_count;
+ const SuperFatArch* object_files =
+ fat_reader.object_files(&object_files_count);
+ if (object_files_count == 0) {
+ fprintf(stderr, "Fat binary file contains *no* architectures: %s\n",
+ object_filename_.c_str());
+ return false;
+ }
+ object_files_.resize(object_files_count);
+ memcpy(&object_files_[0], object_files,
+ sizeof(SuperFatArch) * object_files_count);
+
+ return true;
+}
+
+bool DumpSymbols::SetArchitecture(const ArchInfo& info) {
+ // Find the best match for the architecture the user requested.
+ const SuperFatArch* best_match =
+ FindBestMatchForArchitecture(info.cputype, info.cpusubtype);
+ if (!best_match) return false;
+
+ // Record the selected object file.
+ selected_object_file_ = best_match;
+ return true;
+}
+
+
+SuperFatArch* DumpSymbols::FindBestMatchForArchitecture(
+ cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype) {
+ SuperFatArch* closest_match = nullptr;
+ for (auto& object_file : object_files_) {
+ if (static_cast<cpu_type_t>(object_file.cputype) == cpu_type) {
+ // If there's an exact match, return it directly.
+ if ((static_cast<cpu_subtype_t>(object_file.cpusubtype) &
+ ~CPU_SUBTYPE_MASK) == (cpu_subtype & ~CPU_SUBTYPE_MASK)) {
+ return &object_file;
+ }
+ // Otherwise, hold on to this as the closest match since at least the CPU
+ // type matches.
+ if (!closest_match) {
+ closest_match = &object_file;
+ }
+ }
+ }
+ // No exact match found.
+ fprintf(stderr,
+ "Failed to find an exact match for an object file with cpu "
+ "type: %d and cpu subtype: %d.\n",
+ cpu_type, cpu_subtype);
+ if (closest_match) {
+ fprintf(stderr, "Using %s as the closest match.\n",
+ GetNameFromCPUType(closest_match->cputype,
+ closest_match->cpusubtype));
+ return closest_match;
+ }
+ return nullptr;
+}
+
+string DumpSymbols::Identifier() {
+ scoped_ptr<FileID> file_id;
+
+ if (from_disk_) {
+ file_id.reset(new FileID(object_filename_.c_str()));
+ } else {
+ file_id.reset(new FileID(contents_.get(), size_));
+ }
+ unsigned char identifier_bytes[16];
+ scoped_ptr<Module> module;
+ if (!selected_object_file_) {
+ if (!CreateEmptyModule(module))
+ return string();
+ }
+ cpu_type_t cpu_type = selected_object_file_->cputype;
+ cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype;
+ if (!file_id->MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) {
+ fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n",
+ object_filename_.c_str());
+ return "";
+ }
+
+ char identifier_string[40];
+ FileID::ConvertIdentifierToString(identifier_bytes, identifier_string,
+ sizeof(identifier_string));
+
+ string compacted(identifier_string);
+ for(size_t i = compacted.find('-'); i != string::npos;
+ i = compacted.find('-', i))
+ compacted.erase(i, 1);
+
+ // The pdb for these IDs has an extra byte, so to make everything uniform put
+ // a 0 on the end of mac IDs.
+ compacted += "0";
+
+ return compacted;
+}
+
+// A range handler that accepts rangelist data parsed by
+// RangeListReader and populates a range vector (typically
+// owned by a function) with the results.
+class DumpSymbols::DumperRangesHandler:
+ public DwarfCUToModule::RangesHandler {
+ public:
+ DumperRangesHandler(ByteReader* reader) :
+ reader_(reader) { }
+
+ bool ReadRanges(
+ enum DwarfForm form, uint64_t data,
+ RangeListReader::CURangesInfo* cu_info,
+ vector<Module::Range>* ranges) {
+ DwarfRangeListHandler handler(ranges);
+ RangeListReader range_list_reader(reader_, cu_info,
+ &handler);
+ return range_list_reader.ReadRanges(form, data);
+ }
+
+ private:
+ ByteReader* reader_;
+};
+
+// A line-to-module loader that accepts line number info parsed by
+// LineInfo and populates a Module and a line vector
+// with the results.
+class DumpSymbols::DumperLineToModule:
+ public DwarfCUToModule::LineToModuleHandler {
+ public:
+ // Create a line-to-module converter using BYTE_READER.
+ DumperLineToModule(ByteReader* byte_reader)
+ : byte_reader_(byte_reader) { }
+
+ void StartCompilationUnit(const string& compilation_dir) {
+ compilation_dir_ = compilation_dir;
+ }
+
+ void ReadProgram(const uint8_t* program,
+ uint64_t length,
+ const uint8_t* string_section,
+ uint64_t string_section_length,
+ const uint8_t* line_string_section,
+ uint64_t line_string_section_length,
+ Module* module,
+ vector<Module::Line>* lines,
+ std::map<uint32_t, Module::File*>* files) {
+ DwarfLineToModule handler(module, compilation_dir_, lines, files);
+ LineInfo parser(program, length, byte_reader_, nullptr, 0,
+ nullptr, 0, &handler);
+ parser.Start();
+ }
+ private:
+ string compilation_dir_;
+ ByteReader* byte_reader_; // WEAK
+};
+
+bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) {
+ // Select an object file, if SetArchitecture hasn't been called to set one
+ // explicitly.
+ if (!selected_object_file_) {
+ // If there's only one architecture, that's the one.
+ if (object_files_.size() == 1)
+ selected_object_file_ = &object_files_[0];
+ else {
+ // Look for an object file whose architecture matches our own.
+ ArchInfo local_arch = GetLocalArchInfo();
+ if (!SetArchitecture(local_arch)) {
+ fprintf(stderr, "%s: object file contains more than one"
+ " architecture, none of which match the current"
+ " architecture; specify an architecture explicitly"
+ " with '-a ARCH' to resolve the ambiguity\n",
+ object_filename_.c_str());
+ return false;
+ }
+ }
+ }
+
+ assert(selected_object_file_);
+
+ // Find the name of the selected file's architecture, to appear in
+ // the MODULE record and in error messages.
+ const char* selected_arch_name = GetNameFromCPUType(
+ selected_object_file_->cputype, selected_object_file_->cpusubtype);
+
+ // In certain cases, it is possible that architecture info can't be reliably
+ // determined, e.g. new architectures that breakpad is unware of. In that
+ // case, avoid crashing and return false instead.
+ if (selected_arch_name == kUnknownArchName) {
+ return false;
+ }
+
+ if (strcmp(selected_arch_name, "i386") == 0)
+ selected_arch_name = "x86";
+
+ // Produce a name to use in error messages that includes the
+ // filename, and the architecture, if there is more than one.
+ selected_object_name_ = object_filename_;
+ if (object_files_.size() > 1) {
+ selected_object_name_ += ", architecture ";
+ selected_object_name_ + selected_arch_name;
+ }
+
+ // Compute a module name, to appear in the MODULE record.
+ string module_name;
+ if (!module_name_.empty()) {
+ module_name = module_name_;
+ } else {
+ module_name = google_breakpad::BaseName(object_filename_);
+ }
+
+ // Choose an identifier string, to appear in the MODULE record.
+ string identifier = Identifier();
+ if (identifier.empty())
+ return false;
+
+ // Create a module to hold the debugging information.
+ module.reset(new Module(module_name, "mac", selected_arch_name, identifier,
+ "", enable_multiple_, prefer_extern_name_));
+ return true;
+}
+
+void DumpSymbols::StartProcessSplitDwarf(
+ google_breakpad::CompilationUnit* reader,
+ Module* module,
+ google_breakpad::Endianness endianness,
+ bool handle_inter_cu_refs,
+ bool handle_inline) const {
+ std::string split_file;
+ google_breakpad::SectionMap split_sections;
+ google_breakpad::ByteReader split_byte_reader(endianness);
+ uint64_t cu_offset = 0;
+ if (reader->ProcessSplitDwarf(split_file, split_sections, split_byte_reader,
+ cu_offset))
+ return;
+ DwarfCUToModule::FileContext file_context(split_file, module,
+ handle_inter_cu_refs);
+ for (auto section : split_sections)
+ file_context.AddSectionToSectionMap(section.first, section.second.first,
+ section.second.second);
+ // Because DWP/DWO file doesn't have .debug_addr/.debug_line/.debug_line_str,
+ // its debug info will refer to .debug_addr/.debug_line in the main binary.
+ if (file_context.section_map().find(".debug_addr") ==
+ file_context.section_map().end())
+ file_context.AddSectionToSectionMap(".debug_addr", reader->GetAddrBuffer(),
+ reader->GetAddrBufferLen());
+ if (file_context.section_map().find(".debug_line") ==
+ file_context.section_map().end())
+ file_context.AddSectionToSectionMap(".debug_line", reader->GetLineBuffer(),
+ reader->GetLineBufferLen());
+ if (file_context.section_map().find(".debug_line_str") ==
+ file_context.section_map().end())
+ file_context.AddSectionToSectionMap(".debug_line_str",
+ reader->GetLineStrBuffer(),
+ reader->GetLineStrBufferLen());
+ DumperRangesHandler ranges_handler(&split_byte_reader);
+ DumperLineToModule line_to_module(&split_byte_reader);
+ DwarfCUToModule::WarningReporter reporter(split_file, cu_offset);
+ DwarfCUToModule root_handler(
+ &file_context, &line_to_module, &ranges_handler, &reporter, handle_inline,
+ reader->GetLowPC(), reader->GetAddrBase(), reader->HasSourceLineInfo(),
+ reader->GetSourceLineOffset());
+ google_breakpad::DIEDispatcher die_dispatcher(&root_handler);
+ google_breakpad::CompilationUnit split_reader(
+ split_file, file_context.section_map(), cu_offset, &split_byte_reader,
+ &die_dispatcher);
+ split_reader.SetSplitDwarf(reader->GetAddrBase(), reader->GetDWOID());
+ split_reader.Start();
+ // Normally, it won't happen unless we have transitive reference.
+ if (split_reader.ShouldProcessSplitDwarf()) {
+ StartProcessSplitDwarf(&split_reader, module, endianness,
+ handle_inter_cu_refs, handle_inline);
+ }
+}
+
+void DumpSymbols::ReadDwarf(google_breakpad::Module* module,
+ const mach_o::Reader& macho_reader,
+ const mach_o::SectionMap& dwarf_sections,
+ bool handle_inter_cu_refs) const {
+ // Build a byte reader of the appropriate endianness.
+ google_breakpad::Endianness endianness =
+ macho_reader.big_endian() ? ENDIANNESS_BIG : ENDIANNESS_LITTLE;
+ ByteReader byte_reader(endianness);
+
+ // Construct a context for this file.
+ DwarfCUToModule::FileContext file_context(selected_object_name_,
+ module,
+ handle_inter_cu_refs);
+
+ // Build a SectionMap from our mach_o::SectionMap.
+ for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin();
+ it != dwarf_sections.end(); ++it) {
+ file_context.AddSectionToSectionMap(
+ it->first,
+ it->second.contents.start,
+ it->second.contents.Size());
+ }
+
+ // Find the __debug_info section.
+ SectionMap::const_iterator debug_info_entry =
+ file_context.section_map().find("__debug_info");
+ // There had better be a __debug_info section!
+ if (debug_info_entry == file_context.section_map().end()) {
+ fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n",
+ selected_object_name_.c_str());
+ return;
+ }
+ const std::pair<const uint8_t*, uint64_t>& debug_info_section =
+ debug_info_entry->second;
+
+ // Build a line-to-module loader for the root handler to use.
+ DumperLineToModule line_to_module(&byte_reader);
+
+ // .debug_ranges and .debug_rngslists reader
+ DumperRangesHandler ranges_handler(&byte_reader);
+
+ // Walk the __debug_info section, one compilation unit at a time.
+ uint64_t debug_info_length = debug_info_section.second;
+ bool handle_inline = symbol_data_ & INLINES;
+ for (uint64_t offset = 0; offset < debug_info_length;) {
+ // Make a handler for the root DIE that populates MODULE with the
+ // debug info.
+ DwarfCUToModule::WarningReporter reporter(selected_object_name_,
+ offset);
+ DwarfCUToModule root_handler(&file_context, &line_to_module,
+ &ranges_handler, &reporter, handle_inline);
+ // Make a Dwarf2Handler that drives our DIEHandler.
+ DIEDispatcher die_dispatcher(&root_handler);
+ // Make a DWARF parser for the compilation unit at OFFSET.
+ CompilationUnit dwarf_reader(selected_object_name_,
+ file_context.section_map(),
+ offset,
+ &byte_reader,
+ &die_dispatcher);
+ // Process the entire compilation unit; get the offset of the next.
+ offset += dwarf_reader.Start();
+ // Start to process split dwarf file.
+ if (dwarf_reader.ShouldProcessSplitDwarf()) {
+ StartProcessSplitDwarf(&dwarf_reader, module, endianness,
+ handle_inter_cu_refs, handle_inline);
+ }
+ }
+}
+
+bool DumpSymbols::ReadCFI(google_breakpad::Module* module,
+ const mach_o::Reader& macho_reader,
+ const mach_o::Section& section,
+ bool eh_frame) const {
+ // Find the appropriate set of register names for this file's
+ // architecture.
+ vector<string> register_names;
+ switch (macho_reader.cpu_type()) {
+ case CPU_TYPE_X86:
+ register_names = DwarfCFIToModule::RegisterNames::I386();
+ break;
+ case CPU_TYPE_X86_64:
+ register_names = DwarfCFIToModule::RegisterNames::X86_64();
+ break;
+ case CPU_TYPE_ARM:
+ register_names = DwarfCFIToModule::RegisterNames::ARM();
+ break;
+ case CPU_TYPE_ARM64:
+ register_names = DwarfCFIToModule::RegisterNames::ARM64();
+ break;
+ default: {
+ const char* arch_name = GetNameFromCPUType(macho_reader.cpu_type(),
+ macho_reader.cpu_subtype());
+ fprintf(
+ stderr,
+ "%s: cannot convert DWARF call frame information for architecture "
+ "'%s' (%d, %d) to Breakpad symbol file: no register name table\n",
+ selected_object_name_.c_str(), arch_name, macho_reader.cpu_type(),
+ macho_reader.cpu_subtype());
+ return false;
+ }
+ }
+
+ // Find the call frame information and its size.
+ const uint8_t* cfi = section.contents.start;
+ size_t cfi_size = section.contents.Size();
+
+ // Plug together the parser, handler, and their entourages.
+ DwarfCFIToModule::Reporter module_reporter(selected_object_name_,
+ section.section_name);
+ DwarfCFIToModule handler(module, register_names, &module_reporter);
+ ByteReader byte_reader(macho_reader.big_endian() ?
+ ENDIANNESS_BIG :
+ ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4);
+ // At the moment, according to folks at Apple and some cursory
+ // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so
+ // this is the only base address the CFI parser will need.
+ byte_reader.SetCFIDataBase(section.address, cfi);
+
+ CallFrameInfo::Reporter dwarf_reporter(selected_object_name_,
+ section.section_name);
+ CallFrameInfo parser(cfi, cfi_size,
+ &byte_reader, &handler, &dwarf_reporter,
+ eh_frame);
+ parser.Start();
+ return true;
+}
+
+// A LoadCommandHandler that loads whatever debugging data it finds into a
+// Module.
+class DumpSymbols::LoadCommandDumper:
+ public mach_o::Reader::LoadCommandHandler {
+ public:
+ // Create a load command dumper handling load commands from READER's
+ // file, and adding data to MODULE.
+ LoadCommandDumper(const DumpSymbols& dumper,
+ google_breakpad::Module* module,
+ const mach_o::Reader& reader,
+ SymbolData symbol_data,
+ bool handle_inter_cu_refs)
+ : dumper_(dumper),
+ module_(module),
+ reader_(reader),
+ symbol_data_(symbol_data),
+ handle_inter_cu_refs_(handle_inter_cu_refs) { }
+
+ bool SegmentCommand(const mach_o::Segment& segment);
+ bool SymtabCommand(const ByteBuffer& entries, const ByteBuffer& strings);
+
+ private:
+ const DumpSymbols& dumper_;
+ google_breakpad::Module* module_; // WEAK
+ const mach_o::Reader& reader_;
+ const SymbolData symbol_data_;
+ const bool handle_inter_cu_refs_;
+};
+
+bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment& segment) {
+ mach_o::SectionMap section_map;
+ if (!reader_.MapSegmentSections(segment, &section_map))
+ return false;
+
+ if (segment.name == "__TEXT") {
+ module_->SetLoadAddress(segment.vmaddr);
+ if (symbol_data_ & CFI) {
+ mach_o::SectionMap::const_iterator eh_frame =
+ section_map.find("__eh_frame");
+ if (eh_frame != section_map.end()) {
+ // If there is a problem reading this, don't treat it as a fatal error.
+ dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
+ }
+ }
+ return true;
+ }
+
+ if (segment.name == "__DWARF") {
+ if ((symbol_data_ & SYMBOLS_AND_FILES) || (symbol_data_ & INLINES)) {
+ dumper_.ReadDwarf(module_, reader_, section_map, handle_inter_cu_refs_);
+ }
+ if (symbol_data_ & CFI) {
+ mach_o::SectionMap::const_iterator debug_frame
+ = section_map.find("__debug_frame");
+ if (debug_frame != section_map.end()) {
+ // If there is a problem reading this, don't treat it as a fatal error.
+ dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer& entries,
+ const ByteBuffer& strings) {
+ StabsToModule stabs_to_module(module_);
+ // Mac OS X STABS are never "unitized", and the size of the 'value' field
+ // matches the address size of the executable.
+ StabsReader stabs_reader(entries.start, entries.Size(),
+ strings.start, strings.Size(),
+ reader_.big_endian(),
+ reader_.bits_64() ? 8 : 4,
+ true,
+ &stabs_to_module);
+ if (!stabs_reader.Process())
+ return false;
+ stabs_to_module.Finalize();
+ return true;
+}
+
+bool DumpSymbols::ReadSymbolData(Module** out_module) {
+ scoped_ptr<Module> module;
+ if (!CreateEmptyModule(module))
+ return false;
+
+ // Parse the selected object file.
+ mach_o::Reader::Reporter reporter(selected_object_name_);
+ mach_o::Reader reader(&reporter);
+ if (!reader.Read(&contents_[0]
+ + selected_object_file_->offset,
+ selected_object_file_->size,
+ selected_object_file_->cputype,
+ selected_object_file_->cpusubtype))
+ return false;
+
+ // Walk its load commands, and deal with whatever is there.
+ LoadCommandDumper load_command_dumper(*this, module.get(), reader,
+ symbol_data_, handle_inter_cu_refs_);
+ if (!reader.WalkLoadCommands(&load_command_dumper))
+ return false;
+
+ *out_module = module.release();
+
+ return true;
+}
+
+// Read the selected object file's debugging information, and write out the
+// header only to |stream|. Return true on success; if an error occurs, report
+// it and return false.
+bool DumpSymbols::WriteSymbolFileHeader(std::ostream& stream) {
+ scoped_ptr<Module> module;
+ if (!CreateEmptyModule(module))
+ return false;
+
+ return module->Write(stream, symbol_data_);
+}
+
+} // namespace google_breakpad