Skip to content

Commit

Permalink
Lift MSP430 machine language to RzIL: first 3 instructions and 1 addr…
Browse files Browse the repository at this point in the history
…essing mode
  • Loading branch information
moste00 committed May 5, 2024
1 parent e283bda commit cc9c3f5
Show file tree
Hide file tree
Showing 7 changed files with 527 additions and 2 deletions.
227 changes: 227 additions & 0 deletions librz/arch/isa/msp430/msp430_il.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

#include <msp430/msp430_il.h>

// ``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

static const char *msp430_register_names[] = {
"pc",
"sp",
"sr",
"cg",
"r4",
"r5",
"r6",
"r7",
"r8",
"r9",
"r10",
"r11",
"r12",
"r13",
"r14",
"r15",
NULL
};

#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)

typedef struct {
RzILOpBitVector *first;
RzILOpBitVector *second;
} BVPair;

RZ_OWN RzILOpEffect *rz_msp430_dummy() {
return NOP();
}

RZ_OWN RzILOpPure *get_destination(const Msp430Instruction *op) {
switch (op->dst_mode) {
case DST_REG: {
RzILOpBitVector *reg = MSP430_GETR(op->dst);
if (op->word_sized & 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 DST_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 DST_REG: {
if (op->word_sized & 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<result>
// 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(old_value, U16(0xFF00)), UNSIGNED(16, new_value)));
}
default: return NOP(); // TODO : other addressing modes
}
}

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_rcc(RzILOpBitVector *old_value, RzILOpBool *old_carry, RzILOpBitVector *old_sr_value) {
// the idea is the same as set_nz_flags_from_result(...): 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 LOGOR(
LOGAND(old_sr_value, U16(~(1 << 8))), /* zero out position 8, leave all else the same */
SHIFTL0(BOOL_TO_BV(v_flag_value, 16), U8(8)) /* OR with the new value for the bit */
);
}

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 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("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("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: NZ as usual, v especially for RCC, and the carry flag from the discarded LSB */
MSP430_SETR(MSP430_SR, update_sr_c_flag(LSB(VARL("operand")), update_sr_v_flag_rcc(VARL("operand"), VARL("carry"), update_sr_nz_flags(VARL("result"), VARL("old_sr"))))));
}

RZ_OWN RzILOpEffect *rz_msp430_lift_sxt_instr(RzAnalysis *analysis, const Msp430Instruction *op) {
return set_destination(op, NULL, SIGNED(16, get_destination(op)));
}

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);
}

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->iopcode](analysis, op);
}

RZ_OWN RzILOpEffect *rz_msp430_lift_double_operand_instr(RZ_NONNULL RzAnalysis *analysis, const Msp430Instruction *op) {
return rz_msp430_lift_todo(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->itype) {
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;
}

#include "msp430_instruction_parse.inc"
50 changes: 50 additions & 0 deletions librz/arch/isa/msp430/msp430_il.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2024 Mostafa Mahmoud <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

#ifndef MSP430_IL_H
#define MSP430_IL_H

#include <rz_analysis.h>
#include <msp430/msp430_disas.h>

#define WORD_SIZED_READ 1
#define WORD_SIZED_WRITE 2

typedef enum {
SRC_REG,
SRC_INDX,
SRC_SYM,
SRC_ABS,
SRC_IND_REG,
SRC_IND_AUTOINC,
SRC_IMM
} Msp430SourceAddressingMode;

typedef enum {
DST_REG,
DST_INDX,
DST_SYM,
DST_ABS
} Msp430DestinationAddressingMode;

typedef struct {
ut8 itype;
ut8 iopcode;
ut8 word_sized;

Msp430SourceAddressingMode src_mode;
ut32 src;

Msp430DestinationAddressingMode dst_mode;
ut32 dst;
} Msp430Instruction;

typedef RzILOpEffect *(*MSP430InstructionLifter)(RzAnalysis *analysis, const Msp430Instruction *op);

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);

void rz_msp430_instruction_from_command(RZ_OUT RZ_NONNULL Msp430Instruction *op, RZ_NONNULL struct msp430_cmd *cmd);

#endif
Loading

0 comments on commit cc9c3f5

Please sign in to comment.