Skip to content

Commit c51a9e9

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 5bec823 commit c51a9e9

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed

src/goto-programs/osx_fat_reader.cpp

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Module: Read Mach-O
1717

1818
#ifdef __APPLE__
1919
#include <mach-o/fat.h>
20+
#include <mach-o/loader.h>
21+
#include <mach-o/swap.h>
2022
#endif
2123

2224
#include <util/run.h>
@@ -92,3 +94,143 @@ bool osx_fat_readert::extract_gb(
9294
"lipo", {"lipo", "-thin", "hppa7100LC", "-output", dest, source}) !=
9395
0;
9496
}
97+
98+
// guided by https://lowlevelbits.org/parsing-mach-o-files/
99+
bool is_osx_mach_object(char hdr[4])
100+
{
101+
#ifdef __APPLE__
102+
uint32_t *magic=reinterpret_cast<uint32_t*>(hdr);
103+
104+
switch(*magic)
105+
{
106+
case MH_MAGIC:
107+
case MH_CIGAM:
108+
case MH_MAGIC_64:
109+
case MH_CIGAM_64:
110+
return true;
111+
}
112+
#else
113+
(void)hdr; // unused parameter
114+
#endif
115+
116+
return false;
117+
}
118+
119+
void osx_mach_o_readert::read_32(bool need_swap)
120+
{
121+
}
122+
123+
void osx_mach_o_readert::read_64(bool need_swap)
124+
{
125+
}
126+
127+
osx_mach_o_readert::osx_mach_o_readert(std::istream &_in):in(_in)
128+
{
129+
// read magic
130+
uint32_t magic;
131+
in.read(reinterpret_cast<char *>(&magic), sizeof(magic));
132+
133+
if(!in)
134+
throw deserialization_exceptiont("failed to read Mach-O magic");
135+
136+
#ifdef __APPLE__
137+
bool is_64 = false, need_swap = false;
138+
switch(magic)
139+
{
140+
case MH_CIGAM:
141+
need_swap = true;
142+
break;
143+
case MH_MAGIC:
144+
break;
145+
case MH_CIGAM_64:
146+
need_swap = true;
147+
is_64 = true;
148+
break;
149+
case MH_MAGIC_64:
150+
is_64 = true;
151+
break;
152+
default:
153+
throw deserialization_exceptiont("no Mach-O magic");
154+
}
155+
156+
uint32_t ncmds = 0;
157+
std::size_t offset = 0;
158+
159+
if(!is_64)
160+
{
161+
struct mach_header mh;
162+
in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
163+
164+
if(!in)
165+
throw deserialization_exceptiont("failed to read 32-bit Mach-O header");
166+
167+
if(need_swap)
168+
swap_mach_header(&mh, 0);
169+
170+
ncmds = mh.ncmds;
171+
offset = sizeof(mh);
172+
}
173+
else
174+
{
175+
struct mach_header_64 mh;
176+
in.read(reinterpret_cast<char *>(&mh), sizeof(mh));
177+
178+
if(!in)
179+
throw deserialization_exceptiont("failed to read 64-bit Mach-O header");
180+
181+
if(need_swap)
182+
swap_mach_header_64(mh, 0);
183+
184+
ncmds = mh.ncmds;
185+
offset = sizeof(mh);
186+
}
187+
188+
for(uint32_t i = 0; i < ncmds; ++i)
189+
{
190+
struct load_command lc;
191+
in.read(reinterpret_cast<char *>(&lc), sizeof(lc));
192+
193+
if(!in)
194+
throw deserialization_exceptiont("failed to read Mach-O command");
195+
196+
if(need_swap)
197+
swap_load_command(&lc, 0);
198+
199+
switch(lc.cmd)
200+
{
201+
case LC_SEGMENT:
202+
{
203+
struct segment_command seg;
204+
in.read(reinterpret_cast<char *>(&seg), sizeof(seg));
205+
206+
if(!in)
207+
throw deserialization_exceptiont("failed to read Mach-O segment");
208+
209+
if(need_swap)
210+
swap_segment_command(&seg);
211+
212+
segments.emplace(seg.segname, segmentt(seg.segname, offset, lc.cmdsize));
213+
break;
214+
}
215+
case LC_SEGMENT_64:
216+
{
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);
225+
226+
segments.emplace(seg.segname, segmentt(seg.segname, offset, lc.cmdsize));
227+
break;
228+
}
229+
default:
230+
break;
231+
}
232+
233+
offset += lc.cmdsize;
234+
}
235+
#endif
236+
}

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+
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)