xref: /xnu-12377.41.6/tools/kt-dump/kt-dump.cpp (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1*bbb1b6f9SApple OSS Distributions // clang++ -o kt-dump{,.cpp} -Wall -std=c++20
2*bbb1b6f9SApple OSS Distributions 
3*bbb1b6f9SApple OSS Distributions #include <assert.h>
4*bbb1b6f9SApple OSS Distributions #include <cstdio>
5*bbb1b6f9SApple OSS Distributions #include <filesystem>
6*bbb1b6f9SApple OSS Distributions #include <fstream>
7*bbb1b6f9SApple OSS Distributions #include <iostream>
8*bbb1b6f9SApple OSS Distributions #include <mach-o/fat.h>
9*bbb1b6f9SApple OSS Distributions #include <mach-o/loader.h>
10*bbb1b6f9SApple OSS Distributions #include <optional>
11*bbb1b6f9SApple OSS Distributions #include <set>
12*bbb1b6f9SApple OSS Distributions #include <span>
13*bbb1b6f9SApple OSS Distributions #include <vector>
14*bbb1b6f9SApple OSS Distributions #include <removefile.h>
15*bbb1b6f9SApple OSS Distributions #include <unistd.h>
16*bbb1b6f9SApple OSS Distributions #include <spawn.h>
17*bbb1b6f9SApple OSS Distributions #include <fcntl.h>
18*bbb1b6f9SApple OSS Distributions #include <uuid/uuid.h>
19*bbb1b6f9SApple OSS Distributions 
20*bbb1b6f9SApple OSS Distributions /*
21*bbb1b6f9SApple OSS Distributions  * kt-dump.cpp
22*bbb1b6f9SApple OSS Distributions  *
23*bbb1b6f9SApple OSS Distributions  * Tool to dump the kalloc type information from a given Mach-O binary.
24*bbb1b6f9SApple OSS Distributions  * Usage:
25*bbb1b6f9SApple OSS Distributions  * kt-dump [-f <simple|json|struct|stats>] <mach-o>
26*bbb1b6f9SApple OSS Distributions  *
27*bbb1b6f9SApple OSS Distributions  * The tool will scan the given Mach-O to find the __kalloc_type section.
28*bbb1b6f9SApple OSS Distributions  * It will then walk that section using the kalloc_type_view definition
29*bbb1b6f9SApple OSS Distributions  * provided below, in order to dump the type names and signatures that
30*bbb1b6f9SApple OSS Distributions  * have been compiled into the binary.
31*bbb1b6f9SApple OSS Distributions  *
32*bbb1b6f9SApple OSS Distributions  * The output "format" can be specified with the -f option. The default
33*bbb1b6f9SApple OSS Distributions  * format ("simple") will output the type name and the signature,
34*bbb1b6f9SApple OSS Distributions  * enclosed in square brackets. The "json" format will print a JSON
35*bbb1b6f9SApple OSS Distributions  * dictionary for each kalloc_type_view entry, including the type name,
36*bbb1b6f9SApple OSS Distributions  * its size and the signature. The "struct" output format will use
37*bbb1b6f9SApple OSS Distributions  * __builtin_dump_struct to dump a C-like representation of the view.
38*bbb1b6f9SApple OSS Distributions  * Finally, if the "stats" output format is chosen, the tool will only
39*bbb1b6f9SApple OSS Distributions  * show overall information about the __kalloc_type section.
40*bbb1b6f9SApple OSS Distributions  *
41*bbb1b6f9SApple OSS Distributions  * The tool supports both MH_KEXT_BUNDLE and kernel cache files. If a
42*bbb1b6f9SApple OSS Distributions  * FAT Mach-O is provided, it must contain an arm64 slice.
43*bbb1b6f9SApple OSS Distributions  */
44*bbb1b6f9SApple OSS Distributions 
45*bbb1b6f9SApple OSS Distributions /* Note: these must be kept in sync with the defs in kalloc.h/zalloc.h */
46*bbb1b6f9SApple OSS Distributions 
47*bbb1b6f9SApple OSS Distributions __options_decl(kalloc_type_flags_t, uint32_t, {
48*bbb1b6f9SApple OSS Distributions 	KT_DEFAULT        = 0x0001,
49*bbb1b6f9SApple OSS Distributions 	KT_PRIV_ACCT      = 0x0002,
50*bbb1b6f9SApple OSS Distributions 	KT_SHARED_ACCT    = 0x0004,
51*bbb1b6f9SApple OSS Distributions 	KT_DATA_ONLY      = 0x0008,
52*bbb1b6f9SApple OSS Distributions 	KT_VM             = 0x0010,
53*bbb1b6f9SApple OSS Distributions 	KT_CHANGED        = 0x0020,
54*bbb1b6f9SApple OSS Distributions 	KT_CHANGED2       = 0x0040,
55*bbb1b6f9SApple OSS Distributions 	KT_PTR_ARRAY      = 0x0080,
56*bbb1b6f9SApple OSS Distributions 	KT_NOEARLY        = 0x2000,
57*bbb1b6f9SApple OSS Distributions 	KT_SLID           = 0x4000,
58*bbb1b6f9SApple OSS Distributions 	KT_PROCESSED      = 0x8000,
59*bbb1b6f9SApple OSS Distributions 	KT_HASH           = 0xffff0000,
60*bbb1b6f9SApple OSS Distributions });
61*bbb1b6f9SApple OSS Distributions 
62*bbb1b6f9SApple OSS Distributions __options_decl(kalloc_type_version_t, uint16_t, {
63*bbb1b6f9SApple OSS Distributions 	KT_V1             = 0x0001,
64*bbb1b6f9SApple OSS Distributions });
65*bbb1b6f9SApple OSS Distributions 
66*bbb1b6f9SApple OSS Distributions /* fixme we need to recognize Intel for which this is 20*/
67*bbb1b6f9SApple OSS Distributions #define KHEAP_NUM_ZONES         22
68*bbb1b6f9SApple OSS Distributions 
69*bbb1b6f9SApple OSS Distributions struct zone_view {
70*bbb1b6f9SApple OSS Distributions 	void *zv_zone;
71*bbb1b6f9SApple OSS Distributions 	void *zv_stats;
72*bbb1b6f9SApple OSS Distributions 	const char *zv_name;
73*bbb1b6f9SApple OSS Distributions 	void *zv_next;
74*bbb1b6f9SApple OSS Distributions };
75*bbb1b6f9SApple OSS Distributions 
76*bbb1b6f9SApple OSS Distributions struct kalloc_type_view {
77*bbb1b6f9SApple OSS Distributions 	struct zone_view    kt_zv;
78*bbb1b6f9SApple OSS Distributions 	const char         *kt_signature;
79*bbb1b6f9SApple OSS Distributions 	kalloc_type_flags_t kt_flags;
80*bbb1b6f9SApple OSS Distributions 	uint32_t            kt_size;
81*bbb1b6f9SApple OSS Distributions 	struct zone        *kt_zearly;
82*bbb1b6f9SApple OSS Distributions 	struct zone        *kt_zsig;
83*bbb1b6f9SApple OSS Distributions };
84*bbb1b6f9SApple OSS Distributions 
85*bbb1b6f9SApple OSS Distributions struct kalloc_type_var_view {
86*bbb1b6f9SApple OSS Distributions 	kalloc_type_version_t   kt_version;
87*bbb1b6f9SApple OSS Distributions 	uint16_t                kt_size_hdr;
88*bbb1b6f9SApple OSS Distributions 	/*
89*bbb1b6f9SApple OSS Distributions 	 * Temporary: Needs to be 32bits cause we have many structs that use
90*bbb1b6f9SApple OSS Distributions 	 * IONew/Delete that are larger than 32K.
91*bbb1b6f9SApple OSS Distributions 	 */
92*bbb1b6f9SApple OSS Distributions 	uint32_t                kt_size_type;
93*bbb1b6f9SApple OSS Distributions 	struct zone_stats      *kt_stats;
94*bbb1b6f9SApple OSS Distributions 	const char             *kt_name;
95*bbb1b6f9SApple OSS Distributions 	struct zone_view       *kt_next;
96*bbb1b6f9SApple OSS Distributions 	uint16_t                kt_heap_start;
97*bbb1b6f9SApple OSS Distributions 	uint8_t                 kt_zones[KHEAP_NUM_ZONES];
98*bbb1b6f9SApple OSS Distributions 	const char             *kt_sig_hdr;
99*bbb1b6f9SApple OSS Distributions 	const char             *kt_sig_type;
100*bbb1b6f9SApple OSS Distributions 	kalloc_type_flags_t     kt_flags;
101*bbb1b6f9SApple OSS Distributions };
102*bbb1b6f9SApple OSS Distributions 
103*bbb1b6f9SApple OSS Distributions template <typename T> struct macho_section {
104*bbb1b6f9SApple OSS Distributions 	section_64 section;
105*bbb1b6f9SApple OSS Distributions 	std::span<const T> contents;
106*bbb1b6f9SApple OSS Distributions 
macho_sectionmacho_section107*bbb1b6f9SApple OSS Distributions 	macho_section(const section_64 &sec, std::span<uint8_t> data)
108*bbb1b6f9SApple OSS Distributions 		: section(sec),
109*bbb1b6f9SApple OSS Distributions 		contents(reinterpret_cast<T *>(
110*bbb1b6f9SApple OSS Distributions 			    data.subspan(sec.offset, sec.size / sizeof(T)).data()),
111*bbb1b6f9SApple OSS Distributions 		    sec.size / sizeof(T))
112*bbb1b6f9SApple OSS Distributions 	{
113*bbb1b6f9SApple OSS Distributions 	}
114*bbb1b6f9SApple OSS Distributions 
115*bbb1b6f9SApple OSS Distributions 	size_t
elem_sizemacho_section116*bbb1b6f9SApple OSS Distributions 	elem_size() const
117*bbb1b6f9SApple OSS Distributions 	{
118*bbb1b6f9SApple OSS Distributions 		return sizeof(T);
119*bbb1b6f9SApple OSS Distributions 	}
120*bbb1b6f9SApple OSS Distributions 
121*bbb1b6f9SApple OSS Distributions 	size_t
elem_countmacho_section122*bbb1b6f9SApple OSS Distributions 	elem_count() const
123*bbb1b6f9SApple OSS Distributions 	{
124*bbb1b6f9SApple OSS Distributions 		return section.size / elem_size();
125*bbb1b6f9SApple OSS Distributions 	}
126*bbb1b6f9SApple OSS Distributions };
127*bbb1b6f9SApple OSS Distributions 
128*bbb1b6f9SApple OSS Distributions int
printf_with_indent(const char * indent,const char * format,...)129*bbb1b6f9SApple OSS Distributions printf_with_indent(const char *indent, const char *format, ...)
130*bbb1b6f9SApple OSS Distributions {
131*bbb1b6f9SApple OSS Distributions 	int n = 0;
132*bbb1b6f9SApple OSS Distributions 
133*bbb1b6f9SApple OSS Distributions 	va_list ap;
134*bbb1b6f9SApple OSS Distributions 	if (*indent) {
135*bbb1b6f9SApple OSS Distributions 		std::cout << indent;
136*bbb1b6f9SApple OSS Distributions 		n += strlen(indent);
137*bbb1b6f9SApple OSS Distributions 	}
138*bbb1b6f9SApple OSS Distributions 
139*bbb1b6f9SApple OSS Distributions 	va_start(ap, format);
140*bbb1b6f9SApple OSS Distributions 	n += vprintf(format, ap);
141*bbb1b6f9SApple OSS Distributions 	va_end(ap);
142*bbb1b6f9SApple OSS Distributions 	return n;
143*bbb1b6f9SApple OSS Distributions }
144*bbb1b6f9SApple OSS Distributions 
145*bbb1b6f9SApple OSS Distributions static inline const char *
decode_string(const macho_section<char> & sec_cstring,const char * string)146*bbb1b6f9SApple OSS Distributions decode_string(const macho_section<char> &sec_cstring, const char *string)
147*bbb1b6f9SApple OSS Distributions {
148*bbb1b6f9SApple OSS Distributions 	/*
149*bbb1b6f9SApple OSS Distributions 	 * Compute the offsets into the __cstring section.
150*bbb1b6f9SApple OSS Distributions 	 * This works for both single kexts (MH_KEXT_BUNDLE) and kernel caches.
151*bbb1b6f9SApple OSS Distributions 	 * For the former, the __cstring section addr is the offset of the section
152*bbb1b6f9SApple OSS Distributions 	 * into the slice. For the latter, the __cstring section addr is the virtual
153*bbb1b6f9SApple OSS Distributions 	 * address of the section, and the fields are pointers into such space.
154*bbb1b6f9SApple OSS Distributions 	 */
155*bbb1b6f9SApple OSS Distributions 
156*bbb1b6f9SApple OSS Distributions 	if (string) {
157*bbb1b6f9SApple OSS Distributions 		uintptr_t string_p = reinterpret_cast<uintptr_t>(string);
158*bbb1b6f9SApple OSS Distributions 		uint32_t string_off = (uint32_t)string_p;
159*bbb1b6f9SApple OSS Distributions 
160*bbb1b6f9SApple OSS Distributions 		return &sec_cstring.contents[string_off - sec_cstring.section.offset];
161*bbb1b6f9SApple OSS Distributions 	}
162*bbb1b6f9SApple OSS Distributions 
163*bbb1b6f9SApple OSS Distributions 	return nullptr;
164*bbb1b6f9SApple OSS Distributions }
165*bbb1b6f9SApple OSS Distributions 
166*bbb1b6f9SApple OSS Distributions static enum class out_fmt_type {
167*bbb1b6f9SApple OSS Distributions 	SIMPLE,
168*bbb1b6f9SApple OSS Distributions 	JSON,
169*bbb1b6f9SApple OSS Distributions 	STRUCT,
170*bbb1b6f9SApple OSS Distributions 	STATS
171*bbb1b6f9SApple OSS Distributions } out_fmt = out_fmt_type::SIMPLE;
172*bbb1b6f9SApple OSS Distributions 
173*bbb1b6f9SApple OSS Distributions class image {
174*bbb1b6f9SApple OSS Distributions 	const std::span<uint8_t> slice_contents;
175*bbb1b6f9SApple OSS Distributions 	size_t slice_mh_offs;
176*bbb1b6f9SApple OSS Distributions 
177*bbb1b6f9SApple OSS Distributions 	std::optional<macho_section<kalloc_type_view> > sec_types;
178*bbb1b6f9SApple OSS Distributions 	std::optional<macho_section<kalloc_type_var_view> > sec_types_var;
179*bbb1b6f9SApple OSS Distributions 	std::optional<macho_section<char> > sec_cstring;
180*bbb1b6f9SApple OSS Distributions 	uuid_t img_uuid;
181*bbb1b6f9SApple OSS Distributions 
182*bbb1b6f9SApple OSS Distributions 	std::set<std::pair<const char *, const char *> > dedup_entries;
183*bbb1b6f9SApple OSS Distributions 	std::set<std::tuple<const char *, const char *, const char *> > dedup_entries_var;
184*bbb1b6f9SApple OSS Distributions 	std::set<const char *> dedup_strings;
185*bbb1b6f9SApple OSS Distributions 
186*bbb1b6f9SApple OSS Distributions 	struct {
187*bbb1b6f9SApple OSS Distributions 		size_t uniq_structs = 0;
188*bbb1b6f9SApple OSS Distributions 		size_t uniq_structs_var = 0;
189*bbb1b6f9SApple OSS Distributions 		size_t names_sz = 0;
190*bbb1b6f9SApple OSS Distributions 		size_t sig_sz = 0;
191*bbb1b6f9SApple OSS Distributions 	} stats;
192*bbb1b6f9SApple OSS Distributions 
193*bbb1b6f9SApple OSS Distributions 	void
dump_types(const char * indent)194*bbb1b6f9SApple OSS Distributions 	dump_types(const char *indent)
195*bbb1b6f9SApple OSS Distributions 	{
196*bbb1b6f9SApple OSS Distributions 		const char *sep = "\n";
197*bbb1b6f9SApple OSS Distributions 
198*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
199*bbb1b6f9SApple OSS Distributions 			std::cout << ",\n" << indent << "  \"fixed\": [";
200*bbb1b6f9SApple OSS Distributions 		}
201*bbb1b6f9SApple OSS Distributions 
202*bbb1b6f9SApple OSS Distributions 		for (auto &ktv : sec_types->contents) {
203*bbb1b6f9SApple OSS Distributions 			const char *name = decode_string(*sec_cstring, ktv.kt_zv.zv_name);
204*bbb1b6f9SApple OSS Distributions 			const char *sig = decode_string(*sec_cstring, ktv.kt_signature);
205*bbb1b6f9SApple OSS Distributions 
206*bbb1b6f9SApple OSS Distributions 			/* Only output the equal entries (same name/signature) once */
207*bbb1b6f9SApple OSS Distributions 			if (!dedup_entries.insert(std::make_tuple(name, sig)).second) {
208*bbb1b6f9SApple OSS Distributions 				continue;
209*bbb1b6f9SApple OSS Distributions 			}
210*bbb1b6f9SApple OSS Distributions 
211*bbb1b6f9SApple OSS Distributions 			if (ktv.kt_flags & KT_DATA_ONLY) {
212*bbb1b6f9SApple OSS Distributions 				sig = "data";
213*bbb1b6f9SApple OSS Distributions 			}
214*bbb1b6f9SApple OSS Distributions 			if (dedup_strings.insert(name).second) {
215*bbb1b6f9SApple OSS Distributions 				stats.names_sz += strlen(name) + 1;
216*bbb1b6f9SApple OSS Distributions 			}
217*bbb1b6f9SApple OSS Distributions 			if (dedup_strings.insert(sig).second) {
218*bbb1b6f9SApple OSS Distributions 				stats.sig_sz += strlen(sig) + 1;
219*bbb1b6f9SApple OSS Distributions 			}
220*bbb1b6f9SApple OSS Distributions 
221*bbb1b6f9SApple OSS Distributions 			stats.uniq_structs++;
222*bbb1b6f9SApple OSS Distributions 			if (out_fmt != out_fmt_type::STRUCT) {
223*bbb1b6f9SApple OSS Distributions 				name += strlen("site.");
224*bbb1b6f9SApple OSS Distributions 			}
225*bbb1b6f9SApple OSS Distributions 
226*bbb1b6f9SApple OSS Distributions 			switch (out_fmt) {
227*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::SIMPLE:
228*bbb1b6f9SApple OSS Distributions 				std::cout << indent << name << " [" << sig << "]\n";
229*bbb1b6f9SApple OSS Distributions 				break;
230*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::JSON:
231*bbb1b6f9SApple OSS Distributions 				std::cout << sep << indent
232*bbb1b6f9SApple OSS Distributions 				          << "    { \"name\": \"" << name << "\", "
233*bbb1b6f9SApple OSS Distributions 				          << "\"size\": " << ktv.kt_size << ", "
234*bbb1b6f9SApple OSS Distributions 				          << "\"sig\": \"" << sig << '"'
235*bbb1b6f9SApple OSS Distributions 				          << " }";
236*bbb1b6f9SApple OSS Distributions 				sep = ",\n";
237*bbb1b6f9SApple OSS Distributions 				break;
238*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::STRUCT: {
239*bbb1b6f9SApple OSS Distributions 				/* Make a copy and fill in the pointers to the cstring section */
240*bbb1b6f9SApple OSS Distributions 				kalloc_type_view printable_view = ktv;
241*bbb1b6f9SApple OSS Distributions 				printable_view.kt_zv.zv_name = name;
242*bbb1b6f9SApple OSS Distributions 				printable_view.kt_signature = sig;
243*bbb1b6f9SApple OSS Distributions 				__builtin_dump_struct(&printable_view, &printf_with_indent, indent);
244*bbb1b6f9SApple OSS Distributions 			} break;
245*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::STATS:
246*bbb1b6f9SApple OSS Distributions 				break;
247*bbb1b6f9SApple OSS Distributions 			}
248*bbb1b6f9SApple OSS Distributions 		}
249*bbb1b6f9SApple OSS Distributions 
250*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
251*bbb1b6f9SApple OSS Distributions 			std::cout << std::endl << indent << "  ]";
252*bbb1b6f9SApple OSS Distributions 		}
253*bbb1b6f9SApple OSS Distributions 	}
254*bbb1b6f9SApple OSS Distributions 
255*bbb1b6f9SApple OSS Distributions 	void
dump_types_var(const char * indent)256*bbb1b6f9SApple OSS Distributions 	dump_types_var(const char *indent)
257*bbb1b6f9SApple OSS Distributions 	{
258*bbb1b6f9SApple OSS Distributions 		const char *sep = "\n";
259*bbb1b6f9SApple OSS Distributions 
260*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
261*bbb1b6f9SApple OSS Distributions 			std::cout << ",\n" << indent << "  \"var\": [";
262*bbb1b6f9SApple OSS Distributions 		}
263*bbb1b6f9SApple OSS Distributions 
264*bbb1b6f9SApple OSS Distributions 		for (auto &ktv : sec_types_var->contents) {
265*bbb1b6f9SApple OSS Distributions 			const char *name = decode_string(*sec_cstring, ktv.kt_name);
266*bbb1b6f9SApple OSS Distributions 			const char *sig_hdr = decode_string(*sec_cstring, ktv.kt_sig_hdr);
267*bbb1b6f9SApple OSS Distributions 			const char *sig_type = decode_string(*sec_cstring, ktv.kt_sig_type);
268*bbb1b6f9SApple OSS Distributions 
269*bbb1b6f9SApple OSS Distributions 			/* Only output the equal entries (same name/signature) once */
270*bbb1b6f9SApple OSS Distributions 			if (!dedup_entries_var.insert(std::make_tuple(name, sig_hdr, sig_type)).second) {
271*bbb1b6f9SApple OSS Distributions 				continue;
272*bbb1b6f9SApple OSS Distributions 			}
273*bbb1b6f9SApple OSS Distributions 
274*bbb1b6f9SApple OSS Distributions 			if (dedup_strings.insert(name).second) {
275*bbb1b6f9SApple OSS Distributions 				stats.names_sz += strlen(name) + 1;
276*bbb1b6f9SApple OSS Distributions 			}
277*bbb1b6f9SApple OSS Distributions 			if (sig_hdr && dedup_strings.insert(sig_hdr).second) {
278*bbb1b6f9SApple OSS Distributions 				stats.sig_sz += strlen(sig_hdr) + 1;
279*bbb1b6f9SApple OSS Distributions 			}
280*bbb1b6f9SApple OSS Distributions 			if (dedup_strings.insert(sig_type).second) {
281*bbb1b6f9SApple OSS Distributions 				stats.sig_sz += strlen(sig_type) + 1;
282*bbb1b6f9SApple OSS Distributions 			}
283*bbb1b6f9SApple OSS Distributions 
284*bbb1b6f9SApple OSS Distributions 			if (ktv.kt_flags & KT_DATA_ONLY) {
285*bbb1b6f9SApple OSS Distributions 				sig_type = "data";
286*bbb1b6f9SApple OSS Distributions 				if (ktv.kt_size_hdr) {
287*bbb1b6f9SApple OSS Distributions 					sig_hdr = "data";
288*bbb1b6f9SApple OSS Distributions 				}
289*bbb1b6f9SApple OSS Distributions 			}
290*bbb1b6f9SApple OSS Distributions 			stats.uniq_structs_var++;
291*bbb1b6f9SApple OSS Distributions 			if (out_fmt != out_fmt_type::STRUCT) {
292*bbb1b6f9SApple OSS Distributions 				name += strlen("site.");
293*bbb1b6f9SApple OSS Distributions 			}
294*bbb1b6f9SApple OSS Distributions 
295*bbb1b6f9SApple OSS Distributions 			switch (out_fmt) {
296*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::SIMPLE:
297*bbb1b6f9SApple OSS Distributions 				if (sig_hdr) {
298*bbb1b6f9SApple OSS Distributions 					std::cout << indent << name
299*bbb1b6f9SApple OSS Distributions 					          << " [" << sig_hdr << ", " << sig_type << "]\n";
300*bbb1b6f9SApple OSS Distributions 				} else {
301*bbb1b6f9SApple OSS Distributions 					std::cout << indent << name
302*bbb1b6f9SApple OSS Distributions 					          << " [, " << sig_type << "]\n";
303*bbb1b6f9SApple OSS Distributions 				}
304*bbb1b6f9SApple OSS Distributions 				break;
305*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::JSON:
306*bbb1b6f9SApple OSS Distributions 				std::cout << sep << indent
307*bbb1b6f9SApple OSS Distributions 				          << "    { \"name\": \"" << name << "\", ";
308*bbb1b6f9SApple OSS Distributions 				if (sig_hdr) {
309*bbb1b6f9SApple OSS Distributions 					std::cout << "\"size_hdr\": " << ktv.kt_size_hdr << ", "
310*bbb1b6f9SApple OSS Distributions 					          << "\"sig_hdr\": \"" << sig_hdr << "\", ";
311*bbb1b6f9SApple OSS Distributions 				}
312*bbb1b6f9SApple OSS Distributions 				std::cout << "\"size_type\": " << ktv.kt_size_type << ", "
313*bbb1b6f9SApple OSS Distributions 				          << "\"sig_type\": \"" << sig_type << '"'
314*bbb1b6f9SApple OSS Distributions 				          << " }";
315*bbb1b6f9SApple OSS Distributions 				sep = ",\n";
316*bbb1b6f9SApple OSS Distributions 				break;
317*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::STRUCT: {
318*bbb1b6f9SApple OSS Distributions 				/* Make a copy and fill in the pointers to the cstring section */
319*bbb1b6f9SApple OSS Distributions 				kalloc_type_var_view printable_view = ktv;
320*bbb1b6f9SApple OSS Distributions 				printable_view.kt_name = name;
321*bbb1b6f9SApple OSS Distributions 				printable_view.kt_sig_hdr = sig_hdr;
322*bbb1b6f9SApple OSS Distributions 				printable_view.kt_sig_type = sig_type;
323*bbb1b6f9SApple OSS Distributions 				__builtin_dump_struct(&printable_view, &printf_with_indent, indent);
324*bbb1b6f9SApple OSS Distributions 			} break;
325*bbb1b6f9SApple OSS Distributions 			case out_fmt_type::STATS:
326*bbb1b6f9SApple OSS Distributions 				break;
327*bbb1b6f9SApple OSS Distributions 			}
328*bbb1b6f9SApple OSS Distributions 		}
329*bbb1b6f9SApple OSS Distributions 
330*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
331*bbb1b6f9SApple OSS Distributions 			std::cout << std::endl << indent << "  ]";
332*bbb1b6f9SApple OSS Distributions 		}
333*bbb1b6f9SApple OSS Distributions 	}
334*bbb1b6f9SApple OSS Distributions 
335*bbb1b6f9SApple OSS Distributions 	const mach_header_64 *
mh_hdr() const336*bbb1b6f9SApple OSS Distributions 	mh_hdr() const
337*bbb1b6f9SApple OSS Distributions 	{
338*bbb1b6f9SApple OSS Distributions 		return reinterpret_cast<const mach_header_64 *>(slice_contents.data() + slice_mh_offs);
339*bbb1b6f9SApple OSS Distributions 	}
340*bbb1b6f9SApple OSS Distributions 
341*bbb1b6f9SApple OSS Distributions public:
image(std::span<uint8_t> contents,size_t mh_offs=0)342*bbb1b6f9SApple OSS Distributions 	image(std::span<uint8_t> contents, size_t mh_offs = 0)
343*bbb1b6f9SApple OSS Distributions 		: slice_contents{contents}, slice_mh_offs{mh_offs}
344*bbb1b6f9SApple OSS Distributions 	{
345*bbb1b6f9SApple OSS Distributions 		auto *hdr = mh_hdr();
346*bbb1b6f9SApple OSS Distributions 		std::span<uint8_t> commands = contents.subspan(mh_offs + sizeof(*hdr));
347*bbb1b6f9SApple OSS Distributions 
348*bbb1b6f9SApple OSS Distributions 		assert(hdr->magic == MH_MAGIC_64);
349*bbb1b6f9SApple OSS Distributions 
350*bbb1b6f9SApple OSS Distributions 		for (size_t i = 0; i < hdr->ncmds; i++) {
351*bbb1b6f9SApple OSS Distributions 			auto *cmd = reinterpret_cast<const load_command *>(commands.data());
352*bbb1b6f9SApple OSS Distributions 
353*bbb1b6f9SApple OSS Distributions 			commands = commands.subspan(cmd->cmdsize);
354*bbb1b6f9SApple OSS Distributions 
355*bbb1b6f9SApple OSS Distributions 			switch (cmd->cmd) {
356*bbb1b6f9SApple OSS Distributions 			case LC_SEGMENT_64:
357*bbb1b6f9SApple OSS Distributions 				break;
358*bbb1b6f9SApple OSS Distributions 			case LC_UUID:
359*bbb1b6f9SApple OSS Distributions 				uuid_copy(img_uuid, reinterpret_cast<const uuid_command *>(cmd)->uuid);
360*bbb1b6f9SApple OSS Distributions 				continue;
361*bbb1b6f9SApple OSS Distributions 			default:
362*bbb1b6f9SApple OSS Distributions 				continue;
363*bbb1b6f9SApple OSS Distributions 			}
364*bbb1b6f9SApple OSS Distributions 
365*bbb1b6f9SApple OSS Distributions 			auto *seg_cmd = reinterpret_cast<const segment_command_64 *>(cmd);
366*bbb1b6f9SApple OSS Distributions 			const std::span<section_64> sections((section_64 *)(seg_cmd + 1), seg_cmd->nsects);
367*bbb1b6f9SApple OSS Distributions 
368*bbb1b6f9SApple OSS Distributions 			for (auto &sec : sections) {
369*bbb1b6f9SApple OSS Distributions 				std::string_view segname(sec.segname);
370*bbb1b6f9SApple OSS Distributions 				std::string_view sectname(sec.sectname);
371*bbb1b6f9SApple OSS Distributions 
372*bbb1b6f9SApple OSS Distributions 				if (sectname == "__kalloc_type") {
373*bbb1b6f9SApple OSS Distributions 					assert(!sec_types && "Multiple __kalloc_type sections?");
374*bbb1b6f9SApple OSS Distributions 					sec_types = macho_section<kalloc_type_view>(sec, slice_contents);
375*bbb1b6f9SApple OSS Distributions 					assert(sec.size % sec_types->elem_size() == 0 &&
376*bbb1b6f9SApple OSS Distributions 					    "Check the definition of kalloc_type_view");
377*bbb1b6f9SApple OSS Distributions 				} else if (sectname == "__kalloc_var") {
378*bbb1b6f9SApple OSS Distributions 					assert(!sec_types_var && "Multiple __kalloc_var sections?");
379*bbb1b6f9SApple OSS Distributions 					sec_types_var = macho_section<kalloc_type_var_view>(sec, slice_contents);
380*bbb1b6f9SApple OSS Distributions 					assert(sec.size % sec_types_var->elem_size() == 0 &&
381*bbb1b6f9SApple OSS Distributions 					    "Check the definition of kalloc_type_var_view");
382*bbb1b6f9SApple OSS Distributions 				} else if (segname == "__TEXT" && sectname == "__cstring") {
383*bbb1b6f9SApple OSS Distributions 					assert(!sec_cstring && "Multiple __kalloc_var sections?");
384*bbb1b6f9SApple OSS Distributions 					sec_cstring = macho_section<char>(sec, slice_contents);
385*bbb1b6f9SApple OSS Distributions 				}
386*bbb1b6f9SApple OSS Distributions 			}
387*bbb1b6f9SApple OSS Distributions 		}
388*bbb1b6f9SApple OSS Distributions 	}
389*bbb1b6f9SApple OSS Distributions 
390*bbb1b6f9SApple OSS Distributions 	~image() = default;
391*bbb1b6f9SApple OSS Distributions 
392*bbb1b6f9SApple OSS Distributions 	std::string
uuid() const393*bbb1b6f9SApple OSS Distributions 	uuid() const
394*bbb1b6f9SApple OSS Distributions 	{
395*bbb1b6f9SApple OSS Distributions 		uuid_string_t to_str;
396*bbb1b6f9SApple OSS Distributions 		uuid_unparse_upper(img_uuid, to_str);
397*bbb1b6f9SApple OSS Distributions 		return std::string{to_str};
398*bbb1b6f9SApple OSS Distributions 	}
399*bbb1b6f9SApple OSS Distributions 
400*bbb1b6f9SApple OSS Distributions 	const char *
slice() const401*bbb1b6f9SApple OSS Distributions 	slice() const
402*bbb1b6f9SApple OSS Distributions 	{
403*bbb1b6f9SApple OSS Distributions 		auto *hdr = mh_hdr();
404*bbb1b6f9SApple OSS Distributions 		cpu_type_t cpu;
405*bbb1b6f9SApple OSS Distributions 		cpu_subtype_t sub;
406*bbb1b6f9SApple OSS Distributions 
407*bbb1b6f9SApple OSS Distributions 		if (hdr->magic == MH_CIGAM_64) {
408*bbb1b6f9SApple OSS Distributions 			cpu = OSSwapInt32(hdr->cputype);
409*bbb1b6f9SApple OSS Distributions 			sub = OSSwapInt32(hdr->cpusubtype & CPU_SUBTYPE_MASK);
410*bbb1b6f9SApple OSS Distributions 		} else {
411*bbb1b6f9SApple OSS Distributions 			cpu = hdr->cputype;
412*bbb1b6f9SApple OSS Distributions 			sub = hdr->cpusubtype & OSSwapInt32(CPU_SUBTYPE_MASK);
413*bbb1b6f9SApple OSS Distributions 		}
414*bbb1b6f9SApple OSS Distributions 
415*bbb1b6f9SApple OSS Distributions 		if (cpu == CPU_TYPE_ARM64) {
416*bbb1b6f9SApple OSS Distributions 			if (sub == CPU_SUBTYPE_ARM64E) {
417*bbb1b6f9SApple OSS Distributions 				return "arm64e";
418*bbb1b6f9SApple OSS Distributions 			}
419*bbb1b6f9SApple OSS Distributions 			return "arm64";
420*bbb1b6f9SApple OSS Distributions 		}
421*bbb1b6f9SApple OSS Distributions 
422*bbb1b6f9SApple OSS Distributions 		/* other slices unsupported for now */
423*bbb1b6f9SApple OSS Distributions 		return nullptr;
424*bbb1b6f9SApple OSS Distributions 	}
425*bbb1b6f9SApple OSS Distributions 
426*bbb1b6f9SApple OSS Distributions 	void
dump(const std::string & imgname,const char * indent="")427*bbb1b6f9SApple OSS Distributions 	dump(const std::string &imgname, const char *indent = "")
428*bbb1b6f9SApple OSS Distributions 	{
429*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
430*bbb1b6f9SApple OSS Distributions 			std::cout << indent << "{\n"
431*bbb1b6f9SApple OSS Distributions 			          << indent << "  \"image\": \"" << imgname << "\",\n"
432*bbb1b6f9SApple OSS Distributions 			          << indent << "  \"slice\": \"" << slice() << "\",\n"
433*bbb1b6f9SApple OSS Distributions 			          << indent << "  \"uuid\": \"" << uuid() << '"';
434*bbb1b6f9SApple OSS Distributions 		} else {
435*bbb1b6f9SApple OSS Distributions 			std::cout << imgname << " (" << slice() << ", " << uuid() << ")\n";
436*bbb1b6f9SApple OSS Distributions 		}
437*bbb1b6f9SApple OSS Distributions 
438*bbb1b6f9SApple OSS Distributions 		if (sec_types) {
439*bbb1b6f9SApple OSS Distributions 			dump_types(indent);
440*bbb1b6f9SApple OSS Distributions 		}
441*bbb1b6f9SApple OSS Distributions 
442*bbb1b6f9SApple OSS Distributions 		if (sec_types_var) {
443*bbb1b6f9SApple OSS Distributions 			dump_types_var(indent);
444*bbb1b6f9SApple OSS Distributions 		}
445*bbb1b6f9SApple OSS Distributions 
446*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
447*bbb1b6f9SApple OSS Distributions 			std::cout << std::endl << indent << "}";
448*bbb1b6f9SApple OSS Distributions 		}
449*bbb1b6f9SApple OSS Distributions 
450*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::STATS) {
451*bbb1b6f9SApple OSS Distributions 			if (auto &sec = *sec_types; sec_types) {
452*bbb1b6f9SApple OSS Distributions 				auto ucount = stats.uniq_structs;
453*bbb1b6f9SApple OSS Distributions 				auto usize  = ucount * sec.elem_size();
454*bbb1b6f9SApple OSS Distributions 
455*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "__kalloc_type:      " << std::endl;
456*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  total structs:    " << sec.elem_count() << std::endl;
457*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  unique structs:   " << ucount << std::endl;
458*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  total  size:      " << sec.section.size << std::endl;
459*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  unique size:      " << usize << std::endl;
460*bbb1b6f9SApple OSS Distributions 			}
461*bbb1b6f9SApple OSS Distributions 			if (auto &sec = *sec_types_var; sec_types_var) {
462*bbb1b6f9SApple OSS Distributions 				auto ucount = stats.uniq_structs_var;
463*bbb1b6f9SApple OSS Distributions 				auto usize  = ucount * sec.elem_size();
464*bbb1b6f9SApple OSS Distributions 
465*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "__kalloc_var:       " << std::endl;
466*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  total structs:    " << sec.elem_count() << std::endl;
467*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  unique structs:   " << ucount << std::endl;
468*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  total  size:      " << sec.section.size << std::endl;
469*bbb1b6f9SApple OSS Distributions 				std::cout << indent << "  unique size:      " << usize << std::endl;
470*bbb1b6f9SApple OSS Distributions 			}
471*bbb1b6f9SApple OSS Distributions 			std::cout << indent << "names strings:      " << stats.names_sz << std::endl;
472*bbb1b6f9SApple OSS Distributions 			std::cout << indent << "signatures strings: " << stats.sig_sz << std::endl;
473*bbb1b6f9SApple OSS Distributions 		}
474*bbb1b6f9SApple OSS Distributions 
475*bbb1b6f9SApple OSS Distributions 		stats = {};
476*bbb1b6f9SApple OSS Distributions 		dedup_entries.clear();
477*bbb1b6f9SApple OSS Distributions 		dedup_entries_var.clear();
478*bbb1b6f9SApple OSS Distributions 		dedup_strings.clear();
479*bbb1b6f9SApple OSS Distributions 	}
480*bbb1b6f9SApple OSS Distributions };
481*bbb1b6f9SApple OSS Distributions 
482*bbb1b6f9SApple OSS Distributions static int
do_simple_macho(const std::string filename,std::span<uint8_t> contents)483*bbb1b6f9SApple OSS Distributions do_simple_macho(const std::string filename, std::span<uint8_t> contents)
484*bbb1b6f9SApple OSS Distributions {
485*bbb1b6f9SApple OSS Distributions 	image img{contents};
486*bbb1b6f9SApple OSS Distributions 	img.dump(filename);
487*bbb1b6f9SApple OSS Distributions 	return 0;
488*bbb1b6f9SApple OSS Distributions }
489*bbb1b6f9SApple OSS Distributions 
490*bbb1b6f9SApple OSS Distributions static int
do_fat_macho(const std::string filename,std::span<uint8_t> contents)491*bbb1b6f9SApple OSS Distributions do_fat_macho(const std::string filename, std::span<uint8_t> contents)
492*bbb1b6f9SApple OSS Distributions {
493*bbb1b6f9SApple OSS Distributions 	fat_header *fhdr = reinterpret_cast<fat_header *>(contents.data());
494*bbb1b6f9SApple OSS Distributions 	std::span<fat_arch> fat_archs(
495*bbb1b6f9SApple OSS Distributions 		reinterpret_cast<fat_arch *>(&contents[sizeof(fat_header)]),
496*bbb1b6f9SApple OSS Distributions 		OSSwapInt32(fhdr->nfat_arch));
497*bbb1b6f9SApple OSS Distributions 	const char *sep = "\n";
498*bbb1b6f9SApple OSS Distributions 
499*bbb1b6f9SApple OSS Distributions 	if (out_fmt == out_fmt_type::JSON) {
500*bbb1b6f9SApple OSS Distributions 		std::cout << "[";
501*bbb1b6f9SApple OSS Distributions 	}
502*bbb1b6f9SApple OSS Distributions 
503*bbb1b6f9SApple OSS Distributions 	for (auto &arch : fat_archs) {
504*bbb1b6f9SApple OSS Distributions 		image img{contents.subspan(OSSwapInt32(arch.offset), OSSwapInt32(arch.size))};
505*bbb1b6f9SApple OSS Distributions 
506*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
507*bbb1b6f9SApple OSS Distributions 			std::cout << sep;
508*bbb1b6f9SApple OSS Distributions 		} else {
509*bbb1b6f9SApple OSS Distributions 			std::cout << std::endl;
510*bbb1b6f9SApple OSS Distributions 		}
511*bbb1b6f9SApple OSS Distributions 		img.dump(filename, "  ");
512*bbb1b6f9SApple OSS Distributions 		sep = ",\n";
513*bbb1b6f9SApple OSS Distributions 	}
514*bbb1b6f9SApple OSS Distributions 
515*bbb1b6f9SApple OSS Distributions 	if (out_fmt == out_fmt_type::JSON) {
516*bbb1b6f9SApple OSS Distributions 		std::cout << "\n]";
517*bbb1b6f9SApple OSS Distributions 	}
518*bbb1b6f9SApple OSS Distributions 
519*bbb1b6f9SApple OSS Distributions 	return 0;
520*bbb1b6f9SApple OSS Distributions }
521*bbb1b6f9SApple OSS Distributions 
522*bbb1b6f9SApple OSS Distributions static int
do_fileset(std::span<uint8_t> contents)523*bbb1b6f9SApple OSS Distributions do_fileset(std::span<uint8_t> contents)
524*bbb1b6f9SApple OSS Distributions {
525*bbb1b6f9SApple OSS Distributions 	auto *hdr = reinterpret_cast<const mach_header_64 *>(contents.data());
526*bbb1b6f9SApple OSS Distributions 	std::span<uint8_t> commands = contents.subspan(sizeof(*hdr));
527*bbb1b6f9SApple OSS Distributions 	const char *sep = "\n";
528*bbb1b6f9SApple OSS Distributions 
529*bbb1b6f9SApple OSS Distributions 	if (hdr->cputype != CPU_TYPE_ARM64) {
530*bbb1b6f9SApple OSS Distributions 		std::cerr << "unsupported cpu type";
531*bbb1b6f9SApple OSS Distributions 		return 1;
532*bbb1b6f9SApple OSS Distributions 	}
533*bbb1b6f9SApple OSS Distributions 
534*bbb1b6f9SApple OSS Distributions 	if (out_fmt == out_fmt_type::JSON) {
535*bbb1b6f9SApple OSS Distributions 		std::cout << "[";
536*bbb1b6f9SApple OSS Distributions 	}
537*bbb1b6f9SApple OSS Distributions 
538*bbb1b6f9SApple OSS Distributions 	for (size_t i = 0; i < hdr->ncmds; i++) {
539*bbb1b6f9SApple OSS Distributions 		auto *cmd = reinterpret_cast<const segment_command_64 *>(commands.data());
540*bbb1b6f9SApple OSS Distributions 
541*bbb1b6f9SApple OSS Distributions 		commands = commands.subspan(cmd->cmdsize);
542*bbb1b6f9SApple OSS Distributions 
543*bbb1b6f9SApple OSS Distributions 		if (cmd->cmd != LC_FILESET_ENTRY) {
544*bbb1b6f9SApple OSS Distributions 			continue;
545*bbb1b6f9SApple OSS Distributions 		}
546*bbb1b6f9SApple OSS Distributions 
547*bbb1b6f9SApple OSS Distributions 		auto *fec = reinterpret_cast<const fileset_entry_command *>(cmd);
548*bbb1b6f9SApple OSS Distributions 		const char *name = reinterpret_cast<const char *>(cmd) + fec->entry_id.offset;
549*bbb1b6f9SApple OSS Distributions 		image img{contents, fec->fileoff};
550*bbb1b6f9SApple OSS Distributions 
551*bbb1b6f9SApple OSS Distributions 		if (out_fmt == out_fmt_type::JSON) {
552*bbb1b6f9SApple OSS Distributions 			std::cout << sep;
553*bbb1b6f9SApple OSS Distributions 		} else {
554*bbb1b6f9SApple OSS Distributions 			std::cout << std::endl;
555*bbb1b6f9SApple OSS Distributions 		}
556*bbb1b6f9SApple OSS Distributions 		img.dump(name, "  ");
557*bbb1b6f9SApple OSS Distributions 		sep = ",\n";
558*bbb1b6f9SApple OSS Distributions 	}
559*bbb1b6f9SApple OSS Distributions 
560*bbb1b6f9SApple OSS Distributions 	if (out_fmt == out_fmt_type::JSON) {
561*bbb1b6f9SApple OSS Distributions 		std::cout << "]";
562*bbb1b6f9SApple OSS Distributions 	}
563*bbb1b6f9SApple OSS Distributions 
564*bbb1b6f9SApple OSS Distributions 	return 0;
565*bbb1b6f9SApple OSS Distributions }
566*bbb1b6f9SApple OSS Distributions 
567*bbb1b6f9SApple OSS Distributions void
read_file(std::filesystem::path & path,std::vector<uint8_t> & contents)568*bbb1b6f9SApple OSS Distributions read_file(std::filesystem::path &path, std::vector<uint8_t> &contents)
569*bbb1b6f9SApple OSS Distributions {
570*bbb1b6f9SApple OSS Distributions 	std::ifstream file(path, std::ifstream::binary);
571*bbb1b6f9SApple OSS Distributions 	size_t size(std::filesystem::file_size(path));
572*bbb1b6f9SApple OSS Distributions 
573*bbb1b6f9SApple OSS Distributions 	contents.resize(size);
574*bbb1b6f9SApple OSS Distributions 	file.read(reinterpret_cast<char *>(contents.data()), size);
575*bbb1b6f9SApple OSS Distributions 	file.close();
576*bbb1b6f9SApple OSS Distributions }
577*bbb1b6f9SApple OSS Distributions 
578*bbb1b6f9SApple OSS Distributions enum class file_kind {
579*bbb1b6f9SApple OSS Distributions 	UNKNOWN,
580*bbb1b6f9SApple OSS Distributions 	MACHO,
581*bbb1b6f9SApple OSS Distributions 	FAT_MACHO,
582*bbb1b6f9SApple OSS Distributions 	FILESET,
583*bbb1b6f9SApple OSS Distributions 	IMG4,
584*bbb1b6f9SApple OSS Distributions };
585*bbb1b6f9SApple OSS Distributions 
586*bbb1b6f9SApple OSS Distributions static file_kind
recognize_file(const std::vector<uint8_t> & contents)587*bbb1b6f9SApple OSS Distributions recognize_file(const std::vector<uint8_t> &contents)
588*bbb1b6f9SApple OSS Distributions {
589*bbb1b6f9SApple OSS Distributions 	const mach_header_64 *hdr;
590*bbb1b6f9SApple OSS Distributions 
591*bbb1b6f9SApple OSS Distributions 	if (contents.size() < sizeof(mach_header_64)) {
592*bbb1b6f9SApple OSS Distributions 		return file_kind::UNKNOWN;
593*bbb1b6f9SApple OSS Distributions 	}
594*bbb1b6f9SApple OSS Distributions 
595*bbb1b6f9SApple OSS Distributions 	hdr = reinterpret_cast<const mach_header_64 *>(contents.data());
596*bbb1b6f9SApple OSS Distributions 	if (hdr->magic == MH_MAGIC_64) {
597*bbb1b6f9SApple OSS Distributions 		switch (hdr->filetype) {
598*bbb1b6f9SApple OSS Distributions 		case MH_FILESET:
599*bbb1b6f9SApple OSS Distributions 			return file_kind::FILESET;
600*bbb1b6f9SApple OSS Distributions 		default:
601*bbb1b6f9SApple OSS Distributions 			return file_kind::MACHO;
602*bbb1b6f9SApple OSS Distributions 		}
603*bbb1b6f9SApple OSS Distributions 	}
604*bbb1b6f9SApple OSS Distributions 
605*bbb1b6f9SApple OSS Distributions 	if (hdr->magic == FAT_CIGAM) {
606*bbb1b6f9SApple OSS Distributions 		return file_kind::FAT_MACHO;
607*bbb1b6f9SApple OSS Distributions 	}
608*bbb1b6f9SApple OSS Distributions 
609*bbb1b6f9SApple OSS Distributions 	if (memcmp("IM4P", contents.data() + 8, 4) == 0) {
610*bbb1b6f9SApple OSS Distributions 		return file_kind::IMG4;
611*bbb1b6f9SApple OSS Distributions 	}
612*bbb1b6f9SApple OSS Distributions 
613*bbb1b6f9SApple OSS Distributions 	return file_kind::UNKNOWN;
614*bbb1b6f9SApple OSS Distributions }
615*bbb1b6f9SApple OSS Distributions 
616*bbb1b6f9SApple OSS Distributions static int
call_cmd_silent(const char * const * args)617*bbb1b6f9SApple OSS Distributions call_cmd_silent(const char *const *args)
618*bbb1b6f9SApple OSS Distributions {
619*bbb1b6f9SApple OSS Distributions 	posix_spawn_file_actions_t facts;
620*bbb1b6f9SApple OSS Distributions 	extern char **environ;
621*bbb1b6f9SApple OSS Distributions 	pid_t pid;
622*bbb1b6f9SApple OSS Distributions 	int rc;
623*bbb1b6f9SApple OSS Distributions 
624*bbb1b6f9SApple OSS Distributions 	posix_spawn_file_actions_init(&facts);
625*bbb1b6f9SApple OSS Distributions 	posix_spawn_file_actions_addopen(&facts,
626*bbb1b6f9SApple OSS Distributions 	    STDIN_FILENO, "/dev/null", O_RDONLY, 0777);
627*bbb1b6f9SApple OSS Distributions 	posix_spawn_file_actions_addopen(&facts,
628*bbb1b6f9SApple OSS Distributions 	    STDOUT_FILENO, "/dev/null", O_WRONLY, 0777);
629*bbb1b6f9SApple OSS Distributions 	posix_spawn_file_actions_addopen(&facts,
630*bbb1b6f9SApple OSS Distributions 	    STDERR_FILENO, "/dev/null", O_WRONLY, 0777);
631*bbb1b6f9SApple OSS Distributions 	rc = posix_spawnp(&pid, args[0], &facts, nullptr,
632*bbb1b6f9SApple OSS Distributions 	    (char *const *)args, environ);
633*bbb1b6f9SApple OSS Distributions 	posix_spawn_file_actions_destroy(&facts);
634*bbb1b6f9SApple OSS Distributions 
635*bbb1b6f9SApple OSS Distributions 	if (rc != 0) {
636*bbb1b6f9SApple OSS Distributions 		return 1;
637*bbb1b6f9SApple OSS Distributions 	}
638*bbb1b6f9SApple OSS Distributions 
639*bbb1b6f9SApple OSS Distributions 	waitpid(pid, &rc, 0);
640*bbb1b6f9SApple OSS Distributions 	if (!WIFEXITED(rc) || WEXITSTATUS(rc)) {
641*bbb1b6f9SApple OSS Distributions 		return 1;
642*bbb1b6f9SApple OSS Distributions 	}
643*bbb1b6f9SApple OSS Distributions 
644*bbb1b6f9SApple OSS Distributions 	return 0;
645*bbb1b6f9SApple OSS Distributions }
646*bbb1b6f9SApple OSS Distributions 
647*bbb1b6f9SApple OSS Distributions static int
do_file(const std::filesystem::path & path,std::vector<uint8_t> & contents)648*bbb1b6f9SApple OSS Distributions do_file(const std::filesystem::path &path, std::vector<uint8_t> &contents)
649*bbb1b6f9SApple OSS Distributions {
650*bbb1b6f9SApple OSS Distributions 	int status = 0;
651*bbb1b6f9SApple OSS Distributions 
652*bbb1b6f9SApple OSS Distributions 	switch (recognize_file(contents)) {
653*bbb1b6f9SApple OSS Distributions 	case file_kind::MACHO:
654*bbb1b6f9SApple OSS Distributions 		return do_simple_macho(path.filename().string(), contents);
655*bbb1b6f9SApple OSS Distributions 	case file_kind::FAT_MACHO:
656*bbb1b6f9SApple OSS Distributions 		return do_fat_macho(path.filename().string(), contents);
657*bbb1b6f9SApple OSS Distributions 	case file_kind::FILESET:
658*bbb1b6f9SApple OSS Distributions 		return do_fileset(contents);
659*bbb1b6f9SApple OSS Distributions 	case file_kind::IMG4:
660*bbb1b6f9SApple OSS Distributions 		break;
661*bbb1b6f9SApple OSS Distributions 	case file_kind::UNKNOWN:
662*bbb1b6f9SApple OSS Distributions 		std::cerr << "Unsupported file type\n";
663*bbb1b6f9SApple OSS Distributions 		return 1;
664*bbb1b6f9SApple OSS Distributions 	}
665*bbb1b6f9SApple OSS Distributions 
666*bbb1b6f9SApple OSS Distributions 	char tmp_tpl[] = "/tmp/kt-dump.XXXXXX";
667*bbb1b6f9SApple OSS Distributions 	char *tmp_dir = mkdtemp(tmp_tpl);
668*bbb1b6f9SApple OSS Distributions 
669*bbb1b6f9SApple OSS Distributions 	if (tmp_dir == NULL) {
670*bbb1b6f9SApple OSS Distributions 		std::cerr << "Unable to make temporary directory to unpack img4\n";
671*bbb1b6f9SApple OSS Distributions 		return 1;
672*bbb1b6f9SApple OSS Distributions 	}
673*bbb1b6f9SApple OSS Distributions 
674*bbb1b6f9SApple OSS Distributions 	std::filesystem::path compressed_kc{tmp_dir};
675*bbb1b6f9SApple OSS Distributions 	std::filesystem::path uncompressed_kc{tmp_dir};
676*bbb1b6f9SApple OSS Distributions 
677*bbb1b6f9SApple OSS Distributions 	compressed_kc /= "compressed.kc";
678*bbb1b6f9SApple OSS Distributions 	uncompressed_kc /= "uncompressed.kc";
679*bbb1b6f9SApple OSS Distributions 
680*bbb1b6f9SApple OSS Distributions 	static const char *const img4args[] = {
681*bbb1b6f9SApple OSS Distributions 		"img4utility",
682*bbb1b6f9SApple OSS Distributions 		"--copyBinary",
683*bbb1b6f9SApple OSS Distributions 		"--input",
684*bbb1b6f9SApple OSS Distributions 		path.c_str(),
685*bbb1b6f9SApple OSS Distributions 		"--output",
686*bbb1b6f9SApple OSS Distributions 		compressed_kc.c_str(),
687*bbb1b6f9SApple OSS Distributions 		NULL,
688*bbb1b6f9SApple OSS Distributions 	};
689*bbb1b6f9SApple OSS Distributions 
690*bbb1b6f9SApple OSS Distributions 	static const char *const ct_args[] = {
691*bbb1b6f9SApple OSS Distributions 		"compression_tool",
692*bbb1b6f9SApple OSS Distributions 		"-decode",
693*bbb1b6f9SApple OSS Distributions 		"-v",
694*bbb1b6f9SApple OSS Distributions 		"-v",
695*bbb1b6f9SApple OSS Distributions 		"-v",
696*bbb1b6f9SApple OSS Distributions 		"-i",
697*bbb1b6f9SApple OSS Distributions 		compressed_kc.c_str(),
698*bbb1b6f9SApple OSS Distributions 		"-o",
699*bbb1b6f9SApple OSS Distributions 		uncompressed_kc.c_str(),
700*bbb1b6f9SApple OSS Distributions 		NULL,
701*bbb1b6f9SApple OSS Distributions 	};
702*bbb1b6f9SApple OSS Distributions 
703*bbb1b6f9SApple OSS Distributions 	if (call_cmd_silent(img4args)) {
704*bbb1b6f9SApple OSS Distributions 		std::cerr << "Unable to unpack img4 image\n";
705*bbb1b6f9SApple OSS Distributions 		status = 1;
706*bbb1b6f9SApple OSS Distributions 	} else if (call_cmd_silent(ct_args)) {
707*bbb1b6f9SApple OSS Distributions 		std::cerr << "Unable to decompress KC\n";
708*bbb1b6f9SApple OSS Distributions 		status = 1;
709*bbb1b6f9SApple OSS Distributions 	} else {
710*bbb1b6f9SApple OSS Distributions 		read_file(uncompressed_kc, contents);
711*bbb1b6f9SApple OSS Distributions 	}
712*bbb1b6f9SApple OSS Distributions 
713*bbb1b6f9SApple OSS Distributions 	removefile_state_t s = removefile_state_alloc();
714*bbb1b6f9SApple OSS Distributions 	removefile(tmp_dir, s, REMOVEFILE_RECURSIVE);
715*bbb1b6f9SApple OSS Distributions 	removefile_state_free(s);
716*bbb1b6f9SApple OSS Distributions 
717*bbb1b6f9SApple OSS Distributions 	return status ?: do_file(path, contents);
718*bbb1b6f9SApple OSS Distributions }
719*bbb1b6f9SApple OSS Distributions 
720*bbb1b6f9SApple OSS Distributions int
main(int argc,char const * argv[])721*bbb1b6f9SApple OSS Distributions main(int argc, char const *argv[])
722*bbb1b6f9SApple OSS Distributions {
723*bbb1b6f9SApple OSS Distributions 	if (argc != 2 && argc != 4) {
724*bbb1b6f9SApple OSS Distributions 		std::cout << "Usage: " << argv[0]
725*bbb1b6f9SApple OSS Distributions 		          << " [-f <simple|json|struct|stats>] <mach-o>\n";
726*bbb1b6f9SApple OSS Distributions 		return 1;
727*bbb1b6f9SApple OSS Distributions 	}
728*bbb1b6f9SApple OSS Distributions 
729*bbb1b6f9SApple OSS Distributions 	std::string path_arg;
730*bbb1b6f9SApple OSS Distributions 
731*bbb1b6f9SApple OSS Distributions 	/* Parse command line args */
732*bbb1b6f9SApple OSS Distributions 	for (int i = 1; i < argc; i++) {
733*bbb1b6f9SApple OSS Distributions 		std::string arg(argv[i]);
734*bbb1b6f9SApple OSS Distributions 		if (arg == "-f") {
735*bbb1b6f9SApple OSS Distributions 			if (++i == argc) {
736*bbb1b6f9SApple OSS Distributions 				std::cerr << "Option " << arg << " requires an argument\n";
737*bbb1b6f9SApple OSS Distributions 				return 1;
738*bbb1b6f9SApple OSS Distributions 			}
739*bbb1b6f9SApple OSS Distributions 			arg = argv[i];
740*bbb1b6f9SApple OSS Distributions 			if (arg == "simple") {
741*bbb1b6f9SApple OSS Distributions 				out_fmt = out_fmt_type::SIMPLE;
742*bbb1b6f9SApple OSS Distributions 			} else if (arg == "json" || arg == "JSON") {
743*bbb1b6f9SApple OSS Distributions 				out_fmt = out_fmt_type::JSON;
744*bbb1b6f9SApple OSS Distributions 			} else if (arg == "struct") {
745*bbb1b6f9SApple OSS Distributions 				out_fmt = out_fmt_type::STRUCT;
746*bbb1b6f9SApple OSS Distributions 			} else if (arg == "stats") {
747*bbb1b6f9SApple OSS Distributions 				out_fmt = out_fmt_type::STATS;
748*bbb1b6f9SApple OSS Distributions 			} else {
749*bbb1b6f9SApple OSS Distributions 				std::cerr << "Unknown output format: " << arg << std::endl;
750*bbb1b6f9SApple OSS Distributions 				return 1;
751*bbb1b6f9SApple OSS Distributions 			}
752*bbb1b6f9SApple OSS Distributions 		} else {
753*bbb1b6f9SApple OSS Distributions 			/* Read the file specified as a positional arg */
754*bbb1b6f9SApple OSS Distributions 			path_arg = arg;
755*bbb1b6f9SApple OSS Distributions 		}
756*bbb1b6f9SApple OSS Distributions 	}
757*bbb1b6f9SApple OSS Distributions 
758*bbb1b6f9SApple OSS Distributions 	if (path_arg.length() == 0) {
759*bbb1b6f9SApple OSS Distributions 		std::cerr << "no file specified\n";
760*bbb1b6f9SApple OSS Distributions 		return 1;
761*bbb1b6f9SApple OSS Distributions 	}
762*bbb1b6f9SApple OSS Distributions 
763*bbb1b6f9SApple OSS Distributions 	std::filesystem::path path(path_arg);
764*bbb1b6f9SApple OSS Distributions 	std::vector<uint8_t> contents;
765*bbb1b6f9SApple OSS Distributions 
766*bbb1b6f9SApple OSS Distributions 	read_file(path, contents);
767*bbb1b6f9SApple OSS Distributions 	return do_file(path, contents);
768*bbb1b6f9SApple OSS Distributions }
769