Skip to content

Commit ab8e466

Browse files
committed
Read goto-cc section from Mach-O object files
A basic Mach-O header parser, only functional on OS X.
1 parent b18a181 commit ab8e466

File tree

4 files changed

+299
-0
lines changed

4 files changed

+299
-0
lines changed

src/goto-programs/module_dependencies.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
analyses # dubious - concerns call_graph and does_remove_const
2+
architecture # system
23
assembler # should go away
34
goto-programs
45
goto-symex # dubious - spurious inclusion of symex_target_equation in graphml_witness

src/goto-programs/osx_fat_reader.cpp

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ Module: Read Mach-O
1616
#include <util/invariant.h>
1717

1818
#ifdef __APPLE__
19+
#include <architecture/byte_order.h>
1920
#include <mach-o/fat.h>
21+
#include <mach-o/loader.h>
22+
#include <mach-o/swap.h>
2023
#endif
2124

2225
#include <util/run.h>
@@ -92,3 +95,217 @@ bool osx_fat_readert::extract_gb(
9295
"lipo", {"lipo", "-thin", "hppa7100LC", "-output", dest, source}) !=
9396
0;
9497
}
98+
99+
// guided by https://lowlevelbits.org/parsing-mach-o-files/
100+
bool is_osx_mach_object(char hdr[4])
101+
{
102+
#ifdef __APPLE__
103+
uint32_t *magic = reinterpret_cast<uint32_t *>(hdr);
104+
105+
switch(*magic)
106+
{
107+
case MH_MAGIC:
108+
case MH_CIGAM:
109+
case MH_MAGIC_64:
110+
case MH_CIGAM_64:
111+
return true;
112+
}
113+
#else
114+
(void)hdr; // unused parameter
115+
#endif
116+
117+
return false;
118+
}
119+
120+
void osx_mach_o_readert::process_sections_32(
121+
uint32_t nsects,
122+
bool need_swap)
123+
{
124+
#ifdef __APPLE__
125+
for(uint32_t i = 0; i < nsects; ++i)
126+
{
127+
// NOLINTNEXTLINE(readability/identifiers)
128+
struct section s;
129+
in.read(reinterpret_cast<char *>(&s), sizeof(s));
130+
131+
if(!in)
132+
throw deserialization_exceptiont("failed to read Mach-O section");
133+
134+
if(need_swap)
135+
swap_section(&s, 1, NXHostByteOrder());
136+
137+
sections.emplace(s.sectname, sectiont(s.sectname, s.offset, s.size));
138+
}
139+
#else
140+
// unused parameters
141+
(void)nsects;
142+
(void)need_swap;
143+
#endif
144+
}
145+
146+
void osx_mach_o_readert::process_sections_64(
147+
uint32_t nsects,
148+
bool need_swap)
149+
{
150+
#ifdef __APPLE__
151+
for(uint32_t i = 0; i < nsects; ++i)
152+
{
153+
// NOLINTNEXTLINE(readability/identifiers)
154+
struct section_64 s;
155+
in.read(reinterpret_cast<char *>(&s), sizeof(s));
156+
157+
if(!in)
158+
throw deserialization_exceptiont("failed to read 64-bit Mach-O section");
159+
160+
if(need_swap)
161+
swap_section_64(&s, 1, NXHostByteOrder());
162+
163+
sections.emplace(s.sectname, sectiont(s.sectname, s.offset, s.size));
164+
}
165+
#else
166+
// unused parameters
167+
(void)nsects;
168+
(void)need_swap;
169+
#endif
170+
}
171+
172+
void osx_mach_o_readert::process_commands(
173+
uint32_t ncmds,
174+
std::size_t offset,
175+
bool need_swap)
176+
{
177+
#ifdef __APPLE__
178+
for(uint32_t i = 0; i < ncmds; ++i)
179+
{
180+
in.seekg(offset);
181+
182+
// NOLINTNEXTLINE(readability/identifiers)
183+
struct load_command lc;
184+
in.read(reinterpret_cast<char *>(&lc), sizeof(lc));
185+
186+
if(!in)
187+
throw deserialization_exceptiont("failed to read Mach-O command");
188+
189+
if(need_swap)
190+
swap_load_command(&lc, NXHostByteOrder());
191+
192+
// we may need to re-read the command once we have figured out its type; in
193+
// particular, segment commands contain additional information that we have
194+
// now just read a prefix of
195+
in.seekg(offset);
196+
197+
switch(lc.cmd)
198+
{
199+
case LC_SEGMENT:
200+
{
201+
// NOLINTNEXTLINE(readability/identifiers)
202+
struct segment_command seg;
203+
in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
204+
205+
if(!in)
206+
throw deserialization_exceptiont("failed to read Mach-O segment");
207+
208+
if(need_swap)
209+
swap_segment_command(&seg, NXHostByteOrder());
210+
211+
process_sections_32(seg.nsects, need_swap);
212+
break;
213+
}
214+
case LC_SEGMENT_64:
215+
{
216+
// NOLINTNEXTLINE(readability/identifiers)
217+
struct segment_command_64 seg;
218+
in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
219+
220+
if(!in)
221+
throw deserialization_exceptiont("failed to read Mach-O segment");
222+
223+
if(need_swap)
224+
swap_segment_command_64(&seg, NXHostByteOrder());
225+
226+
process_sections_64(seg.nsects, need_swap);
227+
break;
228+
}
229+
default:
230+
break;
231+
}
232+
233+
offset += lc.cmdsize;
234+
}
235+
#else
236+
// unused parameters
237+
(void)ncmds;
238+
(void)offset;
239+
(void)need_swap;
240+
#endif
241+
}
242+
243+
osx_mach_o_readert::osx_mach_o_readert(std::istream &_in) : in(_in)
244+
{
245+
// read magic
246+
uint32_t magic;
247+
in.read(reinterpret_cast<char *>(&magic), sizeof(magic));
248+
249+
if(!in)
250+
throw deserialization_exceptiont("failed to read Mach-O magic");
251+
252+
#ifdef __APPLE__
253+
bool is_64 = false, need_swap = false;
254+
switch(magic)
255+
{
256+
case MH_CIGAM:
257+
need_swap = true;
258+
break;
259+
case MH_MAGIC:
260+
break;
261+
case MH_CIGAM_64:
262+
need_swap = true;
263+
is_64 = true;
264+
break;
265+
case MH_MAGIC_64:
266+
is_64 = true;
267+
break;
268+
default:
269+
throw deserialization_exceptiont("no Mach-O magic");
270+
}
271+
272+
uint32_t ncmds = 0;
273+
std::size_t offset = 0;
274+
275+
// re-read from the beginning, now reading the full header
276+
in.seekg(0);
277+
278+
if(!is_64)
279+
{
280+
// NOLINTNEXTLINE(readability/identifiers)
281+
struct mach_header mh;
282+
in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
283+
284+
if(!in)
285+
throw deserialization_exceptiont("failed to read 32-bit Mach-O header");
286+
287+
if(need_swap)
288+
swap_mach_header(&mh, NXHostByteOrder());
289+
290+
ncmds = mh.ncmds;
291+
offset = sizeof(mh);
292+
}
293+
else
294+
{
295+
// NOLINTNEXTLINE(readability/identifiers)
296+
struct mach_header_64 mh;
297+
in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
298+
299+
if(!in)
300+
throw deserialization_exceptiont("failed to read 64-bit Mach-O header");
301+
302+
if(need_swap)
303+
swap_mach_header_64(&mh, NXHostByteOrder());
304+
305+
ncmds = mh.ncmds;
306+
offset = sizeof(mh);
307+
}
308+
309+
process_commands(ncmds, offset, need_swap);
310+
#endif
311+
}

src/goto-programs/osx_fat_reader.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Module: Read OS X Fat Binaries
1313
#define CPROVER_GOTO_PROGRAMS_OSX_FAT_READER_H
1414

1515
#include <fstream>
16+
#include <map>
1617
#include <string>
1718

1819
// we follow
@@ -35,4 +36,40 @@ class osx_fat_readert
3536

3637
bool is_osx_fat_magic(char hdr[4]);
3738

39+
class osx_mach_o_readert
40+
{
41+
public:
42+
explicit osx_mach_o_readert(std::istream &_in);
43+
44+
struct sectiont
45+
{
46+
sectiont(const std::string &_name, std::size_t _offset, std::size_t _size)
47+
: name(_name), offset(_offset), size(_size)
48+
{
49+
}
50+
51+
std::string name;
52+
std::size_t offset;
53+
std::size_t size;
54+
};
55+
56+
using sectionst = std::map<std::string, sectiont>;
57+
sectionst sections;
58+
59+
bool has_section(const std::string &name) const
60+
{
61+
return sections.find(name) != sections.end();
62+
}
63+
64+
private:
65+
std::istream &in;
66+
67+
void process_commands(uint32_t ncmds, std::size_t offset, bool need_swap);
68+
69+
void process_sections_32(uint32_t nsects, bool need_swap);
70+
void process_sections_64(uint32_t nsects, bool need_swap);
71+
};
72+
73+
bool is_osx_mach_object(char hdr[4]);
74+
3875
#endif // CPROVER_GOTO_PROGRAMS_OSX_FAT_READER_H

src/goto-programs/read_goto_binary.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,34 @@ bool read_goto_binary(
156156
message.error() << "failed to find goto binary in Mach-O file"
157157
<< messaget::eom;
158158
}
159+
else if(is_osx_mach_object(hdr))
160+
{
161+
messaget message(message_handler);
162+
163+
// Mach-O object file, may contain a goto-cc section
164+
try
165+
{
166+
osx_mach_o_readert mach_o_reader(in);
167+
168+
osx_mach_o_readert::sectionst::const_iterator entry =
169+
mach_o_reader.sections.find("goto-cc");
170+
if(entry != mach_o_reader.sections.end())
171+
{
172+
in.seekg(entry->second.offset);
173+
return read_bin_goto_object(
174+
in, filename, symbol_table, goto_functions, message_handler);
175+
}
176+
177+
// section not found
178+
messaget(message_handler).error()
179+
<< "failed to find goto-cc section in Mach-O binary" << messaget::eom;
180+
}
181+
182+
catch(const deserialization_exceptiont &e)
183+
{
184+
messaget(message_handler).error() << e.what() << messaget::eom;
185+
}
186+
}
159187
else
160188
{
161189
messaget(message_handler).error() <<
@@ -222,6 +250,22 @@ bool is_goto_binary(const std::string &filename)
222250
// ignore any errors
223251
}
224252
}
253+
else if(is_osx_mach_object(hdr))
254+
{
255+
// this _may_ have a goto-cc section
256+
try
257+
{
258+
in.seekg(0);
259+
osx_mach_o_readert mach_o_reader(in);
260+
if(mach_o_reader.has_section("goto-cc"))
261+
return true;
262+
}
263+
264+
catch(...)
265+
{
266+
// ignore any errors
267+
}
268+
}
225269

226270
return false;
227271
}

0 commit comments

Comments
 (0)