Support object files larger than 2**32.

Reviewed at https://breakpad.appspot.com/7834002/#ps340001


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1453 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
erikchen@chromium.org 2015-04-22 20:14:24 +00:00
parent 0f27af628f
commit aa75fa5d4e
7 changed files with 189 additions and 40 deletions

View file

@ -141,6 +141,7 @@
'mac/scoped_task_suspend-inl.h', 'mac/scoped_task_suspend-inl.h',
'mac/string_utilities.cc', 'mac/string_utilities.cc',
'mac/string_utilities.h', 'mac/string_utilities.h',
'mac/super_fat_arch.h',
'md5.cc', 'md5.cc',
'md5.h', 'md5.h',
'memory.h', 'memory.h',

View file

@ -46,6 +46,7 @@
#include "common/byte_cursor.h" #include "common/byte_cursor.h"
#include "common/mac/macho_reader.h" #include "common/mac/macho_reader.h"
#include "common/mac/super_fat_arch.h"
#include "common/module.h" #include "common/module.h"
#include "common/symbol_data.h" #include "common/symbol_data.h"
@ -59,6 +60,7 @@ class DumpSymbols {
input_pathname_(), input_pathname_(),
object_filename_(), object_filename_(),
contents_(), contents_(),
object_files_(),
selected_object_file_(), selected_object_file_(),
selected_object_name_() { } selected_object_name_() { }
~DumpSymbols() { ~DumpSymbols() {
@ -98,14 +100,14 @@ class DumpSymbols {
// architecture matches that of this dumper program. // architecture matches that of this dumper program.
bool SetArchitecture(const std::string &arch_name); bool SetArchitecture(const std::string &arch_name);
// Return a pointer to an array of 'struct fat_arch' structures, // Return a pointer to an array of SuperFatArch structures describing the
// describing the object files contained in this dumper's file. Set // object files contained in this dumper's file. Set *|count| to the number
// *|count| to the number of elements in the array. The returned array is // of elements in the array. The returned array is owned by this DumpSymbols
// owned by this DumpSymbols instance. // instance.
// //
// If there are no available architectures, this function // If there are no available architectures, this function
// may return NULL. // may return NULL.
const struct fat_arch *AvailableArchitectures(size_t *count) { const SuperFatArch* AvailableArchitectures(size_t *count) {
*count = object_files_.size(); *count = object_files_.size();
if (object_files_.size() > 0) if (object_files_.size() > 0)
return &object_files_[0]; return &object_files_[0];
@ -127,6 +129,11 @@ class DumpSymbols {
class DumperLineToModule; class DumperLineToModule;
class LoadCommandDumper; class LoadCommandDumper;
// This method behaves similarly to NXFindBestFatArch, but it supports
// SuperFatArch.
SuperFatArch* FindBestMatchForArchitecture(
cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
// Return an identifier string for the file this DumpSymbols is dumping. // Return an identifier string for the file this DumpSymbols is dumping.
std::string Identifier(); std::string Identifier();
@ -167,15 +174,15 @@ class DumpSymbols {
// The complete contents of object_filename_, mapped into memory. // The complete contents of object_filename_, mapped into memory.
NSData *contents_; NSData *contents_;
// A vector of fat_arch structures describing the object files // A vector of SuperFatArch structures describing the object files
// object_filename_ contains. If object_filename_ refers to a fat binary, // object_filename_ contains. If object_filename_ refers to a fat binary,
// this may have more than one element; if it refers to a Mach-O file, this // this may have more than one element; if it refers to a Mach-O file, this
// has exactly one element. // has exactly one element.
vector<struct fat_arch> object_files_; vector<SuperFatArch> object_files_;
// The object file in object_files_ selected to dump, or NULL if // The object file in object_files_ selected to dump, or NULL if
// SetArchitecture hasn't been called yet. // SetArchitecture hasn't been called yet.
const struct fat_arch *selected_object_file_; const SuperFatArch *selected_object_file_;
// A string that identifies the selected object file, for use in error // A string that identifies the selected object file, for use in error
// messages. This is usually object_filename_, but if that refers to a // messages. This is usually object_filename_, but if that refers to a

View file

@ -35,6 +35,7 @@
#include "common/mac/dump_syms.h" #include "common/mac/dump_syms.h"
#include <assert.h>
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
#include <mach-o/arch.h> #include <mach-o/arch.h>
#include <mach-o/fat.h> #include <mach-o/fat.h>
@ -170,7 +171,7 @@ bool DumpSymbols::Read(NSString *filename) {
// Get our own copy of fat_reader's object file list. // Get our own copy of fat_reader's object file list.
size_t object_files_count; size_t object_files_count;
const struct fat_arch *object_files = const SuperFatArch *object_files =
fat_reader.object_files(&object_files_count); fat_reader.object_files(&object_files_count);
if (object_files_count == 0) { if (object_files_count == 0) {
fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", fprintf(stderr, "Fat binary file contains *no* architectures: %s\n",
@ -179,7 +180,7 @@ bool DumpSymbols::Read(NSString *filename) {
} }
object_files_.resize(object_files_count); object_files_.resize(object_files_count);
memcpy(&object_files_[0], object_files, memcpy(&object_files_[0], object_files,
sizeof(struct fat_arch) * object_files_count); sizeof(SuperFatArch) * object_files_count);
return true; return true;
} }
@ -187,9 +188,8 @@ bool DumpSymbols::Read(NSString *filename) {
bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype) { cpu_subtype_t cpu_subtype) {
// Find the best match for the architecture the user requested. // Find the best match for the architecture the user requested.
const struct fat_arch *best_match const SuperFatArch *best_match = FindBestMatchForArchitecture(
= NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0], cpu_type, cpu_subtype);
static_cast<uint32_t>(object_files_.size()));
if (!best_match) return false; if (!best_match) return false;
// Record the selected object file. // Record the selected object file.
@ -207,6 +207,56 @@ bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
return arch_set; return arch_set;
} }
SuperFatArch* DumpSymbols::FindBestMatchForArchitecture(
cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
// Check if all the object files can be converted to struct fat_arch.
bool can_convert_to_fat_arch = true;
vector<struct fat_arch> fat_arch_vector;
for (vector<SuperFatArch>::const_iterator it = object_files_.begin();
it != object_files_.end();
++it) {
struct fat_arch arch;
bool success = it->ConvertToFatArch(&arch);
if (!success) {
can_convert_to_fat_arch = false;
break;
}
fat_arch_vector.push_back(arch);
}
// If all the object files can be converted to struct fat_arch, use
// NXFindBestFatArch.
if (can_convert_to_fat_arch) {
const struct fat_arch *best_match
= NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0],
static_cast<uint32_t>(fat_arch_vector.size()));
for (size_t i = 0; i < fat_arch_vector.size(); ++i) {
if (best_match == &fat_arch_vector[i])
return &object_files_[i];
}
assert(best_match == NULL);
return NULL;
}
// Check for an exact match with cpu_type and cpu_subtype.
for (vector<SuperFatArch>::iterator it = object_files_.begin();
it != object_files_.end();
++it) {
if (it->cputype == cpu_type && it->cpusubtype == cpu_subtype)
return &*it;
}
// No exact match found.
// TODO(erikchen): If it becomes necessary, we can copy the implementation of
// NXFindBestFatArch, located at
// http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c.
fprintf(stderr, "Failed to find an exact match for an object file with cpu "
"type: %d and cpu subtype: %d. Furthermore, at least one object file is "
"larger than 2**32.\n", cpu_type, cpu_subtype);
return NULL;
}
string DumpSymbols::Identifier() { string DumpSymbols::Identifier() {
FileID file_id([object_filename_ fileSystemRepresentation]); FileID file_id([object_filename_ fileSystemRepresentation]);
unsigned char identifier_bytes[16]; unsigned char identifier_bytes[16];

View file

@ -101,22 +101,26 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
// Read the list of object files. // Read the list of object files.
object_files_.resize(object_files_count); object_files_.resize(object_files_count);
for (size_t i = 0; i < object_files_count; i++) { for (size_t i = 0; i < object_files_count; i++) {
struct fat_arch *objfile = &object_files_[i]; struct fat_arch objfile;
// Read this object file entry, byte-swapping as appropriate. // Read this object file entry, byte-swapping as appropriate.
cursor >> objfile->cputype cursor >> objfile.cputype
>> objfile->cpusubtype >> objfile.cpusubtype
>> objfile->offset >> objfile.offset
>> objfile->size >> objfile.size
>> objfile->align; >> objfile.align;
SuperFatArch super_fat_arch(objfile);
object_files_[i] = super_fat_arch;
if (!cursor) { if (!cursor) {
reporter_->TooShort(); reporter_->TooShort();
return false; return false;
} }
// Does the file actually have the bytes this entry refers to? // Does the file actually have the bytes this entry refers to?
size_t fat_size = buffer_.Size(); size_t fat_size = buffer_.Size();
if (objfile->offset > fat_size || if (objfile.offset > fat_size ||
objfile->size > fat_size - objfile->offset) { objfile.size > fat_size - objfile.offset) {
reporter_->MisplacedObjectFile(); reporter_->MisplacedObjectFile();
return false; return false;
} }
@ -139,16 +143,14 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
} }
object_files_[0].offset = 0; object_files_[0].offset = 0;
object_files_[0].size = static_cast<uint32_t>(buffer_.Size()); object_files_[0].size = static_cast<uint64_t>(buffer_.Size());
// This alignment is correct for 32 and 64-bit x86 and ppc. // This alignment is correct for 32 and 64-bit x86 and ppc.
// See get_align in the lipo source for other architectures: // See get_align in the lipo source for other architectures:
// http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c // http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c
object_files_[0].align = 12; // 2^12 == 4096 object_files_[0].align = 12; // 2^12 == 4096
return true; return true;
} }
} }
reporter_->BadHeader(); reporter_->BadHeader();
return false; return false;
} }
@ -315,7 +317,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
// remainder of the load command series. // remainder of the load command series.
ByteBuffer command(list_cursor.here(), list_cursor.Available()); ByteBuffer command(list_cursor.here(), list_cursor.Available());
ByteCursor cursor(&command, big_endian_); ByteCursor cursor(&command, big_endian_);
// Read the command type and size --- fields common to all commands. // Read the command type and size --- fields common to all commands.
uint32_t type, size; uint32_t type, size;
if (!(cursor >> type)) { if (!(cursor >> type)) {
@ -400,7 +402,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
return false; return false;
break; break;
} }
default: { default: {
if (!handler->UnknownCommand(type, command)) if (!handler->UnknownCommand(type, command))
return false; return false;
@ -419,7 +421,7 @@ class Reader::SegmentFinder : public LoadCommandHandler {
public: public:
// Create a load command handler that looks for a segment named NAME, // Create a load command handler that looks for a segment named NAME,
// and sets SEGMENT to describe it if found. // and sets SEGMENT to describe it if found.
SegmentFinder(const string &name, Segment *segment) SegmentFinder(const string &name, Segment *segment)
: name_(name), segment_(segment), found_() { } : name_(name), segment_(segment), found_() { }
// Return true if the traversal found the segment, false otherwise. // Return true if the traversal found the segment, false otherwise.
@ -482,7 +484,7 @@ bool Reader::WalkSegmentSections(const Segment &segment,
if ((section.flags & SECTION_TYPE) == S_ZEROFILL) { if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
// Zero-fill sections have a size, but no contents. // Zero-fill sections have a size, but no contents.
section.contents.start = section.contents.end = NULL; section.contents.start = section.contents.end = NULL;
} else if (segment.contents.start == NULL && } else if (segment.contents.start == NULL &&
segment.contents.end == NULL) { segment.contents.end == NULL) {
// Mach-O files in .dSYM bundles have the contents of the loaded // Mach-O files in .dSYM bundles have the contents of the loaded
// segments removed, and their file offsets and file sizes zeroed // segments removed, and their file offsets and file sizes zeroed

View file

@ -47,6 +47,7 @@
#include <vector> #include <vector>
#include "common/byte_cursor.h" #include "common/byte_cursor.h"
#include "common/mac/super_fat_arch.h"
namespace google_breakpad { namespace google_breakpad {
namespace mach_o { namespace mach_o {
@ -93,7 +94,7 @@ class FatReader {
// complete header, or the header implies that contents are present // complete header, or the header implies that contents are present
// beyond the actual end of the file. // beyond the actual end of the file.
virtual void TooShort(); virtual void TooShort();
private: private:
// The filename to which the reader should attribute problems. // The filename to which the reader should attribute problems.
string filename_; string filename_;
@ -101,7 +102,7 @@ class FatReader {
// Create a fat binary file reader that uses |reporter| to report problems. // Create a fat binary file reader that uses |reporter| to report problems.
explicit FatReader(Reporter *reporter) : reporter_(reporter) { } explicit FatReader(Reporter *reporter) : reporter_(reporter) { }
// Read the |size| bytes at |buffer| as a fat binary file. On success, // Read the |size| bytes at |buffer| as a fat binary file. On success,
// return true; on failure, report the problem to reporter_ and return // return true; on failure, report the problem to reporter_ and return
// false. // false.
@ -111,13 +112,13 @@ class FatReader {
// single object file is the Mach-O file. // single object file is the Mach-O file.
bool Read(const uint8_t *buffer, size_t size); bool Read(const uint8_t *buffer, size_t size);
// Return an array of 'struct fat_arch' structures describing the // Return an array of 'SuperFatArch' structures describing the
// object files present in this fat binary file. Set |size| to the // object files present in this fat binary file. Set |size| to the
// number of elements in the array. // number of elements in the array.
// //
// Assuming Read returned true, the entries are validated: it is // Assuming Read returned true, the entries are validated: it is safe to
// safe to assume that the offsets and sizes in each 'struct // assume that the offsets and sizes in each SuperFatArch refer to subranges
// fat_arch' refer to subranges of the bytes passed to Read. // of the bytes passed to Read.
// //
// If there are no object files in this fat binary, then this // If there are no object files in this fat binary, then this
// function can return NULL. // function can return NULL.
@ -129,7 +130,7 @@ class FatReader {
// possible to use the result with OS X functions like NXFindBestFatArch, // possible to use the result with OS X functions like NXFindBestFatArch,
// so that the symbol dumper will behave consistently with other OS X // so that the symbol dumper will behave consistently with other OS X
// utilities that work with fat binaries. // utilities that work with fat binaries.
const struct fat_arch *object_files(size_t *count) const { const SuperFatArch* object_files(size_t *count) const {
*count = object_files_.size(); *count = object_files_.size();
if (object_files_.size() > 0) if (object_files_.size() > 0)
return &object_files_[0]; return &object_files_[0];
@ -149,7 +150,7 @@ class FatReader {
// The list of object files in this binary. // The list of object files in this binary.
// object_files_.size() == fat_header.nfat_arch // object_files_.size() == fat_header.nfat_arch
vector<struct fat_arch> object_files_; vector<SuperFatArch> object_files_;
}; };
// A segment in a Mach-O file. All these fields have been byte-swapped as // A segment in a Mach-O file. All these fields have been byte-swapped as
@ -177,7 +178,7 @@ struct Segment {
// The maximum and initial VM protection of this segment's contents. // The maximum and initial VM protection of this segment's contents.
uint32_t maxprot; uint32_t maxprot;
uint32_t initprot; uint32_t initprot;
// The number of sections in section_list. // The number of sections in section_list.
uint32_t nsects; uint32_t nsects;
@ -376,7 +377,7 @@ class Reader {
return Read(buffer.start, return Read(buffer.start,
buffer.Size(), buffer.Size(),
expected_cpu_type, expected_cpu_type,
expected_cpu_subtype); expected_cpu_subtype);
} }
// Return this file's characteristics, as found in the Mach-O header. // Return this file's characteristics, as found in the Mach-O header.

View file

@ -0,0 +1,88 @@
// Copyright (c) 2015, 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: Erik Chen <erikchen@chromium.org>
// super_fat_arch.h: A class to handle 64-bit object files. Has conversions to
// and from struct fat_arch.
#ifndef BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
#define BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
#include <limits>
#include <mach-o/fat.h>
#include <stdint.h>
// Similar to struct fat_arch, except size-related parameters support
// 64-bits.
class SuperFatArch {
public:
uint32_t cputype;
uint32_t cpusubtype;
uint64_t offset;
uint64_t size;
uint64_t align;
SuperFatArch() :
cputype(0),
cpusubtype(0),
offset(0),
size(0),
align(0) {
}
explicit SuperFatArch(const struct fat_arch &arch) :
cputype(arch.cputype),
cpusubtype(arch.cpusubtype),
offset(arch.offset),
size(arch.size),
align(arch.align) {
}
// Returns false if the conversion cannot be made.
// If the conversion succeeds, the result is placed in |output_arch|.
bool ConvertToFatArch(struct fat_arch* output_arch) const {
if (offset > std::numeric_limits<uint32_t>::max())
return false;
if (size > std::numeric_limits<uint32_t>::max())
return false;
if (align > std::numeric_limits<uint32_t>::max())
return false;
struct fat_arch arch;
arch.cputype = cputype;
arch.cpusubtype = cpusubtype;
arch.offset = offset;
arch.size = size;
arch.align = align;
*output_arch = arch;
return true;
}
};
#endif // BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_

View file

@ -126,14 +126,14 @@ static bool Start(const Options &options) {
fprintf(stderr, "%s: no architecture '%s' is present in file.\n", fprintf(stderr, "%s: no architecture '%s' is present in file.\n",
[primary_file fileSystemRepresentation], options.arch->name); [primary_file fileSystemRepresentation], options.arch->name);
size_t available_size; size_t available_size;
const struct fat_arch *available = const SuperFatArch *available =
dump_symbols.AvailableArchitectures(&available_size); dump_symbols.AvailableArchitectures(&available_size);
if (available_size == 1) if (available_size == 1)
fprintf(stderr, "the file's architecture is: "); fprintf(stderr, "the file's architecture is: ");
else else
fprintf(stderr, "architectures present in the file are:\n"); fprintf(stderr, "architectures present in the file are:\n");
for (size_t i = 0; i < available_size; i++) { for (size_t i = 0; i < available_size; i++) {
const struct fat_arch *arch = &available[i]; const SuperFatArch *arch = &available[i];
const NXArchInfo *arch_info = const NXArchInfo *arch_info =
google_breakpad::BreakpadGetArchInfoFromCpuType( google_breakpad::BreakpadGetArchInfoFromCpuType(
arch->cputype, arch->cpusubtype); arch->cputype, arch->cpusubtype);