CBMC
osx_fat_reader.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Read Mach-O
4 
5 Author:
6 
7 \*******************************************************************/
8 
11 
12 #include "osx_fat_reader.h"
13 
14 #include <util/exception_utils.h>
15 #include <util/invariant.h>
16 
17 // we define file-type magic values for all platforms to detect when we find a
18 // file that we might not be able to process
19 #define CPROVER_FAT_MAGIC 0xcafebabe
20 #define CPROVER_FAT_CIGAM 0xbebafeca
21 #define CPROVER_MH_MAGIC 0xfeedface
22 #define CPROVER_MH_CIGAM 0xcefaedfe
23 #define CPROVER_MH_MAGIC_64 0xfeedfacf
24 #define CPROVER_MH_CIGAM_64 0xcffaedfe
25 
26 #ifdef __APPLE__
27 # include <architecture/byte_order.h>
28 # include <mach-o/fat.h>
29 # include <mach-o/loader.h>
30 # include <mach-o/swap.h>
31 
32 # if(CPROVER_FAT_MAGIC != FAT_MAGIC) || (CPROVER_FAT_CIGAM != FAT_CIGAM) || \
33  (CPROVER_MH_MAGIC != MH_MAGIC) || (CPROVER_MH_CIGAM != MH_CIGAM) || \
34  (CPROVER_MH_MAGIC_64 != MH_MAGIC_64) || \
35  (CPROVER_MH_CIGAM_64 != MH_CIGAM_64)
36 # error "Mach-O magic has inconsistent value"
37 # endif
38 #endif
39 
40 #include <util/run.h>
41 
43 {
44  uint32_t magic;
45  uint32_t n_architectures;
46 };
47 
48 static uint32_t u32_to_native_endian(uint32_t input)
49 {
50  const uint8_t *input_as_bytes = reinterpret_cast<uint8_t *>(&input);
51  return (((uint32_t)input_as_bytes[0]) << 24) |
52  (((uint32_t)input_as_bytes[1]) << 16) |
53  (((uint32_t)input_as_bytes[2]) << 8) |
54  (((uint32_t)input_as_bytes[3]) << 0);
55 }
56 
57 bool is_osx_fat_header(char header_bytes[8])
58 {
59  struct fat_header_prefixt *header =
60  reinterpret_cast<struct fat_header_prefixt *>(header_bytes);
61 
62  // Unfortunately for us, both Java class files and Mach fat binaries use the
63  // magic number 0xCAFEBABE. Therefore we must also check the second field,
64  // number of architectures, is in a sensible range (I use at 1 <= archs < 20,
65  // the same criterion used by `GNU file`).
66  // Luckily the class file format stores the file version here, which cannot
67  // fall in this range.
68  uint32_t n_architectures_native =
70  return u32_to_native_endian(header->magic) == CPROVER_FAT_MAGIC &&
71  n_architectures_native >= 1 && n_architectures_native < 20;
72 }
73 
75  std::ifstream &in,
76  message_handlert &message_handler)
77  : log(message_handler), has_gb_arch(false)
78 {
79 #ifdef __APPLE__
80  // NOLINTNEXTLINE(readability/identifiers)
81  struct fat_header fh;
82  // NOLINTNEXTLINE(readability/identifiers)
83  in.read(reinterpret_cast<char*>(&fh), sizeof(struct fat_header));
84 
85  if(!in)
86  throw system_exceptiont("failed to read OSX fat header");
87 
88  static_assert(sizeof(fh) >= 8, "fat_header is at least 8 bytes");
89  if(!is_osx_fat_header(reinterpret_cast<char *>(&fh)))
90  throw deserialization_exceptiont("OSX fat header malformed");
91 
92  static_assert(
93  sizeof(fh.nfat_arch) == 4, "fat_header::nfat_arch is of type uint32_t");
94  unsigned narch = u32_to_native_endian(fh.nfat_arch);
95 
96  for(unsigned i=0; !has_gb_arch && i<narch; ++i)
97  {
98  // NOLINTNEXTLINE(readability/identifiers)
99  struct fat_arch fa;
100  // NOLINTNEXTLINE(readability/identifiers)
101  in.read(reinterpret_cast<char*>(&fa), sizeof(struct fat_arch));
102 
103  static_assert(
104  sizeof(fa.cputype) == 4 && sizeof(fa.cpusubtype) == 4 &&
105  sizeof(fa.size) == 4,
106  "This requires a specific fat architecture");
107  int cputype = u32_to_native_endian(fa.cputype);
108  int cpusubtype = u32_to_native_endian(fa.cpusubtype);
109  unsigned size = u32_to_native_endian(fa.size);
110 
111  has_gb_arch=cputype==CPU_TYPE_HPPA &&
112  cpusubtype==CPU_SUBTYPE_HPPA_7100LC &&
113  size > 0;
114  }
115 #else
116  (void)in; // unused parameter
117 
118  log.warning() << "Cannot read OSX fat archive on this platform"
119  << messaget::eom;
120 #endif
121 }
122 
124  const std::string &source,
125  const std::string &dest) const
126 {
128 
129  return run(
130  "lipo", {"lipo", "-thin", "hppa7100LC", "-output", dest, source}) !=
131  0;
132 }
133 
134 // guided by https://lowlevelbits.org/parsing-mach-o-files/
135 bool is_osx_mach_object(char hdr[4])
136 {
137  uint32_t *magic = reinterpret_cast<uint32_t *>(hdr);
138 
139  switch(*magic)
140  {
141  case CPROVER_MH_MAGIC:
142  case CPROVER_MH_CIGAM:
143  case CPROVER_MH_MAGIC_64:
144  case CPROVER_MH_CIGAM_64:
145  return true;
146  }
147 
148  return false;
149 }
150 
151 void osx_mach_o_readert::process_sections_32(uint32_t nsects, bool need_swap)
152 {
153 #ifdef __APPLE__
154  for(uint32_t i = 0; i < nsects; ++i)
155  {
156  // NOLINTNEXTLINE(readability/identifiers)
157  struct section s;
158  in.read(reinterpret_cast<char *>(&s), sizeof(s));
159 
160  if(!in)
161  throw deserialization_exceptiont("failed to read Mach-O section");
162 
163  if(need_swap)
164  swap_section(&s, 1, NXHostByteOrder());
165 
166  sections.emplace(s.sectname, sectiont(s.sectname, s.offset, s.size));
167  }
168 #else
169  // unused parameters
170  (void)nsects;
171  (void)need_swap;
172 #endif
173 }
174 
175 void osx_mach_o_readert::process_sections_64(uint32_t nsects, bool need_swap)
176 {
177 #ifdef __APPLE__
178  for(uint32_t i = 0; i < nsects; ++i)
179  {
180  // NOLINTNEXTLINE(readability/identifiers)
181  struct section_64 s;
182  in.read(reinterpret_cast<char *>(&s), sizeof(s));
183 
184  if(!in)
185  throw deserialization_exceptiont("failed to read 64-bit Mach-O section");
186 
187  if(need_swap)
188  swap_section_64(&s, 1, NXHostByteOrder());
189 
190  sections.emplace(s.sectname, sectiont(s.sectname, s.offset, s.size));
191  }
192 #else
193  // unused parameters
194  (void)nsects;
195  (void)need_swap;
196 #endif
197 }
198 
200  uint32_t ncmds,
201  std::size_t offset,
202  bool need_swap)
203 {
204 #ifdef __APPLE__
205  for(uint32_t i = 0; i < ncmds; ++i)
206  {
207  in.seekg(offset);
208 
209  // NOLINTNEXTLINE(readability/identifiers)
210  struct load_command lc;
211  in.read(reinterpret_cast<char *>(&lc), sizeof(lc));
212 
213  if(!in)
214  throw deserialization_exceptiont("failed to read Mach-O command");
215 
216  if(need_swap)
217  swap_load_command(&lc, NXHostByteOrder());
218 
219  // we may need to re-read the command once we have figured out its type; in
220  // particular, segment commands contain additional information that we have
221  // now just read a prefix of
222  in.seekg(offset);
223 
224  switch(lc.cmd)
225  {
226  case LC_SEGMENT:
227  {
228  // NOLINTNEXTLINE(readability/identifiers)
229  struct segment_command seg;
230  in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
231 
232  if(!in)
233  throw deserialization_exceptiont("failed to read Mach-O segment");
234 
235  if(need_swap)
236  swap_segment_command(&seg, NXHostByteOrder());
237 
238  process_sections_32(seg.nsects, need_swap);
239  break;
240  }
241  case LC_SEGMENT_64:
242  {
243  // NOLINTNEXTLINE(readability/identifiers)
244  struct segment_command_64 seg;
245  in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
246 
247  if(!in)
248  throw deserialization_exceptiont("failed to read Mach-O segment");
249 
250  if(need_swap)
251  swap_segment_command_64(&seg, NXHostByteOrder());
252 
253  process_sections_64(seg.nsects, need_swap);
254  break;
255  }
256  default:
257  break;
258  }
259 
260  offset += lc.cmdsize;
261  }
262 #else
263  // unused parameters
264  (void)ncmds;
265  (void)offset;
266  (void)need_swap;
267 #endif
268 }
269 
271  std::istream &_in,
272  message_handlert &message_handler)
273  : log(message_handler), in(_in)
274 {
275  // read magic
276  uint32_t magic;
277  in.read(reinterpret_cast<char *>(&magic), sizeof(magic));
278 
279  if(!in)
280  throw deserialization_exceptiont("failed to read Mach-O magic");
281 
282 #ifdef __APPLE__
283  bool is_64 = false, need_swap = false;
284  switch(magic)
285  {
286  case CPROVER_MH_CIGAM:
287  need_swap = true;
288  break;
289  case CPROVER_MH_MAGIC:
290  break;
291  case CPROVER_MH_CIGAM_64:
292  need_swap = true;
293  is_64 = true;
294  break;
295  case CPROVER_MH_MAGIC_64:
296  is_64 = true;
297  break;
298  default:
299  throw deserialization_exceptiont("no Mach-O magic");
300  }
301 
302  uint32_t ncmds = 0;
303  std::size_t offset = 0;
304 
305  // re-read from the beginning, now reading the full header
306  in.seekg(0);
307 
308  if(!is_64)
309  {
310  // NOLINTNEXTLINE(readability/identifiers)
311  struct mach_header mh;
312  in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
313 
314  if(!in)
315  throw deserialization_exceptiont("failed to read 32-bit Mach-O header");
316 
317  if(need_swap)
318  swap_mach_header(&mh, NXHostByteOrder());
319 
320  ncmds = mh.ncmds;
321  offset = sizeof(mh);
322  }
323  else
324  {
325  // NOLINTNEXTLINE(readability/identifiers)
326  struct mach_header_64 mh;
327  in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
328 
329  if(!in)
330  throw deserialization_exceptiont("failed to read 64-bit Mach-O header");
331 
332  if(need_swap)
333  swap_mach_header_64(&mh, NXHostByteOrder());
334 
335  ncmds = mh.ncmds;
336  offset = sizeof(mh);
337  }
338 
339  process_commands(ncmds, offset, need_swap);
340 #else
341  log.warning() << "Cannot read OSX Mach-O on this platform" << messaget::eom;
342 #endif
343 }
Thrown when failing to deserialize a value from some low level format, like JSON or raw bytes.
mstreamt & warning() const
Definition: message.h:404
static eomt eom
Definition: message.h:297
osx_fat_readert(std::ifstream &, message_handlert &)
bool extract_gb(const std::string &source, const std::string &dest) const
std::istream & in
void process_commands(uint32_t ncmds, std::size_t offset, bool need_swap)
osx_mach_o_readert(std::istream &, message_handlert &)
void process_sections_32(uint32_t nsects, bool need_swap)
void process_sections_64(uint32_t nsects, bool need_swap)
Thrown when some external system fails unexpectedly.
double log(double x)
Definition: math.c:2776
bool is_osx_fat_header(char header_bytes[8])
#define CPROVER_FAT_MAGIC
#define CPROVER_MH_CIGAM
#define CPROVER_MH_MAGIC
static uint32_t u32_to_native_endian(uint32_t input)
#define CPROVER_MH_MAGIC_64
bool is_osx_mach_object(char hdr[4])
#define CPROVER_MH_CIGAM_64
Read OS X Fat Binaries.
int run(const std::string &what, const std::vector< std::string > &argv)
Definition: run.cpp:48
#define PRECONDITION(CONDITION)
Definition: invariant.h:463