diff --git a/Makefile b/Makefile index 71706a4..062cc40 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ install: mspdebug mspdebug.man .SUFFIXES: .c .o mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o stab.o util.o \ - bsl.o sim.o symmap.o gdb.o btree.o device.o + bsl.o sim.o symmap.o gdb.o btree.o device.o rtools.o $(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: diff --git a/dis.c b/dis.c index c0e0ef7..c5b6f99 100644 --- a/dis.c +++ b/dis.c @@ -449,6 +449,70 @@ int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len, return ret; } +static const struct { + msp430_op_t op; + const char *mnemonic; +} opcode_names[] = { + /* Single operand */ + {MSP430_OP_RRC, "RRC"}, + {MSP430_OP_SWPB, "SWPB"}, + {MSP430_OP_RRA, "RRA"}, + {MSP430_OP_SXT, "SXT"}, + {MSP430_OP_PUSH, "PUSH"}, + {MSP430_OP_CALL, "CALL"}, + {MSP430_OP_RETI, "RETI"}, + + /* Jump */ + {MSP430_OP_JNZ, "JNZ"}, + {MSP430_OP_JZ, "JZ"}, + {MSP430_OP_JNC, "JNC"}, + {MSP430_OP_JC, "JC"}, + {MSP430_OP_JN, "JN"}, + {MSP430_OP_JL, "JL"}, + {MSP430_OP_JGE, "JGE"}, + {MSP430_OP_JMP, "JMP"}, + + /* Double operand */ + {MSP430_OP_MOV, "MOV"}, + {MSP430_OP_ADD, "ADD"}, + {MSP430_OP_ADDC, "ADDC"}, + {MSP430_OP_SUBC, "SUBC"}, + {MSP430_OP_SUB, "SUB"}, + {MSP430_OP_CMP, "CMP"}, + {MSP430_OP_DADD, "DADD"}, + {MSP430_OP_BIT, "BIT"}, + {MSP430_OP_BIC, "BIC"}, + {MSP430_OP_BIS, "BIS"}, + {MSP430_OP_XOR, "XOR"}, + {MSP430_OP_AND, "AND"}, + + /* Emulated instructions */ + {MSP430_OP_ADC, "ADC"}, + {MSP430_OP_BR, "BR"}, + {MSP430_OP_CLR, "CLR"}, + {MSP430_OP_CLRC, "CLRC"}, + {MSP430_OP_CLRN, "CLRN"}, + {MSP430_OP_CLRZ, "CLRZ"}, + {MSP430_OP_DADC, "DADC"}, + {MSP430_OP_DEC, "DEC"}, + {MSP430_OP_DECD, "DECD"}, + {MSP430_OP_DINT, "DINT"}, + {MSP430_OP_EINT, "EINT"}, + {MSP430_OP_INC, "INC"}, + {MSP430_OP_INCD, "INCD"}, + {MSP430_OP_INV, "INV"}, + {MSP430_OP_NOP, "NOP"}, + {MSP430_OP_POP, "POP"}, + {MSP430_OP_RET, "RET"}, + {MSP430_OP_RLA, "RLA"}, + {MSP430_OP_RLC, "RLC"}, + {MSP430_OP_SBC, "SBC"}, + {MSP430_OP_SETC, "SETC"}, + {MSP430_OP_SETN, "SETN"}, + {MSP430_OP_SETZ, "SETZ"}, + {MSP430_OP_TST, "TST"} +}; + /* Return the mnemonic for an operation, if possible. * * If the argument is not a valid operation, this function returns the @@ -456,74 +520,11 @@ int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len, */ static const char *msp_op_name(msp430_op_t op) { - static const struct { - msp430_op_t op; - const char *mnemonic; - } ops[] = { - /* Single operand */ - {MSP430_OP_RRC, "RRC"}, - {MSP430_OP_SWPB, "SWPB"}, - {MSP430_OP_RRA, "RRA"}, - {MSP430_OP_SXT, "SXT"}, - {MSP430_OP_PUSH, "PUSH"}, - {MSP430_OP_CALL, "CALL"}, - {MSP430_OP_RETI, "RETI"}, - - /* Jump */ - {MSP430_OP_JNZ, "JNZ"}, - {MSP430_OP_JZ, "JZ"}, - {MSP430_OP_JNC, "JNC"}, - {MSP430_OP_JC, "JC"}, - {MSP430_OP_JN, "JN"}, - {MSP430_OP_JL, "JL"}, - {MSP430_OP_JGE, "JGE"}, - {MSP430_OP_JMP, "JMP"}, - - /* Double operand */ - {MSP430_OP_MOV, "MOV"}, - {MSP430_OP_ADD, "ADD"}, - {MSP430_OP_ADDC, "ADDC"}, - {MSP430_OP_SUBC, "SUBC"}, - {MSP430_OP_SUB, "SUB"}, - {MSP430_OP_CMP, "CMP"}, - {MSP430_OP_DADD, "DADD"}, - {MSP430_OP_BIT, "BIT"}, - {MSP430_OP_BIC, "BIC"}, - {MSP430_OP_BIS, "BIS"}, - {MSP430_OP_XOR, "XOR"}, - {MSP430_OP_AND, "AND"}, - - /* Emulated instructions */ - {MSP430_OP_ADC, "ADC"}, - {MSP430_OP_BR, "BR"}, - {MSP430_OP_CLR, "CLR"}, - {MSP430_OP_CLRC, "CLRC"}, - {MSP430_OP_CLRN, "CLRN"}, - {MSP430_OP_CLRZ, "CLRZ"}, - {MSP430_OP_DADC, "DADC"}, - {MSP430_OP_DEC, "DEC"}, - {MSP430_OP_DECD, "DECD"}, - {MSP430_OP_DINT, "DINT"}, - {MSP430_OP_EINT, "EINT"}, - {MSP430_OP_INC, "INC"}, - {MSP430_OP_INCD, "INCD"}, - {MSP430_OP_INV, "INV"}, - {MSP430_OP_NOP, "NOP"}, - {MSP430_OP_POP, "POP"}, - {MSP430_OP_RET, "RET"}, - {MSP430_OP_RLA, "RLA"}, - {MSP430_OP_RLC, "RLC"}, - {MSP430_OP_SBC, "SBC"}, - {MSP430_OP_SETC, "SETC"}, - {MSP430_OP_SETN, "SETN"}, - {MSP430_OP_SETZ, "SETZ"}, - {MSP430_OP_TST, "TST"} - }; int i; - for (i = 0; i < ARRAY_LEN(ops); i++) - if (op == ops[i].op) - return ops[i].mnemonic; + for (i = 0; i < ARRAY_LEN(opcode_names); i++) + if (op == opcode_names[i].op) + return opcode_names[i].mnemonic; return "???"; } @@ -745,3 +746,17 @@ void disassemble(u_int16_t offset, u_int8_t *data, int length) data += count; } } + +int dis_opcode_by_name(const char *name, msp430_op_t *op) +{ + int i; + + for (i = 0; i < ARRAY_LEN(opcode_names); i++) + if (!strcasecmp(name, opcode_names[i].mnemonic)) { + if (op) + *op = opcode_names[i].op; + return 0; + } + + return -1; +} diff --git a/dis.h b/dis.h index 89cdf9e..1a232d7 100644 --- a/dis.h +++ b/dis.h @@ -208,4 +208,7 @@ int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len, /* Print a disassembly on stdout */ void disassemble(u_int16_t offset, u_int8_t *buf, int length); +/* Look up an opcode by name. Returns 0 if successful, -1 otherwise. */ +int dis_opcode_by_name(const char *name, msp430_op_t *op); + #endif diff --git a/main.c b/main.c index 80315a5..5991df5 100644 --- a/main.c +++ b/main.c @@ -29,6 +29,7 @@ #include "stab.h" #include "util.h" #include "gdb.h" +#include "rtools.h" static void usage(const char *progname) { @@ -70,6 +71,7 @@ int main(int argc, char **argv) const char *bsl_device = NULL; const struct device *msp430_dev = NULL; int opt; + int ret = 0; int flags = 0; int want_jtag = 0; int vcc_mv = 3000; @@ -135,6 +137,7 @@ int main(int argc, char **argv) parse_init(); gdb_init(); + rtools_init(); if (stab_init() < 0) return -1; @@ -171,8 +174,12 @@ int main(int argc, char **argv) /* Process commands */ if (optind < argc) { - while (optind < argc) - process_command(argv[optind++], 0); + while (optind < argc) { + if (process_command(argv[optind++], 0) < 0) { + ret = -1; + break; + } + } } else { reader_loop(); } @@ -180,5 +187,5 @@ int main(int argc, char **argv) device_exit(); stab_exit(); - return 0; + return ret; } diff --git a/mspdebug.man b/mspdebug.man index 4aa471b..73d3edc 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -118,6 +118,71 @@ If the specified file already exists, then it will be overwritten. If you need to dump memory from several disjoint memory regions, you can do this by saving each section to a separate file. The resulting files can then be concatenated together to form a single valid HEX file. +.IP "\fBisearch\fR \fIaddress\fR \fIlength\fR [\fIoptions\fR ...]" +Search over the given range for an instruction which matches the specified +search criteria. The search may be narrowed by specifying one or more of +the following terms: +.RS +.IP "\fBopcode\fR \fIopcode\fR" +Match the specified opcode. Byte/word specifiers are not recognised, as +they are specified with other options. +.IP "\fBbyte\fR" +Match only byte operations. +.IP "\fBword\fR" +Match only word operations. +.IP "\fBjump\fR" +Match only jump instructions (conditional and unconditional jumps, but +not instructions such as BR which load the program counter explicitly). +.IP "\fBsingle\fR" +Match only single-operand instructions. +.IP "\fBdouble\fR" +Match only double-operand instructions. +.IP "\fBnoarg\fR" +Match only instructions with no arguments. +.IP "\fBsrc\fR \fIaddress\fR" +Match instructions with the specified value in the source operand. The value +may be given as an address expression. Specifying this option implies matching +of only double-operand instructions. +.IP "\fBdst\fR \fIaddress\fR" +Match instructions with the specified value in the destination +operand. This option implies that no-argument instructions are not +matched. +.IP "\fBsrcreg\fR \fIregister\fR" +Match instructions using the specified register in the source operand. This +option implies matching of only double-operand instructions. +.IP "\fBdstreg\fR \fIregister\fR" +Match instructions using the specified register in the destination operand. +This option implies that no-argument instructions are not matched. +.IP "\fBsrcmode\fR \fImode\fR" +Match instructions using the specified mode in the source operand. See +below for a list of modes recognised. This option implies matching of +only double-operand instructions. +.IP "\fBdstmode\fR \fImode\fR" +Match instructions using the specified mode in the destination operand. See +below for a list of modes. This option implies that no-argument instructions +are not matched. +.RE +For single-operand instructions, the operand is considered to be the +destination operand. + +The seven addressing modes used by the MSP430 are represented by single +characters, and are listed here: +.RS +.IP "\fBR\fR" +Register mode. +.IP "\fBI\fR" +Indexed mode. +.IP "\fBS\fR" +Symbolic mode. +.IP "\fB&\fR" +Absolute mode. +.IP "\fB@\fR" +Register-indirect mode. +.IP "\fB+\fR" +Register-indirect mode with auto-increment. +.IP "\fB#\fR" +Immediate mode. +.RE .IP "\fBmd\fR \fIaddress\fR [\fIlength\fR]" Read the specified section of device memory and display it as a canonical\-style hexdump. Both arguments may be address expressions. If diff --git a/rtools.c b/rtools.c new file mode 100644 index 0000000..956ed3c --- /dev/null +++ b/rtools.c @@ -0,0 +1,441 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009, 2010 Daniel Beer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "device.h" +#include "dis.h" +#include "rtools.h" + +#define ISEARCH_OPCODE 0x0001 +#define ISEARCH_BW 0x0002 +#define ISEARCH_SRC_ADDR 0x0004 +#define ISEARCH_DST_ADDR 0x0008 +#define ISEARCH_SRC_MODE 0x0010 +#define ISEARCH_DST_MODE 0x0020 +#define ISEARCH_SRC_REG 0x0040 +#define ISEARCH_DST_REG 0x0080 +#define ISEARCH_TYPE 0x0100 + +struct isearch_query { + int flags; + struct msp430_instruction insn; +}; + +static int isearch_opcode(const char *term, char **arg, + struct isearch_query *q) +{ + const char *opname = get_arg(arg); + + if (q->flags & ISEARCH_OPCODE) { + fprintf(stderr, "isearch: opcode already specified\n"); + return -1; + } + + if (!opname) { + fprintf(stderr, "isearch: opcode name expected\n"); + return -1; + } + + if (dis_opcode_by_name(opname, &q->insn.op) < 0) { + fprintf(stderr, "isearch: unknown opcode: %s\n", opname); + return -1; + } + + q->flags |= ISEARCH_OPCODE; + return 0; +} + +static int isearch_bw(const char *term, char **arg, + struct isearch_query *q) +{ + if (q->flags & ISEARCH_BW) { + fprintf(stderr, "isearch: operand size already specified\n"); + return -1; + } + + q->flags |= ISEARCH_BW; + q->insn.is_byte_op = (toupper(*term) == 'B'); + return 0; +} + +static int isearch_type(const char *term, char **arg, + struct isearch_query *q) +{ + if (q->flags & ISEARCH_TYPE) { + fprintf(stderr, "isearch: instruction type already " + "specified\n"); + return -1; + } + + q->flags |= ISEARCH_TYPE; + + switch (toupper(*term)) { + case 'J': + q->insn.itype = MSP430_ITYPE_JUMP; + break; + + case 'S': + q->insn.itype = MSP430_ITYPE_SINGLE; + break; + + case 'D': + q->insn.itype = MSP430_ITYPE_DOUBLE; + break; + + default: + q->insn.itype = MSP430_ITYPE_NOARG; + break; + } + + return 0; +} + +static int isearch_addr(const char *term, char **arg, + struct isearch_query *q) +{ + int which = toupper(*term) == 'S' ? + ISEARCH_SRC_ADDR : ISEARCH_DST_ADDR; + const char *addr_text; + int addr; + + if (q->flags & which) { + fprintf(stderr, "isearch: address already specified\n"); + return -1; + } + + addr_text = get_arg(arg); + if (!addr_text) { + fprintf(stderr, "isearch: address expected\n"); + return -1; + } + + if (addr_exp(addr_text, &addr) < 0) + return -1; + + q->flags |= which; + if (which == ISEARCH_SRC_ADDR) + q->insn.src_addr = addr; + else + q->insn.dst_addr = addr; + + return 0; +} + +static int isearch_reg(const char *term, char **arg, + struct isearch_query *q) +{ + int which = toupper(*term) == 'S' ? + ISEARCH_SRC_REG : ISEARCH_DST_REG; + const char *reg_text; + int reg; + + if (q->flags & which) { + fprintf(stderr, "isearch: register already specified\n"); + return -1; + } + + reg_text = get_arg(arg); + if (!reg_text) { + fprintf(stderr, "isearch: register expected\n"); + return -1; + } + + while (*reg_text && !isdigit(*reg_text)) + reg_text++; + reg = atoi(reg_text); + + q->flags |= which; + if (which == ISEARCH_SRC_REG) + q->insn.src_reg = reg; + else + q->insn.dst_reg = reg; + + return 0; +} + +static int isearch_mode(const char *term, char **arg, + struct isearch_query *q) +{ + int which = toupper(*term) == 'S' ? + ISEARCH_SRC_MODE : ISEARCH_DST_MODE; + const char *what_text; + int what; + + if (q->flags & which) { + fprintf(stderr, "isearch: mode already specified\n"); + return -1; + } + + what_text = get_arg(arg); + if (!what_text) { + fprintf(stderr, "isearch: mode must be specified\n"); + return -1; + } + + switch (toupper(*what_text)) { + case 'R': + what = MSP430_AMODE_REGISTER; + break; + + case '@': + what = MSP430_AMODE_INDIRECT; + break; + + case '+': + what = MSP430_AMODE_INDIRECT_INC; + break; + + case '#': + what = MSP430_AMODE_IMMEDIATE; + break; + + case 'I': + what = MSP430_AMODE_INDEXED; + break; + + case '&': + what = MSP430_AMODE_ABSOLUTE; + break; + + case 'S': + what = MSP430_AMODE_SYMBOLIC; + break; + + default: + fprintf(stderr, "isearch: unknown address mode: %s\n", + what_text); + return -1; + } + + q->flags |= which; + if (which == ISEARCH_SRC_MODE) + q->insn.src_mode = what; + else + q->insn.dst_mode = what; + + return 0; +} + +static int isearch_match(const struct msp430_instruction *insn, + const struct isearch_query *q) +{ + if ((q->flags & (ISEARCH_SRC_ADDR | ISEARCH_SRC_MODE | + ISEARCH_SRC_REG)) && + insn->itype != MSP430_ITYPE_DOUBLE) + return 0; + + if ((q->flags & (ISEARCH_DST_ADDR | ISEARCH_DST_MODE | + ISEARCH_DST_REG)) && + insn->itype == MSP430_ITYPE_NOARG) + return 0; + + if ((q->flags & ISEARCH_OPCODE) && + insn->op != q->insn.op) + return 0; + + if ((q->flags & ISEARCH_BW) && + (q->insn.is_byte_op ? 1 : 0) != (insn->is_byte_op ? 1 : 0)) + return 0; + + if (q->flags & ISEARCH_SRC_ADDR) { + if (insn->src_mode != MSP430_AMODE_INDEXED && + insn->src_mode != MSP430_AMODE_SYMBOLIC && + insn->src_mode != MSP430_AMODE_ABSOLUTE && + insn->src_mode != MSP430_AMODE_IMMEDIATE) + return 0; + if (insn->src_addr != q->insn.src_addr) + return 0; + } + + if (q->flags & ISEARCH_DST_ADDR) { + if (insn->dst_mode != MSP430_AMODE_INDEXED && + insn->dst_mode != MSP430_AMODE_SYMBOLIC && + insn->dst_mode != MSP430_AMODE_ABSOLUTE && + insn->dst_mode != MSP430_AMODE_IMMEDIATE) + return 0; + if (insn->dst_addr != q->insn.dst_addr) + return 0; + } + + if ((q->flags & ISEARCH_SRC_MODE) && + insn->src_mode != q->insn.src_mode) + return 0; + + if ((q->flags & ISEARCH_DST_MODE) && + insn->dst_mode != q->insn.dst_mode) + return 0; + + if (q->flags & ISEARCH_SRC_REG) { + if (insn->src_mode != MSP430_AMODE_REGISTER && + insn->src_mode != MSP430_AMODE_INDIRECT && + insn->src_mode != MSP430_AMODE_INDIRECT_INC && + insn->src_mode != MSP430_AMODE_INDEXED) + return 0; + if (insn->src_reg != q->insn.src_reg) + return 0; + } + + if (q->flags & ISEARCH_DST_REG) { + if (insn->dst_mode != MSP430_AMODE_REGISTER && + insn->dst_mode != MSP430_AMODE_INDIRECT && + insn->dst_mode != MSP430_AMODE_INDIRECT_INC && + insn->dst_mode != MSP430_AMODE_INDEXED) + return 0; + if (insn->dst_reg != q->insn.dst_reg) + return 0; + } + + if ((q->flags & ISEARCH_TYPE) && + insn->itype != q->insn.itype) + return 0; + + return 1; +} + +static int do_isearch(int addr, int len, const struct isearch_query *q) +{ + u_int8_t *mbuf; + const struct device *dev = device_active(); + int i; + + if (len <= 0 || len > 0x10000 || + addr <= 0 || addr >= 0x10000 || + addr + len > 0x10000) { + fprintf(stderr, "isearch: invalid memory range\n"); + return -1; + } + + mbuf = malloc(len); + if (!mbuf) { + fprintf(stderr, "isearch: couldn't allocate memory: %s\n", + strerror(errno)); + return -1; + } + + if (dev->readmem(addr, mbuf, len) < 0) { + fprintf(stderr, "isearch: couldn't read device memory\n"); + free(mbuf); + return -1; + } + + for (i = 0; i < len; i += 2) { + struct msp430_instruction insn; + int count = dis_decode(mbuf + i, addr + i, len - i, &insn); + + if (count >= 0 && isearch_match(&insn, q)) + disassemble(addr + i, mbuf + i, count); + } + + free(mbuf); + return 0; +} + +static int cmd_isearch(char **arg) +{ + const static struct { + const char *name; + int (*func)(const char *term, char **arg, + struct isearch_query *q); + } term_handlers[] = { + {"opcode", isearch_opcode}, + {"byte", isearch_bw}, + {"word", isearch_bw}, + {"jump", isearch_type}, + {"single", isearch_type}, + {"double", isearch_type}, + {"src", isearch_addr}, + {"dst", isearch_addr}, + {"srcreg", isearch_reg}, + {"dstreg", isearch_reg}, + {"srcmode", isearch_mode}, + {"dstmode", isearch_mode} + }; + + struct isearch_query q; + const char *addr_text; + const char *len_text; + int addr; + int len; + + addr_text = get_arg(arg); + len_text = get_arg(arg); + + if (!(addr_text && len_text)) { + fprintf(stderr, "isearch: address and length expected\n"); + return -1; + } + + if (addr_exp(addr_text, &addr) < 0 || + addr_exp(len_text, &len) < 0) + return -1; + + q.flags = 0; + for (;;) { + const char *term = get_arg(arg); + int i; + + if (!term) + break; + + for (i = 0; i < ARRAY_LEN(term_handlers); i++) + if (!strcasecmp(term_handlers[i].name, term)) { + if (term_handlers[i].func(term, arg, &q) < 0) + return -1; + break; + } + } + + if (!q.flags) { + fprintf(stderr, "isearch: no query terms given " + "(perhaps you mean \"dis\"?)\n"); + return -1; + } + + return do_isearch(addr, len, &q); +} + +static struct command isearch_command = { + .name = "isearch", + .func = cmd_isearch, + .help = + "isearch
[options ...]\n" + " Search for an instruction matching certain search terms. These\n" + " terms may be any of the following:\n" + " opcode \n" + " byte|word\n" + " jump|single|double|noarg\n" + " src \n" + " dst \n" + " srcreg \n" + " dstreg \n" + " srcmode R|I|S|&|@|+|#\n" + " dstmode R|I|S|&|@|+|#\n" + " For single-operand instructions, the operand is considered the\n" + " destination operand.\n" +}; + +void rtools_init(void) +{ + register_command(&isearch_command); +} diff --git a/rtools.h b/rtools.h new file mode 100644 index 0000000..f718ebd --- /dev/null +++ b/rtools.h @@ -0,0 +1,25 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009, 2010 Daniel Beer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RTOOLS_H_ +#define RTOOLS_H_ + +/* Register reverse-engineering tool commands. */ +void rtools_init(void); + +#endif