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