Breakpad Mac symbol dumper: Add new Mach-O reader class.
This patch adds files defining new classes in the google_breakpad::Mach_O namespace for parsing fat binaries and Mach-O files. These are used in the new dumper to handle STABS debugging information, DWARF call frame information, and .eh_frame exception handling stack walking information. These new classes are independent of endianness and word size, and therefore can be used on binaries of all the relevant architectures: x86, x86_64, ppc, and ARM. The patch adds a complete set of unit tests for the new classes. A=jimb R=mark (http://breakpad.appspot.com/93001/show, http://breakpad.appspot.com/115001/show) git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@610 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
8ffb12eb35
commit
35c41e00ee
@ -43,10 +43,10 @@ using dwarf2reader::DwarfPointerEncoding;
|
||||
using dwarf2reader::ENDIANNESS_BIG;
|
||||
using dwarf2reader::ENDIANNESS_LITTLE;
|
||||
using google_breakpad::CFISection;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::string;
|
||||
using testing::Test;
|
||||
|
||||
|
@ -46,9 +46,9 @@
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::DwarfPointerEncoding;
|
||||
using google_breakpad::TestAssembler::Endianness;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::test_assembler::Endianness;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::string;
|
||||
|
||||
class CFISection: public Section {
|
||||
|
@ -63,10 +63,10 @@ extern "C" {
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
using google_breakpad::CFISection;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
|
||||
using dwarf2reader::DwarfPointerEncoding;
|
||||
using dwarf2reader::ENDIANNESS_BIG;
|
||||
@ -2315,7 +2315,7 @@ TEST_F(CFIReporter, ClearingCFARule) {
|
||||
#ifdef WRITE_ELF
|
||||
// See comments at the top of the file mentioning WRITE_ELF for details.
|
||||
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
|
||||
struct ELFSectionHeader {
|
||||
ELFSectionHeader(unsigned int set_type)
|
||||
|
524
src/common/mac/macho_reader.cc
Normal file
524
src/common/mac/macho_reader.cc
Normal file
@ -0,0 +1,524 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// 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 Inc. 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// macho_reader.cc: Implementation of google_breakpad::Mach_O::FatReader and
|
||||
// google_breakpad::Mach_O::Reader. See macho_reader.h for details.
|
||||
|
||||
#include "common/mac/macho_reader.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace mach_o {
|
||||
|
||||
// If NDEBUG is #defined, then the 'assert' macro doesn't evaluate its
|
||||
// arguments, so you can't place expressions that do necessary work in
|
||||
// the argument of an assert. Nor can you assign the result of the
|
||||
// expression to a variable and assert that the variable's value is
|
||||
// true: you'll get unused variable warnings when NDEBUG is #defined.
|
||||
//
|
||||
// ASSERT_ALWAYS_EVAL always evaluates its argument, and asserts that
|
||||
// the result is true if NDEBUG is not #defined.
|
||||
#if defined(NDEBUG)
|
||||
#define ASSERT_ALWAYS_EVAL(x) (x)
|
||||
#else
|
||||
#define ASSERT_ALWAYS_EVAL(x) assert(x)
|
||||
#endif
|
||||
|
||||
void FatReader::Reporter::BadHeader() {
|
||||
fprintf(stderr, "%s: file is neither a fat binary file"
|
||||
" nor a Mach-O object file\n", filename_.c_str());
|
||||
}
|
||||
|
||||
void FatReader::Reporter::TooShort() {
|
||||
fprintf(stderr, "%s: file too short for the data it claims to contain\n",
|
||||
filename_.c_str());
|
||||
}
|
||||
|
||||
void FatReader::Reporter::MisplacedObjectFile() {
|
||||
fprintf(stderr, "%s: file too short for the object files it claims"
|
||||
" to contain\n", filename_.c_str());
|
||||
}
|
||||
|
||||
bool FatReader::Read(const uint8_t *buffer, size_t size) {
|
||||
buffer_.start = buffer;
|
||||
buffer_.end = buffer + size;
|
||||
ByteCursor cursor(&buffer_);
|
||||
|
||||
// Fat binaries always use big-endian, so read the magic number in
|
||||
// that endianness. To recognize Mach-O magic numbers, which can use
|
||||
// either endianness, check for both the proper and reversed forms
|
||||
// of the magic numbers.
|
||||
cursor.set_big_endian(true);
|
||||
if (cursor >> magic_) {
|
||||
if (magic_ == FAT_MAGIC) {
|
||||
// How many object files does this fat binary contain?
|
||||
uint32_t object_files_count;
|
||||
if (!(cursor >> object_files_count)) { // nfat_arch
|
||||
reporter_->TooShort();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the list of object files.
|
||||
object_files_.resize(object_files_count);
|
||||
for (size_t i = 0; i < object_files_count; i++) {
|
||||
struct fat_arch *objfile = &object_files_[i];
|
||||
|
||||
// Read this object file entry, byte-swapping as appropriate.
|
||||
cursor >> objfile->cputype
|
||||
>> objfile->cpusubtype
|
||||
>> objfile->offset
|
||||
>> objfile->size
|
||||
>> objfile->align;
|
||||
if (!cursor) {
|
||||
reporter_->TooShort();
|
||||
return false;
|
||||
}
|
||||
// Does the file actually have the bytes this entry refers to?
|
||||
size_t fat_size = buffer_.Size();
|
||||
if (objfile->offset > fat_size ||
|
||||
objfile->size > fat_size - objfile->offset) {
|
||||
reporter_->MisplacedObjectFile();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (magic_ == MH_MAGIC || magic_ == MH_MAGIC_64 ||
|
||||
magic_ == MH_CIGAM || magic_ == MH_CIGAM_64) {
|
||||
// If this is a little-endian Mach-O file, fix the cursor's endianness.
|
||||
if (magic_ == MH_CIGAM || magic_ == MH_CIGAM_64)
|
||||
cursor.set_big_endian(false);
|
||||
// Record the entire file as a single entry in the object file list.
|
||||
object_files_.resize(1);
|
||||
|
||||
// Get the cpu type and subtype from the Mach-O header.
|
||||
if (!(cursor >> object_files_[0].cputype
|
||||
>> object_files_[0].cpusubtype)) {
|
||||
reporter_->TooShort();
|
||||
return false;
|
||||
}
|
||||
|
||||
object_files_[0].offset = 0;
|
||||
object_files_[0].size = buffer_.Size();
|
||||
// This alignment is correct for 32 and 64-bit x86 and ppc.
|
||||
// See get_align in the lipo source for other architectures:
|
||||
// http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c
|
||||
object_files_[0].align = 12; // 2^12 == 4096
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
reporter_->BadHeader();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Reader::Reporter::BadHeader() {
|
||||
fprintf(stderr, "%s: file is not a Mach-O object file\n", filename_.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::CPUTypeMismatch(cpu_type_t cpu_type,
|
||||
cpu_subtype_t cpu_subtype,
|
||||
cpu_type_t expected_cpu_type,
|
||||
cpu_subtype_t expected_cpu_subtype) {
|
||||
fprintf(stderr, "%s: CPU type %d, subtype %d does not match expected"
|
||||
" type %d, subtype %d\n",
|
||||
filename_.c_str(), cpu_type, cpu_subtype,
|
||||
expected_cpu_type, expected_cpu_subtype);
|
||||
}
|
||||
|
||||
void Reader::Reporter::HeaderTruncated() {
|
||||
fprintf(stderr, "%s: file does not contain a complete Mach-O header\n",
|
||||
filename_.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::LoadCommandRegionTruncated() {
|
||||
fprintf(stderr, "%s: file too short to hold load command region"
|
||||
" given in Mach-O header\n", filename_.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i,
|
||||
LoadCommandType type) {
|
||||
fprintf(stderr, "%s: file's header claims there are %ld"
|
||||
" load commands, but load command #%ld",
|
||||
filename_.c_str(), claimed, i);
|
||||
if (type) fprintf(stderr, ", of type %d,", type);
|
||||
fprintf(stderr, " extends beyond the end of the load command region\n");
|
||||
}
|
||||
|
||||
void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) {
|
||||
fprintf(stderr, "%s: the contents of load command #%ld, of type %d,"
|
||||
" extend beyond the size given in the load command's header\n",
|
||||
filename_.c_str(), i, type);
|
||||
}
|
||||
|
||||
void Reader::Reporter::SectionsMissing(const string &name) {
|
||||
fprintf(stderr, "%s: the load command for segment '%s'"
|
||||
" is too short to hold the section headers it claims to have\n",
|
||||
filename_.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::MisplacedSegmentData(const string &name) {
|
||||
fprintf(stderr, "%s: the segment '%s' claims its contents lie beyond"
|
||||
" the end of the file\n", filename_.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::MisplacedSectionData(const string §ion,
|
||||
const string &segment) {
|
||||
fprintf(stderr, "%s: the section '%s' in segment '%s'"
|
||||
" claims its contents lie outside the segment's contents\n",
|
||||
filename_.c_str(), section.c_str(), segment.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::MisplacedSymbolTable() {
|
||||
fprintf(stderr, "%s: the LC_SYMTAB load command claims that the symbol"
|
||||
" table's contents are located beyond the end of the file\n",
|
||||
filename_.c_str());
|
||||
}
|
||||
|
||||
void Reader::Reporter::UnsupportedCPUType(cpu_type_t cpu_type) {
|
||||
fprintf(stderr, "%s: CPU type %d is not supported\n",
|
||||
filename_.c_str(), cpu_type);
|
||||
}
|
||||
|
||||
bool Reader::Read(const uint8_t *buffer,
|
||||
size_t size,
|
||||
cpu_type_t expected_cpu_type,
|
||||
cpu_subtype_t expected_cpu_subtype) {
|
||||
assert(!buffer_.start);
|
||||
buffer_.start = buffer;
|
||||
buffer_.end = buffer + size;
|
||||
ByteCursor cursor(&buffer_, true);
|
||||
uint32_t magic;
|
||||
if (!(cursor >> magic)) {
|
||||
reporter_->HeaderTruncated();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_cpu_type != CPU_TYPE_ANY) {
|
||||
uint32_t expected_magic;
|
||||
// validate that magic matches the expected cpu type
|
||||
switch (expected_cpu_type) {
|
||||
case CPU_TYPE_I386:
|
||||
expected_magic = MH_CIGAM;
|
||||
break;
|
||||
case CPU_TYPE_POWERPC:
|
||||
expected_magic = MH_MAGIC;
|
||||
break;
|
||||
case CPU_TYPE_X86_64:
|
||||
expected_magic = MH_CIGAM_64;
|
||||
break;
|
||||
case CPU_TYPE_POWERPC64:
|
||||
expected_magic = MH_MAGIC_64;
|
||||
break;
|
||||
default:
|
||||
reporter_->UnsupportedCPUType(expected_cpu_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_magic != magic) {
|
||||
reporter_->BadHeader();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Since the byte cursor is in big-endian mode, a reversed magic number
|
||||
// always indicates a little-endian file, regardless of our own endianness.
|
||||
switch (magic) {
|
||||
case MH_MAGIC: big_endian_ = true; bits_64_ = false; break;
|
||||
case MH_CIGAM: big_endian_ = false; bits_64_ = false; break;
|
||||
case MH_MAGIC_64: big_endian_ = true; bits_64_ = true; break;
|
||||
case MH_CIGAM_64: big_endian_ = false; bits_64_ = true; break;
|
||||
default:
|
||||
reporter_->BadHeader();
|
||||
return false;
|
||||
}
|
||||
cursor.set_big_endian(big_endian_);
|
||||
uint32_t commands_size, reserved;
|
||||
cursor >> cpu_type_ >> cpu_subtype_ >> file_type_ >> load_command_count_
|
||||
>> commands_size >> flags_;
|
||||
if (bits_64_)
|
||||
cursor >> reserved;
|
||||
if (!cursor) {
|
||||
reporter_->HeaderTruncated();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_cpu_type != CPU_TYPE_ANY &&
|
||||
(expected_cpu_type != cpu_type_ ||
|
||||
expected_cpu_subtype != cpu_subtype_)) {
|
||||
reporter_->CPUTypeMismatch(cpu_type_, cpu_subtype_,
|
||||
expected_cpu_type, expected_cpu_subtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
cursor
|
||||
.PointTo(&load_commands_.start, commands_size)
|
||||
.PointTo(&load_commands_.end, 0);
|
||||
if (!cursor) {
|
||||
reporter_->LoadCommandRegionTruncated();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
|
||||
ByteCursor list_cursor(&load_commands_, big_endian_);
|
||||
|
||||
for (size_t index = 0; index < load_command_count_; ++index) {
|
||||
// command refers to this load command alone, so that cursor will
|
||||
// refuse to read past the load command's end. But since we haven't
|
||||
// read the size yet, let command initially refer to the entire
|
||||
// remainder of the load command series.
|
||||
ByteBuffer command(list_cursor.here(), list_cursor.Available());
|
||||
ByteCursor cursor(&command, big_endian_);
|
||||
|
||||
// Read the command type and size --- fields common to all commands.
|
||||
uint32_t type, size;
|
||||
if (!(cursor >> type)) {
|
||||
reporter_->LoadCommandsOverrun(load_command_count_, index, 0);
|
||||
return false;
|
||||
}
|
||||
if (!(cursor >> size) || size > command.Size()) {
|
||||
reporter_->LoadCommandsOverrun(load_command_count_, index, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now that we've read the length, restrict command's range to this
|
||||
// load command only.
|
||||
command.end = command.start + size;
|
||||
|
||||
switch (type) {
|
||||
case LC_SEGMENT:
|
||||
case LC_SEGMENT_64: {
|
||||
Segment segment;
|
||||
segment.bits_64 = (type == LC_SEGMENT_64);
|
||||
size_t word_size = segment.bits_64 ? 8 : 4;
|
||||
cursor.CString(&segment.name, 16);
|
||||
size_t file_offset, file_size;
|
||||
cursor
|
||||
.Read(word_size, false, &segment.vmaddr)
|
||||
.Read(word_size, false, &segment.vmsize)
|
||||
.Read(word_size, false, &file_offset)
|
||||
.Read(word_size, false, &file_size);
|
||||
cursor >> segment.maxprot
|
||||
>> segment.initprot
|
||||
>> segment.nsects
|
||||
>> segment.flags;
|
||||
if (!cursor) {
|
||||
reporter_->LoadCommandTooShort(index, type);
|
||||
return false;
|
||||
}
|
||||
if (file_offset > buffer_.Size() ||
|
||||
file_size > buffer_.Size() - file_offset) {
|
||||
reporter_->MisplacedSegmentData(segment.name);
|
||||
return false;
|
||||
}
|
||||
// Mach-O files in .dSYM bundles have the contents of the loaded
|
||||
// segments removed, and their file offsets and file sizes zeroed
|
||||
// out. To help us handle this special case properly, give such
|
||||
// segments' contents NULL starting and ending pointers.
|
||||
if (file_offset == 0 && file_size == 0) {
|
||||
segment.contents.start = segment.contents.end = NULL;
|
||||
} else {
|
||||
segment.contents.start = buffer_.start + file_offset;
|
||||
segment.contents.end = segment.contents.start + file_size;
|
||||
}
|
||||
// The section list occupies the remainder of this load command's space.
|
||||
segment.section_list.start = cursor.here();
|
||||
segment.section_list.end = command.end;
|
||||
|
||||
if (!handler->SegmentCommand(segment))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case LC_SYMTAB: {
|
||||
uint32_t symoff, nsyms, stroff, strsize;
|
||||
cursor >> symoff >> nsyms >> stroff >> strsize;
|
||||
if (!cursor) {
|
||||
reporter_->LoadCommandTooShort(index, type);
|
||||
return false;
|
||||
}
|
||||
// How big are the entries in the symbol table?
|
||||
// sizeof(struct nlist_64) : sizeof(struct nlist),
|
||||
// but be paranoid about alignment vs. target architecture.
|
||||
size_t symbol_size = bits_64_ ? 16 : 12;
|
||||
// How big is the entire symbol array?
|
||||
size_t symbols_size = nsyms * symbol_size;
|
||||
if (symoff > buffer_.Size() || symbols_size > buffer_.Size() - symoff ||
|
||||
stroff > buffer_.Size() || strsize > buffer_.Size() - stroff) {
|
||||
reporter_->MisplacedSymbolTable();
|
||||
return false;
|
||||
}
|
||||
ByteBuffer entries(buffer_.start + symoff, symbols_size);
|
||||
ByteBuffer names(buffer_.start + stroff, strsize);
|
||||
if (!handler->SymtabCommand(entries, names))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (!handler->UnknownCommand(type, command))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_cursor.set_here(command.end);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// A load command handler that looks for a segment of a given name.
|
||||
class Reader::SegmentFinder : public LoadCommandHandler {
|
||||
public:
|
||||
// Create a load command handler that looks for a segment named NAME,
|
||||
// and sets SEGMENT to describe it if found.
|
||||
SegmentFinder(const string &name, Segment *segment)
|
||||
: name_(name), segment_(segment), found_() { }
|
||||
|
||||
// Return true if the traversal found the segment, false otherwise.
|
||||
bool found() const { return found_; }
|
||||
|
||||
bool SegmentCommand(const Segment &segment) {
|
||||
if (segment.name == name_) {
|
||||
*segment_ = segment;
|
||||
found_ = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// The name of the segment our creator is looking for.
|
||||
const string &name_;
|
||||
|
||||
// Where we should store the segment if found. (WEAK)
|
||||
Segment *segment_;
|
||||
|
||||
// True if we found the segment.
|
||||
bool found_;
|
||||
};
|
||||
|
||||
bool Reader::FindSegment(const string &name, Segment *segment) const {
|
||||
SegmentFinder finder(name, segment);
|
||||
WalkLoadCommands(&finder);
|
||||
return finder.found();
|
||||
}
|
||||
|
||||
bool Reader::WalkSegmentSections(const Segment &segment,
|
||||
SectionHandler *handler) const {
|
||||
size_t word_size = segment.bits_64 ? 8 : 4;
|
||||
ByteCursor cursor(&segment.section_list, big_endian_);
|
||||
|
||||
for (size_t i = 0; i < segment.nsects; i++) {
|
||||
Section section;
|
||||
section.bits_64 = segment.bits_64;
|
||||
uint64_t size;
|
||||
uint32_t offset, dummy32;
|
||||
cursor
|
||||
.CString(§ion.section_name, 16)
|
||||
.CString(§ion.segment_name, 16)
|
||||
.Read(word_size, false, §ion.address)
|
||||
.Read(word_size, false, &size)
|
||||
>> offset
|
||||
>> section.align
|
||||
>> dummy32
|
||||
>> dummy32
|
||||
>> section.flags
|
||||
>> dummy32
|
||||
>> dummy32;
|
||||
if (section.bits_64)
|
||||
cursor >> dummy32;
|
||||
if (!cursor) {
|
||||
reporter_->SectionsMissing(segment.name);
|
||||
return false;
|
||||
}
|
||||
if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
|
||||
// Zero-fill sections have a size, but no contents.
|
||||
section.contents.start = section.contents.end = NULL;
|
||||
} else if (segment.contents.start == NULL &&
|
||||
segment.contents.end == NULL) {
|
||||
// Mach-O files in .dSYM bundles have the contents of the loaded
|
||||
// segments removed, and their file offsets and file sizes zeroed
|
||||
// out. However, the sections within those segments still have
|
||||
// non-zero sizes. There's no reason to call MisplacedSectionData in
|
||||
// this case; the caller may just need the section's load
|
||||
// address. But do set the contents' limits to NULL, for safety.
|
||||
section.contents.start = section.contents.end = NULL;
|
||||
} else {
|
||||
if (offset < size_t(segment.contents.start - buffer_.start) ||
|
||||
offset > size_t(segment.contents.end - buffer_.start) ||
|
||||
size > size_t(segment.contents.end - buffer_.start - offset)) {
|
||||
reporter_->MisplacedSectionData(section.section_name,
|
||||
section.segment_name);
|
||||
return false;
|
||||
}
|
||||
section.contents.start = buffer_.start + offset;
|
||||
section.contents.end = section.contents.start + size;
|
||||
}
|
||||
if (!handler->HandleSection(section))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// A SectionHandler that builds a SectionMap for the sections within a
|
||||
// given segment.
|
||||
class Reader::SectionMapper: public SectionHandler {
|
||||
public:
|
||||
// Create a SectionHandler that populates MAP with an entry for
|
||||
// each section it is given.
|
||||
SectionMapper(SectionMap *map) : map_(map) { }
|
||||
bool HandleSection(const Section §ion) {
|
||||
(*map_)[section.section_name] = section;
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
// The map under construction. (WEAK)
|
||||
SectionMap *map_;
|
||||
};
|
||||
|
||||
bool Reader::MapSegmentSections(const Segment &segment,
|
||||
SectionMap *section_map) const {
|
||||
section_map->clear();
|
||||
SectionMapper mapper(section_map);
|
||||
return WalkSegmentSections(segment, &mapper);
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace google_breakpad
|
459
src/common/mac/macho_reader.h
Normal file
459
src/common/mac/macho_reader.h
Normal file
@ -0,0 +1,459 @@
|
||||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// 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 Inc. 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// macho_reader.h: A class for parsing Mach-O files.
|
||||
|
||||
#ifndef BREAKPAD_COMMON_MAC_MACHO_READER_H_
|
||||
#define BREAKPAD_COMMON_MAC_MACHO_READER_H_
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/byte_cursor.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace mach_o {
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// The Mac headers don't specify particular types for these groups of
|
||||
// constants, but defining them here provides some documentation
|
||||
// value. We also give them the same width as the fields in which
|
||||
// they appear, which makes them a bit easier to use with ByteCursors.
|
||||
typedef uint32_t Magic;
|
||||
typedef uint32_t FileType;
|
||||
typedef uint32_t FileFlags;
|
||||
typedef uint32_t LoadCommandType;
|
||||
typedef uint32_t SegmentFlags;
|
||||
typedef uint32_t SectionFlags;
|
||||
|
||||
// A parser for fat binary files, used to store universal binaries.
|
||||
// When applied to a (non-fat) Mach-O file, this behaves as if the
|
||||
// file were a fat file containing a single object file.
|
||||
class FatReader {
|
||||
public:
|
||||
|
||||
// A class for reporting errors found while parsing fat binary files. The
|
||||
// default definitions of these methods print messages to stderr.
|
||||
class Reporter {
|
||||
public:
|
||||
// Create a reporter that attributes problems to |filename|.
|
||||
explicit Reporter(const string &filename) : filename_(filename) { }
|
||||
|
||||
virtual ~Reporter() { }
|
||||
|
||||
// The data does not begin with a fat binary or Mach-O magic number.
|
||||
// This is a fatal error.
|
||||
virtual void BadHeader();
|
||||
|
||||
// The Mach-O fat binary file ends abruptly, without enough space
|
||||
// to contain an object file it claims is present.
|
||||
virtual void MisplacedObjectFile();
|
||||
|
||||
// The file ends abruptly: either it is not large enough to hold a
|
||||
// complete header, or the header implies that contents are present
|
||||
// beyond the actual end of the file.
|
||||
virtual void TooShort();
|
||||
|
||||
private:
|
||||
// The filename to which the reader should attribute problems.
|
||||
string filename_;
|
||||
};
|
||||
|
||||
// Create a fat binary file reader that uses |reporter| to report problems.
|
||||
explicit FatReader(Reporter *reporter) : reporter_(reporter) { }
|
||||
|
||||
// Read the |size| bytes at |buffer| as a fat binary file. On success,
|
||||
// return true; on failure, report the problem to reporter_ and return
|
||||
// false.
|
||||
//
|
||||
// If the data is a plain Mach-O file, rather than a fat binary file,
|
||||
// then the reader behaves as if it had found a fat binary file whose
|
||||
// single object file is the Mach-O file.
|
||||
bool Read(const uint8_t *buffer, size_t size);
|
||||
|
||||
// Return an array of 'struct fat_arch' structures describing the
|
||||
// object files present in this fat binary file. Set |size| to the
|
||||
// number of elements in the array.
|
||||
//
|
||||
// Assuming Read returned true, the entries are validated: it is
|
||||
// safe to assume that the offsets and sizes in each 'struct
|
||||
// fat_arch' refer to subranges of the bytes passed to Read.
|
||||
//
|
||||
// If there are no object files in this fat binary, then this
|
||||
// function can return NULL.
|
||||
//
|
||||
// The array is owned by this FatReader instance; it will be freed when
|
||||
// this FatReader is destroyed.
|
||||
//
|
||||
// This function returns a C-style array instead of a vector to make it
|
||||
// possible to use the result with OS X functions like NXFindBestFatArch,
|
||||
// so that the symbol dumper will behave consistently with other OS X
|
||||
// utilities that work with fat binaries.
|
||||
const struct fat_arch *object_files(size_t *count) const {
|
||||
*count = object_files_.size();
|
||||
if (object_files_.size() > 0)
|
||||
return &object_files_[0];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
// We use this to report problems parsing the file's contents. (WEAK)
|
||||
Reporter *reporter_;
|
||||
|
||||
// The contents of the fat binary or Mach-O file we're parsing. We do not
|
||||
// own the storage it refers to.
|
||||
ByteBuffer buffer_;
|
||||
|
||||
// The magic number of this binary, in host byte order.
|
||||
Magic magic_;
|
||||
|
||||
// The list of object files in this binary.
|
||||
// object_files_.size() == fat_header.nfat_arch
|
||||
vector<struct fat_arch> object_files_;
|
||||
};
|
||||
|
||||
// A segment in a Mach-O file. All these fields have been byte-swapped as
|
||||
// appropriate for use by the executing architecture.
|
||||
struct Segment {
|
||||
// The ByteBuffers below point into the bytes passed to the Reader that
|
||||
// created this Segment.
|
||||
|
||||
ByteBuffer section_list; // This segment's section list.
|
||||
ByteBuffer contents; // This segment's contents.
|
||||
|
||||
// This segment's name.
|
||||
string name;
|
||||
|
||||
// The address at which this segment should be loaded in memory. If
|
||||
// bits_64 is false, only the bottom 32 bits of this value are valid.
|
||||
uint64_t vmaddr;
|
||||
|
||||
// The size of this segment when loaded into memory. This may be larger
|
||||
// than contents.Size(), in which case the extra area will be
|
||||
// initialized with zeros. If bits_64 is false, only the bottom 32 bits
|
||||
// of this value are valid.
|
||||
uint64_t vmsize;
|
||||
|
||||
// The maximum and initial VM protection of this segment's contents.
|
||||
uint32_t maxprot;
|
||||
uint32_t initprot;
|
||||
|
||||
// The number of sections in section_list.
|
||||
uint32_t nsects;
|
||||
|
||||
// Flags describing this segment, from SegmentFlags.
|
||||
uint32_t flags;
|
||||
|
||||
// True if this is a 64-bit section; false if it is a 32-bit section.
|
||||
bool bits_64;
|
||||
};
|
||||
|
||||
// A section in a Mach-O file. All these fields have been byte-swapped as
|
||||
// appropriate for use by the executing architecture.
|
||||
struct Section {
|
||||
// This section's contents. This points into the bytes passed to the
|
||||
// Reader that created this Section.
|
||||
ByteBuffer contents;
|
||||
|
||||
// This section's name.
|
||||
string section_name; // section[_64].sectname
|
||||
// The name of the segment this section belongs to.
|
||||
string segment_name; // section[_64].segname
|
||||
|
||||
// The address at which this section's contents should be loaded in
|
||||
// memory. If bits_64 is false, only the bottom 32 bits of this value
|
||||
// are valid.
|
||||
uint64_t address;
|
||||
|
||||
// The contents of this section should be loaded into memory at an
|
||||
// address which is a multiple of (two raised to this power).
|
||||
uint32_t align;
|
||||
|
||||
// Flags from SectionFlags describing the section's contents.
|
||||
uint32_t flags;
|
||||
|
||||
// We don't support reading relocations yet.
|
||||
|
||||
// True if this is a 64-bit section; false if it is a 32-bit section.
|
||||
bool bits_64;
|
||||
};
|
||||
|
||||
// A map from section names to Sections.
|
||||
typedef map<string, Section> SectionMap;
|
||||
|
||||
// A reader for a Mach-O file.
|
||||
//
|
||||
// This does not handle fat binaries; see FatReader above. FatReader
|
||||
// provides a friendly interface for parsing data that could be either a
|
||||
// fat binary or a Mach-O file.
|
||||
class Reader {
|
||||
public:
|
||||
|
||||
// A class for reporting errors found while parsing Mach-O files. The
|
||||
// default definitions of these member functions print messages to
|
||||
// stderr.
|
||||
class Reporter {
|
||||
public:
|
||||
// Create a reporter that attributes problems to |filename|.
|
||||
explicit Reporter(const string &filename) : filename_(filename) { }
|
||||
virtual ~Reporter() { }
|
||||
|
||||
// Reporter functions for fatal errors return void; the reader will
|
||||
// definitely return an error to its caller after calling them
|
||||
|
||||
// The data does not begin with a Mach-O magic number, or the magic
|
||||
// number does not match the expected value for the cpu architecture.
|
||||
// This is a fatal error.
|
||||
virtual void BadHeader();
|
||||
|
||||
// The data contained in a Mach-O fat binary (|cpu_type|, |cpu_subtype|)
|
||||
// does not match the expected CPU architecture
|
||||
// (|expected_cpu_type|, |expected_cpu_subtype|).
|
||||
virtual void CPUTypeMismatch(cpu_type_t cpu_type,
|
||||
cpu_subtype_t cpu_subtype,
|
||||
cpu_type_t expected_cpu_type,
|
||||
cpu_subtype_t expected_cpu_subtype);
|
||||
|
||||
// The file ends abruptly: either it is not large enough to hold a
|
||||
// complete header, or the header implies that contents are present
|
||||
// beyond the actual end of the file.
|
||||
virtual void HeaderTruncated();
|
||||
|
||||
// The file's load command region, as given in the Mach-O header, is
|
||||
// too large for the file.
|
||||
virtual void LoadCommandRegionTruncated();
|
||||
|
||||
// The file's Mach-O header claims the file contains |claimed| load
|
||||
// commands, but the I'th load command, of type |type|, extends beyond
|
||||
// the end of the load command region, as given by the Mach-O header.
|
||||
// If |type| is zero, the command's type was unreadable.
|
||||
virtual void LoadCommandsOverrun(size_t claimed, size_t i,
|
||||
LoadCommandType type);
|
||||
|
||||
// The contents of the |i|'th load command, of type |type|, extend beyond
|
||||
// the size given in the load command's header.
|
||||
virtual void LoadCommandTooShort(size_t i, LoadCommandType type);
|
||||
|
||||
// The LC_SEGMENT or LC_SEGMENT_64 load command for the segment named
|
||||
// |name| is too short to hold the sections that its header says it does.
|
||||
// (This more specific than LoadCommandTooShort.)
|
||||
virtual void SectionsMissing(const string &name);
|
||||
|
||||
// The segment named |name| claims that its contents lie beyond the end
|
||||
// of the file.
|
||||
virtual void MisplacedSegmentData(const string &name);
|
||||
|
||||
// The section named |section| in the segment named |segment| claims that
|
||||
// its contents do not lie entirely within the segment.
|
||||
virtual void MisplacedSectionData(const string §ion,
|
||||
const string &segment);
|
||||
|
||||
// The LC_SYMTAB command claims that symbol table contents are located
|
||||
// beyond the end of the file.
|
||||
virtual void MisplacedSymbolTable();
|
||||
|
||||
// An attempt was made to read a Mach-O file of the unsupported
|
||||
// CPU architecture |cpu_type|.
|
||||
virtual void UnsupportedCPUType(cpu_type_t cpu_type);
|
||||
|
||||
private:
|
||||
string filename_;
|
||||
};
|
||||
|
||||
// A handler for sections parsed from a segment. The WalkSegmentSections
|
||||
// member function accepts an instance of this class, and applies it to
|
||||
// each section defined in a given segment.
|
||||
class SectionHandler {
|
||||
public:
|
||||
virtual ~SectionHandler() { }
|
||||
|
||||
// Called to report that the segment's section list contains |section|.
|
||||
// This should return true if the iteration should continue, or false
|
||||
// if it should stop.
|
||||
virtual bool HandleSection(const Section §ion) = 0;
|
||||
};
|
||||
|
||||
// A handler for the load commands in a Mach-O file.
|
||||
class LoadCommandHandler {
|
||||
public:
|
||||
LoadCommandHandler() { }
|
||||
virtual ~LoadCommandHandler() { }
|
||||
|
||||
// When called from WalkLoadCommands, the following handler functions
|
||||
// should return true if they wish to continue iterating over the load
|
||||
// command list, or false if they wish to stop iterating.
|
||||
//
|
||||
// When called from LoadCommandIterator::Handle or Reader::Handle,
|
||||
// these functions' return values are simply passed through to Handle's
|
||||
// caller.
|
||||
//
|
||||
// The definitions provided by this base class simply return true; the
|
||||
// default is to silently ignore sections whose member functions the
|
||||
// subclass doesn't override.
|
||||
|
||||
// COMMAND is load command we don't recognize. We provide only the
|
||||
// command type and a ByteBuffer enclosing the command's data (If we
|
||||
// cannot parse the command type or its size, we call
|
||||
// reporter_->IncompleteLoadCommand instead.)
|
||||
virtual bool UnknownCommand(LoadCommandType type,
|
||||
const ByteBuffer &contents) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The load command is LC_SEGMENT or LC_SEGMENT_64, defining a segment
|
||||
// with the properties given in |segment|.
|
||||
virtual bool SegmentCommand(const Segment &segment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The load command is LC_SYMTAB. |entries| holds the array of nlist
|
||||
// entries, and |names| holds the strings the entries refer to.
|
||||
virtual bool SymtabCommand(const ByteBuffer &entries,
|
||||
const ByteBuffer &names) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add handler functions for more load commands here as needed.
|
||||
};
|
||||
|
||||
// Create a Mach-O file reader that reports problems to |reporter|.
|
||||
explicit Reader(Reporter *reporter)
|
||||
: reporter_(reporter) { }
|
||||
|
||||
// Read the given data as a Mach-O file. The reader retains pointers
|
||||
// into the data passed, so the data should live as long as the reader
|
||||
// does. On success, return true; on failure, return false.
|
||||
//
|
||||
// At most one of these functions should be invoked once on each Reader
|
||||
// instance.
|
||||
bool Read(const uint8_t *buffer,
|
||||
size_t size,
|
||||
cpu_type_t expected_cpu_type,
|
||||
cpu_subtype_t expected_cpu_subtype);
|
||||
bool Read(const ByteBuffer &buffer,
|
||||
cpu_type_t expected_cpu_type,
|
||||
cpu_subtype_t expected_cpu_subtype) {
|
||||
return Read(buffer.start,
|
||||
buffer.Size(),
|
||||
expected_cpu_type,
|
||||
expected_cpu_subtype);
|
||||
}
|
||||
|
||||
// Return this file's characteristics, as found in the Mach-O header.
|
||||
cpu_type_t cpu_type() const { return cpu_type_; }
|
||||
cpu_subtype_t cpu_subtype() const { return cpu_subtype_; }
|
||||
FileType file_type() const { return file_type_; }
|
||||
FileFlags flags() const { return flags_; }
|
||||
|
||||
// Return true if this is a 64-bit Mach-O file, false if it is a 32-bit
|
||||
// Mach-O file.
|
||||
bool bits_64() const { return bits_64_; }
|
||||
|
||||
// Return true if this is a big-endian Mach-O file, false if it is
|
||||
// little-endian.
|
||||
bool big_endian() const { return big_endian_; }
|
||||
|
||||
// Apply |handler| to each load command in this Mach-O file, stopping when
|
||||
// a handler function returns false. If we encounter a malformed load
|
||||
// command, report it via reporter_ and return false. Return true if all
|
||||
// load commands were parseable and all handlers returned true.
|
||||
bool WalkLoadCommands(LoadCommandHandler *handler) const;
|
||||
|
||||
// Set |segment| to describe the segment named |name|, if present. If
|
||||
// found, |segment|'s byte buffers refer to a subregion of the bytes
|
||||
// passed to Read. If we find the section, return true; otherwise,
|
||||
// return false.
|
||||
bool FindSegment(const string &name, Segment *segment) const;
|
||||
|
||||
// Apply |handler| to each section defined in |segment|. If |handler| returns
|
||||
// false, stop iterating and return false. If all calls to |handler| return
|
||||
// true and we reach the end of the section list, return true.
|
||||
bool WalkSegmentSections(const Segment &segment, SectionHandler *handler)
|
||||
const;
|
||||
|
||||
// Clear |section_map| and then populate it with a map of the sections
|
||||
// in |segment|, from section names to Section structures.
|
||||
// Each Section's contents refer to bytes in |segment|'s contents.
|
||||
// On success, return true; if a problem occurs, report it and return false.
|
||||
bool MapSegmentSections(const Segment &segment, SectionMap *section_map)
|
||||
const;
|
||||
|
||||
private:
|
||||
// Used internally.
|
||||
class SegmentFinder;
|
||||
class SectionMapper;
|
||||
|
||||
// We use this to report problems parsing the file's contents. (WEAK)
|
||||
Reporter *reporter_;
|
||||
|
||||
// The contents of the Mach-O file we're parsing. We do not own the
|
||||
// storage it refers to.
|
||||
ByteBuffer buffer_;
|
||||
|
||||
// True if this file is big-endian.
|
||||
bool big_endian_;
|
||||
|
||||
// True if this file is a 64-bit Mach-O file.
|
||||
bool bits_64_;
|
||||
|
||||
// This file's cpu type and subtype.
|
||||
cpu_type_t cpu_type_; // mach_header[_64].cputype
|
||||
cpu_subtype_t cpu_subtype_; // mach_header[_64].cpusubtype
|
||||
|
||||
// This file's type.
|
||||
FileType file_type_; // mach_header[_64].filetype
|
||||
|
||||
// The region of buffer_ occupied by load commands.
|
||||
ByteBuffer load_commands_;
|
||||
|
||||
// The number of load commands in load_commands_.
|
||||
uint32_t load_command_count_; // mach_header[_64].ncmds
|
||||
|
||||
// This file's header flags.
|
||||
FileFlags flags_;
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // BREAKPAD_COMMON_MAC_MACHO_READER_H_
|
1898
src/common/mac/macho_reader_unittest.cc
Normal file
1898
src/common/mac/macho_reader_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -56,10 +56,10 @@ using ::testing::Test;
|
||||
using ::testing::_;
|
||||
using google_breakpad::StabsHandler;
|
||||
using google_breakpad::StabsReader;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
|
@ -32,14 +32,15 @@
|
||||
// test_assembler.cc: Implementation of google_breakpad::TestAssembler.
|
||||
// See test_assembler.h for details.
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "common/test_assembler.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace TestAssembler {
|
||||
namespace test_assembler {
|
||||
|
||||
using std::back_insert_iterator;
|
||||
|
||||
@ -201,7 +202,7 @@ void Label::Binding::Get(Binding **base, u_int64_t *addend) {
|
||||
}
|
||||
|
||||
template<typename Inserter>
|
||||
static inline void InsertEndian(TestAssembler::Endianness endianness,
|
||||
static inline void InsertEndian(test_assembler::Endianness endianness,
|
||||
size_t size, u_int64_t number, Inserter dest) {
|
||||
assert(size > 0);
|
||||
if (endianness == kLittleEndian) {
|
||||
@ -341,7 +342,7 @@ bool Section::GetContents(string *contents) {
|
||||
Reference &r = references_[i];
|
||||
u_int64_t value;
|
||||
if (!r.label.IsKnownConstant(&value)) {
|
||||
fprintf(stderr, "Undefined label #%d at offset 0x%x\n", i, r.offset);
|
||||
fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset);
|
||||
return false;
|
||||
}
|
||||
assert(r.offset < contents_.size());
|
||||
@ -354,5 +355,5 @@ bool Section::GetContents(string *contents) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace TestAssembler
|
||||
} // namespace google_breakpad
|
||||
} // namespace test_assembler
|
||||
} // namespace google_breakpad
|
||||
|
@ -68,7 +68,7 @@ using std::list;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace TestAssembler {
|
||||
namespace test_assembler {
|
||||
|
||||
// A Label represents a value not yet known that we need to store in a
|
||||
// section. As long as all the labels a section refers to are defined
|
||||
@ -475,7 +475,7 @@ class Section {
|
||||
Label start_;
|
||||
};
|
||||
|
||||
} // namespace TestAssembler
|
||||
} // namespace test_assembler
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_TEST_ASSEMBLER_H_
|
||||
|
@ -7,7 +7,15 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
B88FAE0B11665B5700407530 /* test_assembler.cc in Sources */ = {isa = PBXBuildFile; fileRef = B88FAE0911665B5700407530 /* test_assembler.cc */; };
|
||||
B89E0E601166556C00DD08C9 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B89E0E5F1166556C00DD08C9 /* libcrypto.dylib */; };
|
||||
B89E0E781166576C00DD08C9 /* macho_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0E6E1166571D00DD08C9 /* macho_reader.cc */; };
|
||||
B89E0E7A1166576C00DD08C9 /* macho_dump.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0E701166573700DD08C9 /* macho_dump.cc */; };
|
||||
B89E0E9911665A7200DD08C9 /* macho_reader_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0E6D1166571D00DD08C9 /* macho_reader_unittest.cc */; };
|
||||
B89E0E9A11665A7200DD08C9 /* macho_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0E6E1166571D00DD08C9 /* macho_reader.cc */; };
|
||||
B89E0EA111665AC300DD08C9 /* gtest_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0E9F11665AC300DD08C9 /* gtest_main.cc */; };
|
||||
B89E0EA211665AC300DD08C9 /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0EA011665AC300DD08C9 /* gtest-all.cc */; };
|
||||
B89E0EA411665AEA00DD08C9 /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = B89E0EA311665AEA00DD08C9 /* gmock-all.cc */; };
|
||||
B8C5B5161166534700D34F4E /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C7ED420E8AD93000E953AD /* functioninfo.cc */; };
|
||||
B8C5B5171166534700D34F4E /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F95B422F0E0E22D100DBDE83 /* dwarf2reader.cc */; };
|
||||
B8C5B5181166534700D34F4E /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F95B422C0E0E22D100DBDE83 /* bytereader.cc */; };
|
||||
@ -33,7 +41,18 @@
|
||||
9BE650440B52F6D800611104 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; };
|
||||
9BE650450B52F6D800611104 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; };
|
||||
9BE650460B52F6D800611104 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; };
|
||||
B88FAE0911665B5700407530 /* test_assembler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test_assembler.cc; path = ../../../common/test_assembler.cc; sourceTree = SOURCE_ROOT; };
|
||||
B88FAE0A11665B5700407530 /* test_assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = test_assembler.h; path = ../../../common/test_assembler.h; sourceTree = SOURCE_ROOT; };
|
||||
B89E0E5F1166556C00DD08C9 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = /usr/lib/libcrypto.dylib; sourceTree = "<absolute>"; };
|
||||
B89E0E6D1166571D00DD08C9 /* macho_reader_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_reader_unittest.cc; path = ../../../common/mac/macho_reader_unittest.cc; sourceTree = SOURCE_ROOT; };
|
||||
B89E0E6E1166571D00DD08C9 /* macho_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_reader.cc; path = ../../../common/mac/macho_reader.cc; sourceTree = SOURCE_ROOT; };
|
||||
B89E0E6F1166571D00DD08C9 /* macho_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_reader.h; path = ../../../common/mac/macho_reader.h; sourceTree = SOURCE_ROOT; };
|
||||
B89E0E701166573700DD08C9 /* macho_dump.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_dump.cc; sourceTree = "<group>"; };
|
||||
B89E0E741166575200DD08C9 /* macho_dump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = macho_dump; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B89E0E9511665A6400DD08C9 /* macho_reader_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = macho_reader_unittest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B89E0E9F11665AC300DD08C9 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gtest_main.cc; path = ../../../testing/gtest/src/gtest_main.cc; sourceTree = SOURCE_ROOT; };
|
||||
B89E0EA011665AC300DD08C9 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "../../../testing/gtest/src/gtest-all.cc"; sourceTree = SOURCE_ROOT; };
|
||||
B89E0EA311665AEA00DD08C9 /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "../../../testing/src/gmock-all.cc"; sourceTree = SOURCE_ROOT; };
|
||||
B8C5B5111166531A00D34F4E /* dump_syms */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dump_syms; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B8E8CA0C1156C854009E61B2 /* byteswap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = byteswap.h; path = ../../../common/mac/byteswap.h; sourceTree = SOURCE_ROOT; };
|
||||
F95B422B0E0E22D100DBDE83 /* bytereader-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "bytereader-inl.h"; path = "../../../common/dwarf/bytereader-inl.h"; sourceTree = SOURCE_ROOT; };
|
||||
@ -48,6 +67,20 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
B89E0E721166575200DD08C9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B89E0E9311665A6400DD08C9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B8C5B50F1166531A00D34F4E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -63,19 +96,16 @@
|
||||
08FB7794FE84155DC02AAC07 /* dump_syms */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B89E0E9D11665A9500DD08C9 /* TESTING */,
|
||||
F9F5344B0E7C8FFC0012363F /* DWARF */,
|
||||
B89E0E6C1166569700DD08C9 /* MACHO */,
|
||||
B8E8CA0C1156C854009E61B2 /* byteswap.h */,
|
||||
557800890BE1F3AB00EC23E0 /* macho_utilities.cc */,
|
||||
5578008A0BE1F3AB00EC23E0 /* macho_utilities.h */,
|
||||
9BE650410B52F6D800611104 /* file_id.cc */,
|
||||
9BE650420B52F6D800611104 /* file_id.h */,
|
||||
9BE650430B52F6D800611104 /* macho_id.cc */,
|
||||
9BE650440B52F6D800611104 /* macho_id.h */,
|
||||
9BE650450B52F6D800611104 /* macho_walker.cc */,
|
||||
9BE650460B52F6D800611104 /* macho_walker.h */,
|
||||
9BDF186D0B1BB43700F8391B /* dump_syms.h */,
|
||||
08FB7796FE84155DC02AAC07 /* dump_syms.mm */,
|
||||
9BDF186E0B1BB43700F8391B /* dump_syms_tool.mm */,
|
||||
B89E0E701166573700DD08C9 /* macho_dump.cc */,
|
||||
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||
);
|
||||
@ -95,10 +125,40 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B8C5B5111166531A00D34F4E /* dump_syms */,
|
||||
B89E0E741166575200DD08C9 /* macho_dump */,
|
||||
B89E0E9511665A6400DD08C9 /* macho_reader_unittest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B89E0E6C1166569700DD08C9 /* MACHO */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B89E0E6D1166571D00DD08C9 /* macho_reader_unittest.cc */,
|
||||
B89E0E6E1166571D00DD08C9 /* macho_reader.cc */,
|
||||
B89E0E6F1166571D00DD08C9 /* macho_reader.h */,
|
||||
557800890BE1F3AB00EC23E0 /* macho_utilities.cc */,
|
||||
5578008A0BE1F3AB00EC23E0 /* macho_utilities.h */,
|
||||
9BE650430B52F6D800611104 /* macho_id.cc */,
|
||||
9BE650440B52F6D800611104 /* macho_id.h */,
|
||||
9BE650450B52F6D800611104 /* macho_walker.cc */,
|
||||
9BE650460B52F6D800611104 /* macho_walker.h */,
|
||||
);
|
||||
name = MACHO;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B89E0E9D11665A9500DD08C9 /* TESTING */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B88FAE0911665B5700407530 /* test_assembler.cc */,
|
||||
B88FAE0A11665B5700407530 /* test_assembler.h */,
|
||||
B89E0EA311665AEA00DD08C9 /* gmock-all.cc */,
|
||||
B89E0E9F11665AC300DD08C9 /* gtest_main.cc */,
|
||||
B89E0EA011665AC300DD08C9 /* gtest-all.cc */,
|
||||
);
|
||||
name = TESTING;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F9F5344B0E7C8FFC0012363F /* DWARF */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -118,6 +178,38 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
B89E0E731166575200DD08C9 /* macho_dump */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B89E0E7F116657A100DD08C9 /* Build configuration list for PBXNativeTarget "macho_dump" */;
|
||||
buildPhases = (
|
||||
B89E0E711166575200DD08C9 /* Sources */,
|
||||
B89E0E721166575200DD08C9 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = macho_dump;
|
||||
productName = macho_dump;
|
||||
productReference = B89E0E741166575200DD08C9 /* macho_dump */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
B89E0E9411665A6400DD08C9 /* macho_reader_unittest */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B89E0E9E11665A9600DD08C9 /* Build configuration list for PBXNativeTarget "macho_reader_unittest" */;
|
||||
buildPhases = (
|
||||
B89E0E9211665A6400DD08C9 /* Sources */,
|
||||
B89E0E9311665A6400DD08C9 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = macho_reader_unittest;
|
||||
productName = macho_reader_unittest;
|
||||
productReference = B89E0E9511665A6400DD08C9 /* macho_reader_unittest */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
B8C5B5101166531A00D34F4E /* dump_syms */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B8C5B5151166533900D34F4E /* Build configuration list for PBXNativeTarget "dump_syms" */;
|
||||
@ -139,6 +231,9 @@
|
||||
/* Begin PBXProject section */
|
||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = NO;
|
||||
};
|
||||
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "dump_syms" */;
|
||||
compatibilityVersion = "Xcode 2.4";
|
||||
hasScannedForEncodings = 1;
|
||||
@ -147,11 +242,35 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
B8C5B5101166531A00D34F4E /* dump_syms */,
|
||||
B89E0E731166575200DD08C9 /* macho_dump */,
|
||||
B89E0E9411665A6400DD08C9 /* macho_reader_unittest */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
B89E0E711166575200DD08C9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B89E0E781166576C00DD08C9 /* macho_reader.cc in Sources */,
|
||||
B89E0E7A1166576C00DD08C9 /* macho_dump.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B89E0E9211665A6400DD08C9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B89E0E9911665A7200DD08C9 /* macho_reader_unittest.cc in Sources */,
|
||||
B89E0E9A11665A7200DD08C9 /* macho_reader.cc in Sources */,
|
||||
B89E0EA111665AC300DD08C9 /* gtest_main.cc in Sources */,
|
||||
B89E0EA211665AC300DD08C9 /* gtest-all.cc in Sources */,
|
||||
B89E0EA411665AEA00DD08C9 /* gmock-all.cc in Sources */,
|
||||
B88FAE0B11665B5700407530 /* test_assembler.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B8C5B50E1166531A00D34F4E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -192,6 +311,66 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B89E0E761166575300DD08C9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = macho_dump;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B89E0E771166575300DD08C9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = macho_dump;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B89E0E9711665A6400DD08C9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = macho_reader_unittest;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B89E0E9811665A6400DD08C9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = macho_reader_unittest;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B8C5B5131166531B00D34F4E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -235,6 +414,24 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B89E0E7F116657A100DD08C9 /* Build configuration list for PBXNativeTarget "macho_dump" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B89E0E761166575300DD08C9 /* Debug */,
|
||||
B89E0E771166575300DD08C9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B89E0E9E11665A9600DD08C9 /* Build configuration list for PBXNativeTarget "macho_reader_unittest" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B89E0E9711665A6400DD08C9 /* Debug */,
|
||||
B89E0E9811665A6400DD08C9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B8C5B5151166533900D34F4E /* Build configuration list for PBXNativeTarget "dump_syms" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
196
src/tools/mac/dump_syms/macho_dump.cc
Normal file
196
src/tools/mac/dump_syms/macho_dump.cc
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// 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 Inc. 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// macho_dump.cc: Dump the contents of a Mach-O file. This is mostly
|
||||
// a test program for the Mach_O::FatReader and Mach_O::Reader classes.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <mach-o/arch.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/byte_cursor.h"
|
||||
#include "common/mac/macho_reader.h"
|
||||
|
||||
using google_breakpad::ByteBuffer;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
namespace mach_o = google_breakpad::mach_o;
|
||||
|
||||
string program_name;
|
||||
|
||||
int check_syscall(int result, const char *operation, const char *filename) {
|
||||
if (result < 0) {
|
||||
fprintf(stderr, "%s: %s '%s': %s\n",
|
||||
program_name.c_str(), operation,
|
||||
filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class DumpSection: public mach_o::Reader::SectionHandler {
|
||||
public:
|
||||
DumpSection() : index_(0) { }
|
||||
bool HandleSection(const mach_o::Section §ion) {
|
||||
printf(" section %d '%s' in segment '%s'\n"
|
||||
" address: 0x%llx\n"
|
||||
" alignment: 1 << %d B\n"
|
||||
" flags: %d\n"
|
||||
" size: %ld\n",
|
||||
index_++, section.section_name.c_str(), section.segment_name.c_str(),
|
||||
section.address, section.align,
|
||||
mach_o::SectionFlags(section.flags),
|
||||
section.contents.Size());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
int index_;
|
||||
};
|
||||
|
||||
class DumpCommand: public mach_o::Reader::LoadCommandHandler {
|
||||
public:
|
||||
DumpCommand(mach_o::Reader *reader) : reader_(reader), index_(0) { }
|
||||
bool UnknownCommand(mach_o::LoadCommandType type,
|
||||
const ByteBuffer &contents) {
|
||||
printf(" load command %d: %d", index_++, type);
|
||||
return true;
|
||||
}
|
||||
bool SegmentCommand(const mach_o::Segment &segment) {
|
||||
printf(" load command %d: %s-bit segment '%s'\n"
|
||||
" address: 0x%llx\n"
|
||||
" memory size: 0x%llx\n"
|
||||
" maximum protection: 0x%x\n"
|
||||
" initial protection: 0x%x\n"
|
||||
" flags: %d\n"
|
||||
" section_list size: %ld B\n",
|
||||
index_++, (segment.bits_64 ? "64" : "32"), segment.name.c_str(),
|
||||
segment.vmaddr, segment.vmsize, segment.maxprot,
|
||||
segment.initprot, mach_o::SegmentFlags(segment.flags),
|
||||
segment.section_list.Size());
|
||||
|
||||
DumpSection dump_section;
|
||||
return reader_->WalkSegmentSections(segment, &dump_section);
|
||||
}
|
||||
private:
|
||||
mach_o::Reader *reader_;
|
||||
int index_;
|
||||
};
|
||||
|
||||
void DumpFile(const char *filename) {
|
||||
int fd = check_syscall(open(filename, O_RDONLY), "opening", filename);
|
||||
struct stat attributes;
|
||||
check_syscall(fstat(fd, &attributes),
|
||||
"getting file attributes for", filename);
|
||||
void *mapping = mmap(NULL, attributes.st_size, PROT_READ,
|
||||
MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
check_syscall(mapping == (void *)-1 ? -1 : 0,
|
||||
"mapping contents of", filename);
|
||||
|
||||
mach_o::FatReader::Reporter fat_reporter(filename);
|
||||
mach_o::FatReader fat_reader(&fat_reporter);
|
||||
if (!fat_reader.Read(reinterpret_cast<uint8_t *>(mapping),
|
||||
attributes.st_size)) {
|
||||
exit(1);
|
||||
}
|
||||
printf("filename: %s\n", filename);
|
||||
size_t object_files_size;
|
||||
struct fat_arch *object_files = fat_reader.object_files(&object_files_size);
|
||||
printf(" object file count: %ld\n", object_files_size);
|
||||
for (size_t i = 0; i < object_files_size; i++) {
|
||||
const struct fat_arch &file = object_files[i];
|
||||
const NXArchInfo *fat_arch_info
|
||||
= NXGetArchInfoFromCpuType(file.cputype, file.cpusubtype);
|
||||
printf("\n object file %ld:\n"
|
||||
" fat header:\n:"
|
||||
" CPU type: %s (%s)\n"
|
||||
" size: %d B\n"
|
||||
" alignment: 1<<%d B\n",
|
||||
i, fat_arch_info->name, fat_arch_info->description,
|
||||
file.size, file.align);
|
||||
|
||||
ostringstream name;
|
||||
name << filename;
|
||||
if (object_files_size > 1)
|
||||
name << ", object file #" << i;
|
||||
ByteBuffer file_contents(reinterpret_cast<uint8_t *>(mapping)
|
||||
+ file.offset, file.size);
|
||||
mach_o::Reader::Reporter reporter(name.str());
|
||||
mach_o::Reader reader(&reporter);
|
||||
if (!reader.Read(file_contents, file.cputype, file.cpusubtype)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const NXArchInfo *macho_arch_info =
|
||||
NXGetArchInfoFromCpuType(reader.cpu_type(),
|
||||
reader.cpu_subtype());
|
||||
printf(" Mach-O header:\n"
|
||||
" word size: %s\n"
|
||||
" CPU type: %s (%s)\n"
|
||||
" File type: %d\n"
|
||||
" flags: %x\n",
|
||||
(reader.bits_64() ? "64 bits" : "32 bits"),
|
||||
macho_arch_info->name, macho_arch_info->description,
|
||||
reader.file_type(), reader.flags());
|
||||
|
||||
DumpCommand dump_command(&reader);
|
||||
reader.WalkLoadCommands(&dump_command);
|
||||
}
|
||||
munmap(mapping, attributes.st_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
program_name = basename(argv[0]);
|
||||
if (argc == 1) {
|
||||
fprintf(stderr, "Usage: %s FILE ...\n"
|
||||
"Dump the contents of the Mach-O or fat binary files "
|
||||
"'FILE ...'.\n", program_name.c_str());
|
||||
}
|
||||
for (int i = 1; i < argc; i++) {
|
||||
DumpFile(argv[i]);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user