| //===-- main.cppp ---------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CoreSpec.h" |
| #include "LCNoteWriter.h" |
| #include "MemoryWriter.h" |
| #include "ThreadWriter.h" |
| #include "Utility.h" |
| #include "llvm/BinaryFormat/MachO.h" |
| #include "llvm/Support/CommandLine.h" |
| #include <stdio.h> |
| #include <string> |
| #include <sys/stat.h> |
| |
| std::vector<std::string> get_fields_from_delimited_string(std::string str, |
| const char delim) { |
| std::vector<std::string> result; |
| std::string::size_type prev = std::string::npos; |
| std::string::size_type next = str.find(delim); |
| if (str.empty()) { |
| return result; |
| } |
| if (next == std::string::npos) { |
| result.push_back(str); |
| } else { |
| result.push_back(std::string(str, 0, next)); |
| prev = next; |
| while ((next = str.find(delim, prev + 1)) != std::string::npos) { |
| result.push_back(std::string(str, prev + 1, next - prev - 1)); |
| prev = next; |
| } |
| result.push_back(std::string(str, prev + 1)); |
| } |
| return result; |
| } |
| |
| llvm::cl::opt<std::string> InputFilename("i", llvm::cl::Required, |
| llvm::cl::desc("input yaml filename"), |
| llvm::cl::value_desc("input")); |
| llvm::cl::opt<std::string> |
| OutputFilename("o", llvm::cl::Required, |
| llvm::cl::desc("output core filenames"), |
| llvm::cl::value_desc("output")); |
| llvm::cl::list<std::string> |
| UUIDs("u", llvm::cl::desc("uuid of binary loaded at slide 0"), |
| llvm::cl::value_desc("uuid")); |
| llvm::cl::list<std::string> |
| UUIDAndVAs("L", llvm::cl::desc("UUID,virtual-address-loaded-at"), |
| llvm::cl::value_desc("--uuid-and-load-addr")); |
| llvm::cl::opt<int> |
| AddressableBitsOverride("A", |
| llvm::cl::desc("number of bits used in addressing"), |
| llvm::cl::value_desc("--address-bits")); |
| |
| int main(int argc, char **argv) { |
| llvm::cl::ParseCommandLineOptions(argc, argv); |
| |
| if (InputFilename.empty() || OutputFilename.empty()) { |
| fprintf(stderr, "Missing input or outpur file.\n"); |
| exit(1); |
| } |
| |
| struct stat sb; |
| |
| if (stat(InputFilename.c_str(), &sb) == -1) { |
| fprintf(stderr, "Unable to stat %s, exiting\n", InputFilename.c_str()); |
| exit(1); |
| } |
| |
| FILE *input = fopen(InputFilename.c_str(), "r"); |
| if (!input) { |
| fprintf(stderr, "Unable to open %s, exiting\n", InputFilename.c_str()); |
| exit(1); |
| } |
| auto file_corespec = std::make_unique<char[]>(sb.st_size); |
| if (fread(file_corespec.get(), sb.st_size, 1, input) != 1) { |
| fprintf(stderr, "Unable to read all of %s, exiting\n", |
| InputFilename.c_str()); |
| exit(1); |
| } |
| CoreSpec spec = from_yaml(file_corespec.get(), sb.st_size); |
| fclose(input); |
| |
| for (const std::string &uuid : UUIDs) { |
| Binary binary; |
| binary.uuid = uuid; |
| binary.value = 0; |
| binary.value_is_slide = true; |
| spec.binaries.push_back(binary); |
| } |
| |
| for (const std::string &uuid_and_va : UUIDAndVAs) { |
| std::vector<std::string> parts = |
| get_fields_from_delimited_string(uuid_and_va, ','); |
| |
| std::string uuid = parts[0]; |
| uint64_t va = std::strtoull(parts[1].c_str(), nullptr, 16); |
| Binary binary; |
| binary.uuid = uuid; |
| binary.value = va; |
| binary.value_is_slide = false; |
| spec.binaries.push_back(binary); |
| } |
| |
| if (AddressableBitsOverride) { |
| AddressableBits bits; |
| bits.lowmem_bits = bits.highmem_bits = AddressableBitsOverride; |
| spec.addressable_bits = bits; |
| } |
| |
| // An array of load commands |
| std::vector<std::vector<uint8_t>> load_commands; |
| |
| // An array of corefile contents (memory regions) |
| std::vector<uint8_t> payload; |
| |
| // First add all the load commands / payload so we can figure out how large |
| // the load commands will be. |
| |
| add_lc_threads(spec, load_commands); |
| for (size_t i = 0; i < spec.memory_regions.size(); i++) { |
| std::vector<uint8_t> segment_command_bytes; |
| create_lc_segment_cmd(spec, segment_command_bytes, spec.memory_regions[i], |
| 0); |
| load_commands.push_back(segment_command_bytes); |
| } |
| |
| if (spec.binaries.size() > 0) |
| for (const Binary &binary : spec.binaries) { |
| std::vector<uint8_t> segment_command_bytes; |
| std::vector<uint8_t> payload_bytes; |
| create_lc_note_binary_load_cmd(spec, segment_command_bytes, binary, |
| payload_bytes, 0); |
| load_commands.push_back(segment_command_bytes); |
| } |
| if (spec.addressable_bits) { |
| std::vector<uint8_t> segment_command_bytes; |
| std::vector<uint8_t> payload_bytes; |
| create_lc_note_addressable_bits(spec, segment_command_bytes, |
| *spec.addressable_bits, payload_bytes, 0); |
| load_commands.push_back(segment_command_bytes); |
| } |
| |
| off_t size_of_load_commands = 0; |
| for (const auto &lc : load_commands) |
| size_of_load_commands += lc.size(); |
| |
| off_t header_and_load_cmd_room = |
| sizeof(llvm::MachO::mach_header_64) + size_of_load_commands; |
| off_t initial_payload_fileoff = header_and_load_cmd_room; |
| initial_payload_fileoff = (initial_payload_fileoff + 4096 - 1) & ~(4096 - 1); |
| off_t payload_fileoff = initial_payload_fileoff; |
| |
| // Erase the load commands / payload now that we know how much space is |
| // needed, redo it with real values. |
| load_commands.clear(); |
| payload.clear(); |
| |
| add_lc_threads(spec, load_commands); |
| for (size_t i = 0; i < spec.memory_regions.size(); i++) { |
| std::vector<uint8_t> segment_command_bytes; |
| create_lc_segment_cmd(spec, segment_command_bytes, spec.memory_regions[i], |
| payload_fileoff); |
| load_commands.push_back(segment_command_bytes); |
| payload_fileoff += spec.memory_regions[i].size; |
| payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1); |
| } |
| |
| off_t payload_fileoff_before_lcnotes = payload_fileoff; |
| std::vector<uint8_t> lc_note_payload_bytes; |
| if (spec.binaries.size() > 0) |
| for (const Binary &binary : spec.binaries) { |
| std::vector<uint8_t> segment_command_bytes; |
| std::vector<uint8_t> payload_bytes; |
| create_lc_note_binary_load_cmd(spec, segment_command_bytes, binary, |
| lc_note_payload_bytes, payload_fileoff); |
| payload_fileoff = |
| payload_fileoff_before_lcnotes + lc_note_payload_bytes.size(); |
| load_commands.push_back(segment_command_bytes); |
| } |
| if (spec.addressable_bits) { |
| std::vector<uint8_t> segment_command_bytes; |
| std::vector<uint8_t> payload_bytes; |
| create_lc_note_addressable_bits(spec, segment_command_bytes, |
| *spec.addressable_bits, |
| lc_note_payload_bytes, payload_fileoff); |
| payload_fileoff = |
| payload_fileoff_before_lcnotes + lc_note_payload_bytes.size(); |
| load_commands.push_back(segment_command_bytes); |
| } |
| |
| // Realign our payload offset if we added any LC_NOTEs. |
| if (lc_note_payload_bytes.size() > 0) |
| payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1); |
| |
| FILE *f = fopen(OutputFilename.c_str(), "wb"); |
| if (f == nullptr) { |
| fprintf(stderr, "Unable to open file %s for writing\n", |
| OutputFilename.c_str()); |
| exit(1); |
| } |
| |
| std::vector<uint8_t> mh; |
| // Write the fields of a mach_header_64 struct |
| if (spec.wordsize == 8) |
| add_uint32(mh, llvm::MachO::MH_MAGIC_64); // magic |
| else |
| add_uint32(mh, llvm::MachO::MH_MAGIC); // magic |
| add_uint32(mh, spec.cputype); // cputype |
| add_uint32(mh, spec.cpusubtype); // cpusubtype |
| add_uint32(mh, llvm::MachO::MH_CORE); // filetype |
| add_uint32(mh, load_commands.size()); // ncmds |
| add_uint32(mh, size_of_load_commands); // sizeofcmds |
| add_uint32(mh, 0); // flags |
| if (spec.wordsize == 8) |
| add_uint32(mh, 0); // reserved |
| |
| fwrite(mh.data(), mh.size(), 1, f); |
| |
| for (const auto &lc : load_commands) |
| fwrite(lc.data(), lc.size(), 1, f); |
| |
| // Reset the payload offset back to the first one. |
| payload_fileoff = initial_payload_fileoff; |
| if (spec.memory_regions.size() > 0) { |
| for (size_t i = 0; i < spec.memory_regions.size(); i++) { |
| std::vector<uint8_t> bytes; |
| create_memory_bytes(spec, spec.memory_regions[i], bytes); |
| fseek(f, payload_fileoff, SEEK_SET); |
| fwrite(bytes.data(), bytes.size(), 1, f); |
| |
| payload_fileoff += bytes.size(); |
| payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1); |
| } |
| } |
| |
| if (lc_note_payload_bytes.size() > 0) { |
| fseek(f, payload_fileoff, SEEK_SET); |
| fwrite(lc_note_payload_bytes.data(), lc_note_payload_bytes.size(), 1, f); |
| payload_fileoff += lc_note_payload_bytes.size(); |
| payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1); |
| } |
| |
| fclose(f); |
| } |