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// We may be given a file, set reasonable values.
193#if defined(__x86_64__)
194 cputype = CPU_TYPE_X86;
195 cpusubtype = CPU_SUBTYPE_X86_ALL;
196#else
197 cputype = CPU_TYPE_ARM64;
198 cpusubtype = CPU_SUBTYPE_ARM64_ALL;
199#endif
200 if (strlen(s: fn) == 0)
201 return {};
202
203 FILE *f = fopen(filename: fn, modes: "r");
204 if (f == nullptr) {
205 fprintf(stderr, format: "Unable to open binary '%s' to get uuid\n", fn);
206 exit(status: 1);
207 }
208 uint32_t num_of_load_cmds = 0;
209 uint32_t size_of_load_cmds = 0;
210 std::string uuid;
211 off_t file_offset = 0;
212
213 uint8_t magic[4];
214 if (::fread(ptr: magic, size: 1, n: 4, stream: f) != 4) {
215 fprintf(stderr, format: "Failed to read magic number from input file %s\n", fn);
216 exit(status: 1);
217 }
218 uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce};
219 uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe};
220 uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf};
221 uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe};
222
223 if (memcmp(s1: magic, s2: magic_32_be, n: 4) == 0 ||
224 memcmp(s1: magic, s2: magic_64_be, n: 4) == 0) {
225 fprintf(stderr, format: "big endian corefiles not supported\n");
226 exit(status: 1);
227 }
228
229 ::fseeko(stream: f, off: 0, SEEK_SET);
230 if (memcmp(s1: magic, s2: magic_32_le, n: 4) == 0) {
231 struct mach_header mh;
232 if (::fread(ptr: &mh, size: 1, n: sizeof(mh), stream: f) != sizeof(mh)) {
233 fprintf(stderr, format: "error reading mach header from input file\n");
234 exit(status: 1);
235 }
236 if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) {
237 fprintf(stderr,
238 format: "This tool creates an x86_64/arm64 corefile but "
239 "the supplied binary '%s' is cputype 0x%x\n",
240 fn, (uint32_t)mh.cputype);
241 exit(status: 1);
242 }
243 num_of_load_cmds = mh.ncmds;
244 size_of_load_cmds = mh.sizeofcmds;
245 file_offset += sizeof(struct mach_header);
246 cputype = mh.cputype;
247 cpusubtype = mh.cpusubtype;
248 } else {
249 struct mach_header_64 mh;
250 if (::fread(ptr: &mh, size: 1, n: sizeof(mh), stream: f) != sizeof(mh)) {
251 fprintf(stderr, format: "error reading mach header from input file\n");
252 exit(status: 1);
253 }
254 if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) {
255 fprintf(stderr,
256 format: "This tool creates an x86_64/arm64 corefile but "
257 "the supplied binary '%s' is cputype 0x%x\n",
258 fn, (uint32_t)mh.cputype);
259 exit(status: 1);
260 }
261 num_of_load_cmds = mh.ncmds;
262 size_of_load_cmds = mh.sizeofcmds;
263 file_offset += sizeof(struct mach_header_64);
264 cputype = mh.cputype;
265 cpusubtype = mh.cpusubtype;
266 }
267
268 off_t load_cmds_offset = file_offset;
269
270 for (int i = 0; i < num_of_load_cmds &&
271 (file_offset - load_cmds_offset) < size_of_load_cmds;
272 i++) {
273 ::fseeko(stream: f, off: file_offset, SEEK_SET);
274 uint32_t cmd;
275 uint32_t cmdsize;
276 ::fread(ptr: &cmd, size: sizeof(uint32_t), n: 1, stream: f);
277 ::fread(ptr: &cmdsize, size: sizeof(uint32_t), n: 1, stream: f);
278 if (cmd == LC_UUID) {
279 struct uuid_command uuidcmd;
280 ::fseeko(stream: f, off: file_offset, SEEK_SET);
281 if (::fread(ptr: &uuidcmd, size: 1, n: sizeof(uuidcmd), stream: f) != sizeof(uuidcmd)) {
282 fprintf(stderr, format: "Unable to read LC_UUID load command.\n");
283 exit(status: 1);
284 }
285 uuid_string_t uuidstr;
286 uuid_unparse(uuidcmd.uuid, uuidstr);
287 uuid = uuidstr;
288 break;
289 }
290 file_offset += cmdsize;
291 }
292 return uuid;
293}
294
295int main(int argc, char **argv) {
296 if (argc != 6) {
297 fprintf(
298 stderr,
299 format: "usage: create-empty-corefile version-string|main-bin-spec "
300 "<output-core-name> <binary-to-copy-uuid-from> <address> <slide>\n");
301 fprintf(stderr,
302 format: " <address> is base 16, 0xffffffffffffffff means unknown\n");
303 fprintf(stderr,
304 format: " <slide> is base 16, 0xffffffffffffffff means unknown\n");
305 fprintf(
306 stderr,
307 format: "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n");
308 fprintf(stderr, format: "an LC_NOTE 'main bin spec' load command without an "
309 "address specified, depending on\n");
310 fprintf(stderr, format: "whether the 1st arg is version-string or main-bin-spec\n");
311 fprintf(stderr, format: "\nan LC_NOTE 'kern ver str' with no binary provided "
312 "(empty string filename) to get a UUID\n");
313 fprintf(stderr, format: "means an empty 'kern ver str' will be written, an invalid "
314 "LC_NOTE that lldb should handle.\n");
315 exit(status: 1);
316 }
317 if (strcmp(s1: argv[1], s2: "version-string") != 0 &&
318 strcmp(s1: argv[1], s2: "main-bin-spec") != 0) {
319 fprintf(stderr, format: "arg1 was not version-string or main-bin-spec\n");
320 exit(status: 1);
321 }
322
323 cpu_type_t cputype;
324 cpu_subtype_t cpusubtype;
325 std::string uuid = get_uuid_from_binary(argv[3], cputype, cpusubtype);
326
327 // An array of load commands (in the form of byte arrays)
328 std::vector<std::vector<uint8_t>> load_commands;
329
330 // An array of corefile contents (page data, lc_note data, etc)
331 std::vector<uint8_t> payload;
332
333 errno = 0;
334 uint64_t address = strtoull(nptr: argv[4], NULL, base: 16);
335 if (errno != 0) {
336 fprintf(stderr, format: "Unable to parse address %s as base 16", argv[4]);
337 exit(status: 1);
338 }
339
340 errno = 0;
341 uint64_t slide = strtoull(nptr: argv[5], NULL, base: 16);
342 if (errno != 0) {
343 fprintf(stderr, format: "Unable to parse slide %s as base 16", argv[4]);
344 exit(status: 1);
345 }
346
347 // First add all the load commands / payload so we can figure out how large
348 // the load commands will actually be.
349 load_commands.push_back(lc_thread_load_command(cputype));
350 if (strcmp(s1: argv[1], s2: "version-string") == 0)
351 add_lc_note_kern_ver_str_load_command(loadcmds&: load_commands, payload, payload_file_offset: 0, uuid,
352 address);
353 else
354 add_lc_note_main_bin_spec_load_command(loadcmds&: load_commands, payload, payload_file_offset: 0, uuidstr: uuid,
355 address, slide);
356 add_lc_segment(loadcmds&: load_commands, payload, payload_file_offset: 0);
357
358 int size_of_load_commands = 0;
359 for (const auto &lc : load_commands)
360 size_of_load_commands += lc.size();
361
362 int header_and_load_cmd_room =
363 sizeof(struct mach_header_64) + size_of_load_commands;
364
365 // Erase the load commands / payload now that we know how much space is
366 // needed, redo it.
367 load_commands.clear();
368 payload.clear();
369
370 load_commands.push_back(lc_thread_load_command(cputype));
371
372 if (strcmp(s1: argv[1], s2: "version-string") == 0)
373 add_lc_note_kern_ver_str_load_command(
374 loadcmds&: load_commands, payload, payload_file_offset: header_and_load_cmd_room, uuid, address);
375 else
376 add_lc_note_main_bin_spec_load_command(
377 loadcmds&: load_commands, payload, payload_file_offset: header_and_load_cmd_room, uuidstr: uuid, address, slide);
378
379 add_lc_segment(loadcmds&: load_commands, payload, payload_file_offset: header_and_load_cmd_room);
380
381 struct mach_header_64 mh;
382 mh.magic = MH_MAGIC_64;
383 mh.cputype = cputype;
384
385 mh.cpusubtype = cpusubtype;
386 mh.filetype = MH_CORE;
387 mh.ncmds = load_commands.size();
388 mh.sizeofcmds = size_of_load_commands;
389 mh.flags = 0;
390 mh.reserved = 0;
391
392 FILE *f = fopen(filename: argv[2], modes: "w");
393
394 if (f == nullptr) {
395 fprintf(stderr, format: "Unable to open file %s for writing\n", argv[2]);
396 exit(status: 1);
397 }
398
399 fwrite(&mh, sizeof(struct mach_header_64), 1, f);
400
401 for (const auto &lc : load_commands)
402 fwrite(ptr: lc.data(), size: lc.size(), n: 1, s: f);
403
404 fseek(stream: f, off: header_and_load_cmd_room, SEEK_SET);
405
406 fwrite(ptr: payload.data(), size: payload.size(), n: 1, s: f);
407
408 fclose(stream: f);
409}
410

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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