cprover
Toggle main menu visibility
Loading...
Searching...
No Matches
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
42
struct
fat_header_prefixt
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 =
69
u32_to_native_endian
(header->
n_architectures
);
70
return
u32_to_native_endian
(header->
magic
) ==
CPROVER_FAT_MAGIC
&&
71
n_architectures_native >= 1 && n_architectures_native < 20;
72
}
73
74
osx_fat_readert::osx_fat_readert
(
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
123
bool
osx_fat_readert::extract_gb
(
124
const
std::string &source,
125
const
std::string &dest)
const
126
{
127
PRECONDITION
(
has_gb_arch
);
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
199
void
osx_mach_o_readert::process_commands
(
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
270
osx_mach_o_readert::osx_mach_o_readert
(
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
}
deserialization_exceptiont
Thrown when failing to deserialize a value from some low level format, like JSON or raw bytes.
Definition
exception_utils.h:80
message_handlert
Definition
message.h:27
messaget::eom
static eomt eom
Definition
message.h:289
osx_fat_readert::log
messaget log
Definition
osx_fat_reader.h:37
osx_fat_readert::osx_fat_readert
osx_fat_readert(std::ifstream &, message_handlert &)
Definition
osx_fat_reader.cpp:74
osx_fat_readert::has_gb_arch
bool has_gb_arch
Definition
osx_fat_reader.h:38
osx_fat_readert::extract_gb
bool extract_gb(const std::string &source, const std::string &dest) const
Definition
osx_fat_reader.cpp:123
osx_mach_o_readert::in
std::istream & in
Definition
osx_fat_reader.h:70
osx_mach_o_readert::process_commands
void process_commands(uint32_t ncmds, std::size_t offset, bool need_swap)
Definition
osx_fat_reader.cpp:199
osx_mach_o_readert::log
messaget log
Definition
osx_fat_reader.h:69
osx_mach_o_readert::osx_mach_o_readert
osx_mach_o_readert(std::istream &, message_handlert &)
Definition
osx_fat_reader.cpp:270
osx_mach_o_readert::process_sections_32
void process_sections_32(uint32_t nsects, bool need_swap)
Definition
osx_fat_reader.cpp:151
osx_mach_o_readert::sections
sectionst sections
Definition
osx_fat_reader.h:61
osx_mach_o_readert::process_sections_64
void process_sections_64(uint32_t nsects, bool need_swap)
Definition
osx_fat_reader.cpp:175
system_exceptiont
Thrown when some external system fails unexpectedly.
Definition
exception_utils.h:72
exception_utils.h
is_osx_fat_header
bool is_osx_fat_header(char header_bytes[8])
Definition
osx_fat_reader.cpp:57
CPROVER_FAT_MAGIC
#define CPROVER_FAT_MAGIC
Definition
osx_fat_reader.cpp:19
CPROVER_MH_CIGAM
#define CPROVER_MH_CIGAM
Definition
osx_fat_reader.cpp:22
CPROVER_MH_MAGIC
#define CPROVER_MH_MAGIC
Definition
osx_fat_reader.cpp:21
u32_to_native_endian
static uint32_t u32_to_native_endian(uint32_t input)
Definition
osx_fat_reader.cpp:48
CPROVER_MH_MAGIC_64
#define CPROVER_MH_MAGIC_64
Definition
osx_fat_reader.cpp:23
is_osx_mach_object
bool is_osx_mach_object(char hdr[4])
Definition
osx_fat_reader.cpp:135
CPROVER_MH_CIGAM_64
#define CPROVER_MH_CIGAM_64
Definition
osx_fat_reader.cpp:24
osx_fat_reader.h
Read OS X Fat Binaries.
is_osx_fat_header
bool is_osx_fat_header(char hdr[8])
Definition
osx_fat_reader.cpp:57
run
int run(const std::string &what, const std::vector< std::string > &argv)
Definition
run.cpp:49
run.h
invariant.h
PRECONDITION
#define PRECONDITION(CONDITION)
Definition
invariant.h:463
fat_header_prefixt
Definition
osx_fat_reader.cpp:43
fat_header_prefixt::magic
uint32_t magic
Definition
osx_fat_reader.cpp:44
fat_header_prefixt::n_architectures
uint32_t n_architectures
Definition
osx_fat_reader.cpp:45
osx_mach_o_readert::sectiont
Definition
osx_fat_reader.h:49
goto-programs
osx_fat_reader.cpp
Generated by
1.17.0