From e1f8c5e47ca2cf629143c48d7bf17e8ddfae423e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D9=85=D8=B5=D8=B7=D9=81=D9=8A=20=D9=85=D8=AD=D9=85=D9=88?= =?UTF-8?q?=D8=AF=20=D9=83=D9=85=D8=A7=D9=84=20=D8=A7=D9=84=D8=AF=D9=8A?= =?UTF-8?q?=D9=86?= <48567303+moste00@users.noreply.github.com> Date: Sat, 23 Mar 2024 00:26:57 +0200 Subject: [PATCH] Lift MSP430 machine language to RzIL: first 3 instructions and 1 addressing mode --- librz/arch/isa/msp430/msp430_disas.c | 176 ++++++--- librz/arch/isa/msp430/msp430_disas.h | 61 +++- librz/arch/isa/msp430/msp430_il.c | 337 ++++++++++++++++++ librz/arch/isa/msp430/msp430_il.h | 19 + librz/arch/isa/msp430/msp430_il_flags.inc | 183 ++++++++++ librz/arch/isa/msp430/msp430_il_getset.inc | 131 +++++++ librz/arch/isa/msp430/msp430_il_jmp_utils.inc | 60 ++++ librz/arch/isa/msp430/msp430_register_names.h | 28 ++ librz/arch/meson.build | 1 + librz/arch/p/analysis/analysis_msp430.c | 33 +- test/db/asm/msp430 | 17 +- test/db/cmd/cmd_list | 2 +- test/db/rzil/msp430 | 19 + 13 files changed, 1007 insertions(+), 60 deletions(-) create mode 100644 librz/arch/isa/msp430/msp430_il.c create mode 100644 librz/arch/isa/msp430/msp430_il.h create mode 100644 librz/arch/isa/msp430/msp430_il_flags.inc create mode 100644 librz/arch/isa/msp430/msp430_il_getset.inc create mode 100644 librz/arch/isa/msp430/msp430_il_jmp_utils.inc create mode 100644 librz/arch/isa/msp430/msp430_register_names.h create mode 100644 test/db/rzil/msp430 diff --git a/librz/arch/isa/msp430/msp430_disas.c b/librz/arch/isa/msp430/msp430_disas.c index 19de3fd97a2..e5847f446dd 100644 --- a/librz/arch/isa/msp430/msp430_disas.c +++ b/librz/arch/isa/msp430/msp430_disas.c @@ -9,24 +9,9 @@ #include "msp430_disas.h" -static const char *msp430_register_names[] = { - "pc", - "sp", - "sr", - "cg", - "r4", - "r5", - "r6", - "r7", - "r8", - "r9", - "r10", - "r11", - "r12", - "r13", - "r14", - "r15", -}; +#include + +#define WRITE_INSTR_STR(cmd, str) snprintf(cmd->instr, sizeof(cmd->instr), "%s", str) static const char *two_op_instrs[] = { [MSP430_MOV] = "mov", @@ -104,8 +89,7 @@ static void remove_second_operand(struct msp430_cmd *cmd) { } /* TODO: This code is messy, needs to be refactored. */ -static int decode_emulation(ut16 instr, struct msp430_cmd *cmd) { - int ret = -1; +static void decode_emulation(ut16 instr, struct msp430_cmd *cmd) { ut8 as, ad, src, dst, bw, opcode; as = get_as(instr); @@ -116,83 +100,125 @@ static int decode_emulation(ut16 instr, struct msp430_cmd *cmd) { opcode = get_twoop_opcode(instr); if (opcode == MSP430_ADDC && as == 0 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "adc.b" : "adc"); + WRITE_INSTR_STR(cmd, bw ? "adc.b" : "adc"); snprintf(cmd->operands, sizeof(cmd->operands), "%s", msp430_register_names[dst]); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_ADC; } else if (opcode == MSP430_MOV && as == 0 && src == MSP430_R3) { + cmd->type = MSP430_EMULATE; + if (ad == 0 && dst == MSP430_R3) { snprintf(cmd->instr, sizeof(cmd->instr), "nop"); cmd->operands[0] = '\0'; + cmd->opcode = MSP430_NOP; } else { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "clr.b" : "clr"); + WRITE_INSTR_STR(cmd, bw ? "clr.b" : "clr"); remove_first_operand(cmd); + cmd->opcode = MSP430_CLR; } } else if (opcode == MSP430_MOV && as == 3 && src == MSP430_SP) { + cmd->type = MSP430_EMULATE; + if (dst == MSP430_PC) { snprintf(cmd->instr, sizeof(cmd->instr), "ret"); - cmd->type = MSP430_ONEOP; - cmd->opcode = MSP430_RETI; cmd->operands[0] = '\0'; + cmd->opcode = MSP430_RET; } else { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "pop.b" : "pop"); + WRITE_INSTR_STR(cmd, bw ? "pop.b" : "pop"); remove_first_operand(cmd); + cmd->opcode = MSP430_POP; } } else if (opcode == MSP430_MOV && ad == 0 && dst == MSP430_PC) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", "br"); + WRITE_INSTR_STR(cmd, "br"); remove_second_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_BR; } else if (opcode == MSP430_BIC && as == 2 && src == MSP430_SR && dst == MSP430_SR && ad == 0) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", "clrn"); + WRITE_INSTR_STR(cmd, "clrn"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_CLRN; } else if (opcode == MSP430_BIC && as == 2 && src == MSP430_R3 && dst == MSP430_SR && ad == 0) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", "clrz"); + WRITE_INSTR_STR(cmd, "clrz"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_CLRZ; } else if (opcode == MSP430_BIC && as == 3 && src == MSP430_SR && dst == MSP430_SR && ad == 0) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", "dint"); + WRITE_INSTR_STR(cmd, "dint"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_DINT; } else if (opcode == MSP430_BIS && as == 3 && src == MSP430_SR && dst == MSP430_SR && ad == 0) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", "eint"); + WRITE_INSTR_STR(cmd, "eint"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_EINT; } else if (opcode == MSP430_DADD && as == 0 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "dadc.b" : "dadc"); + WRITE_INSTR_STR(cmd, bw ? "dadc.b" : "dadc"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_DADC; } else if (opcode == MSP430_SUB && as == 1 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "dec.b" : "dec"); + WRITE_INSTR_STR(cmd, bw ? "dec.b" : "dec"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_DEC; } else if (opcode == MSP430_SUB && as == 2 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "decd.b" : "decd"); + WRITE_INSTR_STR(cmd, bw ? "decd.b" : "decd"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_DECD; } else if (opcode == MSP430_ADD && as == 1 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "inc.b" : "inc"); + WRITE_INSTR_STR(cmd, bw ? "inc.b" : "inc"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_INC; } else if (opcode == MSP430_ADD && as == 2 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "incd.b" : "incd"); + WRITE_INSTR_STR(cmd, bw ? "incd.b" : "incd"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_INCD; } else if (opcode == MSP430_XOR && as == 3 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "inv.b" : "inv"); + WRITE_INSTR_STR(cmd, bw ? "inv.b" : "inv"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_INV; } else if (opcode == MSP430_ADD && src == dst) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "rla.b" : "rla"); + WRITE_INSTR_STR(cmd, bw ? "rla.b" : "rla"); remove_second_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_RLA; } else if (opcode == MSP430_ADDC && src == dst) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "rlc.b" : "rlc"); + WRITE_INSTR_STR(cmd, bw ? "rlc.b" : "rlc"); remove_second_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_RLC; } else if (opcode == MSP430_SUBC && as == 0 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "sbc.b" : "sbc"); + WRITE_INSTR_STR(cmd, bw ? "sbc.b" : "sbc"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_SBC; } else if (opcode == MSP430_BIS && as == 1 && src == MSP430_R3 && dst == MSP430_SR && ad == 0) { snprintf(cmd->instr, sizeof(cmd->instr), "setc"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_SETC; } else if (opcode == MSP430_BIS && as == 2 && src == MSP430_SR && dst == MSP430_SR && ad == 0) { snprintf(cmd->instr, sizeof(cmd->instr), "setn"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_SETN; } else if (opcode == MSP430_BIS && as == 2 && src == MSP430_R3 && dst == MSP430_SR && ad == 0) { snprintf(cmd->instr, sizeof(cmd->instr), "setz"); cmd->operands[0] = '\0'; + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_SETZ; } else if (opcode == MSP430_CMP && as == 0 && src == MSP430_R3) { - snprintf(cmd->instr, sizeof(cmd->instr), "%s", bw ? "tst.b" : "tst"); + WRITE_INSTR_STR(cmd, bw ? "tst.b" : "tst"); remove_first_operand(cmd); + cmd->type = MSP430_EMULATE; + cmd->opcode = MSP430_TST; } - - return ret; } /* return #byte of instruction */ @@ -215,9 +241,13 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ switch (src) { case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "#0"); + cmd->src_mode = MSP430_IMM; + cmd->src = 0; break; default: /* register mode */ snprintf(cmd->operands, sizeof(cmd->operands), "%s", msp430_register_names[src]); + cmd->src_mode = MSP430_REG; + cmd->src = src; } ret = 2; break; @@ -226,18 +256,26 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ switch (src) { case MSP430_PC: /* symbolic mode */ snprintf(cmd->operands, sizeof(cmd->operands), "0x%04x", op1); + cmd->src_mode = MSP430_SYM; + cmd->src = op1; srcOperInCodeWord = 1; break; case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "%s", "#1"); + cmd->src_mode = MSP430_IMM; + cmd->src = 1; ret = 2; break; case MSP430_SR: /* absolute mode */ snprintf(cmd->operands, sizeof(cmd->operands), "&0x%04x", op1); + cmd->src_mode = MSP430_ABS; + cmd->src = op1; srcOperInCodeWord = 1; break; default: /* indexed mode */ snprintf(cmd->operands, sizeof(cmd->operands), "0x%x(%s)", op1, msp430_register_names[src]); + cmd->src_mode = MSP430_INDX; + cmd->src = (op1 << 16) | src; srcOperInCodeWord = 1; } break; @@ -245,12 +283,18 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ switch (src) { case MSP430_SR: /* CG1 */ snprintf(cmd->operands, sizeof(cmd->operands), "#4"); + cmd->src_mode = MSP430_IMM; + cmd->src = 4; break; case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "#2"); + cmd->src_mode = MSP430_IMM; + cmd->src = 2; break; default: /* indirect register mode */ snprintf(cmd->operands, sizeof(cmd->operands), "@%s", msp430_register_names[src]); + cmd->src_mode = MSP430_IND_REG; + cmd->src = src; } ret = 2; break; @@ -259,17 +303,25 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ switch (src) { case MSP430_SR: /* CG1 */ snprintf(cmd->operands, sizeof(cmd->operands), "#8"); + cmd->src_mode = MSP430_IMM; + cmd->src = 8; break; case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "#-1"); + cmd->src_mode = MSP430_IMM; + cmd->src = -1; break; case MSP430_PC: /* immediate mode */ snprintf(cmd->operands, sizeof(cmd->operands), "#0x%04x", op1); + cmd->src_mode = MSP430_IMM; + cmd->src = op1; srcOperInCodeWord = 1; ret = 4; break; default: /* indirect autoincrement mode */ snprintf(cmd->operands, sizeof(cmd->operands), "@%s+", msp430_register_names[src]); + cmd->src_mode = MSP430_IND_AUTOINC; + cmd->src = src; } break; } @@ -278,6 +330,8 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ switch (ad) { case 0: /* register mode */ snprintf(dstbuf, sizeof(dstbuf), ", %s", msp430_register_names[dst]); + cmd->dst_mode = MSP430_REG; + cmd->dst = dst; break; case 1: /* check addr. mode of source operand */ @@ -291,12 +345,18 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ switch (get_dst(instr)) { case MSP430_PC: /* symbolic mode */ snprintf(dstbuf, sizeof(dstbuf), ", 0x%04x", op); + cmd->dst_mode = MSP430_SYM; + cmd->dst = op; break; case MSP430_SR: /* absolute mode */ snprintf(dstbuf, sizeof(dstbuf), ", &0x%04x", op); + cmd->dst_mode = MSP430_ABS; + cmd->dst = op; break; default: /* indexed mode */ snprintf(dstbuf, sizeof(dstbuf), ", 0x%x(%s)", op, msp430_register_names[dst]); + cmd->dst_mode = MSP430_INDX; + cmd->dst = (op << 16) | dst; } break; } @@ -309,8 +369,9 @@ static int decode_addressing_mode(ut16 instr, ut16 op1, ut16 op2, struct msp430_ static int decode_twoop_opcode(ut16 instr, ut16 op1, ut16 op2, struct msp430_cmd *cmd) { ut8 opcode = get_twoop_opcode(instr); - snprintf(cmd->instr, sizeof(cmd->instr), "%s", two_op_instrs[opcode]); - if (get_bw(instr)) { + WRITE_INSTR_STR(cmd, two_op_instrs[opcode]); + cmd->is_byte = get_bw(instr); + if (cmd->is_byte) { strncat(cmd->instr, ".b", sizeof(cmd->instr) - 1 - strlen(cmd->instr)); } @@ -329,7 +390,7 @@ static ut8 get_jmp_cond(ut16 instr) { static void decode_jmp(ut16 instr, struct msp430_cmd *cmd) { ut16 addr; - snprintf(cmd->instr, sizeof(cmd->instr), "%s", jmp_instrs[get_jmp_cond(instr)]); + WRITE_INSTR_STR(cmd, jmp_instrs[get_jmp_cond(instr)]); addr = instr & 0x3FF; @@ -354,7 +415,7 @@ static int decode_oneop_opcode(ut16 instr, ut16 op, struct msp430_cmd *cmd) { as = get_as(instr); - snprintf(cmd->instr, sizeof(cmd->instr), "%s", one_op_instrs[opcode]); + WRITE_INSTR_STR(cmd, one_op_instrs[opcode]); cmd->opcode = get_oneop_opcode(instr); @@ -370,10 +431,14 @@ static int decode_oneop_opcode(ut16 instr, ut16 op, struct msp430_cmd *cmd) { switch (get_dst(instr)) { case MSP430_R3: snprintf(cmd->operands, sizeof(cmd->operands), "#0"); + cmd->dst = 0; + cmd->dst_mode = MSP430_IMM; break; default: snprintf(cmd->operands, sizeof(cmd->operands), "%s", msp430_register_names[get_dst(instr)]); + cmd->dst = get_dst(instr); + cmd->dst_mode = MSP430_REG; } ret = 2; break; @@ -383,18 +448,26 @@ static int decode_oneop_opcode(ut16 instr, ut16 op, struct msp430_cmd *cmd) { switch (get_dst(instr)) { case MSP430_R3: snprintf(cmd->operands, sizeof(cmd->operands), "#1"); + cmd->dst = 1; + cmd->dst_mode = MSP430_IMM; /* this is an unusual encoding in that there's no index word */ ret = 2; break; case MSP430_PC: snprintf(cmd->operands, sizeof(cmd->operands), "0x%04x", op); + cmd->dst = op; + cmd->dst_mode = MSP430_SYM; break; case MSP430_SR: snprintf(cmd->operands, sizeof(cmd->operands), "&0x%04x", op); + cmd->dst = op; + cmd->dst_mode = MSP430_ABS; break; default: snprintf(cmd->operands, sizeof(cmd->operands), "0x%x(%s)", op, msp430_register_names[get_dst(instr)]); + cmd->dst = (op << 16) | get_dst(instr); + cmd->dst_mode = MSP430_INDX; } break; @@ -402,19 +475,27 @@ static int decode_oneop_opcode(ut16 instr, ut16 op, struct msp430_cmd *cmd) { switch (get_dst(instr)) { case MSP430_SR: snprintf(cmd->operands, sizeof(cmd->operands), "#4"); + cmd->dst = 4; + cmd->dst_mode = MSP430_IMM; break; case MSP430_R3: snprintf(cmd->operands, sizeof(cmd->operands), "#2"); + cmd->dst = 2; + cmd->dst_mode = MSP430_IMM; break; default: snprintf(cmd->operands, sizeof(cmd->operands), "@%s", msp430_register_names[get_dst(instr)]); + cmd->dst = get_dst(instr); + cmd->dst_mode = MSP430_IND_REG; } ret = 2; break; case 3: snprintf(cmd->operands, sizeof(cmd->operands), "#0x%04x", op); + cmd->dst = op; + cmd->dst_mode = MSP430_IMM; ret = 4; break; default: @@ -427,6 +508,7 @@ static int decode_oneop_opcode(ut16 instr, ut16 op, struct msp430_cmd *cmd) { } cmd->type = MSP430_ONEOP; + cmd->is_byte = get_bw(instr); return ret; } @@ -483,7 +565,7 @@ int msp430_decode_command(const ut8 *in, int len, struct msp430_cmd *cmd) { /* if ret < 0, it's an invalid opcode.Say so and return 2 since * all MSP430 opcodes are of 16 bits,valid or invalid */ if (ret < 0) { - cmd->type = MSP430_INV; + cmd->type = MSP430_INVALID; snprintf(cmd->instr, sizeof(cmd->instr), "invalid"); cmd->operands[0] = '\0'; ret = 2; diff --git a/librz/arch/isa/msp430/msp430_disas.h b/librz/arch/isa/msp430/msp430_disas.h index 9b8330b6cb1..1473ef6bc03 100644 --- a/librz/arch/isa/msp430/msp430_disas.h +++ b/librz/arch/isa/msp430/msp430_disas.h @@ -45,18 +45,39 @@ enum msp430_twoop_opcodes { MSP430_AND, }; -enum msp430_addr_modes { - MSP430_DIRECT, - MSP430_INDEXED, - MSP430_INDIRECT, - MSP430_INDIRECT_INC, +enum msp430_emulated_opcodes { + MSP430_ADC, + MSP430_BR, + MSP430_CLR, + MSP430_CLRC, + MSP430_CLRN, + MSP430_CLRZ, + MSP430_DADC, + MSP430_DEC, + MSP430_DECD, + MSP430_DINT, + MSP430_EINT, + MSP430_INC, + MSP430_INCD, + MSP430_INV, + MSP430_NOP, + MSP430_POP, + MSP430_RET, + MSP430_RLA, + MSP430_RLC, + MSP430_SBC, + MSP430_SETC, + MSP430_SETN, + MSP430_SETZ, + MSP430_TST }; enum msp430_cmd_type { MSP430_ONEOP, MSP430_TWOOP, MSP430_JUMP, - MSP430_INV, + MSP430_EMULATE, + MSP430_INVALID, }; enum msp430_registers { @@ -78,22 +99,48 @@ enum msp430_registers { MSP430_R15, }; +typedef enum Msp430AddressingMode { + // register: Rn, contents of Rn + MSP430_REG, + // indexed: offset(Rn), contents of Memory[offset + Rn] + MSP430_INDX, + // symbolic: offset, contents of Memory[offset + PC] (as if indexed with Rn = PC) + MSP430_SYM, + // absolute: &addr, contents of Memory[addr] (as if indexed with a zeroed Rn) + MSP430_ABS, + // indirect register: @Rn, contents of Memory[Rn] (as if indexed with offset = 0) + MSP430_IND_REG, + // indirect register auto-increment: @Rn+, same as with indirect register but automatically increments Rn + MSP430_IND_AUTOINC, + // immediate: #literal, the literal value itself is the argument + MSP430_IMM +} Msp430AddressingMode; + struct msp430_cmd { ut8 type; ut8 opcode; + st16 jmp_addr; - ut16 call_addr; ut8 jmp_cond; // Null-delimited string representation of an assembly operation mnemonic. // Length of array: 'i', 'n', 'v', 'a', 'l', 'i', 'd', '\0' // (This is longer than any real assembly mnemonic.) char instr[7 + 1]; + // does it have a .b ? + bool is_byte; // Null-delimited string representation of assembly operands. // Length of array: 2 * ('0', 'x', 4-digit hexadecimal numeral, '(', 'r', 2-digit // decimal numeral, ')'), ',', ' ', '\0' char operands[2 * (2 + 4 + 2 + 3) + 2 + 1]; + + // The source and the dst of the operands, along with their modes + // This info is contained in the strings above, but parsing strings to obtain it is ugly so we replicate it here + ut32 src; + ut32 dst; + Msp430AddressingMode src_mode; + Msp430AddressingMode dst_mode; }; int msp430_decode_command(const ut8 *instr, int len, struct msp430_cmd *cmd); diff --git a/librz/arch/isa/msp430/msp430_il.c b/librz/arch/isa/msp430/msp430_il.c new file mode 100644 index 00000000000..ab765f3332d --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il.c @@ -0,0 +1,337 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#include + +// ``The 16-bit program counter (PC/R0) points to the next instruction to be executed.`` +#define PC_SIZE 16 +// ``The low byte of a word is always an even address. The high byte is at the next odd address. +// For example, if a data word is located at address xxx4h, then the low byte of that data word +// is located at address xxx4h, and the high byte of that word is located at address xxx5h.`` +#define IS_BIG_ENDIAN false +// implied by the width of the PC and other registers (which are used as pointers in the relevant addressing modes) +#define MEM_ADDR_SIZE 16U + +#include +#include +#include +#include + +#include "rz_il/rz_il_opbuilder_begin.h" + +// ************************************* ------------------- ********************************* // +// ************************************* ------------------- ********************************* // +// ************************************* One-Operand Lifters ********************************* // +// ************************************* ------------------- ********************************* // +// ************************************* ------------------- ********************************* // + +RZ_OWN RzILOpEffect *rz_msp430_lift_call_instr(RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr, int size) { + return SEQ3( + MSP430_SETR(MSP430_SP, SUB(MSP430_GETR(MSP430_SP), U16(2))), + STOREW(MSP430_GETR(MSP430_SP), ADD(U16(current_addr), U16(size))), + JMP(get_destination(op))); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_rrc_instr(RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr, int size) { + // rotation is just a shift with filling + return SEQ6( + /* 1- get the carry (to use later as the filling for the MSB of the operand) */ + SETL("old_sr", MSP430_GETR(MSP430_SR)), + SETL("old_carry", LSB(VARL("old_sr"))), + /* 2- get the operand (whether register, memory location, ...) */ + SETL("operand", get_destination(op)), + /* 3- Perform the actual Rotate Right through Carry operation. Do: + a- Shift the operand by 1 to the right and fill with carry */ + SETL("result", SHIFTR(VARL("old_carry"), VARL("operand"), U8(1))), + /* ... b- Set the operand to the value of the previous computation */ + set_destination(op, VARL("result")), + /* ... c- Finally set the flags */ + set_rcc_flags("operand", "result", "old_carry", "old_sr")); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_sxt_instr(RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr, int size) { + return SEQ3( + SETL("result", SIGNED(16, get_destination(op))), + set_destination(op, VARL("result")), + set_sxt_flags("result")); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_swpb_instr(RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr, int size) { + // 1- get lower byte and upper byte of the operand + BVPair low_high = get_destination_destructured(op); + RzILOpBitVector *low_byte = low_high.first; + RzILOpBitVector *high_byte = low_high.second; + + // 2- append them in reverse order + RzILOpBitVector *result = APPEND(low_byte, high_byte); + + // 3- set them (flags aren't affected) + return set_destination(op, result); +} + +// ************************************* ------------------- ********************************* // +// ************************************* ------------------- ********************************* // +// ************************************* Two-Operand Lifters ******************************** // +// ************************************* ------------------- ********************************* // +// ************************************* ------------------- ********************************* // + +RZ_OWN RzILOpEffect *rz_msp430_lift_add_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return SEQ5( + SETL("old_destination", get_destination(op)), + SETL("source", get_source(op)), + SETL("result", ADD(VARL("source"), VARL("old_destination"))), + + set_destination(op, VARL("result")), + set_add_flags("source", "old_destination", "result")); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_and_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return SEQ5( + SETL("old_destination", get_destination(op)), + SETL("source", get_source(op)), + SETL("result", LOGAND(VARL("source"), VARL("old_destination"))), + + set_destination(op, VARL("result")), + set_and_flags("result")); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_xor_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return SEQ5( + SETL("old_destination", get_destination(op)), + SETL("source", get_source(op)), + SETL("result", LOGXOR(VARL("source"), VARL("old_destination"))), + + set_destination(op, VARL("result")), + set_xor_flags("source", "old_destination", "result")); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_mov_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return set_destination(op, get_source(op)); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_sub_or_cmp_instr(RzAnalysis *analysis, const Msp430Instruction *op, bool write_dst) { + RzILOpBitVector *increment_by; + RzILOpBitVector *carry_const; + RzILOpBitVector *overflow_const; + if (op->is_byte) { + increment_by = U8(1); + carry_const = U8(0); + overflow_const = U8(~0x7F); + } else { + increment_by = U16(1); + carry_const = U16(0); + overflow_const = U16(~0x7FFF); + } + + RzILOpEffect *get_src = SETL("op0", get_source(op)); + RzILOpEffect *neg_src_add1 = SETL("op1", ADD(increment_by, LOGNOT(VARL("op0")))); + RzILOpEffect *get_dst = SETL("op2", get_destination(op)); + RzILOpEffect *compute_result = SETL("result", ADD(VARL("op1"), VARL("op2"))); + RzILOpEffect *set_flags = set_sub_flags("op0", "op1", "op2", "result", carry_const, overflow_const); + + return (write_dst)? SEQ6( + get_src, + neg_src_add1, + get_dst, + compute_result, + set_destination(op, VARL("result")), + set_flags + ) : SEQ5( + get_src, + neg_src_add1, + get_dst, + compute_result, + set_flags + ); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_sub_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return rz_msp430_lift_sub_or_cmp_instr(analysis, op, true); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_cmp_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return rz_msp430_lift_sub_or_cmp_instr(analysis, op, false); +} + + +RzILOpEffect *rz_msp430_lift_todo(RzAnalysis *analysis, const Msp430Instruction *op) { + NOT_IMPLEMENTED; +} + +RzILOpEffect *rz_msp430_lift_todo_curr_addr(RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr, int size) { + NOT_IMPLEMENTED; +} + +// ************************************* --------------------------- ******************************** // +// ************************************* --------------------------- ******************************** // +// ************************************* Emulated Instruction Lifter ******************************** // +// ************************************* --------------------------- ******************************** // +// ************************************* --------------------------- ******************************** // + +RzILOpEffect *rz_msp430_lift_ret_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return SEQ3( + SETL("return_address", LOADW(16, MSP430_GETR(MSP430_SP))), + MSP430_SETR(MSP430_SP, ADD(MSP430_GETR(MSP430_SP), U16(2))), + JMP(VARL("return_address")) + ); +} + +RzILOpEffect *rz_msp430_lift_inc_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + RzILOpBitVector *increment_by; + RzILOpEffect *(*flags_setter)(const char *); + if (op->is_byte) { + increment_by = U8(1); + flags_setter = set_incb_flags; + } else { + increment_by = U16(1); + flags_setter = set_inc_flags; + } + + return SEQ3( + SETL("old_destination", get_destination(op)), + set_destination(op, ADD(increment_by, VARL("old_destination"))), + flags_setter("old_destination") + ); +} + +RzILOpEffect *rz_msp430_lift_clr_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return set_destination(op, (op->is_byte)? U8(0) : U16(0)); +} + +RzILOpEffect *rz_msp430_lift_tst_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + return set_tst_flags(get_destination(op)); +} + +// ************************************* ------------------------ ******************************** // +// ************************************* ------------------------ ******************************** // +// ************************************* Jump Instructions Lifter ******************************** // +// ************************************* ------------------------ ******************************** // +// ************************************* ------------------------ ******************************** // + +RZ_OWN RzILOpEffect *rz_msp430_lift_jump_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr) { + RzILOpBitVector *abs_addr = abs_addr_from_rel_addr(U16(current_addr), op->jmp_addr); + RzILOpEffect *jmp = JMP(abs_addr); + + // unconditional jump + if (op->jmp_cond == MSP430_JMP) { + return jmp; + } + // otherwise, construct the condition to make a guarded branch + RzILOpBool *jmp_cond = jmp_condition_constructors[op->jmp_cond](MSP430_GETR(MSP430_SR)); + + // if applicable, invert the condition efficiently by swapping THEN and ELSE in the RzIL branch + if (jmp_condition_triggers[op->jmp_cond] == JMP_THEN) { + return BRANCH(jmp_cond, jmp, NOP()); + } else { + return BRANCH(jmp_cond, NOP(), jmp); + } +} + +// ************************************* -------------- ******************************** // +// ************************************* -------------- ******************************** // +// ************************************* End of Lifters ******************************** // +// ************************************* -------------- ******************************** // +// ************************************* -------------- ******************************** // + +RZ_OWN RzILOpEffect *rz_msp430_dummy() { + return NOP(); +} + +#include "rz_il/rz_il_opbuilder_end.h" + +static const MSP430InstructionLifterWithCurrAddr one_op_lifters[] = { + [MSP430_RRC] = rz_msp430_lift_rrc_instr, + [MSP430_SWPB] = rz_msp430_lift_swpb_instr, + [MSP430_RRA] = rz_msp430_lift_todo_curr_addr, // TODO + [MSP430_SXT] = rz_msp430_lift_sxt_instr, + [MSP430_PUSH] = rz_msp430_lift_todo_curr_addr, // TODO + [MSP430_CALL] = rz_msp430_lift_call_instr, + [MSP430_RETI] = rz_msp430_lift_todo_curr_addr, // TODO + [MSP430_UNUSED] = rz_msp430_lift_todo_curr_addr // TODO +}; +RZ_OWN RzILOpEffect *rz_msp430_lift_single_operand_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op, ut64 current_addr, int size) { + return one_op_lifters[op->opcode](analysis, op, current_addr, size); +} + +static const MSP430InstructionLifter two_op_lifters[] = { + [MSP430_JMP_OPC] = rz_msp430_lift_todo, + [MSP430_MOV] = rz_msp430_lift_mov_instr, + [MSP430_ADD] = rz_msp430_lift_add_instr, + [MSP430_ADDC] = rz_msp430_lift_todo, + [MSP430_SUBC] = rz_msp430_lift_todo, + [MSP430_SUB] = rz_msp430_lift_sub_instr, + [MSP430_CMP] = rz_msp430_lift_cmp_instr, + [MSP430_DADD] = rz_msp430_lift_todo, + [MSP430_BIT] = rz_msp430_lift_todo, + [MSP430_BIC] = rz_msp430_lift_todo, + [MSP430_BIS] = rz_msp430_lift_todo, + [MSP430_XOR] = rz_msp430_lift_xor_instr, + [MSP430_AND] = rz_msp430_lift_and_instr +}; +RZ_OWN RzILOpEffect *rz_msp430_lift_double_operand_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op) { + return two_op_lifters[op->opcode](analysis, op); +} + +static const MSP430InstructionLifter emulated_instructions_lifter[] = { + [MSP430_ADC] = rz_msp430_lift_todo, + [MSP430_BR] = rz_msp430_lift_todo, + [MSP430_CLR] = rz_msp430_lift_clr_instr, + [MSP430_CLRC] = rz_msp430_lift_todo, + [MSP430_CLRN] = rz_msp430_lift_todo, + [MSP430_CLRZ] = rz_msp430_lift_todo, + [MSP430_DADC] = rz_msp430_lift_todo, + [MSP430_DEC] = rz_msp430_lift_todo, + [MSP430_DECD] = rz_msp430_lift_todo, + [MSP430_DINT] = rz_msp430_lift_todo, + [MSP430_EINT] = rz_msp430_lift_todo, + [MSP430_INC] = rz_msp430_lift_inc_instr, + [MSP430_INCD] = rz_msp430_lift_todo, + [MSP430_INV] = rz_msp430_lift_todo, + [MSP430_NOP] = rz_msp430_lift_todo, + [MSP430_POP] = rz_msp430_lift_todo, + [MSP430_RET] = rz_msp430_lift_ret_instr, + [MSP430_RLA] = rz_msp430_lift_todo, + [MSP430_RLC] = rz_msp430_lift_todo, + [MSP430_SBC] = rz_msp430_lift_todo, + [MSP430_SETC] = rz_msp430_lift_todo, + [MSP430_SETN] = rz_msp430_lift_todo, + [MSP430_SETZ] = rz_msp430_lift_todo, + [MSP430_TST] = rz_msp430_lift_tst_instr +}; +RZ_OWN RzILOpEffect *rz_msp430_lift_emulated_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op) { + return emulated_instructions_lifter[op->opcode](analysis, op); +} + +RZ_OWN RZ_IPI RzILOpEffect *rz_msp430_lift_instr(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL const Msp430Instruction *op, ut64 current_addr, int size) { + rz_return_val_if_fail(analysis && op, NULL); + + switch (op->type) { + case MSP430_ONEOP: { + return rz_msp430_lift_single_operand_instr(analysis, op, current_addr, size); + } + case MSP430_TWOOP: { + return rz_msp430_lift_double_operand_instr(analysis, op); + } + case MSP430_JUMP: { + return rz_msp430_lift_jump_instr(analysis, op, current_addr); + } + case MSP430_EMULATE: { + return rz_msp430_lift_emulated_instr(analysis, op); + } + + // should never happen, op can't be an invalid instruction + default: + rz_warn_if_reached(); + return rz_msp430_dummy(); + } +} + +RZ_OWN RZ_IPI RzAnalysisILConfig *rz_msp430_il_config(RZ_NONNULL RzAnalysis *analysis) { + rz_return_val_if_fail(analysis, NULL); + + RzAnalysisILConfig *ilconf = rz_analysis_il_config_new(PC_SIZE, IS_BIG_ENDIAN, MEM_ADDR_SIZE); + + //ilconf->reg_bindings = msp430_register_names; + + return ilconf; +} \ No newline at end of file diff --git a/librz/arch/isa/msp430/msp430_il.h b/librz/arch/isa/msp430/msp430_il.h new file mode 100644 index 00000000000..2ffc7bf4493 --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef MSP430_IL_H +#define MSP430_IL_H + +#include +#include + +typedef struct msp430_cmd Msp430Instruction; + +typedef RzILOpEffect *(*MSP430InstructionLifter)(RzAnalysis *analysis, const Msp430Instruction *op); +typedef RzILOpEffect *(*MSP430InstructionLifterWithCurrAddr)(RzAnalysis *analysis, const Msp430Instruction *op, ut64 curr_addr, int size); + +RZ_OWN RZ_IPI RzILOpEffect *rz_msp430_lift_instr(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL const Msp430Instruction *op, ut64 current_addr, int size); + +RZ_OWN RZ_IPI RzAnalysisILConfig *rz_msp430_il_config(RZ_NONNULL RzAnalysis *analysis); + +#endif \ No newline at end of file diff --git a/librz/arch/isa/msp430/msp430_il_flags.inc b/librz/arch/isa/msp430/msp430_il_flags.inc new file mode 100644 index 00000000000..da77feb1553 --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il_flags.inc @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "rz_il/rz_il_opbuilder_begin.h" + +RZ_OWN RzILOpBitVector *update_sr_clear_vcnz(RzILOpBitVector *old_sr_value) { + // the general idea is + // 1- Zero out the N,Z,C,V bits in the old SR value + // By ANDing with a mask of all 1s everywhere and 0s in those flags' positions + // 2- Code can later set those flags by ORing the resulting sr value with a mask + // having 1s in the relevant positions and all 0s everywhere else + int cnz_zero_mask = 0xFFF8; + int vcnz_zero_mask = cnz_zero_mask & (~(1 << 8)); + return LOGAND(old_sr_value, U16(vcnz_zero_mask)); +} + +RZ_OWN RzILOpBitVector *update_sr_nz_flags(RzILOpBitVector *new_value, RzILOpBitVector *old_sr_value) { + // index of the N flag in the SR register: 2 + RzILOpBool *n_flag_value = MSB(new_value); + RzILOpBitVector *n_or_mask = SHIFTL0(BOOL_TO_BV(n_flag_value, 16), U8(2)); + + // index of the Z flag in the SR register: 1 + RzILOpBool *z_flag_value = IS_ZERO(DUP(new_value)); // must DUP new_value because it has been used before + RzILOpBitVector *z_or_mask = SHIFTL0(BOOL_TO_BV(z_flag_value, 16), U8(1)); + + RzILOpBitVector *or_mask = LOGOR(n_or_mask, z_or_mask); + + return LOGOR(old_sr_value, or_mask); +} + +RZ_OWN RzILOpBitVector *update_sr_v_flag(RzILOpBool *new_overflow, RzILOpBitVector *old_sr_value) { + return LOGOR(old_sr_value, SHIFTL0(BOOL_TO_BV(new_overflow, 16), U8(8))); +} + +RZ_OWN RzILOpBitVector *update_sr_v_flag_rcc(RzILOpBitVector *old_value, RzILOpBool *old_carry, RzILOpBitVector *old_sr_value) { + // the idea is the same as update_sr_nz_flags: we AND with a mask that zeroes out the bit we care about + // then we OR with a mask that have the bit we care about in the same position that we zeroed + RzILOpBool *v_flag_value = AND( + INV(MSB(old_value)), + old_carry); + + return update_sr_v_flag(v_flag_value, old_sr_value); +} + +RZ_OWN RzILOpBitVector *update_sr_c_flag(RzILOpBool *new_carry, RzILOpBitVector *old_sr_value) { + return LOGOR(old_sr_value, BOOL_TO_BV(new_carry, 16)); +} + +RZ_OWN RzILOpBitVector *update_sr_c_flag_add(RzILOpBitVector *op1, RzILOpBitVector *op2, RzILOpBitVector *result, RzILOpBitVector *old_sr_value) { + // Review any truth table for a 3-input full adder, and observe that the carry out is 1 if and only if + // 1- Both of the inputs are 1 + // 2- One of the inputs is 1 while the result is 0 + // In this context, the "inputs" are the most significant bits of the full 16-bit operands + RzILOpBool *op1_msb = MSB(op1); + RzILOpBool *op2_msb = MSB(op2); + + RzILOpBool *is_carry1 = AND(op1_msb, op2_msb); + RzILOpBool *not_result_msb = INV(MSB(result)); + + RzILOpBool *is_carry2 = AND(not_result_msb, DUP(op1_msb)); + RzILOpBool *is_carry3 = AND(DUP(op2_msb), DUP(not_result_msb)); + + RzILOpBool *is_carry = OR(OR(is_carry1, is_carry2), is_carry3); + return update_sr_c_flag(is_carry, old_sr_value); +} + +RZ_OWN RzILOpBitVector *update_sr_v_flag_add(RzILOpBitVector *op1, RzILOpBitVector *op2, RzILOpBitVector *result, RzILOpBitVector *old_sr_value) { + // Overflow happens if and only if: + // Positive + Positive = Negative + // Negative + Negative = Positive + RzILOpBool *op1_sign = MSB(op1); + RzILOpBool *op2_sign = MSB(op2); + + // XNOR is the binary equality operator + RzILOpBool *op1_op2_have_same_sign = INV(XOR(op1_sign, op2_sign)); + + RzILOpBool *result_sign = MSB(result); + RzILOpBool *result_has_different_sign = XOR(result_sign, DUP(op1_sign)); + + RzILOpBool *is_overflow = AND(op1_op2_have_same_sign, result_has_different_sign); + return update_sr_v_flag(is_overflow, old_sr_value); +} + +RZ_OWN RzILOpEffect *set_rcc_flags(const char *operand_name, const char *result_name, const char *old_carry_name, const char *old_sr_name) { + // n z as usual + RzILOpBitVector *nz = update_sr_nz_flags(VARL(result_name), update_sr_clear_vcnz(VARL(old_sr_name))); + // v especially for RCC + RzILOpBitVector *vnz = update_sr_v_flag_rcc(VARL(operand_name), VARL(old_carry_name), nz); + // and c from the discarded LSB + RzILOpBitVector *cvnz = update_sr_c_flag(LSB(VARL(operand_name)), vnz); + + return MSP430_SETR(MSP430_SR, cvnz); +} + +RZ_OWN RzILOpEffect *set_sxt_flags(const char *result_name) { + // n z as usual + RzILOpBitVector *nz = update_sr_nz_flags(VARL(result_name), update_sr_clear_vcnz(MSP430_GETR(MSP430_SR))); + // c as not if result is zero + RzILOpBitVector *cnz = update_sr_c_flag(INV(IS_ZERO(VARL(result_name))), nz); + // v as zero + RzILOpBitVector *vcnz = update_sr_v_flag(IL_FALSE, cnz); + + return MSP430_SETR(MSP430_SR, vcnz); +} + +RZ_OWN RzILOpEffect *set_and_flags(const char *result_name) { + return set_sxt_flags(result_name); +} + +RZ_OWN RzILOpEffect *set_xor_flags(const char *source_name, const char *destination_name, const char *result_name) { + // n z as usual + RzILOpBitVector *nz = update_sr_nz_flags(VARL(result_name), update_sr_clear_vcnz(MSP430_GETR(MSP430_SR))); + // c as not if result is zero + RzILOpBitVector *cnz = update_sr_c_flag(INV(IS_ZERO(VARL(result_name))), nz); + // v as if both operands are negative + RzILOpBitVector *vcnz = update_sr_v_flag(AND(MSB(VARL(source_name)), MSB(VARL(destination_name))), cnz); + + return MSP430_SETR(MSP430_SR, vcnz); +} + +RZ_OWN RzILOpEffect *set_add_flags(const char *source_name, const char *destination_name, const char *result_name) { + // n z as usual + RzILOpBitVector *nz = update_sr_nz_flags(VARL(result_name), update_sr_clear_vcnz(MSP430_GETR(MSP430_SR))); + // c especially for the add + RzILOpBitVector *cnz = update_sr_c_flag_add(VARL(source_name), VARL(destination_name), VARL(result_name), nz); + // v especially for the add + RzILOpBitVector *vcnz = update_sr_v_flag_add(VARL(source_name), VARL(destination_name), VARL(result_name), cnz); + + return MSP430_SETR(MSP430_SR, vcnz); +} + +RZ_OWN RzILOpEffect *do_set_inc_flags(const char* destination_name, RzILOpBitVector *zero_carry_const, RzILOpBitVector *overflow_const) { + // n as usual + RzILOpBool *is_negative = MSB(VARL(destination_name)); + RzILOpBitVector *n = LOGOR( + SHIFTL0(BOOL_TO_BV(is_negative, 16), U8(2)), + update_sr_clear_vcnz(MSP430_GETR(MSP430_SR)) + ); + + // z c v by comparison to known constants + RzILOpBool *is_zero_and_carry = EQ(VARL(destination_name), zero_carry_const); + RzILOpBool *is_overflow = EQ(VARL(destination_name), overflow_const); + + RzILOpBitVector *nzc = LOGOR(ITE(is_zero_and_carry, U16(0x0003), U16(0)), n); + RzILOpBitVector *vnzc = update_sr_v_flag(is_overflow, nzc); + + return MSP430_SETR(MSP430_SR, vnzc); +} + +RZ_OWN RzILOpEffect *set_incb_flags(const char* destination_name) { + return do_set_inc_flags(destination_name, U8(0xFF), U8(0x7F)); +} + +RZ_OWN RzILOpEffect *set_inc_flags(const char* destination_name) { + return do_set_inc_flags(destination_name, U16(0xFFFF), U16(0x7FFF)); +} + +RZ_OWN RzILOpEffect *set_tst_flags(RzILOpBitVector *operand) { + RzILOpBitVector *cv = update_sr_v_flag( + IL_FALSE, + update_sr_c_flag(IL_TRUE, + update_sr_clear_vcnz(MSP430_GETR(MSP430_SR)))); + + RzILOpBitVector *nzcv = update_sr_nz_flags(operand, cv); + + return MSP430_SETR(MSP430_SR, nzcv); +} + + +RZ_OWN RzILOpEffect *set_sub_flags(const char *source_name, const char *neginc_source_name, const char *dst_name, const char *result_name, RzILOpBitVector *carry_const, RzILOpBitVector *overflow_const) { + // n z as usual + RzILOpBitVector *nz = update_sr_nz_flags(VARL(result_name), update_sr_clear_vcnz(MSP430_GETR(MSP430_SR))); + // c especially for the sub + RzILOpBitVector *_cnz = update_sr_c_flag_add(VARL(neginc_source_name), VARL(dst_name), VARL(result_name), nz); + RzILOpBitVector *cnz = update_sr_c_flag(EQ(VARL(source_name), carry_const), _cnz); + // v especially for the sub + RzILOpBitVector *_vcnz = update_sr_v_flag_add(VARL(neginc_source_name), VARL(dst_name), VARL(result_name), cnz); + RzILOpBitVector *vcnz = update_sr_v_flag(EQ(VARL(source_name), overflow_const), _vcnz); + + return MSP430_SETR(MSP430_SR, vcnz); +} +#include "rz_il/rz_il_opbuilder_end.h" diff --git a/librz/arch/isa/msp430/msp430_il_getset.inc b/librz/arch/isa/msp430/msp430_il_getset.inc new file mode 100644 index 00000000000..32430b87af7 --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il_getset.inc @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include + +#include "rz_il/rz_il_opbuilder_begin.h" + +#define MSP430_GETR(r) VARG(msp430_register_names[r]) +#define MSP430_SETR(r, v) SETG(msp430_register_names[r], v) + +#define WORD_SIZED_READ 1 +#define WORD_SIZED_WRITE 2 + +ut8 word_sizedness(const Msp430Instruction *op); + +RZ_OWN RzILOpBitVector *calculate_address(Msp430AddressingMode mode, ut32 operand) { + switch (mode) { + case MSP430_INDX: return ADD(U16(operand >> 16), MSP430_GETR(operand & 0x00FF)); + case MSP430_SYM: return ADD(U16(operand), MSP430_GETR(MSP430_PC)); + case MSP430_ABS: return U16(operand); + case MSP430_IND_REG: + case MSP430_IND_AUTOINC: + return MSP430_GETR(operand); + + default: return NULL; + } +} + +RZ_OWN RzILOpBitVector *decode_operand(const Msp430Instruction *op, Msp430AddressingMode mode, ut32 operand) { + switch (mode) { + case MSP430_REG: { + RzILOpBitVector *reg = MSP430_GETR(operand); + if (word_sizedness(op) & WORD_SIZED_READ) { + return reg; + } + return CAST(8, IL_FALSE, reg); + } + + case MSP430_IMM: { + if (word_sizedness(op) & WORD_SIZED_READ) { + return U16(operand); + } + return U8(operand); + } + + default: + RzILOpBitVector *address = calculate_address(mode, operand); + if (address != NULL) { + if (word_sizedness(op) & WORD_SIZED_READ) { + return LOADW(16, address); + } + return LOADW(8, address); + } + + rz_warn_if_reached(); + return U8(0); + } +} + +RZ_OWN RzILOpBitVector *get_source(const Msp430Instruction *op) { + return decode_operand(op, op->src_mode, op->src); +} + +RZ_OWN RzILOpBitVector *get_destination(const Msp430Instruction *op) { + return decode_operand(op, op->dst_mode, op->dst); +} + +typedef struct { + RzILOpBitVector *first; + RzILOpBitVector *second; +} BVPair; + +// special getter for swpb +RZ_OWN BVPair get_destination_destructured(const Msp430Instruction *op) { + switch (op->dst_mode) { + case MSP430_REG: { + RzILOpBitVector *reg = MSP430_GETR(op->dst); + BVPair res; + res.first = CAST(8, IL_FALSE, reg); + res.second = CAST(8, IL_FALSE, SHIFTR0(DUP(reg), U8(8))); + return res; + } + default: { + rz_warn_if_reached(); + BVPair dummy = { .first = U8(0), .second = U8(0) }; + return dummy; // TODO : other addressing modes + } + } +} + +RZ_OWN RzILOpEffect *set_destination(const Msp430Instruction *op, RzILOpBitVector *new_value) { + switch (op->dst_mode) { + case MSP430_REG: { + if (word_sizedness(op) & WORD_SIZED_WRITE) { + return MSP430_SETR(op->dst, new_value); + } + // the general idea is: First we zero the lower byte through ANDing with 0xFF00 + // Then we assign the lower byte to the (byte-length) result through ORing with 0x00 + // the overall effect is that only the lower byte is assigned, which is what byte-sized operations do + return MSP430_SETR(op->dst, UNSIGNED(16, new_value)); + } + + default: + RzILOpBitVector *address = calculate_address(op->dst_mode, op->dst); + if (address != NULL) { + if (word_sizedness(op) & WORD_SIZED_WRITE) { + return STOREW(address, new_value); + } + return STORE(address, new_value); + } + + rz_warn_if_reached(); + return NOP(); + } +} + +ut8 word_sizedness(const Msp430Instruction *op) { + // the sign extend is special: it always reads a byte but writes a word + // this is the only reason there are 2 flags for the sizedness of a read and the sizedness of a write + if (op->opcode == MSP430_SXT) { + return WORD_SIZED_WRITE; + } + + // otherwise, all instructions read and write with the same sizedness + // 0 means the read and write are both byte-sized + // 3 = 1|2 means the read and the write are both word-sized + return (op->is_byte) ? 0 : (WORD_SIZED_READ | WORD_SIZED_WRITE); +} + +#include "rz_il/rz_il_opbuilder_end.h" diff --git a/librz/arch/isa/msp430/msp430_il_jmp_utils.inc b/librz/arch/isa/msp430/msp430_il_jmp_utils.inc new file mode 100644 index 00000000000..8344a368cef --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il_jmp_utils.inc @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include + +#include "rz_il/rz_il_opbuilder_begin.h" + +RZ_OWN RzILOpBitVector *abs_addr_from_rel_addr(RzILOpBitVector *curr_pc, st16 rel_addr) { + return ADD(curr_pc, S16(rel_addr)); +} + +RzILOpBool *mk_jne_cond(RzILOpBitVector *curr_sr) { + // test if the Z bit in the SR register is 0 + return IS_ZERO(LOGAND(curr_sr, U16(2))); +} + +RzILOpBool *mk_jnc_cond(RzILOpBitVector *curr_sr) { + // test if the C bit in the SR register is 0 + return IS_ZERO(LOGAND(curr_sr, U16(1))); +} + +RzILOpBool *mk_jn_cond(RzILOpBitVector *curr_sr) { + // test if the N flag in the SR register is 1 + return EQ(LOGAND(curr_sr, U16(4)), U16(4)); +} + +RzILOpBool *mk_jge_cond(RzILOpBitVector *curr_sr) { + int mask = 4 | (1 << 8); + RzILOpBitVector *nv = LOGAND(curr_sr, U16(mask)); + // test if both the N and V flags are set or reset + return OR(IS_ZERO(nv), EQ(DUP(nv), U16(mask))); +} + +#include "rz_il/rz_il_opbuilder_end.h" + +typedef RzILOpBool *(*JmpConditionConstructor)(RzILOpBitVector *curr_sr); +JmpConditionConstructor jmp_condition_constructors[] = { + [MSP430_JNE] = mk_jne_cond, + [MSP430_JEQ] = mk_jne_cond, // invert by switching the THEN and ELSE + [MSP430_JNC] = mk_jnc_cond, + [MSP430_JC] = mk_jnc_cond, // invert by switching the THEN and ELSE + [MSP430_JN] = mk_jn_cond, + [MSP430_JGE] = mk_jge_cond, + [MSP430_JL] = mk_jge_cond // invert by switching the THEN and ELSE +}; + +enum jmp_trigger { + JMP_THEN, + JMP_ELSE +}; +enum jmp_trigger jmp_condition_triggers[] = { + [MSP430_JNE] = JMP_THEN, + [MSP430_JEQ] = JMP_ELSE, + [MSP430_JNC] = JMP_THEN, + [MSP430_JC] = JMP_ELSE, + [MSP430_JN] = JMP_THEN, + [MSP430_JGE] = JMP_THEN, + [MSP430_JL] = JMP_ELSE +}; \ No newline at end of file diff --git a/librz/arch/isa/msp430/msp430_register_names.h b/librz/arch/isa/msp430/msp430_register_names.h new file mode 100644 index 00000000000..18d35a08572 --- /dev/null +++ b/librz/arch/isa/msp430/msp430_register_names.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef MSP430_REGISTER_NAMES_H +#define MSP430_REGISTER_NAMES_H + +#include +static const char *msp430_register_names[] = { + "pc", + "sp", + "sr", + "cg", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + NULL +}; + +#endif \ No newline at end of file diff --git a/librz/arch/meson.build b/librz/arch/meson.build index e367c4def16..d8c8df2bed5 100644 --- a/librz/arch/meson.build +++ b/librz/arch/meson.build @@ -219,6 +219,7 @@ arch_isa_sources = [ 'isa/mcore/mcore.c', 'isa/mips/mips_assembler.c', 'isa/msp430/msp430_disas.c', + 'isa/msp430/msp430_il.c', 'isa/or1k/or1k_disas.c', 'isa/pic/pic14.c', 'isa/pic/pic16.c', diff --git a/librz/arch/p/analysis/analysis_msp430.c b/librz/arch/p/analysis/analysis_msp430.c index 9dff545e3e2..0babeeeb9f7 100644 --- a/librz/arch/p/analysis/analysis_msp430.c +++ b/librz/arch/p/analysis/analysis_msp430.c @@ -9,13 +9,13 @@ #include #include +#include static int msp430_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) { int ret; struct msp430_cmd cmd; memset(&cmd, 0, sizeof(cmd)); - // op->id = ???; op->size = -1; op->nopcode = 1; op->type = RZ_ANALYSIS_OP_TYPE_UNK; @@ -87,9 +87,38 @@ static int msp430_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut op->type = RZ_ANALYSIS_OP_TYPE_UNK; } + if (mask & RZ_ANALYSIS_OP_MASK_IL) { + RzILOpEffect *il_op = rz_msp430_lift_instr(analysis, &cmd, addr, op->size); + op->il_op = il_op; + } return ret; } +static char *get_reg_profile(RzAnalysis *analysis) { + const char *prof = + "=PC pc\n" + "=SP sp\n" + "=A0 r4\n" + "=A1 r5\n" + "gpr pc .16 0 0\n" + "gpr sp .16 2 0\n" + "gpr sr .16 4 0\n" + "gpr cg .16 6 0\n" + "gpr r4 .16 8 0\n" + "gpr r5 .16 10 0\n" + "gpr r6 .16 12 0\n" + "gpr r7 .16 14 0\n" + "gpr r8 .16 16 0\n" + "gpr r9 .16 18 0\n" + "gpr r10 .16 20 0\n" + "gpr r11 .16 22 0\n" + "gpr r12 .16 24 0\n" + "gpr r13 .16 26 0\n" + "gpr r14 .16 28 0\n" + "gpr r15 .16 30 0\n"; + return strdup(prof); +} + RzAnalysisPlugin rz_analysis_plugin_msp430 = { .name = "msp430", .desc = "TI MSP430 code analysis plugin", @@ -97,4 +126,6 @@ RzAnalysisPlugin rz_analysis_plugin_msp430 = { .arch = "msp430", .bits = 16, .op = msp430_op, + .il_config = rz_msp430_il_config, + .get_reg_profile = get_reg_profile }; diff --git a/test/db/asm/msp430 b/test/db/asm/msp430 index 994c72ed231..766a10c2305 100644 --- a/test/db/asm/msp430 +++ b/test/db/asm/msp430 @@ -1,5 +1,7 @@ d "adc r11" 0b63 d "adc.b r11" 4b63 +d "add #0x0010, r9" 39501000 0x0 (seq (set old_destination (var r9)) (set source (bv 16 0x10)) (set result (+ (var source) (var old_destination))) (set r9 (var result)) (set sr (| (| (| (& (var sr) (bv 16 0xfef8)) (| (<< (ite (msb (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x2) false) (<< (ite (is_zero (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x1) false))) (ite (|| (|| (&& (msb (var source)) (msb (var old_destination))) (&& (! (msb (var result))) (msb (var source)))) (&& (msb (var old_destination)) (! (msb (var result))))) (bv 16 0x1) (bv 16 0x0))) (<< (ite (&& (! (^^ (msb (var source)) (msb (var old_destination)))) (^^ (msb (var result)) (msb (var source)))) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)))) +d "and.b #0xf0f0, r12" 7cf0f0f0 0x0 (seq (set old_destination (cast 8 false (var r12))) (set source (bv 8 0xf0)) (set result (& (var source) (var old_destination))) (set r12 (cast 16 false (var result))) (set sr (| (| (| (& (var sr) (bv 16 0xfef8)) (| (<< (ite (msb (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x2) false) (<< (ite (is_zero (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x1) false))) (ite (! (is_zero (var result))) (bv 16 0x1) (bv 16 0x0))) (<< (ite false (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)))) d "bic #2, r10" 2ac3 d "bic #4, r10" 2ac2 d "bic.b #2, &0x0026" e2c32600 @@ -9,12 +11,12 @@ d "br #0x0021" 30402100 d "br &0x0003" 10420300 d "br sp" 0041 d "br r10" 004a -d "call #0xc096" b01296c0 +d "call #0xc096" b01296c0 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (var sp) (+ (bv 16 0x0) (bv 16 0x4))) (jmp (bv 16 0xc096))) d "clr 0x0033" 80433300 d "clr 0xfffa(r4)" 8443faff d "clrn" 22c2 d "clrz" 22c3 -d "cmp.b #0x003b, 0x0(r11)" fb903b000000 +d "cmp.b #0x003b, 0x0(r11)" fb903b000000 0x0 (seq (set op0 (bv 8 0x3b)) (set op1 (+ (bv 8 0x1) (~ (var op0)))) (set op2 (loadw 0 8 (+ (bv 16 0x0) (var r11)))) (set result (+ (var op1) (var op2))) (set sr (| (| (| (| (| (& (var sr) (bv 16 0xfef8)) (| (<< (ite (msb (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x2) false) (<< (ite (is_zero (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x1) false))) (ite (|| (|| (&& (msb (var op1)) (msb (var op2))) (&& (! (msb (var result))) (msb (var op1)))) (&& (msb (var op2)) (! (msb (var result))))) (bv 16 0x1) (bv 16 0x0))) (ite (== (var op0) (bv 8 0x0)) (bv 16 0x1) (bv 16 0x0))) (<< (ite (&& (! (^^ (msb (var op1)) (msb (var op2)))) (^^ (msb (var result)) (msb (var op1)))) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)) (<< (ite (== (var op0) (bv 8 0x80)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)))) d "dadc r11" 0ba3 d "dadc.b r11" 4ba3 d "dec r11" 1b83 @@ -23,8 +25,10 @@ d "inc r11" 1b53 d "inv 0x0003" b0e30300 d "inv 0x3(r5)" b5e30300 d "inv r10" 3ae3 -d "jeq $+0x0014" 0924 -d "jmp $+0x0010" 073c +d "jeq $+0x0014" 0924 0x0 (branch (is_zero (& (var sr) (bv 16 0x2))) nop (jmp (+ (bv 16 0x0) (bv 16 0x14)))) +d "jeq $+0x0400" ff25 0x0 (branch (is_zero (& (var sr) (bv 16 0x2))) nop (jmp (+ (bv 16 0x0) (bv 16 0x400)))) +d "jeq $-0x01fe" 0027 0x0 (branch (is_zero (& (var sr) (bv 16 0x2))) nop (jmp (+ (bv 16 0x0) (bv 16 0xfe02)))) +d "jmp $+0x0010" 073c 0x0 (jmp (+ (bv 16 0x0) (bv 16 0x10))) d "mov #0x0003, 0x5(r9)" b94003000500 d "mov #0x000a, 0x002a" b0400a002a00 d "mov #0x0021, 0x6(r11)" bb4021000600 @@ -38,15 +42,20 @@ d "mov 0x5(r10), 0x6(r11)" 9b4a05000600 d "mov 0x6(r11), &0x0033" 924b06003300 d "mov.b &0x0021, r15" 5f422100 d "mov.b r15, &0x0021" c24f2100 +d "mov #0x0042, r6" 36404200 0x0 (set r6 (bv 16 0x42)) +d "mov r6, r10" 0a46 0x0 (set r10 (var r6)) d "nop" 0343 d "pop r11" 3b41 d "pop r4" 3441 d "push #1" 1312 d "ret" 3041 +d "rrc r4" 0410 0x0 (seq (set old_sr (var sr)) (set old_carry (lsb (var old_sr))) (set operand (var r4)) (set result (>> (var operand) (bv 8 0x1) (var old_carry))) (set r4 (var result)) (set sr (| (| (| (& (var old_sr) (bv 16 0xfef8)) (| (<< (ite (msb (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x2) false) (<< (ite (is_zero (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x1) false))) (<< (ite (&& (! (msb (var operand))) (var old_carry)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)) (ite (lsb (var operand)) (bv 16 0x1) (bv 16 0x0))))) d "sbc r11" 0b73 d "sbc.b r11" 4b73 d "setc" 12d3 d "setz" 22d3 +d "swpb r15" 8f10 0x0 (set r15 (append (cast 8 false (var r15)) (cast 8 false (>> (var r15) (bv 8 0x8) false)))) +d "sxt r5" 8511 0x0 (seq (set result (cast 16 (msb (cast 8 false (var r5))) (cast 8 false (var r5)))) (set r5 (var result)) (set sr (| (| (| (& (var sr) (bv 16 0xfef8)) (| (<< (ite (msb (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x2) false) (<< (ite (is_zero (var result)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x1) false))) (ite (! (is_zero (var result))) (bv 16 0x1) (bv 16 0x0))) (<< (ite false (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)))) d "tst &0x0003" 82930300 d "tst 0x3(r5)" 85930300 d "tst r10" 0a93 diff --git a/test/db/cmd/cmd_list b/test/db/cmd/cmd_list index 53c8161972b..3244d24ac41 100644 --- a/test/db/cmd/cmd_list +++ b/test/db/cmd/cmd_list @@ -409,7 +409,7 @@ _dA__ 32 malbolge LGPL3 Malbolge Ternary VM (by condret) _dA__ 32 mcore LGPL3 Motorola MCORE disassembler _d___ 16 mcs96 LGPL3 condrets car adAe_ 16 32 64 mips BSD Capstone MIPS disassembler -_dA__ 16 msp430 LGPL3 msp430 disassembly plugin +_dA_I 16 msp430 LGPL3 msp430 disassembly plugin adA__ 16 32 64 null MIT no disassemble (by pancake) v1.0.0 _dA__ 32 or1k LGPL3 OpenRISC 1000 _dAeI 16 32 pic LGPL3 PIC disassembler diff --git a/test/db/rzil/msp430 b/test/db/rzil/msp430 new file mode 100644 index 00000000000..e6197de180c --- /dev/null +++ b/test/db/rzil/msp430 @@ -0,0 +1,19 @@ +NAME=Testing the decryption in emulateme +FILE=bins/msp430/emulateme.msp430 +TIMEOUT=10 +CMDS=<