frontend: implement disassemble command
This commit is contained in:
@@ -3,20 +3,334 @@
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/object/string.h>
|
||||
#include <blue/term.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <ivy/asm/assembler.h>
|
||||
#include <ivy/asm/instr.h>
|
||||
#include <ivy/asm/lex.h>
|
||||
#include <ivy/asm/parse.h>
|
||||
#include <ivy/asm/reader.h>
|
||||
#include <ivy/file.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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("<invalid>");
|
||||
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("<unknown>", 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user