-
-
Notifications
You must be signed in to change notification settings - Fork 363
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Lift MSP430 machine language to RzIL: first 3 instructions and 1 addr…
…essing mode
- Loading branch information
Showing
7 changed files
with
526 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// 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 |
Oops, something went wrong.