From 525219e529634f59944bba54bb2ef4d694ff3948 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 | 58 ++++++- librz/arch/isa/msp430/msp430_disas.h | 19 +++ librz/arch/isa/msp430/msp430_il.c | 167 +++++++++++++++++++++ librz/arch/isa/msp430/msp430_il.h | 38 +++++ librz/arch/isa/msp430/msp430_il_flags.inc | 133 ++++++++++++++++ librz/arch/isa/msp430/msp430_il_getset.inc | 99 ++++++++++++ librz/arch/meson.build | 1 + librz/arch/p/analysis/analysis_msp430.c | 34 ++++- test/db/asm/msp430 | 7 + test/db/cmd/cmd_list | 2 +- 10 files changed, 555 insertions(+), 3 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 diff --git a/librz/arch/isa/msp430/msp430_disas.c b/librz/arch/isa/msp430/msp430_disas.c index 19de3fd97a2..e33bd495b7c 100644 --- a/librz/arch/isa/msp430/msp430_disas.c +++ b/librz/arch/isa/msp430/msp430_disas.c @@ -215,9 +215,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 = IMM; + cmd->src = 0; break; default: /* register mode */ snprintf(cmd->operands, sizeof(cmd->operands), "%s", msp430_register_names[src]); + cmd->src_mode = REG; + cmd->src = src; } ret = 2; break; @@ -226,18 +230,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 = SYM; + cmd->src = op1; srcOperInCodeWord = 1; break; case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "%s", "#1"); + cmd->src_mode = IMM; + cmd->src = 1; ret = 2; break; case MSP430_SR: /* absolute mode */ snprintf(cmd->operands, sizeof(cmd->operands), "&0x%04x", op1); + cmd->src_mode = 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 = INDX; + cmd->src = (op1 << 16) | src; srcOperInCodeWord = 1; } break; @@ -245,12 +257,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 = IMM; + cmd->src = 4; break; case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "#2"); + cmd->src_mode = IMM; + cmd->src = 2; break; default: /* indirect register mode */ snprintf(cmd->operands, sizeof(cmd->operands), "@%s", msp430_register_names[src]); + cmd->src_mode = IND_REG; + cmd->src = src; } ret = 2; break; @@ -259,17 +277,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 = IMM; + cmd->src = 8; break; case MSP430_R3: /* CG2 */ snprintf(cmd->operands, sizeof(cmd->operands), "#-1"); + cmd->src_mode = IMM; + cmd->src = -1; break; case MSP430_PC: /* immediate mode */ snprintf(cmd->operands, sizeof(cmd->operands), "#0x%04x", op1); + cmd->src_mode = 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 = IND_AUTOINC; + cmd->src = src; } break; } @@ -278,6 +304,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 = REG; + cmd->dst = dst; break; case 1: /* check addr. mode of source operand */ @@ -291,12 +319,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 = SYM; + cmd->dst = op; break; case MSP430_SR: /* absolute mode */ snprintf(dstbuf, sizeof(dstbuf), ", &0x%04x", op); + cmd->dst_mode = ABS; + cmd->dst = op; break; default: /* indexed mode */ snprintf(dstbuf, sizeof(dstbuf), ", 0x%x(%s)", op, msp430_register_names[dst]); + cmd->dst_mode = INDX; + cmd->dst = dst; } break; } @@ -310,7 +344,8 @@ static int decode_twoop_opcode(ut16 instr, ut16 op1, ut16 op2, struct msp430_cmd ut8 opcode = get_twoop_opcode(instr); snprintf(cmd->instr, sizeof(cmd->instr), "%s", two_op_instrs[opcode]); - if (get_bw(instr)) { + cmd->is_byte = get_bw(instr); + if (cmd->is_byte) { strncat(cmd->instr, ".b", sizeof(cmd->instr) - 1 - strlen(cmd->instr)); } @@ -370,10 +405,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 = IMM; break; default: snprintf(cmd->operands, sizeof(cmd->operands), "%s", msp430_register_names[get_dst(instr)]); + cmd->dst = get_dst(instr); + cmd->dst_mode = REG; } ret = 2; break; @@ -383,18 +422,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 = 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 = SYM; break; case MSP430_SR: snprintf(cmd->operands, sizeof(cmd->operands), "&0x%04x", op); + cmd->dst = op; + cmd->dst_mode = 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 = INDX; } break; @@ -402,19 +449,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 = IMM; break; case MSP430_R3: snprintf(cmd->operands, sizeof(cmd->operands), "#2"); + cmd->dst = 2; + cmd->dst_mode = IMM; break; default: snprintf(cmd->operands, sizeof(cmd->operands), "@%s", msp430_register_names[get_dst(instr)]); + cmd->dst = get_dst(instr); + cmd->dst_mode = IND_REG; } ret = 2; break; case 3: snprintf(cmd->operands, sizeof(cmd->operands), "#0x%04x", op); + cmd->dst = op; + cmd->dst_mode = IMM; ret = 4; break; default: @@ -427,6 +482,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; } diff --git a/librz/arch/isa/msp430/msp430_disas.h b/librz/arch/isa/msp430/msp430_disas.h index 9b8330b6cb1..c8f990bf47f 100644 --- a/librz/arch/isa/msp430/msp430_disas.h +++ b/librz/arch/isa/msp430/msp430_disas.h @@ -78,6 +78,16 @@ enum msp430_registers { MSP430_R15, }; +typedef enum Msp430AddressingMode { + REG, + INDX, + SYM, + ABS, + IND_REG, + IND_AUTOINC, + IMM +} Msp430AddressingMode; + struct msp430_cmd { ut8 type; ut8 opcode; @@ -89,11 +99,20 @@ struct msp430_cmd { // 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 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..43f0b80abdf --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il.c @@ -0,0 +1,167 @@ +// 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.`` +// -- userguide, p. 44 +#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.`` +// -- userguide, p. 26 +#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 + +ut8 word_sizedness(const Msp430Instruction *); + +#include +#include + +#include "rz_il/rz_il_opbuilder_begin.h" + +RZ_OWN RzILOpEffect *rz_msp430_lift_rrc_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + // 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("operand"), 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) { + return SEQ3( + SETL("result", SIGNED(16, get_destination(op))), + set_destination(op, NULL, VARL("result")), + set_sxt_flags("result")); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_swpb_instr(RzAnalysis *analysis, const Msp430Instruction *op) { + // 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, NULL, result); +} + +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("old_destination"), 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("old_destination"), 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("old_destination"), 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_destination(op), get_source(op)); +} + +RzILOpEffect *rz_msp430_lift_todo(RzAnalysis *analysis, const Msp430Instruction *op) { + NOT_IMPLEMENTED; +} + +#include "rz_il/rz_il_opbuilder_end.h" + +static const MSP430InstructionLifter one_op_lifters[] = { + [MSP430_RRC] = rz_msp430_lift_rrc_instr, + [MSP430_SWPB] = rz_msp430_lift_swpb_instr, + [MSP430_RRA] = rz_msp430_lift_todo, // TODO + [MSP430_SXT] = rz_msp430_lift_sxt_instr, + [MSP430_PUSH] = rz_msp430_lift_todo, // TODO + [MSP430_CALL] = rz_msp430_lift_todo, // TODO + [MSP430_RETI] = rz_msp430_lift_todo, // TODO + [MSP430_UNUSED] = rz_msp430_lift_todo // TODO +}; +RZ_OWN RzILOpEffect *rz_msp430_lift_single_operand_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op) { + return one_op_lifters[op->opcode](analysis, op); +} + +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_todo, + [MSP430_CMP] = rz_msp430_lift_todo, + [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); +} + +RZ_OWN RzILOpEffect *rz_msp430_lift_jump_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op) { + return rz_msp430_lift_todo(analysis, op); +} + +RZ_OWN RZ_IPI RzILOpEffect *rz_msp430_lift_instr(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL const Msp430Instruction *op) { + rz_return_val_if_fail(analysis && op, NULL); + + switch (op->type) { + case MSP430_ONEOP: { + return rz_msp430_lift_single_operand_instr(analysis, op); + } + case MSP430_TWOOP: { + return rz_msp430_lift_double_operand_instr(analysis, op); + } + case MSP430_JUMP: { + return rz_msp430_lift_jump_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..53f33164307 --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il.h @@ -0,0 +1,38 @@ +// 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); + +static const char *msp430_register_names[] = { + "pc", + "sp", + "sr", + "cg", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + NULL +}; + +RZ_OWN RZ_IPI RzILOpEffect *rz_msp430_lift_instr(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL const Msp430Instruction *op); + +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..3070c7d5dea --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il_flags.inc @@ -0,0 +1,133 @@ +// 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_nz_flags(RzILOpBitVector *new_value, RzILOpBitVector *old_sr_value) { + // the general idea is that we zero out the N and Z bits in the old SR value + // (by ANDing with a mask of all 1s except in those positions) + // and then we OR that resulting value with the new bits in the relevant positions and all 0s everywhere else + + // 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 *and_mask = U16(~(1 << 2 | 1 << 1)); + RzILOpBitVector *or_mask = LOGOR(n_or_mask, z_or_mask); + + return LOGOR(LOGAND(old_sr_value, and_mask), or_mask); +} + +RZ_OWN RzILOpBitVector *update_sr_v_flag(RzILOpBool *new_overflow, RzILOpBitVector *old_sr_value) { + return LOGOR( + LOGAND(old_sr_value, U16(~(1 << 8))), /* zero out position 8, leave all else the same */ + SHIFTL0(BOOL_TO_BV(new_overflow, 16), U8(8)) /* OR with the new value for the bit */ + ); +} + +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( + LOGAND(old_sr_value, U16(~1)), /* zero out the 0th position (i.e. the carry bit) */ + BOOL_TO_BV(new_carry, 16) /* OR with the new carry */ + ); +} + +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), 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), 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), 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"), 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); +} + +#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..3af63dea93d --- /dev/null +++ b/librz/arch/isa/msp430/msp430_il_getset.inc @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud +// SPDX-License-Identifier: LGPL-3.0-only + +#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) + +RZ_OWN RzILOpEffect *rz_msp430_dummy() { + return NOP(); +} + +typedef struct { + RzILOpBitVector *first; + RzILOpBitVector *second; +} BVPair; + +#define WORD_SIZED_READ 1 +#define WORD_SIZED_WRITE 2 + +RZ_OWN RzILOpPure *get_source(const Msp430Instruction *op) { + switch (op->src_mode) { + case REG: { + RzILOpBitVector *reg = MSP430_GETR(op->src); + if (word_sizedness(op) & WORD_SIZED_READ) { + return reg; + } + return CAST(8, IL_FALSE, reg); + } + + case IMM: + if (word_sizedness(op) & WORD_SIZED_READ) { + return U16(op->src); + } + return U8(op->src); + + default: NOT_IMPLEMENTED; // TODO : other addressing modes + } +} + +RZ_OWN RzILOpPure *get_destination(const Msp430Instruction *op) { + switch (op->dst_mode) { + case REG: { + RzILOpBitVector *reg = MSP430_GETR(op->dst); + if (word_sizedness(op) & WORD_SIZED_READ) { + return reg; + } + return CAST(8, IL_FALSE, reg); + } + default: NOT_IMPLEMENTED; // TODO : other addressing modes + } +} + +RZ_OWN BVPair get_destination_destructured(const Msp430Instruction *op) { + switch (op->dst_mode) { + case 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: { + BVPair dummy = { .first = U8(0), .second = U8(0) }; + return dummy; // TODO : other addressing modes + } + } +} + +RZ_OWN RzILOpEffect *set_destination(const Msp430Instruction *op, RzILOpBitVector *old_value, RzILOpBitVector *new_value) { + switch (op->dst_mode) { + case 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, LOGOR(LOGAND(MSP430_GETR(op->dst), U16(0xFF00)), UNSIGNED(16, new_value))); + } + default: return NOP(); // TODO : other addressing modes + } +} + +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/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..4cae573a8c1 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,39 @@ 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); + 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 +127,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 3766232d717..466810b888a 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 0xfff9)) (| (<< (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))) (bv 16 0xfffe)) (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))) (bv 16 0xfeff)) (<< (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 (| (& (var r12) (bv 16 0xff00)) (cast 16 false (var result)))) (set sr (| (& (| (& (| (& (var sr) (bv 16 0xfff9)) (| (<< (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))) (bv 16 0xfffe)) (ite (! (is_zero (var result))) (bv 16 0x1) (bv 16 0x0))) (bv 16 0xfeff)) (<< (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 @@ -38,15 +40,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 0xfff9)) (| (<< (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))) (bv 16 0xfeff)) (<< (ite (&& (! (msb (var operand))) (var old_carry)) (bv 16 0x1) (bv 16 0x0)) (bv 8 0x8) false)) (bv 16 0xfffe)) (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 pc" 8011 0x0 (seq (set result (cast 16 (msb (cast 8 false (var pc))) (cast 8 false (var pc)))) (set pc (var result)) (set sr (| (& (| (& (| (& (var sr) (bv 16 0xfff9)) (| (<< (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))) (bv 16 0xfffe)) (ite (! (is_zero (var result))) (bv 16 0x1) (bv 16 0x0))) (bv 16 0xfeff)) (<< (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 9adfd8d9db9..568e4fed289 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 8 pic LGPL3 PIC disassembler