diff --git a/Makefile b/Makefile index 57b4258..0c6ee48 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ clean: .SUFFIXES: .c .o -mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o +mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o stab.o $(CC) $(CFLAGS) -o $@ $^ -lusb .c.o: diff --git a/binfile.h b/binfile.h index bccc442..fa25787 100644 --- a/binfile.h +++ b/binfile.h @@ -32,5 +32,6 @@ 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); #endif diff --git a/elf32.c b/elf32.c index fb9c51a..f001d5f 100644 --- a/elf32.c +++ b/elf32.c @@ -20,6 +20,7 @@ #include #include #include "binfile.h" +#include "stab.h" #define EM_MSP430 0x69 @@ -94,12 +95,12 @@ static int read_shdr(FILE *in) for (i = 0; i < file_ehdr.e_shnum; i++) { if (fseek(in, i * file_ehdr.e_shentsize + file_ehdr.e_shoff, SEEK_SET) < 0) { - fprintf(stderr, "elf32: can't seek to phdr %d\n", i); + fprintf(stderr, "elf32: can't seek to shdr %d\n", i); return -1; } if (fread(&file_shdrs[i], sizeof(file_shdrs[0]), 1, in) < 0) { - fprintf(stderr, "elf32: can't read phdr %d: %s\n", + fprintf(stderr, "elf32: can't read shdr %d: %s\n", i, strerror(errno)); return -1; } @@ -152,10 +153,8 @@ static int feed_section(FILE *in, int offset, int size, imgfunc_t cb) return 0; } -int elf32_extract(FILE *in, imgfunc_t cb) +static int read_all(FILE *in) { - int i; - if (read_ehdr(in) < 0) return -1; if (file_ehdr.e_machine != EM_MSP430) { @@ -168,6 +167,16 @@ int elf32_extract(FILE *in, imgfunc_t cb) if (read_shdr(in) < 0) return -1; + return 0; +} + +int elf32_extract(FILE *in, imgfunc_t cb) +{ + int i; + + if (read_all(in) < 0) + return -1; + for (i = 0; i < file_ehdr.e_shnum; i++) { Elf32_Shdr *s = &file_shdrs[i]; @@ -190,3 +199,117 @@ int elf32_check(FILE *in) return 1; } + +static Elf32_Shdr *find_shdr(Elf32_Word type) +{ + int i; + + for (i = 0; i < file_ehdr.e_shnum; i++) { + Elf32_Shdr *s = &file_shdrs[i]; + + if (s->sh_type == type) + return s; + } + + return NULL; +} + +static int syms_load_strings(FILE *in, Elf32_Shdr *s) +{ + int len = s->sh_size; + char buf[1024]; + + if (fseek(in, s->sh_offset, SEEK_SET) < 0) { + perror("elf32: can't seek to strings"); + return -1; + } + + while (len) { + int req = sizeof(buf) > len ? len : sizeof(buf); + int count = fread(buf, 1, req, in); + + if (!count) { + fprintf(stderr, "elf32: eof reading strings\n"); + return -1; + } + + if (count < 0) { + perror("elf32: error reading strings"); + return -1; + } + + if (stab_add_string(buf, count) < 0) + return -1; + len -= count; + } + + return 0; +} + +#define N_SYMS 128 + +static int syms_load_syms(FILE *in, Elf32_Shdr *s, int soff) +{ + Elf32_Sym syms[N_SYMS]; + int len = s->sh_size / sizeof(syms[0]); + + if (fseek(in, s->sh_offset, SEEK_SET) < 0) { + perror("elf32: can't seek to symbols"); + return -1; + } + + while (len) { + int req = N_SYMS > len ? len : N_SYMS; + int count = fread(syms, sizeof(syms[0]), req, in); + int i; + + if (!count) { + fprintf(stderr, "elf32: eof reading symbols\n"); + return -1; + } + + if (count < 0) { + perror("elf32: error reading symbols"); + return -1; + } + + for (i = 0; i < count; i++) { + Elf32_Sym *y = &syms[i]; + + if (stab_add_symbol(y->st_name + soff, + y->st_value) < 0) + return -1; + } + len -= count; + } + + return 0; +} + +int elf32_syms(FILE *in) +{ + Elf32_Shdr *s; + int soff; + + if (read_all(in) < 0) + return -1; + + s = find_shdr(SHT_SYMTAB); + if (!s) { + fprintf(stderr, "elf32: no symbol table\n"); + return -1; + } + + if (s->sh_link <= 0 || s->sh_link >= file_ehdr.e_shnum) { + fprintf(stderr, "elf32: no string table\n"); + return -1; + } + + soff = stab_add_string(NULL, 0); + if (syms_load_strings(in, &file_shdrs[s->sh_link]) < 0) + return -1; + if (syms_load_syms(in, s, soff) < 0) + return -1; + + return 0; +} diff --git a/main.c b/main.c index a58aa1e..7277704 100644 --- a/main.c +++ b/main.c @@ -27,6 +27,7 @@ #include "dis.h" #include "fet.h" #include "binfile.h" +#include "stab.h" void hexdump(int addr, const u_int8_t *data, int len) { @@ -431,7 +432,55 @@ static int cmd_prog(char **arg) return result; } +static int cmd_nosyms(char **arg) +{ + stab_clear(); + return 0; +} + +static int cmd_eval(char **arg) +{ + u_int16_t addr; + const char *name; + + if (stab_parse(*arg, &addr) < 0) { + fprintf(stderr, "eval: can't parse: %s\n", *arg); + return -1; + } + + printf("%04x", addr); + if (!stab_find(&addr, &name)) + printf(" = %s+%x", name, addr); + printf("\n"); + + return 0; +} + +static int cmd_syms(char **arg) +{ + FILE *in = fopen(*arg, "r"); + int result = 0; + + if (!in) { + fprintf(stderr, "syms: %s: %s\n", *arg, strerror(errno)); + return -1; + } + + stab_clear(); + + if (elf32_check(in)) + result = elf32_syms(in); + else + fprintf(stderr, "%s: unknown file type\n", *arg); + + fclose(in); + return result; +} + static const struct command all_commands[] = { + {"=", cmd_eval, +"= \n" +" Evaluate an expression using the symbol table.\n"}, {"dis", cmd_dis, "dis
\n" " Disassemble a section of memory.\n"}, @@ -443,9 +492,12 @@ static const struct command all_commands[] = { "md
\n" " Read the specified number of bytes from memory at the given address,\n" " and display a hexdump.\n"}, + {"nosyms", cmd_nosyms, +"nosyms\n" +" Clear the symbol table.\n"}, {"prog", cmd_prog, "prog \n" -" Erase the device and flash the data contained in an Intel HEX file.\n"}, +" Erase the device and flash the data contained in a binary file.\n"}, {"regs", cmd_regs, "regs\n" " Read and display the current register contents.\n"}, @@ -462,6 +514,9 @@ static const struct command all_commands[] = { {"step", cmd_step, "step\n" " Single-step the CPU, and display the register state.\n"}, + {"syms", cmd_syms, +"syms \n" +" Load symbols from the given file.\n"}, }; #define NUM_COMMANDS (sizeof(all_commands) / sizeof(all_commands[0])) diff --git a/stab.c b/stab.c new file mode 100644 index 0000000..717c4dd --- /dev/null +++ b/stab.c @@ -0,0 +1,270 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 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 "stab.h" + +static char *strtab; +static int strtab_len; +static int strtab_cap; + +struct symbol { + int name; + u_int16_t addr; +}; + +struct symvec { + struct symbol *syms; + int len; + int cap; +}; + +static struct symvec by_name; +static struct symvec by_addr; +static int need_sort; + +static void vec_clear(struct symvec *v) +{ + if (v->syms) + free(v->syms); + + v->syms = NULL; + v->len = 0; + v->cap = 0; +} + +void stab_clear(void) +{ + if (strtab) + free(strtab); + strtab = NULL; + strtab_len = 0; + strtab_cap = 0; + + need_sort = 0; + vec_clear(&by_name); + vec_clear(&by_addr); +} + +int stab_add_string(const char *text, int len) +{ + int cap = strtab_cap; + + if (!text || !len) + return strtab_len; + + /* Figure out how big the table needs to be after we add this + * string. + */ + if (!cap) + cap = 1024; + while (strtab_len + len + 1 > cap) + cap *= 2; + + /* Reallocate if necessary */ + if (cap != strtab_cap) { + char *n = realloc(strtab, cap); + + if (!n) { + perror("stab: can't allocate memory for string"); + return -1; + } + + strtab = n; + strtab_cap = cap; + } + + /* Copy it in */ + memcpy(strtab + strtab_len, text, len); + strtab_len += len; + strtab[strtab_len] = 0; + + return strtab_len; +} + +static int vec_push(struct symvec *v, int name, u_int16_t addr) +{ + int cap = v->cap; + struct symbol *s; + + if (!cap) + cap = 64; + while (v->len + 1 > cap) + cap *= 2; + + if (cap != v->cap) { + struct symbol *n = realloc(v->syms, cap * sizeof(v->syms[0])); + + if (!n) { + perror("stab: can't allocate memory for symbol"); + return -1; + } + + v->syms = n; + v->cap = cap; + } + + s = &v->syms[v->len++]; + s->name = name; + s->addr = addr; + + return 0; +} + +int stab_add_symbol(int name, u_int16_t addr) +{ + if (name < 0 || name > strtab_len) { + fprintf(stderr, "stab: symbol name out of bounds: %d\n", + name); + return -1; + } + + need_sort = 1; + if (vec_push(&by_name, name, addr) < 0) + return -1; + if (vec_push(&by_addr, name, addr) < 0) + return -1; + + return 0; +} + +static int cmp_by_name(const void *a, const void *b) +{ + const struct symbol *sa = (const struct symbol *)a; + const struct symbol *sb = (const struct symbol *)b; + + return strcmp(strtab + sa->name, strtab + sb->name); +} + +static int cmp_by_addr(const void *a, const void *b) +{ + const struct symbol *sa = (const struct symbol *)a; + const struct symbol *sb = (const struct symbol *)b; + + if (sa->addr < sb->addr) + return -1; + if (sa->addr > sb->addr) + return 1; + return 0; +} + +static void sort_tables(void) +{ + if (!need_sort) + return; + need_sort = 0; + + qsort(by_name.syms, by_name.len, sizeof(by_name.syms[0]), + cmp_by_name); + qsort(by_addr.syms, by_addr.len, sizeof(by_addr.syms[0]), + cmp_by_addr); +} + +static char token_buf[64]; +static int token_len; +static int token_mult; +static int token_sum; + +static void token_add(void) +{ + int low = 0; + int high = by_name.len - 1; + + if (!token_len) + return; + + token_buf[token_len] = 0; + token_len = 0; + + /* Look up the name in the symbol table */ + while (low <= high) { + int mid = (low + high) / 2; + struct symbol *sym = &by_name.syms[mid]; + int cmp = strcmp(strtab + sym->name, token_buf); + + if (!cmp) { + token_sum += token_mult * (int)sym->addr; + return; + } + + if (cmp < 0) + low = mid + 1; + else + high = mid - 1; + } + + /* Not found? Try to parse it the old way */ + token_sum += token_mult * strtol(token_buf, NULL, 16); +} + +int stab_parse(const char *text, u_int16_t *addr) +{ + token_len = 0; + token_mult = 1; + token_sum = 0; + + sort_tables(); + + while (*text) { + if (isalnum(*text) || *text == '_' || *text == '$') { + if (token_len + 1 < sizeof(token_buf)) + token_buf[token_len++] = *text; + } else { + token_add(); + if (*text == '+') + token_mult = 1; + if (*text == '-') + token_mult = -1; + } + + text++; + } + + token_add(); + *addr = token_sum; + return 0; +} + +int stab_find(u_int16_t *addr, const char **name) +{ + int low = 0; + int high = by_addr.len - 1; + + sort_tables(); + + while (low <= high) { + int mid = (low + high) / 2; + struct symbol *sym = &by_addr.syms[mid]; + + if (sym->addr > *addr) { + high = mid - 1; + } else if (mid + 1 > by_addr.len || + by_addr.syms[mid + 1].addr > *addr) { + *addr -= sym->addr; + *name = strtab + sym->name; + return 0; + } else { + low = mid + 1; + } + } + + return -1; +} diff --git a/stab.h b/stab.h new file mode 100644 index 0000000..cc04e33 --- /dev/null +++ b/stab.h @@ -0,0 +1,57 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 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 STAB_H_ +#define STAB_H_ + +#include + +/* Reset the symbol table (delete all symbols) */ +void stab_clear(void); + +/* Add a block of text to the string table. On success, returns the new + * size of the string table. Returns -1 on error. You can fetch the table's + * current size by calling stab_add_string(NULL, 0); + */ +int stab_add_string(const char *text, int len); + +/* Symbol types. Symbols are divided into a fixed number of classes for + * query purposes. + */ +#define STAB_TYPE_CODE 0x01 +#define STAB_TYPE_DATA 0x02 +#define STAB_TYPE_ALL 0x03 + +/* Add a symbol to the table. The name is specified as an offset into + * the string table. + * + * Returns 0 on success, or -1 if an error occurs. + */ +int stab_add_symbol(int name, u_int16_t addr); + +/* Parse a symbol name 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, u_int16_t *addr); + +/* Take an address and find the nearest symbol. */ +int stab_find(u_int16_t *addr, const char **name); + +#endif