Implemented isearch command.

This commit is contained in:
Daniel Beer 2010-04-08 13:15:30 +12:00
parent 188efdca10
commit 757a9fb799
7 changed files with 626 additions and 70 deletions

View File

@ -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:

41
dis.c
View File

@ -449,17 +449,10 @@ int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len,
return ret;
}
/* Return the mnemonic for an operation, if possible.
*
* If the argument is not a valid operation, this function returns the
* string "???".
*/
static const char *msp_op_name(msp430_op_t op)
{
static const struct {
static const struct {
msp430_op_t op;
const char *mnemonic;
} ops[] = {
} opcode_names[] = {
/* Single operand */
{MSP430_OP_RRC, "RRC"},
{MSP430_OP_SWPB, "SWPB"},
@ -518,12 +511,20 @@ static const char *msp_op_name(msp430_op_t op)
{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
* string "???".
*/
static const char *msp_op_name(msp430_op_t op)
{
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;
}

3
dis.h
View File

@ -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

13
main.c
View File

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

View File

@ -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

441
rtools.c Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#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 <address> <length> [options ...]\n"
" Search for an instruction matching certain search terms. These\n"
" terms may be any of the following:\n"
" opcode <opcode>\n"
" byte|word\n"
" jump|single|double|noarg\n"
" src <value>\n"
" dst <value>\n"
" srcreg <register>\n"
" dstreg <register>\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);
}

25
rtools.h Normal file
View File

@ -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