Skip to content

Commit be80f24

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 6049d60 commit be80f24

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed

src/goto-programs/osx_fat_reader.cpp

Lines changed: 140 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,140 @@ 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+
osx_mach_o_readert::osx_mach_o_readert(std::istream &_in) : in(_in)
121+
{
122+
// read magic
123+
uint32_t magic;
124+
in.read(reinterpret_cast<char *>(&magic), sizeof(magic));
125+
126+
if(!in)
127+
throw deserialization_exceptiont("failed to read Mach-O magic");
128+
129+
#ifdef __APPLE__
130+
bool is_64 = false, need_swap = false;
131+
switch(magic)
132+
{
133+
case MH_CIGAM:
134+
need_swap = true;
135+
break;
136+
case MH_MAGIC:
137+
break;
138+
case MH_CIGAM_64:
139+
need_swap = true;
140+
is_64 = true;
141+
break;
142+
case MH_MAGIC_64:
143+
is_64 = true;
144+
break;
145+
default:
146+
throw deserialization_exceptiont("no Mach-O magic");
147+
}
148+
149+
uint32_t ncmds = 0;
150+
std::size_t offset = 0;
151+
152+
if(!is_64)
153+
{
154+
// NOLINTNEXTLINE(readability/identifiers)
155+
struct mach_header mh;
156+
in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
157+
158+
if(!in)
159+
throw deserialization_exceptiont("failed to read 32-bit Mach-O header");
160+
161+
if(need_swap)
162+
swap_mach_header(&mh, NXHostByteOrder());
163+
164+
ncmds = mh.ncmds;
165+
offset = sizeof(mh);
166+
}
167+
else
168+
{
169+
// NOLINTNEXTLINE(readability/identifiers)
170+
struct mach_header_64 mh;
171+
in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
172+
173+
if(!in)
174+
throw deserialization_exceptiont("failed to read 64-bit Mach-O header");
175+
176+
if(need_swap)
177+
swap_mach_header_64(mh, NXHostByteOrder());
178+
179+
ncmds = mh.ncmds;
180+
offset = sizeof(mh);
181+
}
182+
183+
for(uint32_t i = 0; i < ncmds; ++i)
184+
{
185+
// NOLINTNEXTLINE(readability/identifiers)
186+
struct load_command lc;
187+
in.read(reinterpret_cast<char *>(&lc), sizeof(lc));
188+
189+
if(!in)
190+
throw deserialization_exceptiont("failed to read Mach-O command");
191+
192+
if(need_swap)
193+
swap_load_command(&lc, NXHostByteOrder());
194+
195+
switch(lc.cmd)
196+
{
197+
case LC_SEGMENT:
198+
{
199+
// NOLINTNEXTLINE(readability/identifiers)
200+
struct segment_command seg;
201+
in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
202+
203+
if(!in)
204+
throw deserialization_exceptiont("failed to read Mach-O segment");
205+
206+
if(need_swap)
207+
swap_segment_command(&seg, NXHostByteOrder());
208+
209+
segments.emplace(seg.segname, segmentt(seg.segname, offset, lc.cmdsize));
210+
break;
211+
}
212+
case LC_SEGMENT_64:
213+
{
214+
// NOLINTNEXTLINE(readability/identifiers)
215+
struct segment_command_64 seg;
216+
in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
217+
218+
if(!in)
219+
throw deserialization_exceptiont("failed to read Mach-O segment");
220+
221+
if(need_swap)
222+
swap_segment_command_64(&seg, NXHostByteOrder());
223+
224+
segments.emplace(seg.segname, segmentt(seg.segname, offset, lc.cmdsize));
225+
break;
226+
}
227+
default:
228+
break;
229+
}
230+
231+
offset += lc.cmdsize;
232+
}
233+
#endif
234+
}

src/goto-programs/osx_fat_reader.h

Lines changed: 32 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,35 @@ 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 segmentt
45+
{
46+
segmentt(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 segmentst = std::map<std::string, segmentt>;
57+
segmentst segments;
58+
59+
bool has_section(const std::string &name) const
60+
{
61+
return segments.find(name) != segments.end();
62+
}
63+
64+
private:
65+
std::istream &in;
66+
};
67+
68+
bool is_osx_mach_object(char hdr[4]);
69+
3870
#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::segmentst::const_iterator entry =
169+
mach_o_reader.segments.find("goto-cc");
170+
if(entry != mach_o_reader.segments.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)