xref: /aosp_15_r20/external/google-breakpad/src/common/mac/macho_id.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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 
MachoID(const char * path)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 
MachoID(void * memory,size_t size)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 
~MachoID()67 MachoID::~MachoID() {}
68 
UpdateMD5(unsigned char * bytes,size_t size)69 void MachoID::UpdateMD5(unsigned char* bytes, size_t size) {
70   MD5Update(&md5_context_, bytes, static_cast<unsigned>(size));
71 }
72 
Update(MachoWalker * walker,off_t offset,size_t size)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 
UUIDCommand(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,unsigned char bytes[16])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 
MD5(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,unsigned char identifier[16])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 
WalkHeader(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,MachoWalker::LoadCommandCallback callback,void * context)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
WalkerCB(MachoWalker * walker,load_command * cmd,off_t offset,bool swap,void * context)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
UUIDWalkerCB(MachoWalker * walker,load_command * cmd,off_t offset,bool swap,void * context)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
241