summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc539
1 files changed, 0 insertions, 539 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc
deleted file mode 100644
index 52f3c411b..000000000
--- a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc
+++ /dev/null
@@ -1,539 +0,0 @@
-// 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>
-
-// Unfortunately, CPU_TYPE_ARM is not define for 10.4.
-#if !defined(CPU_TYPE_ARM)
-#define CPU_TYPE_ARM 12
-#endif
-
-#if !defined(CPU_TYPE_ARM_64)
-#define CPU_TYPE_ARM_64 16777228
-#endif
-
-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;
-
- // Read this object file entry, byte-swapping as appropriate.
- cursor >> objfile.cputype
- >> objfile.cpusubtype
- >> objfile.offset
- >> objfile.size
- >> objfile.align;
-
- SuperFatArch super_fat_arch(objfile);
- object_files_[i] = super_fat_arch;
-
- 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 = static_cast<uint64_t>(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 %zu"
- " load commands, but load command #%zu",
- 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 #%zu, 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_ARM:
- case CPU_TYPE_I386:
- expected_magic = MH_CIGAM;
- break;
- case CPU_TYPE_POWERPC:
- expected_magic = MH_MAGIC;
- break;
- case CPU_TYPE_ARM_64:
- 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;
- }
- const uint32_t section_type = section.flags & SECTION_TYPE;
- if (section_type == S_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL ||
- section_type == S_GB_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