michael@0: // Copyright (c) 2006, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // macho_walker.cc: Iterate over the load commands in a mach-o file michael@0: // michael@0: // See macho_walker.h for documentation michael@0: // michael@0: // Author: Dan Waylonis michael@0: michael@0: extern "C" { // necessary for Leopard michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: } michael@0: michael@0: #include "common/mac/byteswap.h" michael@0: #include "common/mac/macho_walker.h" michael@0: #include "common/mac/macho_utilities.h" michael@0: michael@0: namespace MacFileUtilities { michael@0: michael@0: MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback, michael@0: void *context) michael@0: : file_(0), michael@0: memory_(NULL), michael@0: memory_size_(0), michael@0: callback_(callback), michael@0: callback_context_(context), michael@0: current_header_(NULL), michael@0: current_header_size_(0), michael@0: current_header_offset_(0) { michael@0: file_ = open(path, O_RDONLY); michael@0: } michael@0: michael@0: MachoWalker::MachoWalker(void *memory, size_t size, michael@0: LoadCommandCallback callback, void *context) michael@0: : file_(0), michael@0: memory_(memory), michael@0: memory_size_(size), michael@0: callback_(callback), michael@0: callback_context_(context), michael@0: current_header_(NULL), michael@0: current_header_size_(0), michael@0: current_header_offset_(0) { michael@0: } michael@0: michael@0: MachoWalker::~MachoWalker() { michael@0: if (file_ != -1) michael@0: close(file_); michael@0: } michael@0: michael@0: bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { michael@0: cpu_type_t valid_cpu_type = cpu_type; michael@0: cpu_subtype_t valid_cpu_subtype = cpu_subtype; michael@0: // if |cpu_type| is 0, use the native cpu type. michael@0: if (cpu_type == 0) { michael@0: const NXArchInfo *arch = NXGetLocalArchInfo(); michael@0: assert(arch); michael@0: valid_cpu_type = arch->cputype; michael@0: valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE; michael@0: } michael@0: off_t offset; michael@0: if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) { michael@0: if (cpu_type & CPU_ARCH_ABI64) michael@0: return WalkHeader64AtOffset(offset); michael@0: michael@0: return WalkHeaderAtOffset(offset); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) { michael@0: if (memory_) { michael@0: if (offset < 0) michael@0: return false; michael@0: bool result = true; michael@0: if (offset + size > memory_size_) { michael@0: if (static_cast(offset) >= memory_size_) michael@0: return false; michael@0: size = memory_size_ - static_cast(offset); michael@0: result = false; michael@0: } michael@0: memcpy(buffer, static_cast(memory_) + offset, size); michael@0: return result; michael@0: } else { michael@0: return pread(file_, buffer, size, offset) == (ssize_t)size; michael@0: } michael@0: } michael@0: michael@0: bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) { michael@0: if (current_header_) { michael@0: memcpy(header, current_header_, sizeof(mach_header_64)); michael@0: *offset = current_header_offset_; michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool MachoWalker::FindHeader(cpu_type_t cpu_type, michael@0: cpu_subtype_t cpu_subtype, michael@0: off_t &offset) { michael@0: // Read the magic bytes that's common amongst all mach-o files michael@0: uint32_t magic; michael@0: if (!ReadBytes(&magic, sizeof(magic), 0)) michael@0: return false; michael@0: michael@0: offset = sizeof(magic); michael@0: michael@0: // Figure out what type of file we've got michael@0: bool is_fat = false; michael@0: if (magic == FAT_MAGIC || magic == FAT_CIGAM) { michael@0: is_fat = true; michael@0: } michael@0: else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 && michael@0: magic != MH_CIGAM_64) { michael@0: return false; michael@0: } michael@0: michael@0: if (!is_fat) { michael@0: // If we don't have a fat header, check if the cpu type matches the single michael@0: // header michael@0: struct mach_header header; michael@0: if (!ReadBytes(&header, sizeof(header), 0)) michael@0: return false; michael@0: michael@0: if (magic == MH_CIGAM || magic == MH_CIGAM_64) michael@0: swap_mach_header(&header, NXHostByteOrder()); michael@0: michael@0: if (cpu_type != header.cputype || michael@0: (cpu_subtype != CPU_SUBTYPE_MULTIPLE && michael@0: cpu_subtype != header.cpusubtype)) { michael@0: return false; michael@0: } michael@0: michael@0: offset = 0; michael@0: return true; michael@0: } else { michael@0: // Read the fat header and find an appropriate architecture michael@0: offset = 0; michael@0: struct fat_header fat; michael@0: if (!ReadBytes(&fat, sizeof(fat), offset)) michael@0: return false; michael@0: michael@0: if (NXHostByteOrder() != NX_BigEndian) michael@0: swap_fat_header(&fat, NXHostByteOrder()); michael@0: michael@0: offset += sizeof(fat); michael@0: michael@0: // Search each architecture for the desired one michael@0: struct fat_arch arch; michael@0: for (uint32_t i = 0; i < fat.nfat_arch; ++i) { michael@0: if (!ReadBytes(&arch, sizeof(arch), offset)) michael@0: return false; michael@0: michael@0: if (NXHostByteOrder() != NX_BigEndian) michael@0: swap_fat_arch(&arch, 1, NXHostByteOrder()); michael@0: michael@0: if (arch.cputype == cpu_type && michael@0: (cpu_subtype == CPU_SUBTYPE_MULTIPLE || michael@0: arch.cpusubtype == cpu_subtype)) { michael@0: offset = arch.offset; michael@0: return true; michael@0: } michael@0: michael@0: offset += sizeof(arch); michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool MachoWalker::WalkHeaderAtOffset(off_t offset) { michael@0: struct mach_header header; michael@0: if (!ReadBytes(&header, sizeof(header), offset)) michael@0: return false; michael@0: michael@0: bool swap = (header.magic == MH_CIGAM); michael@0: if (swap) michael@0: swap_mach_header(&header, NXHostByteOrder()); michael@0: michael@0: // Copy the data into the mach_header_64 structure. Since the 32-bit and michael@0: // 64-bit only differ in the last field (reserved), this is safe to do. michael@0: struct mach_header_64 header64; michael@0: memcpy((void *)&header64, (const void *)&header, sizeof(header)); michael@0: header64.reserved = 0; michael@0: michael@0: current_header_ = &header64; michael@0: current_header_size_ = sizeof(header); // 32-bit, not 64-bit michael@0: current_header_offset_ = offset; michael@0: offset += current_header_size_; michael@0: bool result = WalkHeaderCore(offset, header.ncmds, swap); michael@0: current_header_ = NULL; michael@0: current_header_size_ = 0; michael@0: current_header_offset_ = 0; michael@0: return result; michael@0: } michael@0: michael@0: bool MachoWalker::WalkHeader64AtOffset(off_t offset) { michael@0: struct mach_header_64 header; michael@0: if (!ReadBytes(&header, sizeof(header), offset)) michael@0: return false; michael@0: michael@0: bool swap = (header.magic == MH_CIGAM_64); michael@0: if (swap) michael@0: breakpad_swap_mach_header_64(&header, NXHostByteOrder()); michael@0: michael@0: current_header_ = &header; michael@0: current_header_size_ = sizeof(header); michael@0: current_header_offset_ = offset; michael@0: offset += current_header_size_; michael@0: bool result = WalkHeaderCore(offset, header.ncmds, swap); michael@0: current_header_ = NULL; michael@0: current_header_size_ = 0; michael@0: current_header_offset_ = 0; michael@0: return result; michael@0: } michael@0: michael@0: bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands, michael@0: bool swap) { michael@0: for (uint32_t i = 0; i < number_of_commands; ++i) { michael@0: struct load_command cmd; michael@0: if (!ReadBytes(&cmd, sizeof(cmd), offset)) michael@0: return false; michael@0: michael@0: if (swap) michael@0: swap_load_command(&cmd, NXHostByteOrder()); michael@0: michael@0: // Call the user callback michael@0: if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_)) michael@0: break; michael@0: michael@0: offset += cmd.cmdsize; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace MacFileUtilities