diff options
author | Oxore <oxore@protonmail.com> | 2023-05-25 23:30:08 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2023-05-25 23:32:10 +0300 |
commit | b5505144c731ae992c17a5efdf53410994fdcb11 (patch) | |
tree | b1b5c8185b7c2abdc2ec7b3c0fbe64da8cb79292 | |
parent | 475413c7bf6b9861660e1616d03ad038def435b0 (diff) |
Impl exporting functions and marks
-rw-r--r-- | Readme.md | 2 | ||||
-rw-r--r-- | common.h | 3 | ||||
-rw-r--r-- | disasm.cpp | 16 | ||||
-rw-r--r-- | main.cpp | 248 |
4 files changed, 162 insertions, 107 deletions
@@ -94,7 +94,7 @@ trace table. Or better with marks analysis and some fancy raw comments: ``` -./cmake-build/m68k-disasm -frdc -fxrefs-to -fxrefs-from -fmarks -fabs-marks -frel-marks -fimm-marks -t pc-trace.txt -o disasm.S rom.bin +./cmake-build/m68k-disasm -frdc -fxrefs-to -fxrefs-from -fmarks -fabs-marks -frel-marks -fexport-marks -fexport-functions -t pc-trace.txt -o disasm.S rom.bin ``` It will produce `disasm.S` which you can modify and assemble as shown in @@ -8,6 +8,9 @@ struct Settings { bool rel_marks{}; bool abs_marks{}; bool imm_marks{}; + bool export_marks{}; + bool export_all_marks{}; + bool export_functions{}; bool xrefs_to{}; bool xrefs_from{}; bool raw_data_comment{}; @@ -1857,11 +1857,11 @@ int Arg::SNPrint( const char c = type == ArgType::kLong ? 'l' : 'w'; if (ref_kinds & kRefAbsMask) { if (static_cast<uint32_t>(lword) == ref_addr) { - return snprintf(buf, bufsz, ".L%08x:%c", ref_addr, c); + return snprintf(buf, bufsz, "L%08x:%c", ref_addr, c); } else { // It has to be AFTER the mark we are gonna reference here assert(static_cast<uint32_t>(lword) > ref_addr); - return snprintf(buf, bufsz, ".L%08x+%d:%c", ref_addr, lword - ref_addr, c); + return snprintf(buf, bufsz, "L%08x+%d:%c", ref_addr, lword - ref_addr, c); } } else { return snprintf(buf, bufsz, "0x%x:%c", lword, c); @@ -1874,10 +1874,10 @@ int Arg::SNPrint( const bool has_fix = ref_kinds & kRefPcRelFix2Bytes; const uint32_t arg_addr = self_addr + d16_pc.d16 + kInstructionSizeStepBytes + (has_fix ? kInstructionSizeStepBytes : 0); if (arg_addr == ref_addr) { - return snprintf(buf, bufsz, "%%pc@(.L%08x:w)", ref_addr); + return snprintf(buf, bufsz, "%%pc@(L%08x:w)", ref_addr); } else { assert(arg_addr > ref_addr); - return snprintf(buf, bufsz, "%%pc@(.L%08x+%d:w)", ref_addr, arg_addr - ref_addr); + return snprintf(buf, bufsz, "%%pc@(L%08x+%d:w)", ref_addr, arg_addr - ref_addr); } } else { return snprintf(buf, bufsz, "%%pc@(%d:w)", d16_pc.d16); @@ -1892,11 +1892,11 @@ int Arg::SNPrint( case ArgType::kImmediate: if (ref_kinds & kRef1ImmMask) { if (static_cast<uint32_t>(lword) == ref_addr) { - return snprintf(buf, bufsz, "#.L%08x", ref_addr); + return snprintf(buf, bufsz, "#L%08x", ref_addr); } else { // It has to be AFTER the mark we are gonna reference here assert(static_cast<uint32_t>(lword) > ref_addr); - return snprintf(buf, bufsz, "#.L%08x+%d", ref_addr, lword - ref_addr); + return snprintf(buf, bufsz, "#L%08x+%d", ref_addr, lword - ref_addr); } } else if (imm_as_hex) { return snprintf(buf, bufsz, "#0x%x", lword); @@ -1909,10 +1909,10 @@ int Arg::SNPrint( case ArgType::kDisplacement: if (ref_kinds & kRefRelMask) { if (static_cast<uint32_t>(self_addr + lword) == ref_addr) { - return snprintf(buf, bufsz, ".L%08x", ref_addr); + return snprintf(buf, bufsz, "L%08x", ref_addr); } else { assert(static_cast<uint32_t>(self_addr + lword) > ref_addr); - return snprintf(buf, bufsz, ".L%08x+%d", ref_addr, (self_addr + lword) - ref_addr); + return snprintf(buf, bufsz, "L%08x+%d", ref_addr, (self_addr + lword) - ref_addr); } } else { return snprintf(buf, bufsz, ".%s%d", lword >= 0 ? "+" : "", lword); @@ -221,7 +221,7 @@ static size_t RenderRawDataComment( return overall_sz; } -static const char *ReferenceTypeToString(ReferenceType type) +static constexpr const char *ReferenceTypeToString(ReferenceType type) { switch (type) { case ReferenceType::kUnknown: return "UNKNOWN"; @@ -246,84 +246,122 @@ static constexpr bool ShouldPrintAsRaw(const Op& op) return false; } -static void RenderDisassembly( - FILE *output, const DisasmMap &disasm_map, const DataBuffer &code, const Settings &s) +static constexpr bool HasCallReference(const DisasmNode &node) { - for (size_t i = 0; i < code.occupied_size;) { - const DisasmNode *node = disasm_map.FindNodeByOffset(i); - if (node) { - if (node->ref_by) { - if (s.xrefs_from) { - fprintf(output, "| XREFS:\n"); - for (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.marks) { - fprintf(output, ".L%08x:\n", node->offset); + 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 void RenderNodeDisassembly( + FILE *const output, + const DisasmMap &disasm_map, + const DataBuffer &code, + const Settings &s, + const DisasmNode &node) +{ + if (node.ref_by) { + if (s.marks) { + const bool export_this_function = s.export_functions && HasCallReference(node); + const bool export_this_mark = s.export_all_marks || + (s.export_marks && node.ref_by && (node.ref_by->refs_count > 1)) || + export_this_function; + if (export_this_mark) { + fprintf(output, "\n%s.globl\tL%08x\n", s.indent, node.offset); + if (export_this_function) { + fprintf(output, "%s.type\tL%08x, @function\n", s.indent, node.offset); } } - assert(node->op.opcode != OpCode::kNone); - if (ShouldPrintAsRaw(node->op)) { - auto raw = Op::Raw(GetU16BE(code.buffer + node->offset)); - raw.FPrint(output, s.indent); - uint32_t i = kInstructionSizeStepBytes; - for (; i < node->size; i += kInstructionSizeStepBytes) { - char arg_str[kArgsBufferSize]{}; - const auto arg = Arg::Raw(GetU16BE(code.buffer + node->offset + i)); - arg.SNPrint(arg_str, kArgsBufferSize); - fprintf(output, ", %s", arg_str); + } + if (s.xrefs_from) { + fprintf(output, "| XREFS:\n"); + for (const ReferenceNode *ref{node.ref_by}; ref; ref = ref->next) { + if (ref->refs_count == 0) { + continue; } - fprintf(output, "\n"); - } else { - const bool with_ref = node->ref_kinds && s.marks && (s.abs_marks || s.rel_marks); - const auto *ref1 = (node->ref_kinds & kRef1Mask) - ? disasm_map.FindNodeByOffset(node->ref1_addr) : nullptr; - const auto *ref2 = (node->ref_kinds & kRef2Mask) - ? disasm_map.FindNodeByOffset(node->ref2_addr) : nullptr; - const uint32_t ref1_addr = (with_ref && ref1) ? ref1->offset : 0; - const uint32_t ref2_addr = (with_ref && ref2) ? ref2->offset : 0; - if (with_ref && (ref1 || ref2)) { - const RefKindMask ref_kinds = - (s.abs_marks - ? ((ref1 ? (node->ref_kinds & kRef1AbsMask) : 0) | - (ref2 ? (node->ref_kinds & kRef2AbsMask) : 0)) - : 0) | - (s.rel_marks - ? ((ref1 ? (node->ref_kinds & kRef1RelMask) : 0) | - (ref2 ? (node->ref_kinds & kRef2RelMask) : 0)) - : 0) | - ((s.imm_marks && ref1) ? (node->ref_kinds & kRef1ImmMask) : 0) | - (node->ref_kinds & (kRefDataMask | kRefPcRelFix2Bytes)); - node->op.FPrint(output, s.indent, ref_kinds, node->offset, ref1_addr, ref2_addr); - if (s.xrefs_to && ref1) { - char ref_addr_str[12]{}; - snprintf(ref_addr_str, sizeof(ref_addr_str), ".L%08x", ref1_addr); - fprintf(output, " | %s", ref_addr_str); - } - if (s.xrefs_to && ref2) { - char ref_addr_str[12]{}; - snprintf(ref_addr_str, sizeof(ref_addr_str), ".L%08x", ref2_addr); - fprintf(output, " | %s", ref_addr_str); - } - } else { - node->op.FPrint(output, s.indent); + 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.marks) { + fprintf(output, "L%08x:\n", node.offset); + } + } + assert(node.op.opcode != OpCode::kNone); + if (ShouldPrintAsRaw(node.op)) { + auto raw = Op::Raw(GetU16BE(code.buffer + node.offset)); + raw.FPrint(output, s.indent); + uint32_t i = kInstructionSizeStepBytes; + for (; i < node.size; i += kInstructionSizeStepBytes) { + char arg_str[kArgsBufferSize]{}; + const auto arg = Arg::Raw(GetU16BE(code.buffer + node.offset + i)); + arg.SNPrint(arg_str, kArgsBufferSize); + fprintf(output, ", %s", arg_str); + } + fprintf(output, "\n"); + } else { + const bool with_ref = node.ref_kinds && s.marks && (s.abs_marks || s.rel_marks); + const auto *ref1 = (node.ref_kinds & kRef1Mask) + ? disasm_map.FindNodeByOffset(node.ref1_addr) : nullptr; + const auto *ref2 = (node.ref_kinds & kRef2Mask) + ? disasm_map.FindNodeByOffset(node.ref2_addr) : nullptr; + const uint32_t ref1_addr = (with_ref && ref1) ? ref1->offset : 0; + const uint32_t ref2_addr = (with_ref && ref2) ? ref2->offset : 0; + if (with_ref && (ref1 || ref2)) { + const RefKindMask ref_kinds = + (s.abs_marks + ? ((ref1 ? (node.ref_kinds & kRef1AbsMask) : 0) | + (ref2 ? (node.ref_kinds & kRef2AbsMask) : 0)) + : 0) | + (s.rel_marks + ? ((ref1 ? (node.ref_kinds & kRef1RelMask) : 0) | + (ref2 ? (node.ref_kinds & kRef2RelMask) : 0)) + : 0) | + ((s.imm_marks && ref1) ? (node.ref_kinds & kRef1ImmMask) : 0) | + (node.ref_kinds & (kRefDataMask | kRefPcRelFix2Bytes)); + node.op.FPrint(output, s.indent, ref_kinds, node.offset, ref1_addr, ref2_addr); + if (s.xrefs_to && ref1) { + char ref_addr_str[12]{}; + snprintf(ref_addr_str, (sizeof ref_addr_str), "L%08x", ref1_addr); + fprintf(output, " | %s", ref_addr_str); } - if (s.raw_data_comment) { - char raw_data_comment[100]{}; - RenderRawDataComment(raw_data_comment, sizeof(raw_data_comment) - 1, node->offset, node->size, code); - fprintf(output, " |%s", raw_data_comment); + if (s.xrefs_to && ref2) { + char ref_addr_str[12]{}; + snprintf(ref_addr_str, (sizeof ref_addr_str), "L%08x", ref2_addr); + fprintf(output, " | %s", ref_addr_str); } - fprintf(output, "\n"); + } else { + node.op.FPrint(output, s.indent); + } + } + if (s.raw_data_comment) { + char raw_data_comment[100]{}; + RenderRawDataComment( + raw_data_comment, + (sizeof raw_data_comment) - 1, + node.offset, + node.size, code); + fprintf(output, " |%s", raw_data_comment); + } + fprintf(output, "\n"); +} + +static void RenderDisassembly( + FILE *const output, const DisasmMap &disasm_map, const DataBuffer &code, const Settings &s) +{ + for (size_t i = 0; i < code.occupied_size;) { + const DisasmNode *node = disasm_map.FindNodeByOffset(i); + if (node) { + RenderNodeDisassembly(output, disasm_map, code, s, *node); i += node->size; } else { auto raw = Op::Raw(GetU16BE(code.buffer + i)); @@ -428,7 +466,7 @@ static int M68kDisasmByTrace(FILE *input_stream, FILE *output_stream, FILE *trac // Print output into output_stream RenderDisassembly(output_stream, *disasm_map, code, s); delete disasm_map; - return 0; + return EXIT_SUCCESS; } static int M68kDisasmAll(FILE *input_stream, FILE *output_stream, const Settings &s) @@ -454,7 +492,7 @@ static int M68kDisasmAll(FILE *input_stream, FILE *output_stream, const Settings // Print output into output_stream RenderDisassembly(output_stream, *disasm_map, code, s); delete disasm_map; - return 0; + return EXIT_SUCCESS; } static bool IsValidFeature(const char *feature) @@ -473,6 +511,12 @@ static bool IsValidFeature(const char *feature) return true; } else if (0 == strcmp(feature, "imm-marks")) { return true; + } else if (0 == strcmp(feature, "export-marks")) { + return true; + } else if (0 == strcmp(feature, "export-all-marks")) { + return true; + } else if (0 == strcmp(feature, "export-functions")) { + return true; } else if (0 == strcmp(feature, "xrefs-from")) { return true; } else if (0 == strcmp(feature, "xrefs-to")) { @@ -481,14 +525,11 @@ static bool IsValidFeature(const char *feature) return false; } -static void ApplyFeature(Settings& s, const char *feature) +static void ApplyFeature(Settings& s, const char *feature_arg) { - bool disable{}; - constexpr size_t sizeof_no_prefix = sizeof("no-")-1; - if (0 == memcmp(feature, "no-", sizeof_no_prefix)) { - disable = true; - feature += sizeof_no_prefix; - } + constexpr size_t sizeof_no_prefix = (sizeof "no-") - 1; + const bool disable = (0 == memcmp(feature_arg, "no-", sizeof_no_prefix)); + const char *const feature = feature_arg + (disable ? sizeof_no_prefix : 0); if (0 == strcmp(feature, "rdc")) { s.raw_data_comment = !disable; } else if (0 == strcmp(feature, "marks")) { @@ -499,6 +540,12 @@ static void ApplyFeature(Settings& s, const char *feature) s.abs_marks = !disable; } else if (0 == strcmp(feature, "imm-marks")) { s.imm_marks = !disable; + } else if (0 == strcmp(feature, "export-marks")) { + s.export_marks = !disable; + } else if (0 == strcmp(feature, "export-all-marks")) { + s.export_all_marks = !disable; + } else if (0 == strcmp(feature, "export-functions")) { + s.export_functions = !disable; } else if (0 == strcmp(feature, "xrefs-from")) { s.xrefs_from = !disable; } else if (0 == strcmp(feature, "xrefs-to")) { @@ -508,29 +555,34 @@ static void ApplyFeature(Settings& s, const char *feature) 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", argv0); fprintf(s, "Options:\n"); - fprintf(s, " -h, --help, Show this message\n"); + fprintf(s, " -h, --help, Show this message.\n"); fprintf(s, " -o, --output, Where to write disassembly to (stdout if not set)\n"); fprintf(s, " -t, --pc-trace, File containing PC trace\n"); - fprintf(s, " --indent, Specify instruction indentation, e.g. \"\t\"\n"); + fprintf(s, " --indent, Specify instruction indentation, e.g. \"\t\",\n"); + fprintf(s, " Single tab is used by default.\n"); fprintf(s, " -f, --feature=[no-]<feature>\n"); - fprintf(s, " Enable or disable (with \"no-\" prefix) a feature\n"); - fprintf(s, " Available features:\n"); - fprintf(s, " rdc print raw data comment\n"); - fprintf(s, " marks print marks above all places that have jumps\n"); - fprintf(s, " from somewhere\n"); - fprintf(s, " rel-marks use mark instead of number on relative\n"); - fprintf(s, " branch or call\n"); - fprintf(s, " abs-marks use mark instead of number on absolute\n"); - fprintf(s, " branch or call\n"); - fprintf(s, " imm-marks use mark instead of number when immediate\n"); - fprintf(s, " value moved to address register\n"); - fprintf(s, " xrefs-from print xrefs comments above all places that\n"); - fprintf(s, " have xrefs\n"); - fprintf(s, " xrefs-to print xrefs comments after all branch \n"); - fprintf(s, " instructions\n"); + fprintf(s, " Enable or disable (with \"no-\" prefix) a feature.\n"); + fprintf(s, " Available features described below under the\n"); + fprintf(s, " \"Feature flags\" mark.\n"); fprintf(s, " <input_file_name> Binary file with machine code (stdin if not set)\n"); + fprintf(s, "Feature flags:\n"); + fprintf(s, " rdc Print raw data comment.\n"); + fprintf(s, " marks Print marks above all places that have jumps from\n"); + fprintf(s, " somewhere.\n"); + fprintf(s, " rel-marks Use mark instead of number on relative branch or call.\n"); + fprintf(s, " abs-marks Use mark instead of number on absolute branch or call.\n"); + fprintf(s, " imm-marks Use mark instead of number when immediate value moved to\n"); + fprintf(s, " address register.\n"); + fprintf(s, " export-marks Add `.globl` preamble to marks referenced two or more\n"); + fprintf(s, " times.\n"); + fprintf(s, " export-all-marks Add `.globl` preamble to all marks.\n"); + fprintf(s, " export-functions Add `.globl` and `.type @funciton` preamble to marks\n"); + fprintf(s, " referenced as call.\n"); + fprintf(s, " xrefs-from Print xrefs comments above all places that have xrefs.\n"); + fprintf(s, " xrefs-to Print xrefs comments after all branch instructions.\n"); } int main(int, char* argv[]) |