Home | History | Annotate | Download | only in mac
      1 // Copyright (c) 2006, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // macho_walker.cc: Iterate over the load commands in a mach-o file
     31 //
     32 // See macho_walker.h for documentation
     33 //
     34 // Author: Dan Waylonis
     35 
     36 extern "C" {  // necessary for Leopard
     37   #include <assert.h>
     38   #include <fcntl.h>
     39   #include <mach-o/arch.h>
     40   #include <mach-o/loader.h>
     41   #include <mach-o/swap.h>
     42   #include <string.h>
     43   #include <unistd.h>
     44 }
     45 
     46 #include "common/mac/byteswap.h"
     47 #include "common/mac/macho_walker.h"
     48 #include "common/mac/macho_utilities.h"
     49 
     50 namespace MacFileUtilities {
     51 
     52 MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
     53                          void *context)
     54     : file_(-1),
     55       memory_(NULL),
     56       memory_size_(0),
     57       callback_(callback),
     58       callback_context_(context),
     59       current_header_(NULL),
     60       current_header_size_(0),
     61       current_header_offset_(0) {
     62   file_ = open(path, O_RDONLY);
     63 }
     64 
     65 MachoWalker::MachoWalker(void *memory, size_t size,
     66                          LoadCommandCallback callback, void *context)
     67     : file_(-1),
     68       memory_(memory),
     69       memory_size_(size),
     70       callback_(callback),
     71       callback_context_(context),
     72       current_header_(NULL),
     73       current_header_size_(0),
     74       current_header_offset_(0) {
     75 }
     76 
     77 MachoWalker::~MachoWalker() {
     78   if (file_ != -1)
     79     close(file_);
     80 }
     81 
     82 bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
     83   cpu_type_t valid_cpu_type = cpu_type;
     84   cpu_subtype_t valid_cpu_subtype = cpu_subtype;
     85   // if |cpu_type| is 0, use the native cpu type.
     86   if (cpu_type == 0) {
     87     const NXArchInfo *arch = NXGetLocalArchInfo();
     88     assert(arch);
     89     valid_cpu_type = arch->cputype;
     90     valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE;
     91   }
     92   off_t offset;
     93   if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) {
     94     if (cpu_type & CPU_ARCH_ABI64)
     95       return WalkHeader64AtOffset(offset);
     96 
     97     return WalkHeaderAtOffset(offset);
     98   }
     99 
    100   return false;
    101 }
    102 
    103 bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
    104   if (memory_) {
    105     if (offset < 0)
    106       return false;
    107     bool result = true;
    108     if (offset + size > memory_size_) {
    109       if (static_cast<size_t>(offset) >= memory_size_)
    110         return false;
    111       size = memory_size_ - static_cast<size_t>(offset);
    112       result = false;
    113     }
    114     memcpy(buffer, static_cast<char *>(memory_) + offset, size);
    115     return result;
    116   } else {
    117     return pread(file_, buffer, size, offset) == (ssize_t)size;
    118   }
    119 }
    120 
    121 bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {
    122   if (current_header_) {
    123     memcpy(header, current_header_, sizeof(mach_header_64));
    124     *offset = current_header_offset_;
    125     return true;
    126   }
    127 
    128   return false;
    129 }
    130 
    131 bool MachoWalker::FindHeader(cpu_type_t cpu_type,
    132                              cpu_subtype_t cpu_subtype,
    133                              off_t &offset) {
    134   // Read the magic bytes that's common amongst all mach-o files
    135   uint32_t magic;
    136   if (!ReadBytes(&magic, sizeof(magic), 0))
    137     return false;
    138 
    139   offset = sizeof(magic);
    140 
    141   // Figure out what type of file we've got
    142   bool is_fat = false;
    143   if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
    144     is_fat = true;
    145   }
    146   else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 &&
    147            magic != MH_CIGAM_64) {
    148     return false;
    149   }
    150 
    151   if (!is_fat) {
    152     // If we don't have a fat header, check if the cpu type matches the single
    153     // header
    154     struct mach_header header;
    155     if (!ReadBytes(&header, sizeof(header), 0))
    156       return false;
    157 
    158     if (magic == MH_CIGAM || magic == MH_CIGAM_64)
    159       swap_mach_header(&header, NXHostByteOrder());
    160 
    161     if (cpu_type != header.cputype ||
    162         (cpu_subtype != CPU_SUBTYPE_MULTIPLE &&
    163          cpu_subtype != header.cpusubtype)) {
    164       return false;
    165     }
    166 
    167     offset = 0;
    168     return true;
    169   } else {
    170     // Read the fat header and find an appropriate architecture
    171     offset = 0;
    172     struct fat_header fat;
    173     if (!ReadBytes(&fat, sizeof(fat), offset))
    174       return false;
    175 
    176     if (NXHostByteOrder() != NX_BigEndian)
    177       swap_fat_header(&fat, NXHostByteOrder());
    178 
    179     offset += sizeof(fat);
    180 
    181     // Search each architecture for the desired one
    182     struct fat_arch arch;
    183     for (uint32_t i = 0; i < fat.nfat_arch; ++i) {
    184       if (!ReadBytes(&arch, sizeof(arch), offset))
    185         return false;
    186 
    187       if (NXHostByteOrder() != NX_BigEndian)
    188         swap_fat_arch(&arch, 1, NXHostByteOrder());
    189 
    190       if (arch.cputype == cpu_type &&
    191           (cpu_subtype == CPU_SUBTYPE_MULTIPLE ||
    192            arch.cpusubtype == cpu_subtype)) {
    193         offset = arch.offset;
    194         return true;
    195       }
    196 
    197       offset += sizeof(arch);
    198     }
    199   }
    200 
    201   return false;
    202 }
    203 
    204 bool MachoWalker::WalkHeaderAtOffset(off_t offset) {
    205   struct mach_header header;
    206   if (!ReadBytes(&header, sizeof(header), offset))
    207     return false;
    208 
    209   bool swap = (header.magic == MH_CIGAM);
    210   if (swap)
    211     swap_mach_header(&header, NXHostByteOrder());
    212 
    213   // Copy the data into the mach_header_64 structure.  Since the 32-bit and
    214   // 64-bit only differ in the last field (reserved), this is safe to do.
    215   struct mach_header_64 header64;
    216   memcpy((void *)&header64, (const void *)&header, sizeof(header));
    217   header64.reserved = 0;
    218 
    219   current_header_ = &header64;
    220   current_header_size_ = sizeof(header); // 32-bit, not 64-bit
    221   current_header_offset_ = offset;
    222   offset += current_header_size_;
    223   bool result = WalkHeaderCore(offset, header.ncmds, swap);
    224   current_header_ = NULL;
    225   current_header_size_ = 0;
    226   current_header_offset_ = 0;
    227   return result;
    228 }
    229 
    230 bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
    231   struct mach_header_64 header;
    232   if (!ReadBytes(&header, sizeof(header), offset))
    233     return false;
    234 
    235   bool swap = (header.magic == MH_CIGAM_64);
    236   if (swap)
    237     breakpad_swap_mach_header_64(&header, NXHostByteOrder());
    238 
    239   current_header_ = &header;
    240   current_header_size_ = sizeof(header);
    241   current_header_offset_ = offset;
    242   offset += current_header_size_;
    243   bool result = WalkHeaderCore(offset, header.ncmds, swap);
    244   current_header_ = NULL;
    245   current_header_size_ = 0;
    246   current_header_offset_ = 0;
    247   return result;
    248 }
    249 
    250 bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands,
    251                                  bool swap) {
    252   for (uint32_t i = 0; i < number_of_commands; ++i) {
    253     struct load_command cmd;
    254     if (!ReadBytes(&cmd, sizeof(cmd), offset))
    255       return false;
    256 
    257     if (swap)
    258       swap_load_command(&cmd, NXHostByteOrder());
    259 
    260     // Call the user callback
    261     if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_))
    262       break;
    263 
    264     offset += cmd.cmdsize;
    265   }
    266 
    267   return true;
    268 }
    269 
    270 }  // namespace MacFileUtilities
    271