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 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 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 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 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; 90 if (!uuid.empty()) { 91 ident = "EFI UUID="; 92 ident += uuid; 93 if (address != 0xffffffffffffffff) { 94 ident += "; stext="; 95 char buf[24]; 96 sprintf(buf, "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(loadcmd_data, 40); // note_command.cmdsize 105 char lc_note_name[16]; 106 memset(lc_note_name, 0, 16); 107 strcpy(lc_note_name, "kern ver str"); 108 109 // lc_note.data_owner 110 for (int i = 0; i < 16; i++) 111 loadcmd_data.push_back(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(loadcmd_data, current_payload_offset); // note_command.offset 118 add_uint64(loadcmd_data, 4 + ident.size() + 1); // note_command.size 119 120 loadcmds.push_back(loadcmd_data); 121 122 add_uint32(payload, 1); // kerneL_version_string.version 123 for (int i = 0; i < ident.size() + 1; i++) { 124 payload.push_back(ident[i]); 125 } 126 } 127 128 void 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(loadcmd_data, 40); // note_command.cmdsize 136 char lc_note_name[16]; 137 memset(lc_note_name, 0, 16); 138 strcpy(lc_note_name, "main bin spec"); 139 140 // lc_note.data_owner 141 for (int i = 0; i < 16; i++) 142 loadcmd_data.push_back(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(loadcmd_data, current_payload_offset); // note_command.offset 149 add_uint64(loadcmd_data, 150 sizeof(struct main_bin_spec_payload)); // note_command.size 151 152 loadcmds.push_back(loadcmd_data); 153 154 // Now write the "main bin spec" payload. 155 add_uint32(payload, 2); // version 156 add_uint32(payload, 3); // type == 3 [ firmware, standalone, etc ] 157 add_uint64(payload, address); // load address 158 add_uint64(payload, slide); // slide 159 uuid_t uuid; 160 uuid_parse(uuidstr.c_str(), uuid); 161 for (int i = 0; i < sizeof(uuid_t); i++) 162 payload.push_back(uuid[i]); 163 add_uint32(payload, 0); // log2_pagesize unspecified 164 add_uint32(payload, 0); // platform unspecified 165 } 166 167 void 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(loadcmd_data); 188 } 189 190 std::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(fn) == 0) 201 return {}; 202 203 FILE *f = fopen(fn, "r"); 204 if (f == nullptr) { 205 fprintf(stderr, "Unable to open binary '%s' to get uuid\n", fn); 206 exit(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(magic, 1, 4, f) != 4) { 215 fprintf(stderr, "Failed to read magic number from input file %s\n", fn); 216 exit(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(magic, magic_32_be, 4) == 0 || 224 memcmp(magic, magic_64_be, 4) == 0) { 225 fprintf(stderr, "big endian corefiles not supported\n"); 226 exit(1); 227 } 228 229 ::fseeko(f, 0, SEEK_SET); 230 if (memcmp(magic, magic_32_le, 4) == 0) { 231 struct mach_header mh; 232 if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) { 233 fprintf(stderr, "error reading mach header from input file\n"); 234 exit(1); 235 } 236 if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) { 237 fprintf(stderr, 238 "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(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(&mh, 1, sizeof(mh), f) != sizeof(mh)) { 251 fprintf(stderr, "error reading mach header from input file\n"); 252 exit(1); 253 } 254 if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) { 255 fprintf(stderr, 256 "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(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(f, file_offset, SEEK_SET); 274 uint32_t cmd; 275 uint32_t cmdsize; 276 ::fread(&cmd, sizeof(uint32_t), 1, f); 277 ::fread(&cmdsize, sizeof(uint32_t), 1, f); 278 if (cmd == LC_UUID) { 279 struct uuid_command uuidcmd; 280 ::fseeko(f, file_offset, SEEK_SET); 281 if (::fread(&uuidcmd, 1, sizeof(uuidcmd), f) != sizeof(uuidcmd)) { 282 fprintf(stderr, "Unable to read LC_UUID load command.\n"); 283 exit(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 295 int main(int argc, char **argv) { 296 if (argc != 6) { 297 fprintf( 298 stderr, 299 "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 " <address> is base 16, 0xffffffffffffffff means unknown\n"); 303 fprintf(stderr, 304 " <slide> is base 16, 0xffffffffffffffff means unknown\n"); 305 fprintf( 306 stderr, 307 "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n"); 308 fprintf(stderr, "an LC_NOTE 'main bin spec' load command without an " 309 "address specified, depending on\n"); 310 fprintf(stderr, "whether the 1st arg is version-string or main-bin-spec\n"); 311 fprintf(stderr, "\nan LC_NOTE 'kern ver str' with no binary provided " 312 "(empty string filename) to get a UUID\n"); 313 fprintf(stderr, "means an empty 'kern ver str' will be written, an invalid " 314 "LC_NOTE that lldb should handle.\n"); 315 exit(1); 316 } 317 if (strcmp(argv[1], "version-string") != 0 && 318 strcmp(argv[1], "main-bin-spec") != 0) { 319 fprintf(stderr, "arg1 was not version-string or main-bin-spec\n"); 320 exit(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(argv[4], NULL, 16); 335 if (errno != 0) { 336 fprintf(stderr, "Unable to parse address %s as base 16", argv[4]); 337 exit(1); 338 } 339 340 errno = 0; 341 uint64_t slide = strtoull(argv[5], NULL, 16); 342 if (errno != 0) { 343 fprintf(stderr, "Unable to parse slide %s as base 16", argv[4]); 344 exit(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(argv[1], "version-string") == 0) 351 add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid, 352 address); 353 else 354 add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid, 355 address, slide); 356 add_lc_segment(load_commands, payload, 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(argv[1], "version-string") == 0) 373 add_lc_note_kern_ver_str_load_command( 374 load_commands, payload, header_and_load_cmd_room, uuid, address); 375 else 376 add_lc_note_main_bin_spec_load_command( 377 load_commands, payload, header_and_load_cmd_room, uuid, address, slide); 378 379 add_lc_segment(load_commands, payload, 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(argv[2], "w"); 393 394 if (f == nullptr) { 395 fprintf(stderr, "Unable to open file %s for writing\n", argv[2]); 396 exit(1); 397 } 398 399 fwrite(&mh, sizeof(struct mach_header_64), 1, f); 400 401 for (const auto &lc : load_commands) 402 fwrite(lc.data(), lc.size(), 1, f); 403 404 fseek(f, header_and_load_cmd_room, SEEK_SET); 405 406 fwrite(payload.data(), payload.size(), 1, f); 407 408 fclose(f); 409 } 410