1 #include <inttypes.h>
2 #include <mach-o/loader.h>
3 #include <mach/thread_status.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <string>
8 #include <sys/errno.h>
9 #include <uuid/uuid.h>
10 #include <vector>
11
12 // Create an empty corefile with a "kern ver str" LC_NOTE
13 // or a "main bin spec" LC_NOTE..
14 // If an existing binary is given as a 3rd argument on the cmd line,
15 // the UUID from that binary will be encoded in the corefile.
16 // Otherwise a pre-set UUID will be put in the corefile that
17 // is created.
18
19 struct main_bin_spec_payload {
20 uint32_t version;
21 uint32_t type;
22 uint64_t address;
23 uint64_t slide;
24 uuid_t uuid;
25 uint32_t log2_pagesize;
26 uint32_t platform;
27 };
28
29 union uint32_buf {
30 uint8_t bytebuf[4];
31 uint32_t val;
32 };
33
34 union uint64_buf {
35 uint8_t bytebuf[8];
36 uint64_t val;
37 };
38
add_uint64(std::vector<uint8_t> & buf,uint64_t val)39 void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
40 uint64_buf conv;
41 conv.val = val;
42 for (int i = 0; i < 8; i++)
43 buf.push_back(conv.bytebuf[i]);
44 }
45
add_uint32(std::vector<uint8_t> & buf,uint32_t val)46 void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
47 uint32_buf conv;
48 conv.val = val;
49 for (int i = 0; i < 4; i++)
50 buf.push_back(conv.bytebuf[i]);
51 }
52
lc_thread_load_command(cpu_type_t cputype)53 std::vector<uint8_t> lc_thread_load_command(cpu_type_t cputype) {
54 std::vector<uint8_t> data;
55 // Emit an LC_THREAD register context appropriate for the cputype
56 // of the binary we're embedded. The tests in this case do not
57 // use the register values, so 0's are fine, lldb needs to see at
58 // least one LC_THREAD in the corefile.
59 #if defined(__x86_64__)
60 if (cputype == CPU_TYPE_X86_64) {
61 add_uint32(data, LC_THREAD); // thread_command.cmd
62 add_uint32(data,
63 16 + (x86_THREAD_STATE64_COUNT * 4)); // thread_command.cmdsize
64 add_uint32(data, x86_THREAD_STATE64); // thread_command.flavor
65 add_uint32(data, x86_THREAD_STATE64_COUNT); // thread_command.count
66 for (int i = 0; i < x86_THREAD_STATE64_COUNT; i++) {
67 add_uint32(data, 0); // whatever, just some empty register values
68 }
69 }
70 #endif
71 #if defined(__arm64__) || defined(__aarch64__)
72 if (cputype == CPU_TYPE_ARM64) {
73 add_uint32(data, LC_THREAD); // thread_command.cmd
74 add_uint32(data,
75 16 + (ARM_THREAD_STATE64_COUNT * 4)); // thread_command.cmdsize
76 add_uint32(data, ARM_THREAD_STATE64); // thread_command.flavor
77 add_uint32(data, ARM_THREAD_STATE64_COUNT); // thread_command.count
78 for (int i = 0; i < ARM_THREAD_STATE64_COUNT; i++) {
79 add_uint32(data, 0); // whatever, just some empty register values
80 }
81 }
82 #endif
83 return data;
84 }
85
add_lc_note_kern_ver_str_load_command(std::vector<std::vector<uint8_t>> & loadcmds,std::vector<uint8_t> & payload,int payload_file_offset,std::string uuid,uint64_t address)86 void add_lc_note_kern_ver_str_load_command(
87 std::vector<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
88 int payload_file_offset, std::string uuid, uint64_t address) {
89 std::string ident = "EFI UUID=";
90 ident += uuid;
91
92 if (address != 0xffffffffffffffff) {
93 ident += "; stext=";
94 char buf[24];
95 sprintf(buf, "0x%" PRIx64, address);
96 ident += buf;
97 }
98
99 std::vector<uint8_t> loadcmd_data;
100
101 add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd
102 add_uint32(loadcmd_data, 40); // note_command.cmdsize
103 char lc_note_name[16];
104 memset(lc_note_name, 0, 16);
105 strcpy(lc_note_name, "kern ver str");
106
107 // lc_note.data_owner
108 for (int i = 0; i < 16; i++)
109 loadcmd_data.push_back(lc_note_name[i]);
110
111 // we start writing the payload at payload_file_offset to leave
112 // room at the start for the header & the load commands.
113 uint64_t current_payload_offset = payload.size() + payload_file_offset;
114
115 add_uint64(loadcmd_data, current_payload_offset); // note_command.offset
116 add_uint64(loadcmd_data, 4 + ident.size() + 1); // note_command.size
117
118 loadcmds.push_back(loadcmd_data);
119
120 add_uint32(payload, 1); // kerneL_version_string.version
121 for (int i = 0; i < ident.size() + 1; i++) {
122 payload.push_back(ident[i]);
123 }
124 }
125
add_lc_note_main_bin_spec_load_command(std::vector<std::vector<uint8_t>> & loadcmds,std::vector<uint8_t> & payload,int payload_file_offset,std::string uuidstr,uint64_t address,uint64_t slide)126 void add_lc_note_main_bin_spec_load_command(
127 std::vector<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
128 int payload_file_offset, std::string uuidstr, uint64_t address,
129 uint64_t slide) {
130 std::vector<uint8_t> loadcmd_data;
131
132 add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd
133 add_uint32(loadcmd_data, 40); // note_command.cmdsize
134 char lc_note_name[16];
135 memset(lc_note_name, 0, 16);
136 strcpy(lc_note_name, "main bin spec");
137
138 // lc_note.data_owner
139 for (int i = 0; i < 16; i++)
140 loadcmd_data.push_back(lc_note_name[i]);
141
142 // we start writing the payload at payload_file_offset to leave
143 // room at the start for the header & the load commands.
144 uint64_t current_payload_offset = payload.size() + payload_file_offset;
145
146 add_uint64(loadcmd_data, current_payload_offset); // note_command.offset
147 add_uint64(loadcmd_data,
148 sizeof(struct main_bin_spec_payload)); // note_command.size
149
150 loadcmds.push_back(loadcmd_data);
151
152 // Now write the "main bin spec" payload.
153 add_uint32(payload, 2); // version
154 add_uint32(payload, 3); // type == 3 [ firmware, standalone, etc ]
155 add_uint64(payload, address); // load address
156 add_uint64(payload, slide); // slide
157 uuid_t uuid;
158 uuid_parse(uuidstr.c_str(), uuid);
159 for (int i = 0; i < sizeof(uuid_t); i++)
160 payload.push_back(uuid[i]);
161 add_uint32(payload, 0); // log2_pagesize unspecified
162 add_uint32(payload, 0); // platform unspecified
163 }
164
add_lc_segment(std::vector<std::vector<uint8_t>> & loadcmds,std::vector<uint8_t> & payload,int payload_file_offset)165 void add_lc_segment(std::vector<std::vector<uint8_t>> &loadcmds,
166 std::vector<uint8_t> &payload, int payload_file_offset) {
167 std::vector<uint8_t> loadcmd_data;
168 struct segment_command_64 seg;
169 seg.cmd = LC_SEGMENT_64;
170 seg.cmdsize = sizeof(struct segment_command_64); // no sections
171 memset(seg.segname, 0, 16);
172 seg.vmaddr = 0xffffff7f96400000;
173 seg.vmsize = 4096;
174 seg.fileoff = payload.size() + payload_file_offset;
175 seg.filesize = 0;
176 seg.maxprot = 1;
177 seg.initprot = 1;
178 seg.nsects = 0;
179 seg.flags = 0;
180
181 uint8_t *p = (uint8_t *)&seg;
182 for (int i = 0; i < sizeof(struct segment_command_64); i++) {
183 loadcmd_data.push_back(*(p + i));
184 }
185 loadcmds.push_back(loadcmd_data);
186 }
187
get_uuid_from_binary(const char * fn,cpu_type_t & cputype,cpu_subtype_t & cpusubtype)188 std::string get_uuid_from_binary(const char *fn, cpu_type_t &cputype,
189 cpu_subtype_t &cpusubtype) {
190 FILE *f = fopen(fn, "r");
191 if (f == nullptr) {
192 fprintf(stderr, "Unable to open binary '%s' to get uuid\n", fn);
193 exit(1);
194 }
195 uint32_t num_of_load_cmds = 0;
196 uint32_t size_of_load_cmds = 0;
197 std::string uuid;
198 off_t file_offset = 0;
199
200 uint8_t magic[4];
201 if (::fread(magic, 1, 4, f) != 4) {
202 fprintf(stderr, "Failed to read magic number from input file %s\n", fn);
203 exit(1);
204 }
205 uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce};
206 uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe};
207 uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf};
208 uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe};
209
210 if (memcmp(magic, magic_32_be, 4) == 0 ||
211 memcmp(magic, magic_64_be, 4) == 0) {
212 fprintf(stderr, "big endian corefiles not supported\n");
213 exit(1);
214 }
215
216 ::fseeko(f, 0, SEEK_SET);
217 if (memcmp(magic, magic_32_le, 4) == 0) {
218 struct mach_header mh;
219 if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) {
220 fprintf(stderr, "error reading mach header from input file\n");
221 exit(1);
222 }
223 if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) {
224 fprintf(stderr,
225 "This tool creates an x86_64/arm64 corefile but "
226 "the supplied binary '%s' is cputype 0x%x\n",
227 fn, (uint32_t)mh.cputype);
228 exit(1);
229 }
230 num_of_load_cmds = mh.ncmds;
231 size_of_load_cmds = mh.sizeofcmds;
232 file_offset += sizeof(struct mach_header);
233 cputype = mh.cputype;
234 cpusubtype = mh.cpusubtype;
235 } else {
236 struct mach_header_64 mh;
237 if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) {
238 fprintf(stderr, "error reading mach header from input file\n");
239 exit(1);
240 }
241 if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) {
242 fprintf(stderr,
243 "This tool creates an x86_64/arm64 corefile but "
244 "the supplied binary '%s' is cputype 0x%x\n",
245 fn, (uint32_t)mh.cputype);
246 exit(1);
247 }
248 num_of_load_cmds = mh.ncmds;
249 size_of_load_cmds = mh.sizeofcmds;
250 file_offset += sizeof(struct mach_header_64);
251 cputype = mh.cputype;
252 cpusubtype = mh.cpusubtype;
253 }
254
255 off_t load_cmds_offset = file_offset;
256
257 for (int i = 0; i < num_of_load_cmds &&
258 (file_offset - load_cmds_offset) < size_of_load_cmds;
259 i++) {
260 ::fseeko(f, file_offset, SEEK_SET);
261 uint32_t cmd;
262 uint32_t cmdsize;
263 ::fread(&cmd, sizeof(uint32_t), 1, f);
264 ::fread(&cmdsize, sizeof(uint32_t), 1, f);
265 if (cmd == LC_UUID) {
266 struct uuid_command uuidcmd;
267 ::fseeko(f, file_offset, SEEK_SET);
268 if (::fread(&uuidcmd, 1, sizeof(uuidcmd), f) != sizeof(uuidcmd)) {
269 fprintf(stderr, "Unable to read LC_UUID load command.\n");
270 exit(1);
271 }
272 uuid_string_t uuidstr;
273 uuid_unparse(uuidcmd.uuid, uuidstr);
274 uuid = uuidstr;
275 break;
276 }
277 file_offset += cmdsize;
278 }
279 return uuid;
280 }
281
main(int argc,char ** argv)282 int main(int argc, char **argv) {
283 if (argc != 6) {
284 fprintf(
285 stderr,
286 "usage: create-empty-corefile version-string|main-bin-spec "
287 "<output-core-name> <binary-to-copy-uuid-from> <address> <slide>\n");
288 fprintf(stderr,
289 " <address> is base 16, 0xffffffffffffffff means unknown\n");
290 fprintf(stderr,
291 " <slide> is base 16, 0xffffffffffffffff means unknown\n");
292 fprintf(
293 stderr,
294 "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n");
295 fprintf(stderr, "an LC_NOTE 'main bin spec' load command without an "
296 "address specified, depending on\n");
297 fprintf(stderr, "whether the 1st arg is version-string or main-bin-spec\n");
298 exit(1);
299 }
300 if (strcmp(argv[1], "version-string") != 0 &&
301 strcmp(argv[1], "main-bin-spec") != 0) {
302 fprintf(stderr, "arg1 was not version-string or main-bin-spec\n");
303 exit(1);
304 }
305
306 cpu_type_t cputype;
307 cpu_subtype_t cpusubtype;
308 std::string uuid = get_uuid_from_binary(argv[3], cputype, cpusubtype);
309
310 // An array of load commands (in the form of byte arrays)
311 std::vector<std::vector<uint8_t>> load_commands;
312
313 // An array of corefile contents (page data, lc_note data, etc)
314 std::vector<uint8_t> payload;
315
316 errno = 0;
317 uint64_t address = strtoull(argv[4], NULL, 16);
318 if (errno != 0) {
319 fprintf(stderr, "Unable to parse address %s as base 16", argv[4]);
320 exit(1);
321 }
322
323 errno = 0;
324 uint64_t slide = strtoull(argv[5], NULL, 16);
325 if (errno != 0) {
326 fprintf(stderr, "Unable to parse slide %s as base 16", argv[4]);
327 exit(1);
328 }
329
330 // First add all the load commands / payload so we can figure out how large
331 // the load commands will actually be.
332 load_commands.push_back(lc_thread_load_command(cputype));
333 if (strcmp(argv[1], "version-string") == 0)
334 add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid,
335 address);
336 else
337 add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid,
338 address, slide);
339 add_lc_segment(load_commands, payload, 0);
340
341 int size_of_load_commands = 0;
342 for (const auto &lc : load_commands)
343 size_of_load_commands += lc.size();
344
345 int header_and_load_cmd_room =
346 sizeof(struct mach_header_64) + size_of_load_commands;
347
348 // Erase the load commands / payload now that we know how much space is
349 // needed, redo it.
350 load_commands.clear();
351 payload.clear();
352
353 load_commands.push_back(lc_thread_load_command(cputype));
354
355 if (strcmp(argv[1], "version-string") == 0)
356 add_lc_note_kern_ver_str_load_command(
357 load_commands, payload, header_and_load_cmd_room, uuid, address);
358 else
359 add_lc_note_main_bin_spec_load_command(
360 load_commands, payload, header_and_load_cmd_room, uuid, address, slide);
361
362 add_lc_segment(load_commands, payload, header_and_load_cmd_room);
363
364 struct mach_header_64 mh;
365 mh.magic = MH_MAGIC_64;
366 mh.cputype = cputype;
367
368 mh.cpusubtype = cpusubtype;
369 mh.filetype = MH_CORE;
370 mh.ncmds = load_commands.size();
371 mh.sizeofcmds = size_of_load_commands;
372 mh.flags = 0;
373 mh.reserved = 0;
374
375 FILE *f = fopen(argv[2], "w");
376
377 if (f == nullptr) {
378 fprintf(stderr, "Unable to open file %s for writing\n", argv[2]);
379 exit(1);
380 }
381
382 fwrite(&mh, sizeof(struct mach_header_64), 1, f);
383
384 for (const auto &lc : load_commands)
385 fwrite(lc.data(), lc.size(), 1, f);
386
387 fseek(f, header_and_load_cmd_room, SEEK_SET);
388
389 fwrite(payload.data(), payload.size(), 1, f);
390
391 fclose(f);
392 }
393