1#include <stdio.h>
2#include <stdlib.h>
3#include <mach-o/loader.h>
4#include <vector>
5#include <string>
6#include <mach/thread_status.h>
7#include <string.h>
8#include <uuid/uuid.h>
9
10// Create an empty corefile with a "kern ver str" LC_NOTE.
11// If an existing binary is given as an optional 2nd argument on the cmd line,
12// the UUID from that binary will be encoded in the corefile.
13// Otherwise a pre-set UUID will be put in the corefile that
14// is created.
15
16
17union uint32_buf {
18 uint8_t bytebuf[4];
19 uint32_t val;
20};
21
22union uint64_buf {
23 uint8_t bytebuf[8];
24 uint64_t val;
25};
26
27void
28add_uint64(std::vector<uint8_t> &buf, uint64_t val)
29{
30 uint64_buf conv;
31 conv.val = val;
32 for (int i = 0; i < 8; i++)
33 buf.push_back(x: conv.bytebuf[i]);
34}
35
36void
37add_uint32(std::vector<uint8_t> &buf, uint32_t val)
38{
39 uint32_buf conv;
40 conv.val = val;
41 for (int i = 0; i < 4; i++)
42 buf.push_back(x: conv.bytebuf[i]);
43}
44
45std::vector<uint8_t>
46x86_lc_thread_load_command ()
47{
48 std::vector<uint8_t> data;
49 add_uint32 (data, LC_THREAD); // thread_command.cmd
50 add_uint32 (buf&: data, val: 184); // thread_command.cmdsize
51 add_uint32 (data, x86_THREAD_STATE64); // thread_command.flavor
52 add_uint32 (data, x86_THREAD_STATE64_COUNT); // thread_command.count
53 add_uint64 (buf&: data, val: 0x0000000000000000); // rax
54 add_uint64 (buf&: data, val: 0x0000000000000400); // rbx
55 add_uint64 (buf&: data, val: 0x0000000000000000); // rcx
56 add_uint64 (buf&: data, val: 0x0000000000000000); // rdx
57 add_uint64 (buf&: data, val: 0x0000000000000000); // rdi
58 add_uint64 (buf&: data, val: 0x0000000000000000); // rsi
59 add_uint64 (buf&: data, val: 0xffffff9246e2ba20); // rbp
60 add_uint64 (buf&: data, val: 0xffffff9246e2ba10); // rsp
61 add_uint64 (buf&: data, val: 0x0000000000000000); // r8
62 add_uint64 (buf&: data, val: 0x0000000000000000); // r9
63 add_uint64 (buf&: data, val: 0x0000000000000000); // r10
64 add_uint64 (buf&: data, val: 0x0000000000000000); // r11
65 add_uint64 (buf&: data, val: 0xffffff7f96ce5fe1); // r12
66 add_uint64 (buf&: data, val: 0x0000000000000000); // r13
67 add_uint64 (buf&: data, val: 0x0000000000000000); // r14
68 add_uint64 (buf&: data, val: 0xffffff9246e2bac0); // r15
69 add_uint64 (buf&: data, val: 0xffffff8015a8f6d0); // rip
70 add_uint64 (buf&: data, val: 0x0000000000011111); // rflags
71 add_uint64 (buf&: data, val: 0x0000000000022222); // cs
72 add_uint64 (buf&: data, val: 0x0000000000033333); // fs
73 add_uint64 (buf&: data, val: 0x0000000000044444); // gs
74 return data;
75}
76
77void
78add_lc_note_kern_ver_str_load_command (std::vector<std::vector<uint8_t> > &loadcmds,
79 std::vector<uint8_t> &payload,
80 int payload_file_offset,
81 std::string ident)
82{
83 std::vector<uint8_t> loadcmd_data;
84
85 add_uint32 (loadcmd_data, LC_NOTE); // note_command.cmd
86 add_uint32 (buf&: loadcmd_data, val: 40); // note_command.cmdsize
87 char lc_note_name[16];
88 memset (s: lc_note_name, c: 0, n: 16);
89 strcpy (dest: lc_note_name, src: "kern ver str");
90
91 // lc_note.data_owner
92 for (int i = 0; i < 16; i++)
93 loadcmd_data.push_back (x: lc_note_name[i]);
94
95 // we start writing the payload at payload_file_offset to leave
96 // room at the start for the header & the load commands.
97 uint64_t current_payload_offset = payload.size() + payload_file_offset;
98
99 add_uint64 (buf&: loadcmd_data, val: current_payload_offset); // note_command.offset
100 add_uint64 (buf&: loadcmd_data, val: 4 + ident.size() + 1); // note_command.size
101
102 loadcmds.push_back (x: loadcmd_data);
103
104 add_uint32 (buf&: payload, val: 1); // kerneL_version_string.version
105 for (int i = 0; i < ident.size() + 1; i++)
106 {
107 payload.push_back (x: ident[i]);
108 }
109}
110
111void
112add_lc_segment (std::vector<std::vector<uint8_t> > &loadcmds,
113 std::vector<uint8_t> &payload,
114 int payload_file_offset)
115{
116 std::vector<uint8_t> loadcmd_data;
117 struct segment_command_64 seg;
118 seg.cmd = LC_SEGMENT_64;
119 seg.cmdsize = sizeof (struct segment_command_64); // no sections
120 memset (seg.segname, 0, 16);
121 seg.vmaddr = 0xffffff7f96400000;
122 seg.vmsize = 4096;
123 seg.fileoff = payload.size() + payload_file_offset;
124 seg.filesize = 0;
125 seg.maxprot = 1;
126 seg.initprot = 1;
127 seg.nsects = 0;
128 seg.flags = 0;
129
130 uint8_t *p = (uint8_t*) &seg;
131 for (int i = 0; i < sizeof (struct segment_command_64); i++)
132 {
133 loadcmd_data.push_back (*(p + i));
134 }
135 loadcmds.push_back (x: loadcmd_data);
136}
137
138std::string
139get_uuid_from_binary (const char *fn)
140{
141 FILE *f = fopen(filename: fn, modes: "r");
142 if (f == nullptr)
143 {
144 fprintf (stderr, format: "Unable to open binary '%s' to get uuid\n", fn);
145 exit(status: 1);
146 }
147 uint32_t num_of_load_cmds = 0;
148 uint32_t size_of_load_cmds = 0;
149 std::string uuid;
150 off_t file_offset = 0;
151
152 uint8_t magic[4];
153 if (::fread (ptr: magic, size: 1, n: 4, stream: f) != 4)
154 {
155 fprintf (stderr, format: "Failed to read magic number from input file %s\n", fn);
156 exit (status: 1);
157 }
158 uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce};
159 uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe};
160 uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf};
161 uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe};
162
163 if (memcmp (s1: magic, s2: magic_32_be, n: 4) == 0 || memcmp (s1: magic, s2: magic_64_be, n: 4) == 0)
164 {
165 fprintf (stderr, format: "big endian corefiles not supported\n");
166 exit (status: 1);
167 }
168
169 ::fseeko (stream: f, off: 0, SEEK_SET);
170 if (memcmp (s1: magic, s2: magic_32_le, n: 4) == 0)
171 {
172 struct mach_header mh;
173 if (::fread (ptr: &mh, size: 1, n: sizeof (mh), stream: f) != sizeof (mh))
174 {
175 fprintf (stderr, format: "error reading mach header from input file\n");
176 exit (status: 1);
177 }
178 if (mh.cputype != CPU_TYPE_X86_64)
179 {
180 fprintf (stderr, format: "This tool creates an x86_64 corefile but "
181 "the supplied binary '%s' is cputype 0x%x\n",
182 fn, (uint32_t) mh.cputype);
183 exit (status: 1);
184 }
185 num_of_load_cmds = mh.ncmds;
186 size_of_load_cmds = mh.sizeofcmds;
187 file_offset += sizeof (struct mach_header);
188 }
189 else
190 {
191 struct mach_header_64 mh;
192 if (::fread (ptr: &mh, size: 1, n: sizeof (mh), stream: f) != sizeof (mh))
193 {
194 fprintf (stderr, format: "error reading mach header from input file\n");
195 exit (status: 1);
196 }
197 if (mh.cputype != CPU_TYPE_X86_64)
198 {
199 fprintf (stderr, format: "This tool creates an x86_64 corefile but "
200 "the supplied binary '%s' is cputype 0x%x\n",
201 fn, (uint32_t) mh.cputype);
202 exit (status: 1);
203 }
204 num_of_load_cmds = mh.ncmds;
205 size_of_load_cmds = mh.sizeofcmds;
206 file_offset += sizeof (struct mach_header_64);
207 }
208
209 off_t load_cmds_offset = file_offset;
210
211 for (int i = 0; i < num_of_load_cmds && (file_offset - load_cmds_offset) < size_of_load_cmds; i++)
212 {
213 ::fseeko (stream: f, off: file_offset, SEEK_SET);
214 uint32_t cmd;
215 uint32_t cmdsize;
216 ::fread (ptr: &cmd, size: sizeof (uint32_t), n: 1, stream: f);
217 ::fread (ptr: &cmdsize, size: sizeof (uint32_t), n: 1, stream: f);
218 if (cmd == LC_UUID)
219 {
220 struct uuid_command uuidcmd;
221 ::fseeko (stream: f, off: file_offset, SEEK_SET);
222 if (::fread (ptr: &uuidcmd, size: 1, n: sizeof (uuidcmd), stream: f) != sizeof (uuidcmd))
223 {
224 fprintf (stderr, format: "Unable to read LC_UUID load command.\n");
225 exit (status: 1);
226 }
227 uuid_string_t uuidstr;
228 uuid_unparse (uuidcmd.uuid, uuidstr);
229 uuid = uuidstr;
230 break;
231 }
232 file_offset += cmdsize;
233 }
234 return uuid;
235}
236
237int main (int argc, char **argv)
238{
239 if (argc != 2 && argc != 3)
240 {
241 fprintf (stderr, format: "usage: create-empty-corefile <output-core-name> [binary-to-copy-uuid-from]\n");
242 fprintf (stderr, format: "Create a Mach-O corefile with an LC_NOTE 'kern ver str' load command/payload\n");
243 fprintf (stderr, format: "If a binary is given as a second argument, the Mach-O UUID of that file will\n");
244 fprintf (stderr, format: "be read and used in the corefile's LC_NOTE payload.\n");
245 exit (status: 1);
246 }
247
248 std::string ident = "EFI UUID=3F9BA21F-55EA-356A-A349-BBA6F51FE8B1";
249 if (argc == 3)
250 {
251 std::string uuid_from_file = get_uuid_from_binary (fn: argv[2]);
252 if (!uuid_from_file.empty())
253 {
254 ident = "EFI UUID=";
255 ident += uuid_from_file;
256 }
257 }
258
259 // An array of load commands (in the form of byte arrays)
260 std::vector<std::vector<uint8_t> > load_commands;
261
262 // An array of corefile contents (page data, lc_note data, etc)
263 std::vector<uint8_t> payload;
264
265 // First add all the load commands / payload so we can figure out how large
266 // the load commands will actually be.
267 load_commands.push_back (x: x86_lc_thread_load_command());
268 add_lc_note_kern_ver_str_load_command (loadcmds&: load_commands, payload, payload_file_offset: 0, ident);
269 add_lc_segment (loadcmds&: load_commands, payload, payload_file_offset: 0);
270
271 int size_of_load_commands = 0;
272 for (const auto &lc : load_commands)
273 size_of_load_commands += lc.size();
274
275 int header_and_load_cmd_room = sizeof (struct mach_header_64) + size_of_load_commands;
276
277 // Erase the load commands / payload now that we know how much space is needed,
278 // redo it.
279 load_commands.clear();
280 payload.clear();
281
282 load_commands.push_back (x: x86_lc_thread_load_command());
283 add_lc_note_kern_ver_str_load_command (loadcmds&: load_commands, payload, payload_file_offset: header_and_load_cmd_room, ident);
284 add_lc_segment (loadcmds&: load_commands, payload, payload_file_offset: header_and_load_cmd_room);
285
286 struct mach_header_64 mh;
287 mh.magic = MH_MAGIC_64;
288 mh.cputype = CPU_TYPE_X86_64;
289 mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL;
290 mh.filetype = MH_CORE;
291 mh.ncmds = load_commands.size();
292 mh.sizeofcmds = size_of_load_commands;
293 mh.flags = 0;
294 mh.reserved = 0;
295
296
297 FILE *f = fopen (filename: argv[1], modes: "w");
298
299 if (f == nullptr)
300 {
301 fprintf (stderr, format: "Unable to open file %s for writing\n", argv[1]);
302 exit (status: 1);
303 }
304
305 fwrite (&mh, sizeof (struct mach_header_64), 1, f);
306
307 for (const auto &lc : load_commands)
308 fwrite (ptr: lc.data(), size: lc.size(), n: 1, s: f);
309
310 fseek (stream: f, off: header_and_load_cmd_room, SEEK_SET);
311
312 fwrite (ptr: payload.data(), size: payload.size(), n: 1, s: f);
313
314 fclose (stream: f);
315}
316

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