From aa75fa5d4ebff1511c6b5e638ba99534c00255c7 Mon Sep 17 00:00:00 2001 From: "erikchen@chromium.org" Date: Wed, 22 Apr 2015 20:14:24 +0000 Subject: 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 --- src/common/common.gyp | 1 + src/common/mac/dump_syms.h | 23 +++++--- src/common/mac/dump_syms.mm | 60 +++++++++++++++++++-- src/common/mac/macho_reader.cc | 32 +++++------ src/common/mac/macho_reader.h | 21 ++++---- src/common/mac/super_fat_arch.h | 88 +++++++++++++++++++++++++++++++ src/tools/mac/dump_syms/dump_syms_tool.mm | 4 +- 7 files changed, 189 insertions(+), 40 deletions(-) create mode 100644 src/common/mac/super_fat_arch.h (limited to 'src') diff --git a/src/common/common.gyp b/src/common/common.gyp index f01ede57..6ccd21a2 100644 --- a/src/common/common.gyp +++ b/src/common/common.gyp @@ -141,6 +141,7 @@ 'mac/scoped_task_suspend-inl.h', 'mac/string_utilities.cc', 'mac/string_utilities.h', + 'mac/super_fat_arch.h', 'md5.cc', 'md5.h', 'memory.h', diff --git a/src/common/mac/dump_syms.h b/src/common/mac/dump_syms.h index 34e83976..babed701 100644 --- a/src/common/mac/dump_syms.h +++ b/src/common/mac/dump_syms.h @@ -46,6 +46,7 @@ #include "common/byte_cursor.h" #include "common/mac/macho_reader.h" +#include "common/mac/super_fat_arch.h" #include "common/module.h" #include "common/symbol_data.h" @@ -59,6 +60,7 @@ class DumpSymbols { input_pathname_(), object_filename_(), contents_(), + object_files_(), selected_object_file_(), selected_object_name_() { } ~DumpSymbols() { @@ -98,14 +100,14 @@ class DumpSymbols { // architecture matches that of this dumper program. bool SetArchitecture(const std::string &arch_name); - // Return a pointer to an array of 'struct fat_arch' structures, - // describing the object files contained in this dumper's file. Set - // *|count| to the number of elements in the array. The returned array is - // owned by this DumpSymbols instance. + // Return a pointer to an array of SuperFatArch structures describing the + // object files contained in this dumper's file. Set *|count| to the number + // of elements in the array. The returned array is owned by this DumpSymbols + // instance. // // If there are no available architectures, this function // may return NULL. - const struct fat_arch *AvailableArchitectures(size_t *count) { + const SuperFatArch* AvailableArchitectures(size_t *count) { *count = object_files_.size(); if (object_files_.size() > 0) return &object_files_[0]; @@ -127,6 +129,11 @@ class DumpSymbols { class DumperLineToModule; 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. std::string Identifier(); @@ -167,15 +174,15 @@ class DumpSymbols { // The complete contents of object_filename_, mapped into memory. 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, // this may have more than one element; if it refers to a Mach-O file, this // has exactly one element. - vector object_files_; + vector object_files_; // The object file in object_files_ selected to dump, or NULL if // 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 // messages. This is usually object_filename_, but if that refers to a diff --git a/src/common/mac/dump_syms.mm b/src/common/mac/dump_syms.mm index f2420f17..58cf4c20 100644 --- a/src/common/mac/dump_syms.mm +++ b/src/common/mac/dump_syms.mm @@ -35,6 +35,7 @@ #include "common/mac/dump_syms.h" +#include #include #include #include @@ -170,7 +171,7 @@ bool DumpSymbols::Read(NSString *filename) { // Get our own copy of fat_reader's object file list. size_t object_files_count; - const struct fat_arch *object_files = + const SuperFatArch *object_files = fat_reader.object_files(&object_files_count); if (object_files_count == 0) { 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); memcpy(&object_files_[0], object_files, - sizeof(struct fat_arch) * object_files_count); + sizeof(SuperFatArch) * object_files_count); return true; } @@ -187,9 +188,8 @@ bool DumpSymbols::Read(NSString *filename) { bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { // Find the best match for the architecture the user requested. - const struct fat_arch *best_match - = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0], - static_cast(object_files_.size())); + const SuperFatArch *best_match = FindBestMatchForArchitecture( + cpu_type, cpu_subtype); if (!best_match) return false; // Record the selected object file. @@ -207,6 +207,56 @@ bool DumpSymbols::SetArchitecture(const std::string &arch_name) { 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 fat_arch_vector; + for (vector::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(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::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() { FileID file_id([object_filename_ fileSystemRepresentation]); unsigned char identifier_bytes[16]; diff --git a/src/common/mac/macho_reader.cc b/src/common/mac/macho_reader.cc index 84b595a3..9fcb58e3 100644 --- a/src/common/mac/macho_reader.cc +++ b/src/common/mac/macho_reader.cc @@ -101,22 +101,26 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) { // 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]; + struct fat_arch objfile; // Read this object file entry, byte-swapping as appropriate. - cursor >> objfile->cputype - >> objfile->cpusubtype - >> objfile->offset - >> objfile->size - >> objfile->align; + 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) { + if (objfile.offset > fat_size || + objfile.size > fat_size - objfile.offset) { reporter_->MisplacedObjectFile(); return false; } @@ -139,16 +143,14 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) { } object_files_[0].offset = 0; - object_files_[0].size = static_cast(buffer_.Size()); + object_files_[0].size = static_cast(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; } @@ -315,7 +317,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const { // 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)) { @@ -400,7 +402,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const { return false; break; } - + default: { if (!handler->UnknownCommand(type, command)) return false; @@ -419,7 +421,7 @@ 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) + SegmentFinder(const string &name, Segment *segment) : name_(name), segment_(segment), found_() { } // 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) { // Zero-fill sections have a size, but no contents. section.contents.start = section.contents.end = NULL; - } else if (segment.contents.start == 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 diff --git a/src/common/mac/macho_reader.h b/src/common/mac/macho_reader.h index 75376482..30db742d 100644 --- a/src/common/mac/macho_reader.h +++ b/src/common/mac/macho_reader.h @@ -47,6 +47,7 @@ #include #include "common/byte_cursor.h" +#include "common/mac/super_fat_arch.h" namespace google_breakpad { namespace mach_o { @@ -93,7 +94,7 @@ class FatReader { // 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_; @@ -101,7 +102,7 @@ class FatReader { // 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. @@ -111,13 +112,13 @@ class FatReader { // 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 + // Return an array of 'SuperFatArch' 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. + // Assuming Read returned true, the entries are validated: it is safe to + // assume that the offsets and sizes in each SuperFatArch 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. @@ -129,7 +130,7 @@ class FatReader { // 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 { + const SuperFatArch* object_files(size_t *count) const { *count = object_files_.size(); if (object_files_.size() > 0) return &object_files_[0]; @@ -149,7 +150,7 @@ class FatReader { // The list of object files in this binary. // object_files_.size() == fat_header.nfat_arch - vector object_files_; + vector object_files_; }; // 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. uint32_t maxprot; uint32_t initprot; - + // The number of sections in section_list. uint32_t nsects; @@ -376,7 +377,7 @@ class Reader { return Read(buffer.start, buffer.Size(), expected_cpu_type, - expected_cpu_subtype); + expected_cpu_subtype); } // Return this file's characteristics, as found in the Mach-O header. diff --git a/src/common/mac/super_fat_arch.h b/src/common/mac/super_fat_arch.h new file mode 100644 index 00000000..501c8652 --- /dev/null +++ b/src/common/mac/super_fat_arch.h @@ -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 + +// 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 +#include +#include + +// 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::max()) + return false; + if (size > std::numeric_limits::max()) + return false; + if (align > std::numeric_limits::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_ diff --git a/src/tools/mac/dump_syms/dump_syms_tool.mm b/src/tools/mac/dump_syms/dump_syms_tool.mm index 1880cd3b..7d56809a 100644 --- a/src/tools/mac/dump_syms/dump_syms_tool.mm +++ b/src/tools/mac/dump_syms/dump_syms_tool.mm @@ -126,14 +126,14 @@ static bool Start(const Options &options) { fprintf(stderr, "%s: no architecture '%s' is present in file.\n", [primary_file fileSystemRepresentation], options.arch->name); size_t available_size; - const struct fat_arch *available = + const SuperFatArch *available = dump_symbols.AvailableArchitectures(&available_size); if (available_size == 1) fprintf(stderr, "the file's architecture is: "); else fprintf(stderr, "architectures present in the file are:\n"); 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 = google_breakpad::BreakpadGetArchInfoFromCpuType( arch->cputype, arch->cpusubtype); -- cgit v1.2.1