From c254495d3a733ab95fc1dbde5bb4e3359b8e5031 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 12 May 2025 15:53:54 +0100 Subject: [PATCH] frontend: implement disassemble command --- frontend/cmd/disassemble.c | 398 +++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) diff --git a/frontend/cmd/disassemble.c b/frontend/cmd/disassemble.c index 364cafc..643be4f 100644 --- a/frontend/cmd/disassemble.c +++ b/frontend/cmd/disassemble.c @@ -3,20 +3,334 @@ #include #include #include +#include #include +#include #include +#include #include #include +#include #include #include +#include + +enum dump_flags { + DUMP_INSTRUCTIONS = 0x01u, +}; enum { ARG_BIN_FILE, + OPT_DUMP, }; +static void dump_instruction(b_i32 x) +{ + struct ivy_instr instr = {}; + enum ivy_status status = ivy_instr_decode(x, &instr); + + if (status != IVY_OK) { + printf(""); + return; + } + +#define PRINT_CASE(code, str) \ + case (IVY_INSTR_##code): \ + fputs(str, stdout); \ + break + + const struct ivy_instr_definition *op = instr.i_op; + + switch (op->i_id) { + PRINT_CASE(LDR, "ldr"); + PRINT_CASE(STR, "str"); + PRINT_CASE(PUSH, "push"); + PRINT_CASE(POP, "pop"); + PRINT_CASE(MSG, "msg"); + PRINT_CASE(ADD, "add"); + PRINT_CASE(SUB, "sub"); + PRINT_CASE(MUL, "mul"); + PRINT_CASE(DIV, "div"); + PRINT_CASE(C_EQ, "c.eq"); + PRINT_CASE(C_NE, "c.ne"); + PRINT_CASE(C_LT, "c.lt"); + PRINT_CASE(C_LE, "c.le"); + PRINT_CASE(C_GT, "c.gt"); + PRINT_CASE(C_GE, "c.ge"); + PRINT_CASE(BR, "br"); + PRINT_CASE(BR_T, "br.t"); + PRINT_CASE(BR_F, "br.f"); + PRINT_CASE(OB_C, "ob.c"); + PRINT_CASE(OB_E, "ob.e"); + PRINT_CASE(LAM_C, "lam.c"); + PRINT_CASE(IT_G, "it.g"); + PRINT_CASE(IT_N, "it.n"); + PRINT_CASE(IT_V, "it.v"); + PRINT_CASE(RET, "ret"); + PRINT_CASE(RET_N, "ret.n"); + default: + fputs("", stdout); + } +#undef PRINT_CASE + + switch (op->i_opcode) { + case IVY_OP_LDR_SP_REG: + printf(" x%ld, [sp, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_SP_CONST: + printf(" x%ld, [sp, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_BP_REG: + printf(" x%ld, [bp, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_BP_CONST: + printf(" x%ld, [bp, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_SELF_REG: + printf(" x%ld, [self, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_SELF_CONST: + printf(" x%ld, [self, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_POOL_REG: + printf(" x%ld, [pool, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_POOL_CONST: + printf(" x%ld, [pool, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_LDR_CONST: + printf(" x%ld, #%ld", instr.i_arg[0], instr.i_arg[1]); + break; + + case IVY_OP_STR_SP_REG: + printf(" x%ld, [sp, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_STR_SP_CONST: + printf(" x%ld, [sp, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_STR_BP_REG: + printf(" x%ld, [bp, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_STR_BP_CONST: + printf(" x%ld, [bp, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_STR_SELF_REG: + printf(" x%ld, [self, x%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + case IVY_OP_STR_SELF_CONST: + printf(" x%ld, [self, #%ld]", instr.i_arg[0], instr.i_arg[1]); + break; + + case IVY_OP_PUSH_REG: + printf(" x%ld", instr.i_arg[0]); + break; + case IVY_OP_PUSH_CONST: + printf(" #%ld", instr.i_arg[0]); + break; + case IVY_OP_POP: + printf(" x%ld", instr.i_arg[0]); + break; + + case IVY_OP_MSG_REG: + printf(" x%ld, x%ld, x%lx", instr.i_arg[0], instr.i_arg[1], + instr.i_arg[2]); + break; + case IVY_OP_MSG_CONST: + printf(" x%ld, x%ld, #%lx", instr.i_arg[0], instr.i_arg[1], + instr.i_arg[2]); + break; + + case IVY_OP_ADD: + case IVY_OP_SUB: + case IVY_OP_MUL: + case IVY_OP_DIV: + printf(" x%ld, x%ld, x%lx", instr.i_arg[0], instr.i_arg[1], + instr.i_arg[2]); + break; + + case IVY_OP_C_EQ: + case IVY_OP_C_NE: + case IVY_OP_C_LT: + case IVY_OP_C_LE: + case IVY_OP_C_GT: + case IVY_OP_C_GE: + printf(" x%ld, x%ld, x%lx", instr.i_arg[0], instr.i_arg[1], + instr.i_arg[2]); + break; + + case IVY_OP_BR: + case IVY_OP_BR_T: + case IVY_OP_BR_F: + printf(" $%ld", instr.i_arg[0]); + break; + + case IVY_OP_OB_C_REG: + break; + case IVY_OP_OB_C_CONST: + break; + case IVY_OP_OB_E: + break; + + case IVY_OP_LAM_C_REG: + break; + case IVY_OP_LAM_C_CONST: + break; + + case IVY_OP_IT_G: + break; + case IVY_OP_IT_N: + break; + case IVY_OP_IT_V: + break; + + case IVY_OP_RET: + break; + case IVY_OP_RET_N: + break; + default: + break; + } +} + +static void dump_instructions(b_i32 *x, size_t buffer_size) +{ + size_t nr_instr = buffer_size / sizeof *x; + + for (size_t i = 0; i < nr_instr; i++) { + if (i > 0) { + fputs("; ", stdout); + } + + dump_instruction(x[i]); + } +} + +static void dump_strings(uint32_t *x, size_t buffer_size) +{ + char *c = (char *)x; + + for (int ii = 0; ii < buffer_size; ii++) { + if (isgraph(c[ii])) { + fputc(c[ii], stdout); + } else { + fputc('.', stdout); + } + } +} + +static enum ivy_status dump_header( + struct ivy_asm_section_reader *sect, size_t *dump_offset) +{ + const struct ivy_asm_section_info *info + = ivy_asm_section_reader_get_info(sect); + enum ivy_status status = IVY_OK; + size_t r = 0; + + switch (info->s_type) { + case IVY_TABLE_CLASS: { + struct ivy_bin_class xcls; + status = ivy_asm_section_reader_read( + sect, 0, sizeof xcls, &xcls, &r); + if (status != IVY_OK) { + break; + } + + if (r != sizeof xcls) { + status = IVY_ERR_BAD_FORMAT; + break; + } + + printf(" ident=0x%04x\n", b_i32_btoh(xcls.c_ident)); + *dump_offset = sizeof xcls; + break; + } + case IVY_TABLE_BLOCK: { + struct ivy_bin_block text; + status = ivy_asm_section_reader_read( + sect, 0, sizeof text, &text, &r); + if (status != IVY_OK) { + break; + } + + if (r != sizeof text) { + status = IVY_ERR_BAD_FORMAT; + break; + } + + printf(" index=0x%04x\n", b_i32_btoh(text.b_index)); + *dump_offset = sizeof text; + break; + } + case IVY_TABLE_XDAT: + default: + *dump_offset = 0; + break; + } + + return status; +} + +static enum ivy_status dump_section( + struct ivy_asm_reader *reader, size_t index, size_t chunks_per_line, + enum dump_flags flags) +{ + struct ivy_asm_section_reader *sect = NULL; + enum ivy_status status = ivy_asm_reader_open_section(reader, index, §); + if (status != IVY_OK) { + return status; + } + + const struct ivy_asm_section_info *info + = ivy_asm_section_reader_get_info(sect); + size_t len = info->s_length; + size_t buffer_size = chunks_per_line * sizeof(uint32_t); + uint32_t *x = malloc(buffer_size); + + size_t offset = 0; + status = dump_header(sect, &offset); + if (status != IVY_OK) { + return status; + } + + for (size_t i = offset; i < len; i += buffer_size) { + size_t r; + status = ivy_asm_section_reader_read(sect, i, buffer_size, x, &r); + + if (status != IVY_OK) { + break; + } + + if (r != buffer_size) { + break; + } + + printf(" %08zx | ", i); + + for (size_t ii = 0; ii < chunks_per_line; ii++) { + printf("%08" PRIx32 " ", x[ii]); + } + + printf(" | "); + + if (flags & DUMP_INSTRUCTIONS) { + dump_instructions((b_i32 *)x, buffer_size); + } else { + dump_strings(x, buffer_size); + } + + fputc('\n', stdout); + } + + free(x); + ivy_asm_section_reader_close(sect); + return status; +} + static int disassemble( const b_command *cmd, const b_arglist *args, const b_array *_) { + bool dump = b_arglist_get_count(args, OPT_DUMP, B_COMMAND_INVALID_ID) > 0; + const char *in_path = NULL; b_arglist_get_string(args, B_COMMAND_INVALID_ID, ARG_BIN_FILE, 0, &in_path); @@ -25,6 +339,81 @@ static int disassemble( return -1; } + FILE *in = fopen(in_path, "rb"); + if (!in) { + b_err("cannot open object file"); + b_i("reason: %s", strerror(errno)); + + return -1; + } + + struct ivy_asm_reader *reader = NULL; + enum ivy_status status = ivy_asm_reader_open(in, &reader); + + if (status != IVY_OK) { + b_err("cannot open object file"); + b_i("reason: %s", ivy_status_to_string(status)); + + fclose(in); + return -1; + } + + const struct ivy_asm_object_info *object_info + = ivy_asm_reader_get_info(reader); + const struct ivy_asm_section_info *sections + = ivy_asm_reader_get_sections(reader); + + printf("header:\n"); + printf("\tmagic: 0x%08" PRIx32 "\n", object_info->obj_magic); + printf("\ttable offset: 0x%zx\n", object_info->obj_table_offset); + printf("\tnr sections: %zu\n", object_info->obj_nr_sections); + + printf("\n"); + + printf("sections:\n"); + for (size_t i = 0; i < object_info->obj_nr_sections; i++) { + const struct ivy_asm_section_info *sect = §ions[i]; + char type[5]; + + printf(" * #%03zu: ", i); + + if (ivy_asm_section_type_to_string(sect->s_type, type)) { + printf("[%4s]", type); + } else { + printf("[%08" PRIx32 "]", sect->s_type); + } + + printf(" offset=0x%04zx, length=0x%04zx\n", sect->s_offset, + sect->s_length); + + size_t chunks_per_line = 1; + enum dump_flags flags = 0; + + switch (sect->s_type) { + case IVY_TABLE_XDAT: + case IVY_TABLE_CLASS: + chunks_per_line = 4; + break; + case IVY_TABLE_POOL: + chunks_per_line = 2; + break; + case IVY_TABLE_BLOCK: + chunks_per_line = 1; + flags |= DUMP_INSTRUCTIONS; + break; + default: + chunks_per_line = 1; + break; + } + + if (dump) { + dump_section(reader, i, chunks_per_line, flags); + fputc('\n', stdout); + } + } + + ivy_asm_reader_close(reader); + return 0; } @@ -43,5 +432,14 @@ B_COMMAND(CMD_DISASSEMBLE, CMD_ROOT) B_ARG_NR_VALUES(1); } + B_COMMAND_OPTION(OPT_DUMP) + { + B_OPTION_SHORT_NAME('d'); + B_OPTION_LONG_NAME("dump"); + B_OPTION_DESC( + "decode and print the contents of each object " + "section."); + } + B_COMMAND_HELP_OPTION(); }