diff --git a/Makefile b/Makefile index cfdc87f..3eaeda6 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ else endif # Mac OS X/MacPorts stuff -UNAME :=$(shell sh -c 'uname -s') +UNAME := $(shell sh -c 'uname -s') ifeq ($(UNAME),Darwin) MACPORTS_CFLAGS = -I/opt/local/include MACPORTS_LDFLAGS = -L/opt/local/lib @@ -56,7 +56,7 @@ install: mspdebug mspdebug.man mspdebug: main.o fet.o rf2500.o dis.o uif.o olimex.o ihex.o elf32.o stab.o \ util.o bsl.o sim.o symmap.o gdb.o btree.o rtools.o sym.o devcmd.o \ cproc.o vector.o cproc_util.o expr.o fet_error.o binfile.o fet_db.o \ - usbutil.o titext.o srec.o device.o + usbutil.o titext.o srec.o device.o coff.o $(CC) $(LDFLAGS) $(MACPORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: diff --git a/binfile.c b/binfile.c index 4f4c459..0c31582 100644 --- a/binfile.c +++ b/binfile.c @@ -23,6 +23,7 @@ #include "symmap.h" #include "titext.h" #include "srec.h" +#include "coff.h" struct file_format { int (*check)(FILE *in); @@ -51,6 +52,11 @@ static const struct file_format formats[] = { { .check = srec_check, .extract = srec_extract + }, + { + .check = coff_check, + .extract = coff_extract, + .syms = coff_syms } }; diff --git a/coff.c b/coff.c new file mode 100644 index 0000000..627acc8 --- /dev/null +++ b/coff.c @@ -0,0 +1,397 @@ +/* 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 "coff.h" +#include "util.h" + +struct coff_header { + uint16_t version; + int sec_count; + uint32_t timestamp; + int stab_start; + int stab_count; + int opt_bytes; + uint16_t flags; + uint16_t target_id; +}; + +/* Header sizes */ +#define FILE_HEADER_SIZE 22 +#define OPT_HEADER_SIZE 28 +#define SHDR_SIZE 48 +#define STAB_ENTRY_SIZE 18 + +/* Bits in the flags field */ +#define F_RELFLG 0x0001 +#define F_EXEC 0x0002 +#define F_LSYMS 0x0008 +#define F_LITTLE 0x0100 +#define F_BIG 0x0200 +#define F_SYMMERGE 0x1000 + +/* Section header flags */ +#define STYP_REG 0x00000000 +#define STYP_DSECT 0x00000001 +#define STYP_NOLOAD 0x00000002 +#define STYP_GROUP 0x00000004 +#define STYP_PAD 0x00000008 +#define STYP_COPY 0x00000010 +#define STYP_TEXT 0x00000020 +#define STYP_DATA 0x00000040 +#define STYP_BSS 0x00000080 +#define STYP_BLOCK 0x00001000 +#define STYP_PASS 0x00002000 +#define STYP_CLINK 0x00004000 +#define STYP_VECTOR 0x00008000 +#define STYP_PADDED 0x00010000 + +/* Symbol storage classes */ +#define C_NULL 0 +#define C_AUTO 1 +#define C_EXT 2 +#define C_STAT 3 +#define C_REG 4 +#define C_EXTREF 5 +#define C_LABEL 6 +#define C_ULABEL 7 +#define C_MOS 8 +#define C_ARG 9 +#define C_STRTAG 10 +#define C_MOU 11 +#define C_UNTAG 12 +#define C_TPDEF 13 +#define C_USTATIC 14 +#define C_ENTAG 15 +#define C_MOE 16 +#define C_REGPARM 17 +#define C_FIELD 18 +#define C_UEXT 19 +#define C_STATLAB 20 +#define C_EXTLAB 21 +#define C_VARARG 22 +#define C_BLOCK 100 +#define C_FCN 101 +#define C_EOS 102 +#define C_FILE 103 +#define C_LINE 104 + +/* MSP430 magic number */ +#define MSP430_MAGIC 0x00a0 + +static int read_block(FILE *in, int offset, int size, void *buf) +{ + int len; + + if (size < 0) { + fprintf(stderr, "coff: invalid size: %d\n", size); + return -1; + } + + if (fseek(in, offset, SEEK_SET) < 0) { + fprintf(stderr, "coff: can't seek to offset %d: %s\n", + offset, strerror(errno)); + return -1; + } + + len = fread(buf, 1, size, in); + if (len < 0) { + fprintf(stderr, "coff: can't read %d bytes from " + "offset %d: %s\n", + size, offset, strerror(errno)); + return -1; + } + + if (len < size) { + fprintf(stderr, "coff: can't read %d bytes from " + "offset %d: short read\n", + size, offset); + return -1; + } + + return 0; +} + +static void parse_header(const uint8_t *data, struct coff_header *hdr) +{ + hdr->version = LE_WORD(data, 0); + hdr->sec_count = LE_WORD(data, 2); + hdr->timestamp = LE_LONG(data, 4); + hdr->stab_start = LE_LONG(data, 8); + hdr->stab_count = LE_LONG(data, 12); + hdr->opt_bytes = LE_WORD(data, 16); + hdr->flags = LE_WORD(data, 18); + hdr->target_id = LE_WORD(data, 20); +} + +static int read_header(FILE *in, struct coff_header *hdr) +{ + uint8_t hdr_data[FILE_HEADER_SIZE]; + + if (read_block(in, 0, FILE_HEADER_SIZE, hdr_data) < 0) { + fprintf(stderr, "coff: failed to extract COFF header\n"); + return -1; + } + + parse_header(hdr_data, hdr); + return 0; +} + +int coff_check(FILE *in) +{ + uint8_t data[FILE_HEADER_SIZE]; + + rewind(in); + if (fread(data, 1, FILE_HEADER_SIZE, in) != FILE_HEADER_SIZE) + return 0; + + return data[20] == 0xa0 && !data[21]; +} + +static int read_sechdrs(FILE *in, const struct coff_header *hdr, + uint8_t **ret_tab) +{ + uint8_t *table; + int alloc_size = SHDR_SIZE * hdr->sec_count; + + if (!hdr->sec_count) { + *ret_tab = NULL; + return 0; + } + + table = malloc(alloc_size); + if (!table) { + perror("coff: can't allocate memory for section headers"); + return -1; + } + + if (read_block(in, hdr->opt_bytes + FILE_HEADER_SIZE, + SHDR_SIZE * hdr->sec_count, table) < 0) { + fprintf(stderr, "coff: can't read section headers\n"); + free(table); + return -1; + } + + *ret_tab = table; + return hdr->sec_count; +} + +static int load_section(FILE *in, uint32_t addr, uint32_t offset, + uint32_t size, + binfile_imgcb_t cb, void *user_data) +{ + uint8_t *section; + + if (!size) + return 0; + + section = malloc(size); + if (!section) { + fprintf(stderr, "coff: couldn't allocate memory for " + "section at 0x%x: %s\n", offset, strerror(errno)); + return -1; + } + + if (read_block(in, offset, size, section) < 0) { + fprintf(stderr, "coff: couldn't read section at 0x%x\n", + offset); + free(section); + return -1; + } + + if (cb(user_data, addr, section, size) < 0) { + free(section); + return -1; + } + + free(section); + return 0; +} + +int coff_extract(FILE *in, binfile_imgcb_t cb, void *user_data) +{ + struct coff_header hdr; + uint8_t *shdrs; + int ret = 0; + int i; + + if (read_header(in, &hdr) < 0) + return -1; + + if (read_sechdrs(in, &hdr, &shdrs) < 0) + return -1; + + for (i = 0; i < hdr.sec_count; i++) { + uint8_t *header = shdrs + SHDR_SIZE * i; + uint32_t flags = LE_LONG(header, 40); + + if (((flags & STYP_TEXT) || (flags & STYP_DATA)) && + !(flags & STYP_NOLOAD)) { + uint32_t addr = LE_LONG(header, 8); + uint32_t offset = LE_LONG(header, 20); + uint32_t size = LE_LONG(header, 16); + + if (load_section(in, addr, offset, size, + cb, user_data) < 0) { + fprintf(stderr, "coff: error while loading " + "section %d\n", i); + ret = -1; + break; + } + } + } + + if (shdrs) + free(shdrs); + + return ret; +} + +static int read_strtab(FILE *in, const struct coff_header *hdr, + char **ret_tab) +{ + char *strtab; + int file_size; + int alloc_size; + int strtab_len; + int strtab_start = hdr->stab_count * STAB_ENTRY_SIZE + hdr->stab_start; + + if (fseek(in, 0, SEEK_END) < 0) { + fprintf(stderr, "coff: can't seek to end\n"); + return -1; + } + + file_size = ftell(in); + strtab_len = file_size - strtab_start; + + if (strtab_len < 0) { + fprintf(stderr, "coff: invalid string table size\n"); + return -1; + } + + if (!strtab_len) { + *ret_tab = NULL; + return 0; + } + + alloc_size = strtab_len + 1; + strtab = malloc(alloc_size); + if (!strtab) { + perror("coff: can't allocate memory for string table"); + return -1; + } + + if (read_block(in, strtab_start, strtab_len, strtab) < 0) { + fprintf(stderr, "coff: failed to read string table\n"); + free(strtab); + return -1; + } + + strtab[strtab_len] = 0; + *ret_tab = strtab; + return strtab_len; +} + +static int read_symtab(FILE *in, const struct coff_header *hdr, + uint8_t **ret_tab) +{ + uint8_t *table; + int alloc_size = STAB_ENTRY_SIZE * hdr->stab_count; + + if (!hdr->stab_count) { + *ret_tab = NULL; + return 0; + } + + table = malloc(alloc_size); + if (!table) { + perror("coff: failed to allocate memory for symbol table"); + return -1; + } + + if (read_block(in, hdr->stab_start, + STAB_ENTRY_SIZE * hdr->stab_count, table) < 0) { + fprintf(stderr, "coff: failed to read symbol table\n"); + free(table); + return -1; + } + + *ret_tab = table; + return hdr->stab_count; +} + +int coff_syms(FILE *in, stab_t stab) +{ + struct coff_header hdr; + char *strtab; + uint8_t *symtab; + int strtab_len; + int i; + int ret = 0; + + if (read_header(in, &hdr) < 0) + return -1; + + strtab_len = read_strtab(in, &hdr, &strtab); + if (strtab_len < 0) + return -1; + + if (read_symtab(in, &hdr, &symtab) < 0) { + if (strtab) + free(strtab); + return -1; + } + + for (i = 0; i < hdr.stab_count; i++) { + uint8_t *entry = symtab + i * STAB_ENTRY_SIZE; + uint32_t value = LE_LONG(entry, 8); + int storage_class = entry[16]; + char namebuf[9]; + const char *name = NULL; + + if (LE_LONG(entry, 0)) { + memcpy(namebuf, entry, 8); + namebuf[8] = 0; + name = namebuf; + } else { + uint32_t offset = LE_LONG(entry, 4); + + if (offset >= 4 && offset < strtab_len) + name = strtab + offset; + } + + if ((storage_class == C_EXT || storage_class == C_LABEL) && + stab_set(stab, name, value) < 0) { + fprintf(stderr, "coff: failed to insert symbol\n"); + ret = -1; + break; + } + + /* Skip auxiliary entries */ + i += entry[17]; + } + + if (symtab) + free(symtab); + if (strtab) + free(strtab); + + return ret; +} diff --git a/coff.h b/coff.h new file mode 100644 index 0000000..cfa0d75 --- /dev/null +++ b/coff.h @@ -0,0 +1,28 @@ +/* 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 COFF_H_ +#define COFF_H_ + +#include "binfile.h" + +int coff_check(FILE *in); +int coff_extract(FILE *in, binfile_imgcb_t cb, void *user_data); +int coff_syms(FILE *in, stab_t stab); + +#endif