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:
ted.mielczarek 2010-06-25 16:56:16 +00:00
parent 8ffb12eb35
commit 35c41e00ee
11 changed files with 3306 additions and 31 deletions

View File

@ -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;

View File

@ -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 {

View File

@ -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)

View 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 &section,
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(&section.section_name, 16)
.CString(&section.segment_name, 16)
.Read(word_size, false, &section.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 &section) {
(*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

View 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 &section,
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 &section) = 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_

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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

View File

@ -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_

View File

@ -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 = (

View 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 &section) {
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]);
}
}