Commands are now registered by each module.

This commit is contained in:
Daniel Beer 2010-03-31 19:33:28 +13:00
parent f3c66c5aa4
commit ceb78ff053
13 changed files with 1368 additions and 1159 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
bsl.o sim.o symmap.o gdb.o btree.o device.o
$(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o:

View File

@ -25,6 +25,9 @@
/* Callback for binary image data */
typedef int (*imgfunc_t)(u_int16_t addr, const u_int8_t *data, int len);
/* Callback for symbol data */
typedef int (*symfunc_t)(const char *name, int value);
/* Intel HEX file support */
int ihex_check(FILE *in);
int ihex_extract(FILE *in, imgfunc_t cb);
@ -32,10 +35,10 @@ int ihex_extract(FILE *in, imgfunc_t cb);
/* ELF32 file support */
int elf32_check(FILE *in);
int elf32_extract(FILE *in, imgfunc_t cb);
int elf32_syms(FILE *in);
int elf32_syms(FILE *in, symfunc_t cb);
/* *.map file support */
int symmap_check(FILE *in);
int symmap_syms(FILE *in);
int symmap_syms(FILE *in, symfunc_t cb);
#endif

668
device.c Normal file
View File

@ -0,0 +1,668 @@
/* MSPDebug - debugging tool for the eZ430
* 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 <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "device.h"
#include "binfile.h"
#include "stab.h"
#include "util.h"
#include "dis.h"
static const struct device *msp430_dev;
#define REG_COLUMNS 4
#define REG_ROWS ((DEVICE_NUM_REGS + REG_COLUMNS - 1) / REG_COLUMNS)
static void show_regs(u_int16_t *regs)
{
int i;
for (i = 0; i < REG_ROWS; i++) {
int j;
printf(" ");
for (j = 0; j < REG_COLUMNS; j++) {
int k = j * REG_ROWS + i;
if (k < DEVICE_NUM_REGS)
printf("(r%02d: %04x) ", k, regs[k]);
}
printf("\n");
}
}
static int cmd_regs(char **arg)
{
u_int16_t regs[DEVICE_NUM_REGS];
u_int8_t code[16];
if (msp430_dev->getregs(regs) < 0)
return -1;
show_regs(regs);
/* Try to disassemble the instruction at PC */
if (msp430_dev->readmem(regs[0], code, sizeof(code)) < 0)
return 0;
disassemble(regs[0], (u_int8_t *)code, sizeof(code));
return 0;
}
static struct command command_regs = {
.name = "regs",
.func = cmd_regs,
.help =
"regs\n"
" Read and display the current register contents.\n"
};
static int cmd_md(char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
int offset = 0;
int length = 0x40;
if (!off_text) {
fprintf(stderr, "md: offset must be specified\n");
return -1;
}
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
return -1;
}
if (len_text) {
if (addr_exp(len_text, &length) < 0) {
fprintf(stderr, "md: can't parse length: %s\n",
len_text);
return -1;
}
} else if (offset + length > 0x10000) {
length = 0x10000 - offset;
}
if (offset < 0 || length <= 0 || (offset + length) > 0x10000) {
fprintf(stderr, "md: memory out of range\n");
return -1;
}
while (length) {
u_int8_t buf[128];
int blen = length > sizeof(buf) ? sizeof(buf) : length;
if (msp430_dev->readmem(offset, buf, blen) < 0)
return -1;
hexdump(offset, buf, blen);
offset += blen;
length -= blen;
}
return 0;
}
static struct command command_md = {
.name = "md",
.func = cmd_md,
.help =
"md <address> [length]\n"
" Read the specified number of bytes from memory at the given\n"
" address, and display a hexdump.\n"
};
static int cmd_mw(char **arg)
{
char *off_text = get_arg(arg);
char *byte_text;
int offset = 0;
int length = 0;
u_int8_t buf[1024];
if (!off_text) {
fprintf(stderr, "md: offset must be specified\n");
return -1;
}
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
return -1;
}
while ((byte_text = get_arg(arg))) {
if (length >= sizeof(buf)) {
fprintf(stderr, "md: maximum length exceeded\n");
return -1;
}
buf[length++] = strtoul(byte_text, NULL, 16);
}
if (!length)
return 0;
if (offset < 0 || (offset + length) > 0x10000) {
fprintf(stderr, "md: memory out of range\n");
return -1;
}
if (msp430_dev->writemem(offset, buf, length) < 0)
return -1;
return 0;
}
static struct command command_mw = {
.name = "mw",
.func = cmd_mw,
.help =
"mw <address> bytes ...\n"
" Write a sequence of bytes to a memory address. Byte values are\n"
" two-digit hexadecimal numbers.\n"
};
static int cmd_reset(char **arg)
{
return msp430_dev->control(DEVICE_CTL_RESET);
}
static struct command command_reset = {
.name = "reset",
.func = cmd_reset,
.help =
"reset\n"
" Reset (and halt) the CPU.\n"
};
static int cmd_erase(char **arg)
{
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
return -1;
printf("Erasing...\n");
return msp430_dev->control(DEVICE_CTL_ERASE);
}
static struct command command_erase = {
.name = "erase",
.func = cmd_erase,
.help =
"erase\n"
" Erase the device under test.\n"
};
static int cmd_step(char **arg)
{
char *count_text = get_arg(arg);
int count = 1;
if (count_text)
count = atoi(count_text);
while (count > 0) {
if (msp430_dev->control(DEVICE_CTL_STEP) < 0)
return -1;
count--;
}
return cmd_regs(NULL);
}
static struct command command_step = {
.name = "step",
.func = cmd_step,
.help =
"step [count]\n"
" Single-step the CPU, and display the register state.\n"
};
static int cmd_run(char **arg)
{
char *bp_text = get_arg(arg);
int bp_addr;
device_status_t status;
if (bp_text) {
if (addr_exp(bp_text, &bp_addr) < 0) {
fprintf(stderr, "run: can't parse breakpoint: %s\n",
bp_text);
return -1;
}
msp430_dev->breakpoint(bp_addr);
}
if (msp430_dev->control(bp_text ?
DEVICE_CTL_RUN_BP : DEVICE_CTL_RUN) < 0)
return -1;
if (bp_text)
printf("Running to 0x%04x.", bp_addr);
else
printf("Running.");
printf(" Press Ctrl+C to interrupt...\n");
status = msp430_dev->wait(1);
if (status == DEVICE_STATUS_INTR)
printf("\n");
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
return -1;
return cmd_regs(NULL);
}
static struct command command_run = {
.name = "run",
.func = cmd_run,
.help =
"run [breakpoint]\n"
" Run the CPU until either a specified breakpoint occurs or the\n"
" command is interrupted.\n"
};
static int cmd_set(char **arg)
{
char *reg_text = get_arg(arg);
char *val_text = get_arg(arg);
int reg;
int value = 0;
u_int16_t regs[DEVICE_NUM_REGS];
if (!(reg_text && val_text)) {
fprintf(stderr, "set: must specify a register and a value\n");
return -1;
}
while (*reg_text && !isdigit(*reg_text))
reg_text++;
reg = atoi(reg_text);
if (addr_exp(val_text, &value) < 0) {
fprintf(stderr, "set: can't parse value: %s\n", val_text);
return -1;
}
if (reg < 0 || reg >= DEVICE_NUM_REGS) {
fprintf(stderr, "set: register out of range: %d\n", reg);
return -1;
}
if (msp430_dev->getregs(regs) < 0)
return -1;
regs[reg] = value;
if (msp430_dev->setregs(regs) < 0)
return -1;
show_regs(regs);
return 0;
}
static struct command command_set = {
.name = "set",
.func = cmd_set,
.help =
"set <register> <value>\n"
" Change the value of a CPU register.\n"
};
static int cmd_dis(char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
int offset = 0;
int length = 0x40;
u_int8_t buf[4096];
if (!off_text) {
fprintf(stderr, "dis: offset must be specified\n");
return -1;
}
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "dis: can't parse offset: %s\n", off_text);
return -1;
}
if (len_text) {
if (addr_exp(len_text, &length) < 0) {
fprintf(stderr, "dis: can't parse length: %s\n",
len_text);
return -1;
}
} else if (offset + length > 0x10000) {
length = 0x10000 - offset;
}
if (offset < 0 || length <= 0 || length > sizeof(buf) ||
(offset + length) > 0x10000) {
fprintf(stderr, "dis: memory out of range\n");
return -1;
}
if (msp430_dev->readmem(offset, buf, length) < 0)
return -1;
disassemble(offset, (u_int8_t *)buf, length);
return 0;
}
static struct command command_dis = {
.name = "dis",
.func = cmd_dis,
.help =
"dis <address> [length]\n"
" Disassemble a section of memory.\n"
};
static FILE *hexout_file;
static u_int16_t hexout_addr;
static u_int8_t hexout_buf[16];
static int hexout_len;
static int hexout_start(const char *filename)
{
hexout_file = fopen(filename, "w");
if (!hexout_file) {
perror("hexout: couldn't open output file");
return -1;
}
return 0;
}
static int hexout_flush(void)
{
int i;
int cksum = 0;
if (!hexout_len)
return 0;
if (fprintf(hexout_file, ":%02X%04X00", hexout_len, hexout_addr) < 0)
goto fail;
cksum += hexout_len;
cksum += hexout_addr & 0xff;
cksum += hexout_addr >> 8;
for (i = 0; i < hexout_len; i++) {
if (fprintf(hexout_file, "%02X", hexout_buf[i]) < 0)
goto fail;
cksum += hexout_buf[i];
}
if (fprintf(hexout_file, "%02X\n", ~(cksum - 1) & 0xff) < 0)
goto fail;
hexout_len = 0;
return 0;
fail:
perror("hexout: can't write HEX data");
return -1;
}
static int hexout_feed(u_int16_t addr, const u_int8_t *buf, int len)
{
while (len) {
int count;
if ((hexout_addr + hexout_len != addr ||
hexout_len >= sizeof(hexout_buf)) &&
hexout_flush() < 0)
return -1;
if (!hexout_len)
hexout_addr = addr;
count = sizeof(hexout_buf) - hexout_len;
if (count > len)
count = len;
memcpy(hexout_buf + hexout_len, buf, count);
hexout_len += count;
addr += count;
buf += count;
len -= count;
}
return 0;
}
static int cmd_hexout(char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
char *filename = *arg;
int off;
int length;
if (!(off_text && len_text && *filename)) {
fprintf(stderr, "hexout: need offset, length and filename\n");
return -1;
}
if (addr_exp(off_text, &off) < 0 ||
addr_exp(len_text, &length) < 0)
return -1;
if (hexout_start(filename) < 0)
return -1;
while (length) {
u_int8_t buf[128];
int count = length;
if (count > sizeof(buf))
count = sizeof(buf);
printf("Reading %d bytes from 0x%04x...\n", count, off);
if (msp430_dev->readmem(off, buf, count) < 0) {
perror("hexout: can't read memory");
goto fail;
}
if (hexout_feed(off, buf, count) < 0)
goto fail;
length -= count;
off += count;
}
if (hexout_flush() < 0)
goto fail;
if (fclose(hexout_file) < 0) {
perror("hexout: error on close");
return -1;
}
return 0;
fail:
fclose(hexout_file);
unlink(filename);
return -1;
}
static struct command command_hexout = {
.name = "hexout",
.func = cmd_hexout,
.help =
"hexout <address> <length> <filename.hex>\n"
" Save a region of memory into a HEX file.\n"
};
static u_int8_t prog_buf[128];
static u_int16_t prog_addr;
static int prog_len;
static int prog_have_erased;
static void prog_init(void)
{
prog_len = 0;
prog_have_erased = 0;
}
static int prog_flush(void)
{
while (prog_len) {
int wlen = prog_len;
/* Writing across this address seems to cause a hang */
if (prog_addr < 0x999a && wlen + prog_addr > 0x999a)
wlen = 0x999a - prog_addr;
if (!prog_have_erased) {
printf("Erasing...\n");
if (device_active()->control(DEVICE_CTL_ERASE) < 0)
return -1;
prog_have_erased = 1;
}
printf("Writing %3d bytes to %04x...\n", wlen, prog_addr);
if (device_active()->writemem(prog_addr, prog_buf, wlen) < 0)
return -1;
memmove(prog_buf, prog_buf + wlen, prog_len - wlen);
prog_len -= wlen;
prog_addr += wlen;
}
return 0;
}
static int prog_feed(u_int16_t addr, const u_int8_t *data, int len)
{
/* Flush if this section is discontiguous */
if (prog_len && prog_addr + prog_len != addr && prog_flush() < 0)
return -1;
if (!prog_len)
prog_addr = addr;
/* Add the buffer in piece by piece, flushing when it gets
* full.
*/
while (len) {
int count = sizeof(prog_buf) - prog_len;
if (count > len)
count = len;
if (!count) {
if (prog_flush() < 0)
return -1;
} else {
memcpy(prog_buf + prog_len, data, count);
prog_len += count;
data += count;
len -= count;
}
}
return 0;
}
static int cmd_prog(char **arg)
{
FILE *in;
int result = 0;
if (modify_prompt(MODIFY_SYMS))
return 0;
in = fopen(*arg, "r");
if (!in) {
fprintf(stderr, "prog: %s: %s\n", *arg, strerror(errno));
return -1;
}
if (device_active()->control(DEVICE_CTL_HALT) < 0) {
fclose(in);
return -1;
}
prog_init();
if (elf32_check(in)) {
result = elf32_extract(in, prog_feed);
stab_clear();
elf32_syms(in, stab_set);
} else if (ihex_check(in)) {
result = ihex_extract(in, prog_feed);
} else {
fprintf(stderr, "prog: %s: unknown file type\n", *arg);
}
fclose(in);
if (prog_flush() < 0)
return -1;
if (device_active()->control(DEVICE_CTL_RESET) < 0) {
fprintf(stderr, "prog: failed to reset after programming\n");
return -1;
}
modify_clear(MODIFY_SYMS);
return result;
}
static struct command command_prog = {
.name = "prog",
.func = cmd_prog,
.help =
"prog <filename>\n"
" Erase the device and flash the data contained in a binary file.\n"
" This command also loads symbols from the file, if available.\n"
};
void device_init(const struct device *dev)
{
msp430_dev = dev;
register_command(&command_md);
register_command(&command_mw);
register_command(&command_reset);
register_command(&command_erase);
register_command(&command_regs);
register_command(&command_run);
register_command(&command_step);
register_command(&command_set);
register_command(&command_dis);
register_command(&command_hexout);
register_command(&command_prog);
}
const struct device *device_active(void)
{
return msp430_dev;
}
void device_exit(void)
{
if (msp430_dev)
msp430_dev->close();
}

View File

@ -64,4 +64,9 @@ const struct device *bsl_open(const char *device);
/* Dummy/simulation implementation. */
const struct device *sim_open(void);
/* Register device commands */
void device_init(const struct device *dev);
void device_exit(void);
const struct device *device_active(void);
#endif

View File

@ -21,7 +21,6 @@
#include <string.h>
#include <elf.h>
#include "binfile.h"
#include "stab.h"
#define EM_MSP430 0x69
@ -265,7 +264,7 @@ static int syms_load_strings(FILE *in, Elf32_Shdr *s)
#define N_SYMS 128
static int syms_load_syms(FILE *in, Elf32_Shdr *s)
static int syms_load_syms(FILE *in, Elf32_Shdr *s, symfunc_t cb)
{
Elf32_Sym syms[N_SYMS];
int len = s->sh_size / sizeof(syms[0]);
@ -299,7 +298,7 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s)
return -1;
}
if (stab_set(string_tab + y->st_name, y->st_value) < 0)
if (cb(string_tab + y->st_name, y->st_value) < 0)
return -1;
}
@ -309,7 +308,7 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s)
return 0;
}
int elf32_syms(FILE *in)
int elf32_syms(FILE *in, symfunc_t cb)
{
Elf32_Shdr *s;
@ -331,7 +330,7 @@ int elf32_syms(FILE *in)
string_len = 0;
if (syms_load_strings(in, &file_shdrs[s->sh_link]) < 0 ||
syms_load_syms(in, s) < 0) {
syms_load_syms(in, s, cb) < 0) {
if (string_tab)
free(string_tab);
return -1;

69
gdb.c
View File

@ -28,10 +28,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include "device.h"
#include "util.h"
#include "gdb.h"
static const struct device *gdb_device;
/************************************************************************
* GDB IO routines
*/
@ -208,7 +207,7 @@ static int read_registers(void)
int i;
printf("Reading registers\n");
if (gdb_device->getregs(regs) < 0)
if (device_active()->getregs(regs) < 0)
return gdb_send("E00");
gdb_packet_start();
@ -230,11 +229,11 @@ static int monitor_command(char *buf)
if (!strcasecmp(cmd, "reset")) {
printf("Resetting device\n");
if (gdb_device->control(DEVICE_CTL_RESET) < 0)
if (device_active()->control(DEVICE_CTL_RESET) < 0)
return gdb_send_hex("Reset failed\n");
} else if (!strcasecmp(cmd, "erase")) {
printf("Erasing device\n");
if (gdb_device->control(DEVICE_CTL_ERASE) < 0)
if (device_active()->control(DEVICE_CTL_ERASE) < 0)
return gdb_send_hex("Erase failed\n");
}
@ -258,7 +257,7 @@ static int write_registers(char *buf)
buf += 4;
}
if (gdb_device->setregs(regs) < 0)
if (device_active()->setregs(regs) < 0)
return gdb_send("E00");
return gdb_send("OK");
@ -286,7 +285,7 @@ static int read_memory(char *text)
printf("Reading %d bytes from 0x%04x\n", length, addr);
if (gdb_device->readmem(addr, buf, length) < 0)
if (device_active()->readmem(addr, buf, length) < 0)
return gdb_send("E00");
gdb_packet_start();
@ -329,7 +328,7 @@ static int write_memory(char *text)
printf("Writing %d bytes to 0x%04x\n", buflen, addr);
if (gdb_device->writemem(addr, buf, buflen) < 0)
if (device_active()->writemem(addr, buf, buflen) < 0)
return gdb_send("E00");
return gdb_send("OK");
@ -342,11 +341,11 @@ static int run_set_pc(char *buf)
if (!*buf)
return 0;
if (gdb_device->getregs(regs) < 0)
if (device_active()->getregs(regs) < 0)
return -1;
regs[0] = strtoul(buf, NULL, 16);
return gdb_device->setregs(regs);
return device_active()->setregs(regs);
}
static int run_final_status(void)
@ -354,7 +353,7 @@ static int run_final_status(void)
u_int16_t regs[DEVICE_NUM_REGS];
int i;
if (gdb_device->getregs(regs) < 0)
if (device_active()->getregs(regs) < 0)
return gdb_send("E00");
gdb_packet_start();
@ -371,7 +370,7 @@ static int single_step(char *buf)
printf("Single stepping\n");
if (run_set_pc(buf) < 0 ||
gdb_device->control(DEVICE_CTL_STEP) < 0)
device_active()->control(DEVICE_CTL_STEP) < 0)
gdb_send("E00");
return run_final_status();
@ -382,13 +381,13 @@ static int run(char *buf)
printf("Running\n");
if (run_set_pc(buf) < 0 ||
gdb_device->control(DEVICE_CTL_RUN) < 0) {
device_active()->control(DEVICE_CTL_RUN) < 0) {
gdb_send("E00");
return run_final_status();
}
for (;;) {
device_status_t status = gdb_device->wait(0);
device_status_t status = device_active()->wait(0);
if (status == DEVICE_STATUS_ERROR) {
gdb_send("E00");
@ -417,13 +416,13 @@ static int run(char *buf)
}
out:
if (gdb_device->control(DEVICE_CTL_HALT) < 0)
if (device_active()->control(DEVICE_CTL_HALT) < 0)
gdb_send("E00");
return run_final_status();
}
static int process_command(char *buf, int len)
static int process_gdb_command(char *buf, int len)
{
switch (buf[0]) {
case '?': /* Return target halt reason */
@ -457,7 +456,7 @@ static int process_command(char *buf, int len)
return gdb_send("");
}
static void reader_loop(void)
static void gdb_reader_loop(void)
{
for (;;) {
char buf[1024];
@ -515,12 +514,12 @@ static void reader_loop(void)
if (gdb_flush() < 0)
return;
if (len && process_command(buf, len) < 0)
if (len && process_gdb_command(buf, len) < 0)
return;
}
}
int gdb_server(const struct device *dev, int port)
static int gdb_server(int port)
{
int sock;
int client;
@ -573,9 +572,37 @@ int gdb_server(const struct device *dev, int port)
gdb_head = 0;
gdb_tail = 0;
gdb_outlen = 0;
gdb_device = dev;
reader_loop();
gdb_reader_loop();
return gdb_errno ? -1 : 0;
}
static int cmd_gdb(char **arg)
{
char *port_text = get_arg(arg);
int port = 2000;
if (port_text)
port = atoi(port_text);
if (port <= 0 || port > 65535) {
fprintf(stderr, "gdb: invalid port: %d\n", port);
return -1;
}
return gdb_server(port);
}
static struct command command_gdb = {
.name = "gdb",
.func = cmd_gdb,
.help =
"gdb [port]\n"
" Run a GDB remote stub on the given TCP/IP port.\n"
};
void gdb_init(void)
{
register_command(&command_gdb);
}

8
gdb.h
View File

@ -19,11 +19,7 @@
#ifndef GDB_H_
#define GDB_H_
/* Start a GDB remote stub, listening on the given TCP/IP port. It will
* wait for GDB to connect, and then return once GDB has disconnected.
*
* Returns 0 for success, or -1 in the case of an error.
*/
int gdb_server(const struct device *dev, int port);
/* Register the "gdb" command */
void gdb_init(void);
#endif

883
main.c
View File

@ -30,872 +30,6 @@
#include "util.h"
#include "gdb.h"
static const struct device *msp430_dev;
/**********************************************************************
* Modification tracking and prompting
*/
static int syms_are_modified;
static int syms_modify_check(voiv)
{
char buf[32];
if (!syms_are_modified)
return 0;
for (;;) {
printf("Symbols have not been saved since modification. "
"Continue (y/n)? ");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
printf("\n");
return 1;
}
if (toupper(buf[0]) == 'Y')
return 0;
if (toupper(buf[0]) == 'N')
return 1;
printf("Please answer \"y\" or \"n\".\n");
}
return 0;
}
/************************************************************************
* Command definitions
*/
#define REG_COLUMNS 4
#define REG_ROWS ((DEVICE_NUM_REGS + REG_COLUMNS - 1) / REG_COLUMNS)
static void show_regs(u_int16_t *regs)
{
int i;
for (i = 0; i < REG_ROWS; i++) {
int j;
printf(" ");
for (j = 0; j < REG_COLUMNS; j++) {
int k = j * REG_ROWS + i;
if (k < DEVICE_NUM_REGS)
printf("(r%02d: %04x) ", k, regs[k]);
}
printf("\n");
}
}
static int cmd_md(char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
int offset = 0;
int length = 0x40;
if (!off_text) {
fprintf(stderr, "md: offset must be specified\n");
return -1;
}
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
return -1;
}
if (len_text) {
if (addr_exp(len_text, &length) < 0) {
fprintf(stderr, "md: can't parse length: %s\n",
len_text);
return -1;
}
} else if (offset + length > 0x10000) {
length = 0x10000 - offset;
}
if (offset < 0 || length <= 0 || (offset + length) > 0x10000) {
fprintf(stderr, "md: memory out of range\n");
return -1;
}
while (length) {
u_int8_t buf[128];
int blen = length > sizeof(buf) ? sizeof(buf) : length;
if (msp430_dev->readmem(offset, buf, blen) < 0)
return -1;
hexdump(offset, buf, blen);
offset += blen;
length -= blen;
}
return 0;
}
static int cmd_mw(char **arg)
{
char *off_text = get_arg(arg);
char *byte_text;
int offset = 0;
int length = 0;
u_int8_t buf[1024];
if (!off_text) {
fprintf(stderr, "md: offset must be specified\n");
return -1;
}
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "md: can't parse offset: %s\n", off_text);
return -1;
}
while ((byte_text = get_arg(arg))) {
if (length >= sizeof(buf)) {
fprintf(stderr, "md: maximum length exceeded\n");
return -1;
}
buf[length++] = strtoul(byte_text, NULL, 16);
}
if (!length)
return 0;
if (offset < 0 || (offset + length) > 0x10000) {
fprintf(stderr, "md: memory out of range\n");
return -1;
}
if (msp430_dev->writemem(offset, buf, length) < 0)
return -1;
return 0;
}
static int cmd_dis(char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
int offset = 0;
int length = 0x40;
u_int8_t buf[4096];
if (!off_text) {
fprintf(stderr, "md: offset must be specified\n");
return -1;
}
if (addr_exp(off_text, &offset) < 0) {
fprintf(stderr, "dis: can't parse offset: %s\n", off_text);
return -1;
}
if (len_text) {
if (addr_exp(len_text, &length) < 0) {
fprintf(stderr, "dis: can't parse length: %s\n",
len_text);
return -1;
}
} else if (offset + length > 0x10000) {
length = 0x10000 - offset;
}
if (offset < 0 || length <= 0 || length > sizeof(buf) ||
(offset + length) > 0x10000) {
fprintf(stderr, "dis: memory out of range\n");
return -1;
}
if (msp430_dev->readmem(offset, buf, length) < 0)
return -1;
disassemble(offset, (u_int8_t *)buf, length);
return 0;
}
static FILE *hexout_file;
static u_int16_t hexout_addr;
static u_int8_t hexout_buf[16];
static int hexout_len;
static int hexout_start(const char *filename)
{
hexout_file = fopen(filename, "w");
if (!hexout_file) {
perror("hexout: couldn't open output file");
return -1;
}
return 0;
}
static int hexout_flush(void)
{
int i;
int cksum = 0;
if (!hexout_len)
return 0;
if (fprintf(hexout_file, ":%02X%04X00", hexout_len, hexout_addr) < 0)
goto fail;
cksum += hexout_len;
cksum += hexout_addr & 0xff;
cksum += hexout_addr >> 8;
for (i = 0; i < hexout_len; i++) {
if (fprintf(hexout_file, "%02X", hexout_buf[i]) < 0)
goto fail;
cksum += hexout_buf[i];
}
if (fprintf(hexout_file, "%02X\n", ~(cksum - 1) & 0xff) < 0)
goto fail;
hexout_len = 0;
return 0;
fail:
perror("hexout: can't write HEX data");
return -1;
}
static int hexout_feed(u_int16_t addr, const u_int8_t *buf, int len)
{
while (len) {
int count;
if ((hexout_addr + hexout_len != addr ||
hexout_len >= sizeof(hexout_buf)) &&
hexout_flush() < 0)
return -1;
if (!hexout_len)
hexout_addr = addr;
count = sizeof(hexout_buf) - hexout_len;
if (count > len)
count = len;
memcpy(hexout_buf + hexout_len, buf, count);
hexout_len += count;
addr += count;
buf += count;
len -= count;
}
return 0;
}
static int cmd_hexout(char **arg)
{
char *off_text = get_arg(arg);
char *len_text = get_arg(arg);
char *filename = *arg;
int off;
int length;
if (!(off_text && len_text && *filename)) {
fprintf(stderr, "hexout: need offset, length and filename\n");
return -1;
}
if (addr_exp(off_text, &off) < 0 ||
addr_exp(len_text, &length) < 0)
return -1;
if (hexout_start(filename) < 0)
return -1;
while (length) {
u_int8_t buf[128];
int count = length;
if (count > sizeof(buf))
count = sizeof(buf);
printf("Reading %d bytes from 0x%04x...\n", count, off);
if (msp430_dev->readmem(off, buf, count) < 0) {
perror("hexout: can't read memory");
goto fail;
}
if (hexout_feed(off, buf, count) < 0)
goto fail;
length -= count;
off += count;
}
if (hexout_flush() < 0)
goto fail;
if (fclose(hexout_file) < 0) {
perror("hexout: error on close");
return -1;
}
return 0;
fail:
fclose(hexout_file);
unlink(filename);
return -1;
}
static int cmd_reset(char **arg)
{
return msp430_dev->control(DEVICE_CTL_RESET);
}
static int cmd_regs(char **arg)
{
u_int16_t regs[DEVICE_NUM_REGS];
u_int8_t code[16];
if (msp430_dev->getregs(regs) < 0)
return -1;
show_regs(regs);
/* Try to disassemble the instruction at PC */
if (msp430_dev->readmem(regs[0], code, sizeof(code)) < 0)
return 0;
disassemble(regs[0], (u_int8_t *)code, sizeof(code));
return 0;
}
static int cmd_run(char **arg)
{
char *bp_text = get_arg(arg);
int bp_addr;
device_status_t status;
if (bp_text) {
if (addr_exp(bp_text, &bp_addr) < 0) {
fprintf(stderr, "run: can't parse breakpoint: %s\n",
bp_text);
return -1;
}
msp430_dev->breakpoint(bp_addr);
}
if (msp430_dev->control(bp_text ?
DEVICE_CTL_RUN_BP : DEVICE_CTL_RUN) < 0)
return -1;
if (bp_text)
printf("Running to 0x%04x.", bp_addr);
else
printf("Running.");
printf(" Press Ctrl+C to interrupt...\n");
status = msp430_dev->wait(1);
if (status == DEVICE_STATUS_INTR)
printf("\n");
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
return -1;
return cmd_regs(NULL);
}
static int cmd_set(char **arg)
{
char *reg_text = get_arg(arg);
char *val_text = get_arg(arg);
int reg;
int value = 0;
u_int16_t regs[DEVICE_NUM_REGS];
if (!(reg_text && val_text)) {
fprintf(stderr, "set: must specify a register and a value\n");
return -1;
}
while (*reg_text && !isdigit(*reg_text))
reg_text++;
reg = atoi(reg_text);
if (addr_exp(val_text, &value) < 0) {
fprintf(stderr, "set: can't parse value: %s\n", val_text);
return -1;
}
if (reg < 0 || reg >= DEVICE_NUM_REGS) {
fprintf(stderr, "set: register out of range: %d\n", reg);
return -1;
}
if (msp430_dev->getregs(regs) < 0)
return -1;
regs[reg] = value;
if (msp430_dev->setregs(regs) < 0)
return -1;
show_regs(regs);
return 0;
}
static int cmd_step(char **arg)
{
char *count_text = get_arg(arg);
int count = 1;
if (count_text)
count = atoi(count_text);
while (count > 0) {
if (msp430_dev->control(DEVICE_CTL_STEP) < 0)
return -1;
count--;
}
return cmd_regs(NULL);
}
static u_int8_t prog_buf[128];
static u_int16_t prog_addr;
static int prog_len;
static int prog_have_erased;
static void prog_init(void)
{
prog_len = 0;
prog_have_erased = 0;
}
static int prog_flush(void)
{
while (prog_len) {
int wlen = prog_len;
/* Writing across this address seems to cause a hang */
if (prog_addr < 0x999a && wlen + prog_addr > 0x999a)
wlen = 0x999a - prog_addr;
if (!prog_have_erased) {
printf("Erasing...\n");
if (msp430_dev->control(DEVICE_CTL_ERASE) < 0)
return -1;
prog_have_erased = 1;
}
printf("Writing %3d bytes to %04x...\n", wlen, prog_addr);
if (msp430_dev->writemem(prog_addr, prog_buf, wlen) < 0)
return -1;
memmove(prog_buf, prog_buf + wlen, prog_len - wlen);
prog_len -= wlen;
prog_addr += wlen;
}
return 0;
}
static int prog_feed(u_int16_t addr, const u_int8_t *data, int len)
{
/* Flush if this section is discontiguous */
if (prog_len && prog_addr + prog_len != addr && prog_flush() < 0)
return -1;
if (!prog_len)
prog_addr = addr;
/* Add the buffer in piece by piece, flushing when it gets
* full.
*/
while (len) {
int count = sizeof(prog_buf) - prog_len;
if (count > len)
count = len;
if (!count) {
if (prog_flush() < 0)
return -1;
} else {
memcpy(prog_buf + prog_len, data, count);
prog_len += count;
data += count;
len -= count;
}
}
return 0;
}
static int cmd_erase(char **arg)
{
if (msp430_dev->control(DEVICE_CTL_HALT) < 0)
return -1;
printf("Erasing...\n");
return msp430_dev->control(DEVICE_CTL_ERASE);
}
static int cmd_prog(char **arg)
{
FILE *in;
int result = 0;
if (is_interactive() && syms_modify_check())
return 0;
in = fopen(*arg, "r");
if (!in) {
fprintf(stderr, "prog: %s: %s\n", *arg, strerror(errno));
return -1;
}
if (msp430_dev->control(DEVICE_CTL_HALT) < 0) {
fclose(in);
return -1;
}
prog_init();
if (elf32_check(in)) {
result = elf32_extract(in, prog_feed);
stab_clear();
elf32_syms(in);
} else if (ihex_check(in)) {
result = ihex_extract(in, prog_feed);
} else {
fprintf(stderr, "prog: %s: unknown file type\n", *arg);
}
fclose(in);
if (prog_flush() < 0)
return -1;
if (msp430_dev->control(DEVICE_CTL_RESET) < 0) {
fprintf(stderr, "prog: failed to reset after programming\n");
return -1;
}
syms_are_modified = 0;
return result;
}
static int cmd_eval(char **arg)
{
int addr;
u_int16_t offset;
char name[64];
if (addr_exp(*arg, &addr) < 0) {
fprintf(stderr, "=: can't parse: %s\n", *arg);
return -1;
}
printf("0x%04x", addr);
if (!stab_nearest(addr, name, sizeof(name), &offset)) {
printf(" = %s", name);
if (offset)
printf("+0x%x", offset);
}
printf("\n");
return 0;
}
static int cmd_sym_load_add(int clear, char **arg)
{
FILE *in;
int result = 0;
if (clear && is_interactive() && syms_modify_check())
return 0;
in = fopen(*arg, "r");
if (!in) {
fprintf(stderr, "sym: %s: %s\n", *arg, strerror(errno));
return -1;
}
if (clear)
stab_clear();
if (elf32_check(in))
result = elf32_syms(in);
else if (symmap_check(in))
result = symmap_syms(in);
else
fprintf(stderr, "sym: %s: unknown file type\n", *arg);
fclose(in);
syms_are_modified = !clear;
return result;
}
static FILE *savemap_out;
static int savemap_write(const char *name, u_int16_t value)
{
if (fprintf(savemap_out, "%08x t %s\n", value, name) < 0) {
fprintf(stderr, "sym: error writing symbols: %s\n",
strerror(errno));
return -1;
}
return 0;
}
static int cmd_sym_savemap(char **arg)
{
char *fname = get_arg(arg);
if (!fname) {
fprintf(stderr, "sym: filename required to save map\n");
return -1;
}
savemap_out = fopen(fname, "w");
if (!savemap_out) {
fprintf(stderr, "sym: couldn't write to %s: %s\n", fname,
strerror(errno));
return -1;
}
if (stab_enum(savemap_write) < 0)
return -1;
if (fclose(savemap_out) < 0) {
fprintf(stderr, "sym: error closing %s: %s\n", fname,
strerror(errno));
return -1;
}
syms_are_modified = 0;
return 0;
}
static int printsym(const char *name, u_int16_t value)
{
printf("0x%04x: %s\n", value, name);
return 0;
}
static int cmd_sym(char **arg)
{
char *subcmd = get_arg(arg);
if (!subcmd) {
fprintf(stderr, "sym: need to specify a subcommand "
"(try \"help sym\")\n");
return -1;
}
if (!strcasecmp(subcmd, "clear")) {
if (is_interactive() && syms_modify_check())
return 0;
stab_clear();
syms_are_modified = 0;
return 0;
}
if (!strcasecmp(subcmd, "set")) {
char *name = get_arg(arg);
char *val_text = get_arg(arg);
int value;
if (!(name && val_text)) {
fprintf(stderr, "sym: need a name and value to set "
"symbol table entries\n");
return -1;
}
if (addr_exp(val_text, &value) < 0) {
fprintf(stderr, "sym: can't parse value: %s\n",
val_text);
return -1;
}
if (stab_set(name, value) < 0)
return -1;
syms_are_modified = 1;
return 0;
}
if (!strcasecmp(subcmd, "del")) {
char *name = get_arg(arg);
if (!name) {
fprintf(stderr, "sym: need a name to delete "
"symbol table entries\n");
return -1;
}
if (stab_del(name) < 0)
return -1;
syms_are_modified = 1;
return 0;
}
if (!strcasecmp(subcmd, "load"))
return cmd_sym_load_add(1, arg);
if (!strcasecmp(subcmd, "add"))
return cmd_sym_load_add(0, arg);
if (!strcasecmp(subcmd, "savemap"))
return cmd_sym_savemap(arg);
if (!strcasecmp(subcmd, "find")) {
char *expr = get_arg(arg);
if (!expr) {
stab_enum(printsym);
return 0;
}
return stab_re_search(expr, printsym) < 0 ? -1 : 0;
}
fprintf(stderr, "sym: unknown subcommand: %s\n", subcmd);
return -1;
}
static int cmd_gdb(char **arg)
{
char *port_text = get_arg(arg);
int port = 2000;
if (port_text)
port = atoi(port_text);
if (port <= 0 || port > 65535) {
fprintf(stderr, "gdb: invalid port: %d\n", port);
return -1;
}
return gdb_server(msp430_dev, port);
}
static int cmd_read(char **arg)
{
char *filename = get_arg(arg);
FILE *in;
char buf[1024];
if (!filename) {
fprintf(stderr, "read: filename must be specified\n");
return -1;
}
in = fopen(filename, "r");
if (!in) {
fprintf(stderr, "read: can't open %s: %s\n",
filename, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), in)) {
char *cmd = buf;
while (*cmd && isspace(*cmd))
cmd++;
if (*cmd == '#')
continue;
if (process_command(cmd, 0) < 0) {
fprintf(stderr, "read: error processing %s\n",
filename);
fclose(in);
return -1;
}
}
fclose(in);
return 0;
}
const struct command all_commands[] = {
{"=", cmd_eval,
"= <expression>\n"
" Evaluate an expression using the symbol table.\n"},
{"dis", cmd_dis,
"dis <address> [length]\n"
" Disassemble a section of memory.\n"},
{"erase", cmd_erase,
"erase\n"
" Erase the device under test.\n"},
{"gdb", cmd_gdb,
"gdb [port]\n"
" Run a GDB remote stub on the given TCP/IP port.\n"},
{"help", cmd_help,
"help [command]\n"
" Without arguments, displays a list of commands. With a command\n"
" name as an argument, displays help for that command.\n"},
{"hexout", cmd_hexout,
"hexout <address> <length> <filename.hex>\n"
" Save a region of memory into a HEX file.\n"},
{"md", cmd_md,
"md <address> [length]\n"
" Read the specified number of bytes from memory at the given\n"
" address, and display a hexdump.\n"},
{"mw", cmd_mw,
"mw <address> bytes ...\n"
" Write a sequence of bytes to a memory address. Byte values are\n"
" two-digit hexadecimal numbers.\n"},
{"opt", cmd_opt,
"opt [name] [value]\n"
" Query or set option variables. With no arguments, displays all\n"
" available options.\n"},
{"prog", cmd_prog,
"prog <filename>\n"
" Erase the device and flash the data contained in a binary file.\n"
" This command also loads symbols from the file, if available.\n"},
{"read", cmd_read,
"read <filename>\n"
" Read commands from a file and evaluate them.\n"},
{"regs", cmd_regs,
"regs\n"
" Read and display the current register contents.\n"},
{"reset", cmd_reset,
"reset\n"
" Reset (and halt) the CPU.\n"},
{"run", cmd_run,
"run [breakpoint]\n"
" Run the CPU until either a specified breakpoint occurs or the\n"
" command is interrupted.\n"},
{"set", cmd_set,
"set <register> <value>\n"
" Change the value of a CPU register.\n"},
{"step", cmd_step,
"step [count]\n"
" Single-step the CPU, and display the register state.\n"},
{"sym", cmd_sym,
"sym clear\n"
" Clear the symbol table.\n"
"sym set <name> <value>\n"
" Set or overwrite the value of a symbol.\n"
"sym del <name>\n"
" Delete a symbol from the symbol table.\n"
"sym load <filename>\n"
" Load symbols from the given file.\n"
"sym add <filename>\n"
" Load additional symbols from the given file.\n"
"sym savemap <filename>\n"
" Save the current symbols to a BSD-style symbol file.\n"
"sym find <regex>\n"
" Search for symbols by regular expression.\n"},
{NULL, NULL, NULL}
};
static void usage(const char *progname)
{
fprintf(stderr,
@ -934,6 +68,7 @@ int main(int argc, char **argv)
const struct fet_transport *trans;
const char *uif_device = NULL;
const char *bsl_device = NULL;
const struct device *msp430_dev = NULL;
int opt;
int flags = 0;
int want_jtag = 0;
@ -999,6 +134,7 @@ int main(int argc, char **argv)
}
parse_init();
gdb_init();
if (stab_init() < 0)
return -1;
@ -1026,23 +162,22 @@ int main(int argc, char **argv)
msp430_dev = fet_open(trans, flags, vcc_mv);
}
if (!msp430_dev)
if (!msp430_dev) {
stab_exit();
return -1;
}
device_init(msp430_dev);
/* Process commands */
if (optind < argc) {
while (optind < argc)
process_command(argv[optind++], 0);
} else {
printf("\n");
cmd_help(NULL);
do {
reader_loop();
} while (syms_modify_check());
reader_loop();
}
msp430_dev->close();
device_exit();
stab_exit();
return 0;

276
stab.c
View File

@ -22,7 +22,10 @@
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include <errno.h>
#include "stab.h"
#include "util.h"
#include "binfile.h"
#include "btree.h"
struct sym_key {
@ -100,33 +103,37 @@ static const struct btree_def addr_table_def = {
static btree_t sym_table;
static btree_t addr_table;
/************************************************************************
* Public interface
*/
int stab_init(void)
static int cmd_eval(char **arg)
{
sym_table = btree_alloc(&sym_table_def);
if (!sym_table) {
fprintf(stderr, "stab: failed to allocate symbol table\n");
int addr;
u_int16_t offset;
char name[64];
if (addr_exp(*arg, &addr) < 0) {
fprintf(stderr, "=: can't parse: %s\n", *arg);
return -1;
}
addr_table = btree_alloc(&addr_table_def);
if (!addr_table) {
fprintf(stderr, "stab: failed to allocate address table\n");
btree_free(sym_table);
return -1;
printf("0x%04x", addr);
if (!stab_nearest(addr, name, sizeof(name), &offset)) {
printf(" = %s", name);
if (offset)
printf("+0x%x", offset);
}
printf("\n");
return 0;
}
void stab_exit(void)
{
btree_free(sym_table);
btree_free(addr_table);
}
static struct command command_eval = {
.name = "=",
.func = cmd_eval,
.help =
"= <expression>\n"
" Evaluate an expression using the symbol table.\n"
};
typedef int (*stab_callback_t)(const char *name, u_int16_t value);
void stab_clear(void)
{
@ -134,10 +141,11 @@ void stab_clear(void)
btree_clear(addr_table);
}
int stab_set(const char *name, u_int16_t addr)
int stab_set(const char *name, int value)
{
struct sym_key skey;
struct addr_key akey;
u_int16_t addr = value;
u_int16_t old_addr;
sym_key_init(&skey, name);
@ -161,7 +169,7 @@ int stab_set(const char *name, u_int16_t addr)
return 0;
}
int stab_get(const char *name, u_int16_t *value)
static int stab_get(const char *name, u_int16_t *value)
{
struct sym_key skey;
@ -172,7 +180,7 @@ int stab_get(const char *name, u_int16_t *value)
return 0;
}
int stab_del(const char *name)
static int stab_del(const char *name)
{
struct sym_key skey;
u_int16_t value;
@ -192,7 +200,7 @@ int stab_del(const char *name)
return 0;
}
int stab_enum(stab_callback_t cb)
static int stab_enum(stab_callback_t cb)
{
struct sym_key skey;
u_int16_t value;
@ -210,7 +218,7 @@ int stab_enum(stab_callback_t cb)
return count;
}
int stab_re_search(const char *regex, stab_callback_t cb)
static int stab_re_search(const char *regex, stab_callback_t cb)
{
struct sym_key skey;
u_int16_t value;
@ -261,3 +269,225 @@ int stab_nearest(u_int16_t addr, char *ret_name, int max_len,
return -1;
}
static int cmd_sym_load_add(int clear, char **arg)
{
FILE *in;
int result = 0;
if (clear && modify_prompt(MODIFY_SYMS))
return 0;
in = fopen(*arg, "r");
if (!in) {
fprintf(stderr, "sym: %s: %s\n", *arg, strerror(errno));
return -1;
}
if (clear)
stab_clear();
if (elf32_check(in))
result = elf32_syms(in, stab_set);
else if (symmap_check(in))
result = symmap_syms(in, stab_set);
else
fprintf(stderr, "sym: %s: unknown file type\n", *arg);
fclose(in);
if (clear)
modify_clear(MODIFY_SYMS);
else
modify_set(MODIFY_SYMS);
return result;
}
static FILE *savemap_out;
static int savemap_write(const char *name, u_int16_t value)
{
if (fprintf(savemap_out, "%08x t %s\n", value, name) < 0) {
fprintf(stderr, "sym: error writing symbols: %s\n",
strerror(errno));
return -1;
}
return 0;
}
static int cmd_sym_savemap(char **arg)
{
char *fname = get_arg(arg);
if (!fname) {
fprintf(stderr, "sym: filename required to save map\n");
return -1;
}
savemap_out = fopen(fname, "w");
if (!savemap_out) {
fprintf(stderr, "sym: couldn't write to %s: %s\n", fname,
strerror(errno));
return -1;
}
if (stab_enum(savemap_write) < 0)
return -1;
if (fclose(savemap_out) < 0) {
fprintf(stderr, "sym: error closing %s: %s\n", fname,
strerror(errno));
return -1;
}
modify_clear(MODIFY_SYMS);
return 0;
}
static int printsym(const char *name, u_int16_t value)
{
printf("0x%04x: %s\n", value, name);
return 0;
}
static int cmd_sym(char **arg)
{
char *subcmd = get_arg(arg);
if (!subcmd) {
fprintf(stderr, "sym: need to specify a subcommand "
"(try \"help sym\")\n");
return -1;
}
if (!strcasecmp(subcmd, "clear")) {
if (modify_prompt(MODIFY_SYMS))
return 0;
stab_clear();
modify_clear(MODIFY_SYMS);
return 0;
}
if (!strcasecmp(subcmd, "set")) {
char *name = get_arg(arg);
char *val_text = get_arg(arg);
int value;
if (!(name && val_text)) {
fprintf(stderr, "sym: need a name and value to set "
"symbol table entries\n");
return -1;
}
if (addr_exp(val_text, &value) < 0) {
fprintf(stderr, "sym: can't parse value: %s\n",
val_text);
return -1;
}
if (stab_set(name, value) < 0)
return -1;
modify_set(MODIFY_SYMS);
return 0;
}
if (!strcasecmp(subcmd, "del")) {
char *name = get_arg(arg);
if (!name) {
fprintf(stderr, "sym: need a name to delete "
"symbol table entries\n");
return -1;
}
if (stab_del(name) < 0)
return -1;
modify_set(MODIFY_SYMS);
return 0;
}
if (!strcasecmp(subcmd, "load"))
return cmd_sym_load_add(1, arg);
if (!strcasecmp(subcmd, "add"))
return cmd_sym_load_add(0, arg);
if (!strcasecmp(subcmd, "savemap"))
return cmd_sym_savemap(arg);
if (!strcasecmp(subcmd, "find")) {
char *expr = get_arg(arg);
if (!expr) {
stab_enum(printsym);
return 0;
}
return stab_re_search(expr, printsym) < 0 ? -1 : 0;
}
fprintf(stderr, "sym: unknown subcommand: %s\n", subcmd);
return -1;
}
static struct command command_sym = {
.name = "sym",
.func = cmd_sym,
.help =
"sym clear\n"
" Clear the symbol table.\n"
"sym set <name> <value>\n"
" Set or overwrite the value of a symbol.\n"
"sym del <name>\n"
" Delete a symbol from the symbol table.\n"
"sym load <filename>\n"
" Load symbols from the given file.\n"
"sym add <filename>\n"
" Load additional symbols from the given file.\n"
"sym savemap <filename>\n"
" Save the current symbols to a BSD-style symbol file.\n"
"sym find <regex>\n"
" Search for symbols by regular expression.\n"
};
static int lookup_token(const char *name, int *value)
{
u_int16_t val;
if (stab_get(name, &val) < 0)
return -1;
*value = val;
return 0;
}
int stab_init(void)
{
sym_table = btree_alloc(&sym_table_def);
if (!sym_table) {
fprintf(stderr, "stab: failed to allocate symbol table\n");
return -1;
}
addr_table = btree_alloc(&addr_table_def);
if (!addr_table) {
fprintf(stderr, "stab: failed to allocate address table\n");
btree_free(sym_table);
return -1;
}
set_token_func(lookup_token);
register_command(&command_eval);
register_command(&command_sym);
return 0;
}
void stab_exit(void)
{
btree_free(sym_table);
btree_free(addr_table);
}

33
stab.h
View File

@ -31,38 +31,7 @@ void stab_exit(void);
void stab_clear(void);
/* Set a symbol in the table. Returns 0 on success, or -1 on error. */
int stab_set(const char *name, u_int16_t value);
/* Fetch the value of a symbol. Returns 0 on success, or -1 if no such
* symbol exists.
*/
int stab_get(const char *name, u_int16_t *value);
/* Delete a symbol from the symbol table.
*
* Returns 0 if successful, -1 if no such symbol exists.
*/
int stab_del(const char *name);
/* Enumerate all symbols. Returns total symbol count, or -1 if a callback
* invocation returns an error.
*/
typedef int (*stab_callback_t)(const char *name, u_int16_t value);
int stab_enum(stab_callback_t cb);
/* Search for a symbol by supplying a regular expression. The given
* callback is invoked for each symbol matching the regex. Returns the
* total number of symbols found, or -1 if an error occurs.
*/
int stab_re_search(const char *regex, stab_callback_t cb);
/* Parse an address expression and return an address. The text may be an
* address, a symbol name or a combination (using + or -).
*
* Returns 0 if parsed successfully, -1 if an error occurs.
*/
int stab_parse(const char *text, int *addr);
int stab_set(const char *name, int value);
/* Take an address and find the nearest symbol and offset (always
* non-negative).

View File

@ -20,7 +20,6 @@
#include <string.h>
#include <ctype.h>
#include "binfile.h"
#include "stab.h"
int symmap_check(FILE *in)
{
@ -46,7 +45,7 @@ int symmap_check(FILE *in)
return spc_count >= 2;
}
int symmap_syms(FILE *in)
int symmap_syms(FILE *in, symfunc_t cb)
{
rewind(in);
char buf[128];
@ -61,7 +60,7 @@ int symmap_syms(FILE *in)
if (addr && name) {
int addr_val = strtoul(addr, NULL, 16);
if (stab_set(name, addr_val) < 0)
if (cb(name, addr_val) < 0)
return -1;
}
}

528
util.c
View File

@ -33,10 +33,10 @@
#include <readline/history.h>
#endif
#include "stab.h"
#include "util.h"
static struct option *option_list;
static struct command *command_list;
void register_option(struct option *o)
{
@ -55,7 +55,24 @@ static struct option *find_option(const char *name)
return NULL;
}
static int interactive_call;
static struct command *find_command(const char *name)
{
struct command *c;
for (c = command_list; c; c = c->next)
if (!strcasecmp(c->name, name))
return c;
return NULL;
}
void register_command(struct command *c)
{
c->next = command_list;
command_list = c;
}
static int interactive_call = 1;
int is_interactive(void)
{
@ -89,17 +106,6 @@ char *get_arg(char **text)
return start;
}
const struct command *find_command(const char *name)
{
int i;
for (i = 0; all_commands[i].name; i++)
if (!strcasecmp(name, all_commands[i].name))
return &all_commands[i];
return NULL;
}
int process_command(char *arg, int interactive)
{
const char *cmd_text;
@ -131,6 +137,141 @@ int process_command(char *arg, int interactive)
return 0;
}
const char *type_text(option_type_t type)
{
switch (type) {
case OPTION_BOOLEAN:
return "boolean";
case OPTION_NUMERIC:
return "numeric";
case OPTION_TEXT:
return "text";
}
return "unknown";
}
static const char *name_list[128];
static int num_names;
static int name_max_len;
static void name_start(void)
{
num_names = 0;
name_max_len = 0;
}
static void name_push(const char *text)
{
if (num_names < ARRAY_LEN(name_list)) {
int len = strlen(text);
name_list[num_names++] = text;
if (len > name_max_len)
name_max_len = len;
}
}
static int compare_name(const void *left, const void *right)
{
return strcasecmp(*(const char *const *)left,
*(const char *const *)right);
}
static void name_list_show(void)
{
int i;
int max_len = name_max_len + 2;
int rows, cols;
qsort(name_list, num_names, sizeof(name_list[0]),
compare_name);
cols = 72 / max_len;
rows = (num_names + cols - 1) / cols;
for (i = 0; i < rows; i++) {
int j;
printf(" ");
for (j = 0; j < cols; j++) {
int k = j * rows + i;
if (k >= num_names)
break;
printf("%s", name_list[k]);
for (k = strlen(name_list[k]); k < max_len; k++)
printf(" ");
}
printf("\n");
}
}
static int cmd_help(char **arg)
{
const char *topic = get_arg(arg);
if (topic) {
const struct command *cmd = find_command(topic);
const struct option *opt = find_option(topic);
if (cmd) {
printf("COMMAND: %s\n", cmd->name);
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
printf("OPTION: %s (%s)\n", opt->name,
type_text(opt->type));
fputs(opt->help, stdout);
}
if (!(cmd || opt)) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
} else {
const struct command *cmd;
const struct option *opt;
name_start();
for (cmd = command_list; cmd; cmd = cmd->next)
name_push(cmd->name);
printf("Available commands:\n");
name_list_show();
printf("\n");
name_start();
for (opt = option_list; opt; opt = opt->next)
name_push(opt->name);
printf("Available options:\n");
name_list_show();
printf("\n");
printf("Type \"help <topic>\" for more information.\n");
printf("Press Ctrl+D to quit.\n");
}
return 0;
}
static struct command command_help = {
.func = cmd_help,
.name = "help",
.help =
"help [command]\n"
" Without arguments, displays a list of commands. With a command\n"
" name as an argument, displays help for that command.\n"
};
#ifndef USE_READLINE
#define LINE_BUF_SIZE 128
@ -165,177 +306,26 @@ static char *readline(const char *prompt)
void reader_loop(void)
{
for (;;) {
char *buf = readline("(mspdebug) ");
printf("\n");
cmd_help(NULL);
printf("\n");
if (!buf)
break;
do {
for (;;) {
char *buf = readline("(mspdebug) ");
add_history(buf);
process_command(buf, 1);
free(buf);
}
if (!buf)
break;
add_history(buf);
process_command(buf, 1);
free(buf);
}
} while (modify_prompt(MODIFY_ALL));
printf("\n");
}
const char *type_text(option_type_t type)
{
switch (type) {
case OPTION_BOOLEAN:
return "boolean";
case OPTION_NUMERIC:
return "numeric";
case OPTION_TEXT:
return "text";
}
return "unknown";
}
int cmd_help(char **arg)
{
char *topic = get_arg(arg);
if (topic) {
const struct command *cmd = find_command(topic);
const struct option *opt = find_option(topic);
if (cmd) {
printf("COMMAND: %s\n", cmd->name);
fputs(cmd->help, stdout);
if (opt)
printf("\n");
}
if (opt) {
printf("OPTION: %s (%s)\n", opt->name,
type_text(opt->type));
fputs(opt->help, stdout);
}
if (!(cmd || opt)) {
fprintf(stderr, "help: unknown command: %s\n", topic);
return -1;
}
} else {
int i;
int max_len = 0;
int rows, cols;
int total = 0;
for (i = 0; all_commands[i].name; i++) {
int len = strlen(all_commands[i].name);
if (len > max_len)
max_len = len;
total++;
}
max_len += 2;
cols = 72 / max_len;
rows = (total + cols - 1) / cols;
printf("Available commands:\n");
for (i = 0; i < rows; i++) {
int j;
printf(" ");
for (j = 0; j < cols; j++) {
int k = j * rows + i;
const struct command *cmd = &all_commands[k];
if (k >= total)
break;
printf("%s", cmd->name);
for (k = strlen(cmd->name); k < max_len; k++)
printf(" ");
}
printf("\n");
}
printf("Type \"help <command>\" for more information.\n");
printf("Press Ctrl+D to quit.\n");
}
return 0;
}
static char token_buf[64];
static int token_len;
static int token_mult;
static int token_sum;
static int token_add(void)
{
int i;
u_int16_t value;
if (!token_len)
return 0;
token_buf[token_len] = 0;
token_len = 0;
/* Is it a decimal? */
i = 0;
while (token_buf[i] && isdigit(token_buf[i]))
i++;
if (!token_buf[i]) {
token_sum += token_mult * atoi(token_buf);
return 0;
}
/* Is it hex? */
if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') {
token_sum += token_mult * strtol(token_buf + 2, NULL, 16);
return 0;
}
/* Look up the name in the symbol table */
if (!stab_get(token_buf, &value)) {
token_sum += token_mult * value;
return 0;
}
fprintf(stderr, "unknown token: %s\n", token_buf);
return -1;
}
int addr_exp(const char *text, int *addr)
{
token_len = 0;
token_mult = 1;
token_sum = 0;
while (*text) {
if (isalnum(*text) || *text == '_' || *text == '$' ||
*text == '.' || *text == ':') {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else {
if (token_add() < 0)
return -1;
if (*text == '+')
token_mult = 1;
if (*text == '-')
token_mult = -1;
}
text++;
}
if (token_add() < 0)
return -1;
*addr = token_sum & 0xffff;
return 0;
}
static void display_option(const struct option *o)
{
printf("%32s = ", o->name);
@ -379,7 +369,7 @@ static int parse_option(struct option *o, const char *word)
return 0;
}
int cmd_opt(char **arg)
static int cmd_opt(char **arg)
{
const char *opt_text = get_arg(arg);
struct option *opt = NULL;
@ -411,6 +401,62 @@ int cmd_opt(char **arg)
return 0;
}
static struct command command_opt = {
.name = "opt",
.func = cmd_opt,
.help =
"opt [name] [value]\n"
" Query or set option variables. With no arguments, displays all\n"
" available options.\n"
};
static int cmd_read(char **arg)
{
char *filename = get_arg(arg);
FILE *in;
char buf[1024];
if (!filename) {
fprintf(stderr, "read: filename must be specified\n");
return -1;
}
in = fopen(filename, "r");
if (!in) {
fprintf(stderr, "read: can't open %s: %s\n",
filename, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), in)) {
char *cmd = buf;
while (*cmd && isspace(*cmd))
cmd++;
if (*cmd == '#')
continue;
if (process_command(cmd, 0) < 0) {
fprintf(stderr, "read: error processing %s\n",
filename);
fclose(in);
return -1;
}
}
fclose(in);
return 0;
}
static struct command command_read = {
.name = "read",
.func = cmd_read,
.help =
"read <filename>\n"
" Read commands from a file and evaluate them.\n"
};
static struct option option_color = {
.name = "color",
.type = OPTION_BOOLEAN,
@ -432,7 +478,6 @@ static void sigint_handler(int signum)
ctrlc_flag = 1;
}
void parse_init(void)
{
const static struct sigaction siga = {
@ -441,7 +486,12 @@ void parse_init(void)
};
sigaction(SIGINT, &siga, NULL);
register_option(&option_color);
register_command(&command_help);
register_command(&command_opt);
register_command(&command_read);
}
void hexdump(int addr, const u_int8_t *data, int len)
@ -605,3 +655,121 @@ int ctrlc_check(void)
{
return ctrlc_flag;
}
static char token_buf[64];
static int token_len;
static int token_mult;
static int token_sum;
static token_func_t token_func;
static int token_add(void)
{
int i;
int value;
if (!token_len)
return 0;
token_buf[token_len] = 0;
token_len = 0;
/* Is it a decimal? */
i = 0;
while (token_buf[i] && isdigit(token_buf[i]))
i++;
if (!token_buf[i]) {
token_sum += token_mult * atoi(token_buf);
return 0;
}
/* Is it hex? */
if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') {
token_sum += token_mult * strtol(token_buf + 2, NULL, 16);
return 0;
}
/* Look up the name in the symbol table */
if (token_func && !token_func(token_buf, &value)) {
token_sum += token_mult * value;
return 0;
}
fprintf(stderr, "unknown token: %s\n", token_buf);
return -1;
}
void set_token_func(token_func_t func)
{
token_func = func;
}
int addr_exp(const char *text, int *addr)
{
token_len = 0;
token_mult = 1;
token_sum = 0;
while (*text) {
if (isalnum(*text) || *text == '_' || *text == '$' ||
*text == '.' || *text == ':') {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else {
if (token_add() < 0)
return -1;
if (*text == '+')
token_mult = 1;
if (*text == '-')
token_mult = -1;
}
text++;
}
if (token_add() < 0)
return -1;
*addr = token_sum & 0xffff;
return 0;
}
static int modify_flags;
void modify_set(int flags)
{
modify_flags |= flags;
}
void modify_clear(int flags)
{
modify_flags &= ~flags;
}
int modify_prompt(int flags)
{
char buf[32];
if (!(interactive_call && (modify_flags & flags)))
return 0;
for (;;) {
printf("Symbols have not been saved since modification. "
"Continue (y/n)? ");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
printf("\n");
return 1;
}
if (toupper(buf[0]) == 'Y')
return 0;
if (toupper(buf[0]) == 'N')
return 1;
printf("Please answer \"y\" or \"n\".\n");
}
return 0;
}

34
util.h
View File

@ -27,10 +27,12 @@ struct command {
const char *name;
int (*func)(char **arg);
const char *help;
struct command *next;
};
/* The global command table, defined in main.c */
extern const struct command all_commands[];
/* Add a command to the global command table */
void register_command(struct command *c);
/* Retrieve the next word from a pointer to the rest of a command
* argument buffer. Returns NULL if no more words.
@ -52,12 +54,9 @@ int process_command(char *arg, int interactive);
*/
void reader_loop(void);
/* Help command. Displays a command list with no argument, or help
* for a particular command.
/* Print an ANSI colour code, if the colour option has been set by
* the user.
*/
int cmd_help(char **arg);
/* Colourized output has been requested by the user. */
int colorize(const char *text);
/* Return non-zero if executing in an interactive context. */
@ -66,7 +65,22 @@ int is_interactive(void);
/* Parse an address expression, storing the result in the integer
* pointed to. Returns 0 if parsed successfully, -1 if not.
*/
typedef int (*token_func_t)(const char *text, int *value);
int addr_exp(const char *text, int *value);
void set_token_func(token_func_t func);
/* Mark/unmark items as modified. The modify_prompt function, when
* called in interactive context, prompts the user before continuing
* if any of the items specified are modified. If the user elects
* to abort the operation, it returns non-zero.
*/
#define MODIFY_SYMS 0x01
#define MODIFY_ALL 0x01
void modify_set(int flags);
void modify_clear(int flags);
int modify_prompt(int flags);
/* Options interface. Options may be declared by any module and
* registered with the parser.
@ -99,14 +113,10 @@ struct option {
*/
void register_option(struct option *o);
/* Command function for settings options. With no arguments, displays
* a list of available options.
*/
int cmd_opt(char **arg);
/* Initialise the parser, and register built-ins. */
void parse_init(void);
/* Display a canonical hexdump */
void hexdump(int addr, const u_int8_t *data, int len);
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))