diff options
Diffstat (limited to 'main.cpp')
-rw-r--r-- | main.cpp | 836 |
1 files changed, 0 insertions, 836 deletions
diff --git a/main.cpp b/main.cpp deleted file mode 100644 index a6f73b3..0000000 --- a/main.cpp +++ /dev/null @@ -1,836 +0,0 @@ -/* SPDX-License-Identifier: Unlicense - */ - -#include "elf_image.h" -#include "data_buffer.h" -#include "disasm.h" -#include "common.h" - -#define OPTPARSE_IMPLEMENTATION -#define OPTPARSE_API static -#include "optparse/optparse.h" - -#include <cassert> -#include <cinttypes> -#include <cstdio> -#include <cstdint> -#include <cstdlib> -#include <cstring> -#include <cerrno> -#include <climits> - -enum class DisasmMapType { - kTraced, - kRaw, -}; - -class DisasmMap { - const DisasmMapType _type; - DisasmNode *_map[kDisasmMapSizeElements]{}; - constexpr DisasmNode *findNodeByAddress(uint32_t address) const; - DisasmNode &insertNode(uint32_t address, NodeType); - DisasmNode &insertReferencedBy( - const uint32_t by_addr, - const uint32_t ref_addr, - const NodeType type, - const ReferenceType ref_type); - constexpr bool canBeAllocated(const DisasmNode& node) const; -public: - constexpr const DisasmNode *FindNodeByAddress(uint32_t address) const - { - return findNodeByAddress(address); - }; - void InsertNode(uint32_t address, NodeType type) - { - assert(_type == DisasmMapType::kTraced); - insertNode(address, type); - } - void Disasm(const DataView &code, const Settings &, size_t from=0, bool nested=false); - DisasmMap(DisasmMapType type): _type(type) {} - ~DisasmMap(); -}; - -constexpr DisasmNode *DisasmMap::findNodeByAddress(uint32_t address) const -{ - if (address < kRomSizeBytes) - return _map[address / kInstructionSizeStepBytes]; - return nullptr; -} - -static constexpr uint32_t AlignInstructionAddress(const uint32_t address) -{ - return address & ~1UL; -} - -DisasmNode &DisasmMap::insertNode(const uint32_t address, const NodeType type) -{ - auto *node = findNodeByAddress(address); - if (node) { - // Instruction nodes take precedence over data nodes. If a node that - // was previously accessed only as data now turns out to be an - // instruction, then it must become an instruction node. - if (IsInstruction(type) && !IsInstruction(node->type)) { - *const_cast<NodeType*>(&node->type) = type; - // Make sure it is OpCode::kNone so it will be properly disassembled - node->op = Op{}; - } - return *node; - } - node = new DisasmNode(DisasmNode{type, AlignInstructionAddress(address)}); - assert(node); - _map[address / kInstructionSizeStepBytes] = node; - return *node; -} - -DisasmNode &DisasmMap::insertReferencedBy( - const uint32_t by_addr, - const uint32_t ref_addr, - const NodeType type, - const ReferenceType ref_type) -{ - auto &ref_node = insertNode(ref_addr, type); - ref_node.AddReferencedBy(by_addr, ref_type); - return ref_node; -} - -constexpr bool DisasmMap::canBeAllocated(const DisasmNode& node) const -{ - const auto size = node.size / kInstructionSizeStepBytes; - const auto *const node_real = findNodeByAddress(node.address); - for (size_t i = 1; i < size; i++) { - const auto *const ptr = _map[node.address / kInstructionSizeStepBytes + i]; - if (ptr != nullptr && ptr != node_real) { - return false; - } - } - return true; -} - -static constexpr ReferenceType ReferenceTypeFromRefKindMask1(const RefKindMask ref_kinds) -{ - return (ref_kinds & kRefCallMask) - ? ReferenceType::kCall - : (ref_kinds & kRef1ReadMask) - ? ReferenceType::kRead - : (ref_kinds & kRef1WriteMask) - ? ReferenceType::kWrite - : ReferenceType::kBranch; -} - -static constexpr ReferenceType ReferenceTypeFromRefKindMask2(const RefKindMask ref_kinds) -{ - return (ref_kinds & kRefCallMask) - ? ReferenceType::kCall - : (ref_kinds & kRef2ReadMask) - ? ReferenceType::kRead - : (ref_kinds & kRef2WriteMask) - ? ReferenceType::kWrite - : ReferenceType::kBranch; -} - -static constexpr bool IsNextLikelyAnInstruction(const Op &op) -{ - return (op.opcode != OpCode::kNone && - op.opcode != OpCode::kRaw && - !IsBRA(op) && - op.opcode != OpCode::kJMP && - op.opcode != OpCode::kRTS && - op.opcode != OpCode::kRTE && - op.opcode != OpCode::kSTOP); -} - -void DisasmMap::Disasm( - const DataView &code, const Settings &s, size_t at, bool nested) -{ - // Some of logic of this function is covered by integration tests in - // `test_walk_and_follow_jumps.bash`. - bool inside_code_span = nested; - while (at < Min(kRomSizeBytes, code.size)) { - DisasmNode *node; - if (_type == DisasmMapType::kTraced) { - node = _map[at / kInstructionSizeStepBytes]; - if (!node) { - if (inside_code_span) { - node = &insertNode(at, NodeType::kTracedInstruction); - } else { - at += kInstructionSizeStepBytes; - continue; - } - } - } else { - node = &insertNode(at, NodeType::kTracedInstruction); - } - if (node->op.opcode == OpCode::kNone || inside_code_span) { - const auto size = node->Disasm(code); - assert(size >= kInstructionSizeStepBytes); - if (canBeAllocated(*node)) { - // Spread across the size - for (size_t o = kInstructionSizeStepBytes; o < size; o++) { - _map[(node->address + o) / kInstructionSizeStepBytes] = node; - } - } else { - node->DisasmAsRaw(code); - } - } - inside_code_span = s.walk && IsNextLikelyAnInstruction(node->op); - if (nested && !inside_code_span) { - return; - } - at += node->size; - // NOTE: There is not much information about a reference passed further, - // so just don't add a reference of immediate if s.imm_labels is false - // enabled. - const bool has_ref1 = (node->ref_kinds & kRef1ImmMask) - ? s.imm_labels - : (node->ref_kinds & kRef1Mask); - const bool has_code_ref1 = node->ref1_addr < code.size && has_ref1; - if (has_code_ref1) { - const NodeType type = (node->ref_kinds & (kRef1ReadMask | kRef1WriteMask)) - ? NodeType::kData : NodeType::kRefInstruction; - const auto ref_type = ReferenceTypeFromRefKindMask1(node->ref_kinds); - auto &ref_node = insertReferencedBy( - node->address, node->ref1_addr, type, ref_type); - if (ref_node.op.opcode == OpCode::kNone) { - if (s.follow_jumps) { - Disasm(code, s, ref_node.address, true); - } else { - ref_node.DisasmAsRaw(code); - } - } - } - const bool has_ref2 = (node->ref_kinds & kRef2Mask); - const bool has_code_ref2 = (has_ref2 && node->ref2_addr < code.size); - if (has_code_ref2) { - const NodeType type = (node->ref_kinds & (kRef2ReadMask | kRef2WriteMask)) - ? NodeType::kData : NodeType::kRefInstruction; - const auto ref_type = ReferenceTypeFromRefKindMask2(node->ref_kinds); - auto &ref_node = insertReferencedBy( - node->address, node->ref2_addr, type, ref_type); - if (ref_node.op.opcode == OpCode::kNone) { - if (s.follow_jumps) { - Disasm(code, s, ref_node.address, true); - } else { - ref_node.DisasmAsRaw(code); - } - } - } - } -} - -DisasmMap::~DisasmMap() -{ - for (size_t i = 0; i < kDisasmMapSizeElements; i++) { - auto *const node = _map[i]; - if (!node) { - continue; - } - const auto size = node->size / kInstructionSizeStepBytes; - for (size_t o = 0; o < size; o++) { - assert(_map[i + o] == node); - _map[i + o] = nullptr; - } - delete node; - i += size - 1; - } -} - -static size_t RenderRawDataComment( - char *out, size_t out_sz, uint32_t address, size_t instr_sz, const DataView &code) -{ - size_t overall_sz{}; - for (size_t i = 0; i < instr_sz; i += kInstructionSizeStepBytes) - { - overall_sz += Min( - out_sz - overall_sz, - snprintf( - out + overall_sz, - out_sz - overall_sz, - " %04x", - GetU16BE(code.buffer + address + i))); - } - overall_sz += Min( - out_sz - overall_sz, - snprintf(out + overall_sz, out_sz - overall_sz, " @%08x", address)); - return overall_sz; -} - -static constexpr const char *ReferenceTypeToString(ReferenceType type) -{ - switch (type) { - case ReferenceType::kUnknown: return "UNKNOWN"; - case ReferenceType::kCall: return "CALL"; - case ReferenceType::kBranch: return "BRANCH"; - case ReferenceType::kRead: return "READ"; - case ReferenceType::kWrite: return "WRITE"; - } - return "UNKN"; -} - -static constexpr bool ShouldPrintAsRaw(const Op& op) -{ - if (op.arg1.type == ArgType::kImmediate) { - if (op.opcode == OpCode::kADD || op.opcode == OpCode::kSUB || - op.opcode == OpCode::kAND || op.opcode == OpCode::kOR || - op.opcode == OpCode::kEOR || op.opcode == OpCode::kCMP) - { - return true; - } - } - return false; -} - -static constexpr bool HasCallReference(const DisasmNode &node) -{ - for (const ReferenceNode *ref{node.ref_by}; ref; ref = ref->next) { - for (size_t i = 0; i < ref->refs_count; i++) { - if (ref->refs[i].type == ReferenceType::kCall) { - return true; - } - } - } - return false; -} - -static constexpr size_t GetNodeSizeByAddress(const DisasmMap &disasm_map, const uint32_t address) -{ - const auto *node = disasm_map.FindNodeByAddress(address); - if (node == nullptr) { - return kInstructionSizeStepBytes; - } - return node->size; -} - -static constexpr bool IsLocalLocation(const DisasmMap &disasm_map, const DisasmNode &node) -{ - for (const ReferenceNode *ref{node.ref_by}; ref; ref = ref->next) { - for (size_t i = 0; i < ref->refs_count; i++) { - const ReferenceRecord &ref_rec = ref->refs[i]; - if (ref_rec.type == ReferenceType::kCall) { - // Locals are definitely not made for calls - return false; - } - const bool forward = ref_rec.address < node.address; - const size_t min_addr = forward ? ref_rec.address : node.address; - const size_t start = min_addr + GetNodeSizeByAddress(disasm_map, min_addr); - const size_t max_addr = forward ? node.address : ref_rec.address; - const size_t end = max_addr + (forward ? 0 : GetNodeSizeByAddress(disasm_map, min_addr)); - for (size_t addr = start; addr < end;) { - const auto *intermediate_node = disasm_map.FindNodeByAddress(addr); - if (intermediate_node) { - if (intermediate_node->ref_by) { - // Another labeled node detected on the jump path, hence - // current node's location cannot be considered local - return false; - } - addr += intermediate_node->size; - } else { - addr += kInstructionSizeStepBytes; - } - } - } - } - return true; -} - -static constexpr const char *StringWihoutFristNChars(const char *str, const size_t n) -{ - for (size_t i = 0, tab = 0; i < n && *str; i++, str++) { - if (*str == '\t') { - tab++; - if (tab == 7) { - tab = 0; - str++; - } - } else { - str++; - } - } - return str; -} - -static void RenderNodeDisassembly( - FILE *const output, - const DisasmMap &disasm_map, - const DataView &code, - const Settings &s, - const DisasmNode &node) -{ - if (node.ref_by) { - const bool is_local = IsLocalLocation(disasm_map, node); - if (s.labels && !(s.short_ref_local_labels && is_local)) { - const bool export_this_function = s.export_functions && HasCallReference(node); - const bool export_this_label = s.export_all_labels || - (s.export_labels && node.ref_by && (node.ref_by->refs_count > 1)) || - export_this_function; - if (export_this_label) { - fprintf(output, "\n%s.globl\tL%08x\n", s.indent, node.address); - if (export_this_function) { - fprintf(output, "%s.type\tL%08x, @function\n", s.indent, node.address); - } - } - } - if (s.xrefs_from && !(s.short_ref_local_labels && is_local)) { - fprintf(output, "| XREFS:\n"); - for (const ReferenceNode *ref{node.ref_by}; ref; ref = ref->next) { - if (ref->refs_count == 0) { - continue; - } - fprintf(output, "|"); - for (size_t i = 0; i < ref->refs_count; i++) { - const ReferenceRecord r = ref->refs[i]; - fprintf(output, " %s @%08x", ReferenceTypeToString(r.type), r.address); - } - fprintf(output, "\n"); - } - } - if (s.labels) { - if (s.short_ref_local_labels && is_local) { - fprintf(output, "1:%s", StringWihoutFristNChars(s.indent, (sizeof "1:") - 1)); - } else { - fprintf(output, "L%08x:\n", node.address); - } - } - } - assert(node.op.opcode != OpCode::kNone); - if (ShouldPrintAsRaw(node.op)) { - auto raw = Op::Raw(GetU16BE(code.buffer + node.address)); - raw.FPrint(output, s.indent, s.imm_hex); - uint32_t i = kInstructionSizeStepBytes; - for (; i < node.size; i += kInstructionSizeStepBytes) { - char arg_str[kArgsBufferSize]{}; - const auto arg = Arg::Raw(GetU16BE(code.buffer + node.address + i)); - arg.SNPrint(arg_str, kArgsBufferSize); - fprintf(output, ", %s", arg_str); - } - } else { - const bool with_ref = node.ref_kinds && s.labels && (s.abs_labels || s.rel_labels); - const auto *ref1 = (node.ref_kinds & kRef1Mask) - ? disasm_map.FindNodeByAddress(node.ref1_addr) : nullptr; - const auto *ref2 = (node.ref_kinds & kRef2Mask) - ? disasm_map.FindNodeByAddress(node.ref2_addr) : nullptr; - const uint32_t ref1_addr = (with_ref && ref1) ? ref1->address : 0; - const uint32_t ref2_addr = (with_ref && ref2) ? ref2->address : 0; - if (with_ref && (ref1 || ref2)) { - const RefKindMask ref_kinds = - (s.abs_labels - ? ((ref1 ? (node.ref_kinds & kRef1AbsMask) : 0) | - (ref2 ? (node.ref_kinds & kRef2AbsMask) : 0)) - : 0) | - (s.rel_labels - ? ((ref1 ? (node.ref_kinds & kRef1RelMask) : 0) | - (ref2 ? (node.ref_kinds & kRef2RelMask) : 0)) - : 0) | - ((s.imm_labels && ref1) ? (node.ref_kinds & kRef1ImmMask) : 0) | - (node.ref_kinds & (kRefDataMask | kRefPcRelFix2Bytes)); - const bool ref1_is_local = !ref1 || IsLocalLocation(disasm_map, *ref1); - char ref1_label[32]{}; - if (ref1) { - if (s.short_ref_local_labels && ref1_is_local) { - const char dir = ref1_addr <= node.address ? 'b' : 'f'; - snprintf(ref1_label, (sizeof ref1_label), "1%c", dir); - } else { - snprintf(ref1_label, (sizeof ref1_label), "L%08x", ref1_addr); - } - } - const bool ref2_is_local = !ref2 || IsLocalLocation(disasm_map, *ref2); - char ref2_label[32]{}; - if (ref2) { - if (s.short_ref_local_labels && ref2_is_local) { - const char dir = ref2_addr <= node.address ? 'b' : 'f'; - snprintf(ref2_label, (sizeof ref2_label), "1%c", dir); - } else { - snprintf(ref2_label, (sizeof ref2_label), "L%08x", ref2_addr); - } - } - node.op.FPrint( - output, - s.indent, - s.imm_hex, - ref_kinds, - ref1_label, - ref2_label, - node.address, - ref1_addr, - ref2_addr); - const bool ref1_from_imm_ok = ((node.ref_kinds & kRef1ImmMask) ? s.imm_labels : true); - if (s.xrefs_to && !(s.short_ref_local_labels && ref1_is_local) && ref1_from_imm_ok) - { - fprintf(output, " | L%08x", ref1_addr); - } - if (s.xrefs_to && !(s.short_ref_local_labels && ref2_is_local)) { - fprintf(output, " | L%08x", ref2_addr); - } - } else { - node.op.FPrint(output, s.indent, s.imm_hex); - } - } - if (s.raw_data_comment) { - char raw_data_comment[100]{}; - RenderRawDataComment( - raw_data_comment, - (sizeof raw_data_comment) - 1, - node.address, - node.size, code); - fprintf(output, " |%s", raw_data_comment); - } - fprintf(output, "\n"); -} - -static void RenderDisassembly( - FILE *const output, const DisasmMap &disasm_map, const DataView &code, const Settings &s) -{ - for (size_t i = 0; i < code.size;) { - const DisasmNode *node = disasm_map.FindNodeByAddress(i); - if (node) { - RenderNodeDisassembly(output, disasm_map, code, s, *node); - i += node->size; - } else { - auto raw = Op::Raw(GetU16BE(code.buffer + i)); - raw.FPrint(output, s.indent, s.imm_hex); - fprintf(output, "\n"); - i += kInstructionSizeStepBytes; - } - } -} - -static void ParseTraceData(DisasmMap &disasm_map, const DataView &trace_data) -{ - // FIXME make a full blown parser with various radixes support and different - // trace types support - bool parse = true; - for (size_t i = 0; i < trace_data.size; i++) { - if (trace_data.buffer[i] == '\n' || trace_data.buffer[i] == '\r') { - parse = true; - } else if (parse) { - errno = 0; - const char *startptr = reinterpret_cast<const char *>(trace_data.buffer + i); - char *endptr = nullptr; - const long address = strtol(startptr, &endptr, 10); - if ((address == LONG_MAX || address == LONG_MIN) && errno == ERANGE) { - // Parsing error, just skip - } else if (startptr == endptr) { - // Parsing error, just skip - } else if (address % 2) { - fprintf(stderr, "Error: Uneven PC values are not supported (got PC=0x%08lx), exiting\n", address); - exit(1); - } else if (static_cast<unsigned long>(address) > kRomSizeBytes) { - fprintf(stderr, "Error: PC values > 4MiB are not supported (got PC=0x%08lx), exiting\n", address); - exit(1); - } else { - // Valid value - disasm_map.InsertNode(address, NodeType::kTracedInstruction); - } - if (startptr != endptr) { - i += endptr - startptr - 1; - } - parse = false; - } - } -} - -static size_t ReadFromStream(DataBuffer &db, FILE *stream) -{ - assert(db.buffer && db.buffer_size >= db.kInitialSize); - while (1) { - const size_t read_size = db.buffer_size - db.occupied_size; - const size_t fread_ret = fread( - db.buffer + db.occupied_size, sizeof(*db.buffer), read_size, stream); - db.occupied_size += fread_ret; - if (fread_ret >= db.buffer_size) { - assert(fread_ret == db.buffer_size); - db.Expand(db.buffer_size * 2); - } else { - const int err = errno; - if (feof(stream)) { - break; - } else if (ferror(stream)) { - fprintf(stderr, "ReadFromStream: fread(%zu): Error (%d): \"%s\"\n", read_size, err, strerror(err)); - return EXIT_FAILURE; - } else if (db.buffer_size == db.occupied_size) { - db.Expand(db.buffer_size * 2); - } else { - assert(false); - } - } - } - return db.occupied_size; -} - -static DisasmMap *NewDisasmMap(FILE *trace_stream) -{ - if (trace_stream == nullptr) { - DisasmMap *disasm_map = new DisasmMap{DisasmMapType::kRaw}; - assert(disasm_map); - return disasm_map; - } - // Read trace file into buffer - DataBuffer trace_data{}; - const size_t trace_size = ReadFromStream(trace_data, trace_stream); - if (trace_size == 0) { - fprintf(stderr, "ReadFromStream(trace_data, trace_stream): Error: No data has been read\n"); - return nullptr; - } - // Parse trace file into map - DisasmMap *disasm_map = new DisasmMap{DisasmMapType::kTraced}; - assert(disasm_map); - ParseTraceData(*disasm_map, trace_data.View()); - return disasm_map; -} - -static int M68kDisasm( - FILE *input_stream, FILE *output_stream, FILE *trace_stream, const Settings &s) -{ - // Read input file into buffer - DataBuffer input{}; - const size_t input_size = ReadFromStream(input, input_stream); - if (input_size == 0) { - fprintf(stderr, "ReadFromStream(input, input_stream): Error: No data has been read\n"); - return EXIT_FAILURE; - } - const ELF::Image elf(static_cast<DataBuffer&&>(input)); - if (s.bfd == BFDTarget::kELF && !elf.IsValid()) { - fprintf(stderr, "Error: ELF image is not valid: %s\n", elf.Error()); - return EXIT_FAILURE; - } - const bool from_elf = s.bfd == BFDTarget::kELF || (s.bfd == BFDTarget::kAuto && elf.IsValid()); - const DataView code(from_elf ? elf.ProgramView() : elf.Data().View()); - assert(code.buffer != nullptr); - assert(code.size != 0); - // It is not worth it to check this somewhere while disassembling or - // emitting. Odd size is just not supported. - if (code.size % 2) { - fprintf(stderr, "M68kDisasm: Error: code blob must be of even size\n"); - return EXIT_FAILURE; - } - auto *disasm_map = NewDisasmMap(trace_stream); - if (disasm_map == nullptr) { - return EXIT_FAILURE; - } - // Disasm into output map - disasm_map->Disasm(code, s); - // Print output into output_stream - RenderDisassembly(output_stream, *disasm_map, code, s); - delete disasm_map; - return EXIT_SUCCESS; -} - -static bool FeatureStringHasPrefixNo(const char *feature) -{ - assert(feature); - // There is also implicit, embedded and free check for null terminator - if (feature[0] == 'n' && feature[1] == 'o' && feature[2] == '-') { - return true; - } - return false; -} - -static bool ApplyFeature(Settings& s, const char *feature_arg) -{ - struct { - bool Settings::* setting; - const char* feature_name; - } const features[]{ - { &Settings::raw_data_comment, "rdc" }, - { &Settings::labels, "labels" }, - { &Settings::rel_labels, "rel-labels" }, - { &Settings::abs_labels, "abs-labels" }, - { &Settings::imm_labels, "imm-labels" }, - { &Settings::short_ref_local_labels, "short-ref-local-labels" }, - { &Settings::export_labels, "export-labels" }, - { &Settings::export_all_labels, "export-all-labels" }, - { &Settings::export_functions, "export-functions" }, - { &Settings::xrefs_from, "xrefs-from" }, - { &Settings::xrefs_to, "xrefs-to" }, - { &Settings::imm_hex, "imm-hex" }, - { &Settings::follow_jumps, "follow-jumps" }, - { &Settings::walk, "walk" }, - }; - constexpr size_t sizeof_no_prefix = (sizeof "no-") - 1; - const bool disable = FeatureStringHasPrefixNo(feature_arg); - const char *const feature = feature_arg + (disable ? sizeof_no_prefix : 0); - for (size_t i = 0; i < (sizeof features) / (sizeof *features); i++) { - if (0 == strcmp(feature, features[i].feature_name)) { - s.*(features[i].setting) = !disable; - return true; - } - } - return false; -} - -static void PrintUsage(FILE *s, const char *argv0) -{ - // Please, keep all lines in 80 columns range when printed. - fprintf(s, - "Usage: %s [options] <input-file-name>\n" - "Options:\n" - " -h, --help, Show this message.\n" - " -o, --output, Where to write disassembly to (stdout if not set)\n" - " -t, --pc-trace, File containing PC trace\n" - " --indent, Specify instruction indentation, e.g. \"\t\",\n" - " Single tab is used by default.\n" - " -f, --feature=[no-]<feature>\n" - " Enable or disable (with \"no-\" prefix) a feature.\n" - " Available features described below under the\n" - " \"Feature flags\" section.\n" - " -b, --bfd-target=bfdname\n" - " Specify target object format as `bfdname`. Will attempt\n" - " to detect automatically if not set. Only `auto,\n" - " `binary` and `elf` are currently supported.\n" - " <input_file_name> Binary or elf file with the machine code to disassemble\n" - "Feature flags:\n" - " rdc Print raw data comment.\n" - " labels Print labels above all places that have jumps from\n" - " somewhere.\n" - " rel-labels Use label instead of number on relative branch or call.\n" - " abs-labels Use label instead of number on absolute branch or call.\n" - " imm-labels Use label instead of number when immediate value moved\n" - " to address register.\n" - " short-ref-local-labels\n" - " Use local labels (numbers) for short jumps or loops.\n" - " Jump is considered short when it does not cross other\n" - " labels and has no calls.\n" - " export-labels Add `.globl` preamble to labels referenced two or more\n" - " times.\n" - " export-all-labels Add `.globl` preamble to all labels.\n" - " export-functions Add `.globl` and `.type @funciton` preamble to a label\n" - " referenced as a call.\n" - " xrefs-from Print xrefs comments above all places that have xrefs.\n" - " xrefs-to Print xrefs comments after all branch instructions.\n" - " imm-hex Print all immediate values as hexadecimal numbers.\n" - " follow-jumps Follow jumps to statically known locations.\n" - " walk Try best to detect further instructions following known\n" - " traced locations without overcommitting.\n" - , argv0); -} - -int main(int, char* argv[]) -{ - struct optparse_long longopts[] = { - {"help", 'h', OPTPARSE_NONE}, - {"output", 'o', OPTPARSE_REQUIRED}, - {"pc-trace", 't', OPTPARSE_REQUIRED}, - {"feature", 'f', OPTPARSE_REQUIRED}, - {"bfd-target", 'b', OPTPARSE_REQUIRED}, - {"indent", 80, OPTPARSE_REQUIRED}, - {}, - }; - const char *trace_file_name = nullptr; - const char *output_file_name = nullptr; - const char *input_file_name = nullptr; - Settings s{}; - struct optparse options; - optparse_init(&options, argv); - // Parse opts - int option; - while ((option = optparse_long(&options, longopts, NULL)) != -1) { - switch (option) { - case 'h': - PrintUsage(stdout, argv[0]); - return EXIT_SUCCESS; - break; - case 'o': - output_file_name = options.optarg; - break; - case 't': - trace_file_name = options.optarg; - break; - case 'f': - if (!ApplyFeature(s, options.optarg)) { - fprintf(stderr, "main: Error: Unknown feature \"%s\", exiting\n", options.optarg); - return EXIT_FAILURE; - } - break; - case 'b': - { - const auto *bfd_str = options.optarg; - if (0 == strcmp(bfd_str, "auto")) { - s.bfd = BFDTarget::kAuto; - } else if (0 == strcmp(bfd_str, "binary")) { - s.bfd = BFDTarget::kBinary; - } else if (0 == strcmp(bfd_str, "elf")) { - s.bfd = BFDTarget::kELF; - } else { - fprintf( - stderr, - "Unknown BFD target specified: \"%s\". " - "Refer to usage below to find correct BFD values.\n", - bfd_str); - PrintUsage(stderr, argv[0]); - return EXIT_FAILURE; - } - } - break; - case 80: - s.indent = options.optarg; - break; - case '?': - fprintf(stderr, "main: optparse_long: Error: \"%s\"\n", options.errmsg); - return EXIT_FAILURE; - } - } - // Parse input file name - char *arg; - while ((arg = optparse_arg(&options))) { - if (input_file_name == nullptr) { - input_file_name = arg; - } else { - fprintf(stderr, "error: too many free arguments provided\n"); - return EXIT_FAILURE; - } - } - // Open the files - FILE *input_stream = nullptr; - FILE *output_stream = stdout; - FILE *trace_stream = nullptr; - if (input_file_name) { - if (0 == strcmp(input_file_name, "-")) { - input_stream = stdin; - } else { - input_stream = fopen(input_file_name, "r"); - } - if (input_stream == nullptr) { - const int err = errno; - fprintf(stderr, "main: fopen(\"%s\", \"r\"): Error (%d): \"%s\"\n", input_file_name, err, strerror(err)); - return EXIT_FAILURE; - } - } else { - fprintf(stderr, "main: Error: no input file name specified, see usage below.\n"); - PrintUsage(stderr, argv[0]); - return EXIT_FAILURE; - } - if (output_file_name) { - output_stream = fopen(output_file_name, "w"); - if (output_stream == nullptr) { - const int err = errno; - fprintf(stderr, "main: fopen(\"%s\", \"w\"): Error (%d): \"%s\"\n", output_file_name, err, strerror(err)); - fclose(input_stream); - return EXIT_FAILURE; - } - } - if (trace_file_name) { - if (0 == strcmp(trace_file_name, "-")) { - if (input_stream == stdin) { - fprintf(stderr, "error: trace stream and input stream cannot be both stdin\n"); - return EXIT_FAILURE; - } - trace_stream = stdin; - } else { - trace_stream = fopen(trace_file_name, "r"); - } - if (trace_stream == nullptr) { - const int err = errno; - fprintf(stderr, "main: fopen(\"%s\", \"r\"): Error (%d): \"%s\"\n", trace_file_name, err, strerror(err)); - fclose(input_stream); - fclose(output_stream); - return EXIT_FAILURE; - } - } - // Run the program - const int ret = M68kDisasm(input_stream, output_stream, trace_stream, s); - if (trace_stream != nullptr) { - fclose(trace_stream); - } - fclose(output_stream); - fclose(input_stream); - return ret; -} |