/ src / common / mac / macho_id.cc
macho_id.cc
  1  // Copyright 2006 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  // macho_id.cc: Functions to gather identifying information from a macho file
 30  //
 31  // See macho_id.h for documentation
 32  //
 33  // Author: Dan Waylonis
 34  
 35  
 36  #ifdef HAVE_CONFIG_H
 37  #include <config.h>  // Must come first
 38  #endif
 39  
 40  #include <fcntl.h>
 41  #include <mach-o/loader.h>
 42  #include <stdio.h>
 43  #include <string.h>
 44  
 45  #include "common/mac/macho_id.h"
 46  #include "common/mac/macho_walker.h"
 47  #include "common/mac/macho_utilities.h"
 48  
 49  namespace MacFileUtilities {
 50  
 51  using google_breakpad::MD5Init;
 52  using google_breakpad::MD5Update;
 53  using google_breakpad::MD5Final;
 54  
 55  MachoID::MachoID(const char* path)
 56      : memory_(0), memory_size_(0), md5_context_(), update_function_(NULL) {
 57    snprintf(path_, sizeof(path_), "%s", path);
 58  }
 59  
 60  MachoID::MachoID(void* memory, size_t size)
 61      : path_(),
 62        memory_(memory),
 63        memory_size_(size),
 64        md5_context_(),
 65        update_function_(NULL) {}
 66  
 67  MachoID::~MachoID() {}
 68  
 69  void MachoID::UpdateMD5(unsigned char* bytes, size_t size) {
 70    MD5Update(&md5_context_, bytes, static_cast<unsigned>(size));
 71  }
 72  
 73  void MachoID::Update(MachoWalker* walker, off_t offset, size_t size) {
 74    if (!update_function_ || !size)
 75      return;
 76  
 77    // Read up to 4k bytes at a time
 78    unsigned char buffer[4096];
 79    size_t buffer_size;
 80    off_t file_offset = offset;
 81    while (size > 0) {
 82      if (size > sizeof(buffer)) {
 83        buffer_size = sizeof(buffer);
 84        size -= buffer_size;
 85      } else {
 86        buffer_size = size;
 87        size = 0;
 88      }
 89  
 90      if (!walker->ReadBytes(buffer, buffer_size, file_offset))
 91        return;
 92  
 93      (this->*update_function_)(buffer, buffer_size);
 94      file_offset += buffer_size;
 95    }
 96  }
 97  
 98  bool MachoID::UUIDCommand(cpu_type_t cpu_type,
 99                            cpu_subtype_t cpu_subtype,
100                            unsigned char bytes[16]) {
101    struct breakpad_uuid_command uuid_cmd;
102    uuid_cmd.cmd = 0;
103    if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd))
104      return false;
105  
106    // If we found the command, we'll have initialized the uuid_command
107    // structure
108    if (uuid_cmd.cmd == LC_UUID) {
109      memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid));
110      return true;
111    }
112  
113    return false;
114  }
115  
116  bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) {
117    update_function_ = &MachoID::UpdateMD5;
118  
119    MD5Init(&md5_context_);
120  
121    if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
122      return false;
123  
124    MD5Final(identifier, &md5_context_);
125    return true;
126  }
127  
128  bool MachoID::WalkHeader(cpu_type_t cpu_type,
129                           cpu_subtype_t cpu_subtype,
130                           MachoWalker::LoadCommandCallback callback,
131                           void* context) {
132    if (memory_) {
133      MachoWalker walker(memory_, memory_size_, callback, context);
134      return walker.WalkHeader(cpu_type, cpu_subtype);
135    } else {
136      MachoWalker walker(path_, callback, context);
137      return walker.WalkHeader(cpu_type, cpu_subtype);
138    }
139  }
140  
141  // static
142  bool MachoID::WalkerCB(MachoWalker* walker, load_command* cmd, off_t offset,
143                         bool swap, void* context) {
144    MachoID* macho_id = (MachoID*)context;
145  
146    if (cmd->cmd == LC_SEGMENT) {
147      struct segment_command seg;
148  
149      if (!walker->ReadBytes(&seg, sizeof(seg), offset))
150        return false;
151  
152      if (swap)
153        breakpad_swap_segment_command(&seg);
154  
155      struct mach_header_64 header;
156      off_t header_offset;
157      
158      if (!walker->CurrentHeader(&header, &header_offset))
159        return false;
160          
161      // Process segments that have sections:
162      // (e.g., __TEXT, __DATA, __IMPORT, __OBJC)
163      offset += sizeof(struct segment_command);
164      struct section sec;
165      for (unsigned long i = 0; i < seg.nsects; ++i) {
166        if (!walker->ReadBytes(&sec, sizeof(sec), offset))
167          return false;
168  
169        if (swap)
170          breakpad_swap_section(&sec, 1);
171  
172        // sections of type S_ZEROFILL are "virtual" and contain no data
173        // in the file itself
174        if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0)
175          macho_id->Update(walker, header_offset + sec.offset, sec.size);
176  
177        offset += sizeof(struct section);
178      }
179    } else if (cmd->cmd == LC_SEGMENT_64) {
180      struct segment_command_64 seg64;
181  
182      if (!walker->ReadBytes(&seg64, sizeof(seg64), offset))
183        return false;
184  
185      if (swap)
186        breakpad_swap_segment_command_64(&seg64);
187  
188      struct mach_header_64 header;
189      off_t header_offset;
190      
191      if (!walker->CurrentHeader(&header, &header_offset))
192        return false;
193      
194      // Process segments that have sections:
195      // (e.g., __TEXT, __DATA, __IMPORT, __OBJC)
196      offset += sizeof(struct segment_command_64);
197      struct section_64 sec64;
198      for (unsigned long i = 0; i < seg64.nsects; ++i) {
199        if (!walker->ReadBytes(&sec64, sizeof(sec64), offset))
200          return false;
201  
202        if (swap)
203          breakpad_swap_section_64(&sec64, 1);
204  
205        // sections of type S_ZEROFILL are "virtual" and contain no data
206        // in the file itself
207        if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0)
208          macho_id->Update(walker, 
209                           header_offset + sec64.offset, 
210                           (size_t)sec64.size);
211  
212        offset += sizeof(struct section_64);
213      }
214    }
215  
216    // Continue processing
217    return true;
218  }
219  
220  // static
221  bool MachoID::UUIDWalkerCB(MachoWalker* walker, load_command* cmd, off_t offset,
222                             bool swap, void* context) {
223    if (cmd->cmd == LC_UUID) {
224      struct breakpad_uuid_command* uuid_cmd =
225        (struct breakpad_uuid_command*)context;
226  
227      if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command),
228                             offset))
229        return false;
230  
231      if (swap)
232        breakpad_swap_uuid_command(uuid_cmd);
233  
234      return false;
235    }
236  
237    // Continue processing
238    return true;
239  }
240  }  // namespace MacFileUtilities