mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-03-08 09:59:55 +00:00
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_BIG;
|
||||||
using dwarf2reader::ENDIANNESS_LITTLE;
|
using dwarf2reader::ENDIANNESS_LITTLE;
|
||||||
using google_breakpad::CFISection;
|
using google_breakpad::CFISection;
|
||||||
using google_breakpad::TestAssembler::Label;
|
using google_breakpad::test_assembler::Label;
|
||||||
using google_breakpad::TestAssembler::kBigEndian;
|
using google_breakpad::test_assembler::kBigEndian;
|
||||||
using google_breakpad::TestAssembler::kLittleEndian;
|
using google_breakpad::test_assembler::kLittleEndian;
|
||||||
using google_breakpad::TestAssembler::Section;
|
using google_breakpad::test_assembler::Section;
|
||||||
using std::string;
|
using std::string;
|
||||||
using testing::Test;
|
using testing::Test;
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,9 @@
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using dwarf2reader::DwarfPointerEncoding;
|
using dwarf2reader::DwarfPointerEncoding;
|
||||||
using google_breakpad::TestAssembler::Endianness;
|
using google_breakpad::test_assembler::Endianness;
|
||||||
using google_breakpad::TestAssembler::Label;
|
using google_breakpad::test_assembler::Label;
|
||||||
using google_breakpad::TestAssembler::Section;
|
using google_breakpad::test_assembler::Section;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
class CFISection: public Section {
|
class CFISection: public Section {
|
||||||
|
|
|
@ -63,10 +63,10 @@ extern "C" {
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
|
|
||||||
using google_breakpad::CFISection;
|
using google_breakpad::CFISection;
|
||||||
using google_breakpad::TestAssembler::Label;
|
using google_breakpad::test_assembler::Label;
|
||||||
using google_breakpad::TestAssembler::kBigEndian;
|
using google_breakpad::test_assembler::kBigEndian;
|
||||||
using google_breakpad::TestAssembler::kLittleEndian;
|
using google_breakpad::test_assembler::kLittleEndian;
|
||||||
using google_breakpad::TestAssembler::Section;
|
using google_breakpad::test_assembler::Section;
|
||||||
|
|
||||||
using dwarf2reader::DwarfPointerEncoding;
|
using dwarf2reader::DwarfPointerEncoding;
|
||||||
using dwarf2reader::ENDIANNESS_BIG;
|
using dwarf2reader::ENDIANNESS_BIG;
|
||||||
|
@ -2315,7 +2315,7 @@ TEST_F(CFIReporter, ClearingCFARule) {
|
||||||
#ifdef WRITE_ELF
|
#ifdef WRITE_ELF
|
||||||
// See comments at the top of the file mentioning WRITE_ELF for details.
|
// 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 {
|
struct ELFSectionHeader {
|
||||||
ELFSectionHeader(unsigned int set_type)
|
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 ::testing::_;
|
||||||
using google_breakpad::StabsHandler;
|
using google_breakpad::StabsHandler;
|
||||||
using google_breakpad::StabsReader;
|
using google_breakpad::StabsReader;
|
||||||
using google_breakpad::TestAssembler::Label;
|
using google_breakpad::test_assembler::Label;
|
||||||
using google_breakpad::TestAssembler::Section;
|
using google_breakpad::test_assembler::Section;
|
||||||
using google_breakpad::TestAssembler::kBigEndian;
|
using google_breakpad::test_assembler::kBigEndian;
|
||||||
using google_breakpad::TestAssembler::kLittleEndian;
|
using google_breakpad::test_assembler::kLittleEndian;
|
||||||
using std::map;
|
using std::map;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,15 @@
|
||||||
// test_assembler.cc: Implementation of google_breakpad::TestAssembler.
|
// test_assembler.cc: Implementation of google_breakpad::TestAssembler.
|
||||||
// See test_assembler.h for details.
|
// See test_assembler.h for details.
|
||||||
|
|
||||||
#include <cassert>
|
#include <assert.h>
|
||||||
#include <cstdio>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
#include "common/test_assembler.h"
|
#include "common/test_assembler.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
namespace TestAssembler {
|
namespace test_assembler {
|
||||||
|
|
||||||
using std::back_insert_iterator;
|
using std::back_insert_iterator;
|
||||||
|
|
||||||
|
@ -201,7 +202,7 @@ void Label::Binding::Get(Binding **base, u_int64_t *addend) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Inserter>
|
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) {
|
size_t size, u_int64_t number, Inserter dest) {
|
||||||
assert(size > 0);
|
assert(size > 0);
|
||||||
if (endianness == kLittleEndian) {
|
if (endianness == kLittleEndian) {
|
||||||
|
@ -341,7 +342,7 @@ bool Section::GetContents(string *contents) {
|
||||||
Reference &r = references_[i];
|
Reference &r = references_[i];
|
||||||
u_int64_t value;
|
u_int64_t value;
|
||||||
if (!r.label.IsKnownConstant(&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;
|
return false;
|
||||||
}
|
}
|
||||||
assert(r.offset < contents_.size());
|
assert(r.offset < contents_.size());
|
||||||
|
@ -354,5 +355,5 @@ bool Section::GetContents(string *contents) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace TestAssembler
|
} // namespace test_assembler
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -68,7 +68,7 @@ using std::list;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TestAssembler {
|
namespace test_assembler {
|
||||||
|
|
||||||
// A Label represents a value not yet known that we need to store in a
|
// 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
|
// section. As long as all the labels a section refers to are defined
|
||||||
|
@ -475,7 +475,7 @@ class Section {
|
||||||
Label start_;
|
Label start_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TestAssembler
|
} // namespace test_assembler
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
#endif // PROCESSOR_TEST_ASSEMBLER_H_
|
#endif // PROCESSOR_TEST_ASSEMBLER_H_
|
||||||
|
|
|
@ -7,7 +7,15 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
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 */; };
|
B8C5B5161166534700D34F4E /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C7ED420E8AD93000E953AD /* functioninfo.cc */; };
|
||||||
B8C5B5171166534700D34F4E /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F95B422F0E0E22D100DBDE83 /* dwarf2reader.cc */; };
|
B8C5B5171166534700D34F4E /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F95B422F0E0E22D100DBDE83 /* dwarf2reader.cc */; };
|
||||||
B8C5B5181166534700D34F4E /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F95B422C0E0E22D100DBDE83 /* bytereader.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; };
|
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; };
|
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; };
|
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>"; };
|
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; };
|
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; };
|
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; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
B89E0E721166575200DD08C9 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
B89E0E9311665A6400DD08C9 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
B8C5B50F1166531A00D34F4E /* Frameworks */ = {
|
B8C5B50F1166531A00D34F4E /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -63,19 +96,16 @@
|
||||||
08FB7794FE84155DC02AAC07 /* dump_syms */ = {
|
08FB7794FE84155DC02AAC07 /* dump_syms */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
B89E0E9D11665A9500DD08C9 /* TESTING */,
|
||||||
F9F5344B0E7C8FFC0012363F /* DWARF */,
|
F9F5344B0E7C8FFC0012363F /* DWARF */,
|
||||||
|
B89E0E6C1166569700DD08C9 /* MACHO */,
|
||||||
B8E8CA0C1156C854009E61B2 /* byteswap.h */,
|
B8E8CA0C1156C854009E61B2 /* byteswap.h */,
|
||||||
557800890BE1F3AB00EC23E0 /* macho_utilities.cc */,
|
|
||||||
5578008A0BE1F3AB00EC23E0 /* macho_utilities.h */,
|
|
||||||
9BE650410B52F6D800611104 /* file_id.cc */,
|
9BE650410B52F6D800611104 /* file_id.cc */,
|
||||||
9BE650420B52F6D800611104 /* file_id.h */,
|
9BE650420B52F6D800611104 /* file_id.h */,
|
||||||
9BE650430B52F6D800611104 /* macho_id.cc */,
|
|
||||||
9BE650440B52F6D800611104 /* macho_id.h */,
|
|
||||||
9BE650450B52F6D800611104 /* macho_walker.cc */,
|
|
||||||
9BE650460B52F6D800611104 /* macho_walker.h */,
|
|
||||||
9BDF186D0B1BB43700F8391B /* dump_syms.h */,
|
9BDF186D0B1BB43700F8391B /* dump_syms.h */,
|
||||||
08FB7796FE84155DC02AAC07 /* dump_syms.mm */,
|
08FB7796FE84155DC02AAC07 /* dump_syms.mm */,
|
||||||
9BDF186E0B1BB43700F8391B /* dump_syms_tool.mm */,
|
9BDF186E0B1BB43700F8391B /* dump_syms_tool.mm */,
|
||||||
|
B89E0E701166573700DD08C9 /* macho_dump.cc */,
|
||||||
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
|
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
|
||||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||||
);
|
);
|
||||||
|
@ -95,10 +125,40 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B8C5B5111166531A00D34F4E /* dump_syms */,
|
B8C5B5111166531A00D34F4E /* dump_syms */,
|
||||||
|
B89E0E741166575200DD08C9 /* macho_dump */,
|
||||||
|
B89E0E9511665A6400DD08C9 /* macho_reader_unittest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
F9F5344B0E7C8FFC0012363F /* DWARF */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -118,6 +178,38 @@
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget 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 */ = {
|
B8C5B5101166531A00D34F4E /* dump_syms */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = B8C5B5151166533900D34F4E /* Build configuration list for PBXNativeTarget "dump_syms" */;
|
buildConfigurationList = B8C5B5151166533900D34F4E /* Build configuration list for PBXNativeTarget "dump_syms" */;
|
||||||
|
@ -139,6 +231,9 @@
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = NO;
|
||||||
|
};
|
||||||
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "dump_syms" */;
|
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "dump_syms" */;
|
||||||
compatibilityVersion = "Xcode 2.4";
|
compatibilityVersion = "Xcode 2.4";
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
|
@ -147,11 +242,35 @@
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
B8C5B5101166531A00D34F4E /* dump_syms */,
|
B8C5B5101166531A00D34F4E /* dump_syms */,
|
||||||
|
B89E0E731166575200DD08C9 /* macho_dump */,
|
||||||
|
B89E0E9411665A6400DD08C9 /* macho_reader_unittest */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase 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 */ = {
|
B8C5B50E1166531A00D34F4E /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -192,6 +311,66 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
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 */ = {
|
B8C5B5131166531B00D34F4E /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -235,6 +414,24 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
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" */ = {
|
B8C5B5151166533900D34F4E /* Build configuration list for PBXNativeTarget "dump_syms" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
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…
Reference in a new issue