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

source code of lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp