CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
osx_fat_reader.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Read Mach-O
4
5Author:
6
7\*******************************************************************/
8
11
12#include "osx_fat_reader.h"
13
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
47
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
58{
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.
69 u32_to_native_endian(header->n_architectures);
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
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/
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:
145 return true;
146 }
147
148 return false;
149}
150
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)
165
166 sections.emplace(s.sectname, sectiont(s.sectname, s.offset, s.size));
167 }
168#else
169 // unused parameters
170 (void)nsects;
172#endif
173}
174
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)
189
190 sections.emplace(s.sectname, sectiont(s.sectname, s.offset, s.size));
191 }
192#else
193 // unused parameters
194 (void)nsects;
196#endif
197}
198
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)
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)
237
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)
252
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;
267#endif
268}
269
271 std::istream &_in,
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;
292 need_swap = true;
293 is_64 = true;
294 break;
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)
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)
334
335 ncmds = mh.ncmds;
336 offset = sizeof(mh);
337 }
338
340#else
341 log.warning() << "Cannot read OSX Mach-O on this platform" << messaget::eom;
342#endif
343}
message_handlert & message_handler
Definition ai.h:521
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
ait()
Definition ai.h:565
Thrown when failing to deserialize a value from some low level format, like JSON or raw bytes.
mstreamt & warning() const
Definition message.h:396
static eomt eom
Definition message.h:289
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:2449
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.
bool is_osx_fat_header(char hdr[8])
int run(const std::string &what, const std::vector< std::string > &argv)
Definition run.cpp:48
#define PRECONDITION(CONDITION)
Definition invariant.h:463