diff --git a/Makefile b/Makefile index d02e125..e8dc5ef 100644 --- a/Makefile +++ b/Makefile @@ -40,8 +40,8 @@ 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 + bsl.o sim.o symmap.o gdb.o btree.o $(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: - $(CC) $(CFLAGS) $(READLINE_CFLAGS) -O1 -Wall -o $@ -c $*.c + $(CC) $(CFLAGS) $(READLINE_CFLAGS) -O1 -Wall -ggdb -o $@ -c $*.c diff --git a/btree.c b/btree.c new file mode 100644 index 0000000..f1f2ada --- /dev/null +++ b/btree.c @@ -0,0 +1,767 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009, 2010 Daniel Beer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "btree.h" + +#define MAX_HEIGHT 16 + +/* Btree pages consist of the following: a page header (struct btree_page), + * followed by a block of memory consisting of: + * + * For a leaf node: + * An array of N keys, then an array of N data. + * + * For a non-leaf node: + * An array of N keys, then an array of N struct btree_page *. + * + * Where N is the branch factor. + */ +struct btree_page { + int height; + int num_children; + struct btree *owner; + const struct btree_def *def; +}; + +#define PAGE_KEY(p, i) \ + (((char *)(p)) + sizeof(struct btree_page) + \ + (i) * (p)->def->key_size) +#define PAGE_DATA(p, i) \ + (((char *)(p)) + sizeof(struct btree_page) + \ + (p)->def->branches * (p)->def->key_size + \ + (i) * (p)->def->data_size) +#define PAGE_PTR(p, i) \ + ((struct btree_page **) \ + (((char *)(p)) + sizeof(struct btree_page) + \ + (p)->def->branches * (p)->def->key_size + \ + (i) * sizeof(struct btree_page *))) \ + +struct btree { + const struct btree_def *def; + struct btree_page *root; + + struct btree_page *path[MAX_HEIGHT]; + int slot[MAX_HEIGHT]; +}; + +/************************************************************************ + * Debugging + */ + +#ifdef DEBUG_BTREE + +static void check_page(struct btree_page *p, + const void *lbound, const void *ubound, + int height) +{ + const struct btree_def *def = p->def; + int i; + + assert (p); + assert (p->height == height); + + if (p != p->owner->root) { + assert (p->num_children >= def->branches / 2); + assert (p->num_children <= def->branches); + } + + for (i = 0; i < p->num_children; i++) { + const void *key = PAGE_KEY(p, i); + const void *next_key = ubound; + + if (i + 1 < p->num_children) + next_key = PAGE_KEY(p, i + 1); + + assert (def->compare(key, lbound) >= 0); + if (next_key) { + assert (def->compare(key, next_key) < 0); + } + if (ubound) { + assert (def->compare(key, ubound) < 0); + } + + if (p->height) + check_page(*PAGE_PTR(p, i), key, next_key, height - 1); + } +} + +static void check_btree(btree_t bt) +{ + assert (bt->def); + + if (bt->root->height) { + assert (bt->root->num_children >= 2); + } + + check_page(bt->root, bt->def->zero, NULL, bt->root->height); +} + +#else +#define check_btree(bt) +#endif + +/************************************************************************ + * B+Tree auxiliary functions + */ + +static void destroy_page(struct btree_page *p) +{ + if (!p) + return; + + if (p->height) { + int i; + + for (i = 0; i < p->num_children; i++) + destroy_page(*PAGE_PTR(p, i)); + } + + free(p); +} + +static struct btree_page *allocate_page(btree_t bt, int height) +{ + const struct btree_def *def = bt->def; + struct btree_page *p; + int size = sizeof(*p) + def->key_size * def->branches; + + if (height) + size += sizeof(struct btree_page *) * def->branches; + else + size += sizeof(def->data_size) * def->branches; + + p = malloc(size); + if (!p) { + fprintf(stderr, "btree: couldn't allocate page: %s\n", + strerror(errno)); + return NULL; + } + + memset(p, 0, size); + p->def = bt->def; + p->owner = bt; + p->height = height; + + return p; +} + +static void split_page(struct btree_page *op, struct btree_page *np) +{ + const struct btree_def *def = op->def; + btree_t bt = op->owner; + const int halfsize = def->branches / 2; + + assert (op->num_children == def->branches); + + memcpy(PAGE_KEY(np, 0), PAGE_KEY(op, halfsize), + halfsize * def->key_size); + + if (op->height) + memcpy(PAGE_PTR(np, 0), PAGE_PTR(op, halfsize), + halfsize * sizeof(struct btree_page *)); + else + memcpy(PAGE_DATA(np, 0), PAGE_DATA(op, halfsize), + halfsize * def->data_size); + + op->num_children = halfsize; + np->num_children = halfsize; + + /* Fix up the cursor if we split an active page */ + if (bt->slot[0] >= 0 && bt->path[op->height] == op && + bt->slot[op->height] > op->num_children) { + bt->slot[op->height] -= op->num_children; + bt->path[op->height] = np; + } +} + +static void insert_data(struct btree_page *p, int s, + const void *key, const void *data) +{ + const struct btree_def *def = p->def; + btree_t bt = p->owner; + int r = p->num_children - s; + + assert (!p->height); + assert (p->num_children < def->branches); + assert (s >= 0 && s <= p->num_children); + + memmove(PAGE_KEY(p, s + 1), PAGE_KEY(p, s), + r * def->key_size); + memmove(PAGE_DATA(p, s + 1), PAGE_DATA(p, s), + r * def->data_size); + + memcpy(PAGE_KEY(p, s), key, def->key_size); + memcpy(PAGE_DATA(p, s), data, def->data_size); + p->num_children++; + + /* Fix up the cursor if we inserted before it, or if we're inserting + * a pointer to the cursor data itself (as in a borrow). + */ + if (bt->slot[0] >= 0) { + if (data == PAGE_DATA(bt->path[0], bt->slot[0])) { + bt->path[0] = p; + bt->slot[0] = s; + } else if (bt->path[0] == p && s <= bt->slot[0]) { + bt->slot[0]++; + } + } +} + +static void insert_ptr(struct btree_page *p, int s, + const void *key, struct btree_page *ptr) +{ + const struct btree_def *def = p->def; + btree_t bt = p->owner; + int r = p->num_children - s; + + assert (p->height); + assert (p->num_children < def->branches); + assert (s >= 0 && s <= p->num_children); + + memmove(PAGE_KEY(p, s + 1), PAGE_KEY(p, s), + r * def->key_size); + memmove(PAGE_PTR(p, s + 1), PAGE_PTR(p, s), + r * sizeof(struct btree_page *)); + + memcpy(PAGE_KEY(p, s), key, def->key_size); + *PAGE_PTR(p, s) = ptr; + p->num_children++; + + /* Fix up the cursor if we inserted before it, or if we just inserted + * the pointer for the active path (as in a split or borrow). + */ + if (bt->slot[0] >= 0) { + if (ptr == bt->path[p->height - 1]) { + bt->path[p->height] = p; + bt->slot[p->height] = s; + } else if (bt->path[p->height] == p && + s <= bt->slot[p->height]) { + bt->slot[p->height]++; + } + } +} + +static void delete_item(struct btree_page *p, int s) +{ + const struct btree_def *def = p->def; + btree_t bt = p->owner; + int r = p->num_children - s - 1; + + assert (s >= 0 && s < p->num_children); + + memmove(PAGE_KEY(p, s), PAGE_KEY(p, s + 1), + r * def->key_size); + + if (p->height) + memmove(PAGE_PTR(p, s), PAGE_PTR(p, s + 1), + r * sizeof(struct btree_page *)); + else + memmove(PAGE_DATA(p, s), PAGE_DATA(p, s + 1), + r * def->data_size); + + p->num_children--; + + /* Fix up the cursor if we deleted before it */ + if (bt->slot[0] >= 0 && bt->path[p->height] == p && + s <= bt->slot[p->height]) + bt->slot[p->height]--; +} + +static void move_item(struct btree_page *from, int from_pos, + struct btree_page *to, int to_pos) +{ + if (from->height) + insert_ptr(to, to_pos, PAGE_KEY(from, from_pos), + *PAGE_PTR(from, from_pos)); + else + insert_data(to, to_pos, PAGE_KEY(from, from_pos), + PAGE_DATA(from, from_pos)); + + delete_item(from, from_pos); +} + +static void merge_pages(struct btree_page *lower, + struct btree_page *higher) +{ + const struct btree_def *def = lower->def; + btree_t bt = lower->owner; + + assert (lower->num_children + higher->num_children < def->branches); + + memcpy(PAGE_KEY(lower, lower->num_children), + PAGE_KEY(higher, 0), + higher->num_children * def->key_size); + + if (lower->height) + memcpy(PAGE_PTR(lower, lower->num_children), + PAGE_PTR(higher, 0), + higher->num_children * sizeof(struct btree_page *)); + else + memcpy(PAGE_DATA(lower, lower->num_children), + PAGE_DATA(higher, 0), + higher->num_children * def->data_size); + + /* Fix up the cursor if we subsumed an active page */ + if (bt->slot[0] >= 0) { + if (bt->path[higher->height] == higher) { + bt->path[higher->height] = lower; + bt->slot[higher->height] += lower->num_children; + } + } +} + +static int find_key_le(const struct btree_page *p, const void *key) +{ + const struct btree_def *def = p->def; + int i; + + for (i = 0; i < p->num_children; i++) + if (def->compare(key, PAGE_KEY(p, i)) < 0) + return i - 1; + + return p->num_children - 1; +} + +static int trace_path(btree_t bt, const void *key, + struct btree_page **path, int *slot) +{ + const struct btree_def *def = bt->def; + struct btree_page *p = bt->root; + int h; + + for (h = p->height; h >= 0; h--) { + int s = find_key_le(p, key); + + path[h] = p; + slot[h] = s; + + if (h) { + assert (s >= 0); + p = *PAGE_PTR(p, s); + } else if (s >= 0 && !def->compare(key, PAGE_KEY(p, s))) { + return 1; + } + } + + return 0; +} + +static void cursor_first(btree_t bt) +{ + int h; + struct btree_page *p = bt->root; + + if (!bt->root->num_children) { + bt->slot[0] = -1; + return; + } + + for (h = bt->root->height; h >= 0; h--) { + assert (p->num_children > 0); + + bt->path[h] = p; + bt->slot[h] = 0; + + if (h) + p = *PAGE_PTR(p, 0); + } +} + +static void cursor_next(btree_t bt) +{ + int h; + + if (bt->slot[0] < 0) + return; + + /* Ascend until we find a suitable sibling */ + for (h = 0; h <= bt->root->height; h++) { + struct btree_page *p = bt->path[h]; + + if (bt->slot[h] + 1 < p->num_children) { + bt->slot[h]++; + + while (h > 0) { + p = *PAGE_PTR(p, bt->slot[h]); + h--; + bt->slot[h] = 0; + bt->path[h] = p; + } + + return; + } + } + + /* Exhausted all levels */ + bt->slot[0] = -1; +} + +/************************************************************************ + * Public interface + */ + +btree_t btree_alloc(const struct btree_def *def) +{ + btree_t bt; + + if (def->branches < 2 || (def->branches & 1)) { + fprintf(stderr, "btree: invalid branch count: %d\n", + def->branches); + return NULL; + } + + bt = malloc(sizeof(*bt)); + if (!bt) { + fprintf(stderr, "btree: couldn't allocate tree: %s\n", + strerror(errno)); + return NULL; + } + + memset(bt, 0, sizeof(*bt)); + bt->def = def; + bt->slot[0] = -1; + + bt->root = allocate_page(bt, 0); + if (!bt->root) { + fprintf(stderr, "btree: couldn't allocate root node: %s\n", + strerror(errno)); + free(bt); + return NULL; + } + + return bt; +} + +void btree_free(btree_t bt) +{ + check_btree(bt); + destroy_page(bt->root); + free(bt); +} + +void btree_clear(btree_t bt) +{ + struct btree_page *p; + struct btree_page *path_up = 0; + + check_btree(bt); + + /* The cursor will have nothing to point to after this. */ + bt->slot[0] = -1; + + /* First, find the last leaf node, which we can re-use as an + * empty root. + */ + p = bt->root; + while (p->height) { + path_up = p; + p = *PAGE_PTR(p, p->num_children - 1); + } + + /* Unlink it from the tree and then destroy everything else. */ + if (path_up) { + path_up->num_children--; + destroy_page(bt->root); + } + + /* Clear it out and make it the new root */ + p->num_children = 0; + bt->root = p; +} + +int btree_put(btree_t bt, const void *key, const void *data) +{ + const struct btree_def *def = bt->def; + struct btree_page *new_root = NULL; + struct btree_page *path_new[MAX_HEIGHT] = {0}; + struct btree_page *path_old[MAX_HEIGHT] = {0}; + int slot_old[MAX_HEIGHT] = {0}; + int h; + + check_btree(bt); + + /* Find a path down the tree that leads to the page which should + * contain this datum (though the page might be too big to hold it). + */ + if (trace_path(bt, key, path_old, slot_old)) { + /* Special case: overwrite existing item */ + memcpy(PAGE_DATA(path_old[0], slot_old[0]), data, + def->data_size); + return 1; + } + + /* Trace from the leaf up. If the leaf is at its maximum size, it will + * need to split, and cause a pointer to be added in the parent page + * of the same node (which may in turn cause it to split). + */ + for (h = 0; h <= bt->root->height; h++) { + if (path_old[h]->num_children < def->branches) + break; + + path_new[h] = allocate_page(bt, h); + if (!path_new[h]) + goto fail; + } + + /* If the split reaches the top (i.e. the root splits), then we need + * to allocate a new root node. + */ + if (h > bt->root->height) { + if (h >= MAX_HEIGHT) { + fprintf(stderr, "btree: maximum height exceeded\n"); + goto fail; + } + + new_root = allocate_page(bt, h); + if (!new_root) + goto fail; + } + + /* Trace up to one page above the split. At each page that needs + * splitting, copy the top half of keys into the new page. Also, + * insert a key into one of the pages at all pages from the leaf + * to the page above the top of the split. + */ + for (h = 0; h <= bt->root->height; h++) { + int s = slot_old[h] + 1; + struct btree_page *p = path_old[h]; + + /* If there's a split at this level, copy the top half of + * the keys from the old page to the new one. Check to see + * if the position we were going to insert into is in the + * old page or the new one. + */ + if (path_new[h]) { + split_page(path_old[h], path_new[h]); + + if (s > p->num_children) { + s -= p->num_children; + p = path_new[h]; + } + } + + /* Insert the key in the appropriate page */ + if (h) + insert_ptr(p, s, PAGE_KEY(path_new[h - 1], 0), + path_new[h - 1]); + else + insert_data(p, s, key, data); + + /* If there was no split at this level, there's nothing to + * insert higher up, and we're all done. + */ + if (!path_new[h]) + return 0; + } + + /* If we made it this far, the split reached the top of the tree, and + * we need to grow it using the extra page we allocated. + */ + assert (new_root); + + if (bt->slot[0] >= 0) { + /* Fix up the cursor, if active */ + bt->slot[new_root->height] = + bt->path[bt->root->height] == new_root ? 1 : 0; + bt->path[new_root->height] = new_root; + } + + memcpy(PAGE_KEY(new_root, 0), def->zero, def->key_size); + *PAGE_PTR(new_root, 0) = path_old[h - 1]; + memcpy(PAGE_KEY(new_root, 1), PAGE_KEY(path_new[h - 1], 0), + def->key_size); + *PAGE_PTR(new_root, 1) = path_new[h - 1]; + new_root->num_children = 2; + bt->root = new_root; + + return 0; + + fail: + for (h = 0; h <= bt->root->height; h++) + if (path_new[h]) + free(path_new[h]); + return -1; +} + +int btree_delete(btree_t bt, const void *key) +{ + const struct btree_def *def = bt->def; + const int halfsize = def->branches / 2; + struct btree_page *path[MAX_HEIGHT] = {0}; + int slot[MAX_HEIGHT] = {0}; + int h; + + check_btree(bt); + + /* Trace a path to the item to be deleted */ + if (!key) { + if (bt->slot[0] < 0) + return 1; + + memcpy(path, bt->path, sizeof(path)); + memcpy(slot, bt->slot, sizeof(slot)); + } else if (!trace_path(bt, key, path, slot)) { + return 1; + } + + /* Select the next item if we're deleting at the cursor */ + if (bt->slot[0] == slot[0] && bt->path[0] == path[0]) + cursor_next(bt); + + /* Delete from the leaf node. If it's still full enough, then we don't + * need to do anything else. + */ + delete_item(path[0], slot[0]); + if (path[0]->num_children >= halfsize) + return 0; + + /* Trace back up the tree, fixing underfull nodes. If we can fix by + * borrowing, do it and we're done. Otherwise, we need to fix by + * merging, which may result in another underfull node, and we need + * to continue. + */ + for (h = 1; h <= bt->root->height; h++) { + struct btree_page *p = path[h]; + struct btree_page *c = path[h - 1]; + int s = slot[h]; + + if (s > 0) { + /* Borrow/merge from lower page */ + struct btree_page *d = *PAGE_PTR(p, s - 1); + + if (d->num_children > halfsize) { + move_item(d, d->num_children - 1, c, 0); + memcpy(PAGE_KEY(p, s), PAGE_KEY(c, 0), + def->key_size); + return 0; + } + + merge_pages(d, c); + delete_item(p, s); + free(c); + } else { + /* Borrow/merge from higher page */ + struct btree_page *d = *PAGE_PTR(p, s + 1); + + if (d->num_children > halfsize) { + move_item(d, 0, c, c->num_children); + memcpy(PAGE_KEY(p, s + 1), + PAGE_KEY(d, 0), + def->key_size); + return 0; + } + + merge_pages(c, d); + delete_item(p, s + 1); + free(d); + } + + if (p->num_children >= halfsize) + return 0; + } + + /* If the root contains only a single pointer to another page, + * shrink the tree. This does not affect the cursor. + */ + if (bt->root->height && bt->root->num_children == 1) { + struct btree_page *old = bt->root; + + bt->root = *PAGE_PTR(old, 0); + free(old); + } + + return 0; +} + +int btree_get(btree_t bt, const void *key, void *data) +{ + const struct btree_def *def = bt->def; + struct btree_page *p = bt->root; + int h; + + check_btree(bt); + + if (!key) + return btree_select(bt, NULL, BTREE_READ, NULL, data); + + for (h = bt->root->height; h >= 0; h--) { + int s = find_key_le(p, key); + + if (h) { + assert (s >= 0 && s < p->num_children); + p = *PAGE_PTR(p, s); + } else if (s >= 0 && !def->compare(key, PAGE_KEY(p, s))) { + memcpy(data, PAGE_DATA(p, s), def->data_size); + return 0; + } + } + + return 1; +} + +int btree_select(btree_t bt, const void *key, btree_selmode_t mode, + void *key_ret, void *data_ret) +{ + const struct btree_def *def = bt->def; + + check_btree(bt); + + switch (mode) { + case BTREE_CLEAR: + bt->slot[0] = -1; + break; + + case BTREE_READ: + break; + + case BTREE_EXACT: + case BTREE_LE: + if (!trace_path(bt, key, bt->path, bt->slot) && + mode == BTREE_EXACT) + bt->slot[0] = -1; + break; + + case BTREE_FIRST: + cursor_first(bt); + break; + + case BTREE_NEXT: + cursor_next(bt); + break; + } + + /* Return the data at the cursor */ + if (bt->slot[0] >= 0) { + if (key_ret) + memcpy(key_ret, + PAGE_KEY(bt->path[0], bt->slot[0]), + def->key_size); + if (data_ret) + memcpy(data_ret, + PAGE_DATA(bt->path[0], bt->slot[0]), + def->data_size); + return 0; + } + + return 1; +} diff --git a/btree.h b/btree.h new file mode 100644 index 0000000..3a28a8c --- /dev/null +++ b/btree.h @@ -0,0 +1,94 @@ +/* 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 BTREE_H_ +#define BTREE_H_ + +typedef int (*btree_compare_t)(const void *left, const void *right); + +struct btree_def { + int key_size; + int data_size; + int branches; + + const void *zero; + btree_compare_t compare; +}; + +struct btree; +typedef struct btree *btree_t; + +/* Instantiate a new B+Tree, given the definition. The definition must + * remain valid for the duration of the B+Tree's existence. + * + * Returns NULL on error. + */ +btree_t btree_alloc(const struct btree_def *def); + +/* Destroy a B+Tree */ +void btree_free(btree_t bt); + +/* Clear all data from a B+Tree */ +void btree_clear(btree_t bt); + +/* Add or update a record in a B+Tree. Any existing data for the key will + * be overwritten. + * + * Returns 1 if the key already existed, 0 if a new key was inserted, + * or -1 if an error occurs (failed memory allocation). + */ +int btree_put(btree_t bt, const void *key, const void *data); + +/* Delete a value from a B+Tree. If the key is NULL, the value at the cursor + * is deleted, and the cursor is updated to point to the next item. + * + * Returns 0 if the key existed, 1 if it didn't. + */ +int btree_delete(btree_t bt, const void *key); + +/* Retrieve an item from the B+Tree. If the key is NULL, the value at the + * cursor is retrieved. Optionally, the actual key value can be retrieved + * into key_ret (this may be useful if the keys are compared case-insensitive, + * or if fetching the cursor item). + * + * Returns 0 if the key existed, 1 if it didn't. + */ +int btree_get(btree_t bt, const void *key, void *data); + +/* Cursor manipulation. This function takes a cursor movement command, + * some of which require a key argument. + * + * After the command is completed, the currently selected key and value + * are returned via the supplied pointers key_ret and data_ret (each of + * which may be NULL). + * + * Returns 0 if a record was selected, 1 if not. + */ +typedef enum { + BTREE_EXACT, /* find the exact item */ + BTREE_LE, /* find the largest item <= the key */ + BTREE_NEXT, /* find the next item after the cursor */ + BTREE_FIRST, /* find the first item in the tree */ + BTREE_CLEAR, /* clear the cursor */ + BTREE_READ /* fetch the current record without moving */ +} btree_selmode_t; + +int btree_select(btree_t bt, const void *key, btree_selmode_t mode, + void *key_ret, void *data_ret); + +#endif diff --git a/dis.c b/dis.c index 70a0785..53f90bd 100644 --- a/dis.c +++ b/dis.c @@ -519,9 +519,9 @@ static const char *const msp430_reg_names[] = { static int format_addr(char *buf, int max_len, const char *prefix, u_int16_t addr) { - const char *name; + char name[64]; - if (stab_find(&addr, &name) < 0) + if (stab_nearest(addr, name, sizeof(name), &addr) < 0) return snprintf(buf, max_len, "%s0x%04x", prefix, addr); if (addr) diff --git a/elf32.c b/elf32.c index 9115b70..16f21b7 100644 --- a/elf32.c +++ b/elf32.c @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include @@ -35,6 +36,9 @@ static Elf32_Ehdr file_ehdr; static Elf32_Phdr file_phdrs[MAX_PHDRS]; static Elf32_Shdr file_shdrs[MAX_SHDRS]; +static char *string_tab; +static int string_len; + static int read_ehdr(FILE *in) { int i; @@ -217,14 +221,26 @@ static Elf32_Shdr *find_shdr(Elf32_Word type) static int syms_load_strings(FILE *in, Elf32_Shdr *s) { int len = s->sh_size; - char buf[1024]; + int offset = 0; + + if (!len) + return 0; if (fseek(in, s->sh_offset, SEEK_SET) < 0) { perror("elf32: can't seek to strings"); return -1; } + string_len = len; + string_tab = malloc(len + 1); + + if (!string_tab) { + perror("elf32: can't allocate string table memory"); + return -1; + } + while (len) { + char buf[1024]; int req = sizeof(buf) > len ? len : sizeof(buf); int count = fread(buf, 1, req, in); @@ -238,17 +254,18 @@ static int syms_load_strings(FILE *in, Elf32_Shdr *s) return -1; } - if (stab_add_string(buf, count) < 0) - return -1; + memcpy(string_tab + offset, buf, count); + offset += count; len -= count; } + string_tab[string_len] = 0; return 0; } #define N_SYMS 128 -static int syms_load_syms(FILE *in, Elf32_Shdr *s, int soff) +static int syms_load_syms(FILE *in, Elf32_Shdr *s) { Elf32_Sym syms[N_SYMS]; int len = s->sh_size / sizeof(syms[0]); @@ -276,10 +293,16 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s, int soff) for (i = 0; i < count; i++) { Elf32_Sym *y = &syms[i]; - if (stab_add_symbol(y->st_name + soff, - y->st_value) < 0) + if (y->st_name > string_len) { + fprintf(stderr, "elf32: symbol out of " + "bounds\n"); + return -1; + } + + if (stab_set(string_tab + y->st_name, y->st_value) < 0) return -1; } + len -= count; } @@ -289,7 +312,6 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s, int soff) int elf32_syms(FILE *in) { Elf32_Shdr *s; - int soff; if (read_all(in) < 0) return -1; @@ -305,11 +327,17 @@ int elf32_syms(FILE *in) 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; + string_tab = NULL; + string_len = 0; + if (syms_load_strings(in, &file_shdrs[s->sh_link]) < 0 || + syms_load_syms(in, s) < 0) { + if (string_tab) + free(string_tab); + return -1; + } + + if (string_tab) + free(string_tab); return 0; } diff --git a/main.c b/main.c index 15bdb31..51242c6 100644 --- a/main.c +++ b/main.c @@ -233,10 +233,10 @@ static void disassemble(u_int16_t offset, u_int8_t *data, int length) int retval; int count; int i; - u_int16_t oboff = offset; - const char *obname; + u_int16_t oboff; + char obname[64]; - if (stab_find(&oboff, &obname) >= 0) { + if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) { if (!oboff) printf("%s:\n", obname); else if (first_line) @@ -686,7 +686,7 @@ static int cmd_eval(char **arg) { int addr; u_int16_t offset; - const char *name; + char name[64]; if (stab_parse(*arg, &addr) < 0) { fprintf(stderr, "=: can't parse: %s\n", *arg); @@ -694,8 +694,7 @@ static int cmd_eval(char **arg) } printf("0x%04x", addr); - offset = addr; - if (!stab_find(&offset, &name)) { + if (!stab_nearest(addr, name, sizeof(name), &offset)) { printf(" = %s", name); if (offset) printf("+0x%x", offset); @@ -1049,6 +1048,8 @@ int main(int argc, char **argv) } ctrlc_init(); + if (stab_init() < 0) + return -1; /* Open a device */ if (mode == MODE_SIM) { @@ -1086,5 +1087,7 @@ int main(int argc, char **argv) } msp430_dev->close(); + stab_exit(); + return 0; } diff --git a/sim.c b/sim.c index 6dc1a18..a737dbc 100644 --- a/sim.c +++ b/sim.c @@ -47,10 +47,10 @@ static u_int16_t current_insn; static void io_prefix(const char *prefix, u_int16_t addr, int is_byte) { - const char *name; + char name[64]; u_int16_t pc = current_insn; - if (!stab_find(&pc, &name)) { + if (!stab_nearest(pc, name, sizeof(name), &pc)) { printf("%s", name); if (pc) printf("+0x%x", addr); @@ -59,7 +59,7 @@ static void io_prefix(const char *prefix, u_int16_t addr, int is_byte) } printf(": IO %s.%c: 0x%04x", prefix, is_byte ? 'B' : 'W', addr); - if (!stab_find(&addr, &name)) { + if (!stab_nearest(addr, name, sizeof(name), &addr)) { printf(" (%s", name); if (addr) printf("+0x%x", addr); diff --git a/stab.c b/stab.c index 664e6e2..3124a7c 100644 --- a/stab.c +++ b/stab.c @@ -17,165 +17,242 @@ */ #include -#include -#include #include +#include +#include +#include +#include #include "stab.h" +#include "btree.h" -static char *strtab; -static int strtab_len; -static int strtab_cap; - -struct symbol { - int name; - u_int16_t addr; +struct sym_key { + char name[64]; }; -struct symvec { - struct symbol *syms; - int len; - int cap; -}; +static const struct sym_key sym_key_zero; -static struct symvec by_name; -static struct symvec by_addr; -static int need_sort; - -static void vec_clear(struct symvec *v) +static int sym_key_compare(const void *left, const void *right) { - if (v->syms) - free(v->syms); + return strcmp(((const struct sym_key *)left)->name, + ((const struct sym_key *)right)->name); +} - v->syms = NULL; - v->len = 0; - v->cap = 0; +static void sym_key_init(struct sym_key *key, const char *text) +{ + int len = strlen(text); + + if (len >= sizeof(key->name)) + len = sizeof(key->name) - 1; + + memcpy(key->name, text, len); + key->name[len] = 0; +} + +struct addr_key { + u_int16_t addr; + char name[64]; +}; + +static const struct addr_key addr_key_zero; + +static int addr_key_compare(const void *left, const void *right) +{ + const struct addr_key *kl = (const struct addr_key *)left; + const struct addr_key *kr = (const struct addr_key *)right; + + if (kl->addr < kr->addr) + return -1; + if (kl->addr > kr->addr) + return 1; + + return strcmp(kl->name, kr->name); +} + +static void addr_key_init(struct addr_key *key, u_int16_t addr, + const char *text) +{ + int len = strlen(text); + + if (len >= sizeof(key->name)) + len = sizeof(key->name) - 1; + + key->addr = addr; + memcpy(key->name, text, len); + key->name[len] = 0; +} + +static const struct btree_def sym_table_def = { + .compare = sym_key_compare, + .zero = &sym_key_zero, + .branches = 32, + .key_size = sizeof(struct sym_key), + .data_size = sizeof(u_int16_t) +}; + +static const struct btree_def addr_table_def = { + .compare = addr_key_compare, + .zero = &addr_key_zero, + .branches = 32, + .key_size = sizeof(struct addr_key), + .data_size = 0 +}; + +static btree_t sym_table; +static btree_t addr_table; +static int is_modified; + +/************************************************************************ + * Public interface + */ + +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; + } + + return 0; +} + +void stab_exit(void) +{ + btree_free(sym_table); + btree_free(addr_table); } 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); + btree_clear(sym_table); + btree_clear(addr_table); + is_modified = 0; } -int stab_add_string(const char *text, int len) +int stab_is_modified(void) { - int cap = strtab_cap; + return is_modified; +} - if (!text || !len) - return strtab_len; +void stab_clear_modified(void) +{ + is_modified = 0; +} - /* Figure out how big the table needs to be after we add this - * string. +int stab_set(const char *name, u_int16_t addr) +{ + struct sym_key skey; + struct addr_key akey; + u_int16_t old_addr; + + sym_key_init(&skey, name); + + /* Look for an old address first, and delete the reverse mapping + * if it's there. */ - 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; + if (!btree_get(sym_table, &skey, &old_addr)) { + addr_key_init(&akey, old_addr, skey.name); + btree_delete(addr_table, &akey); } - /* 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; + /* Put the new mapping into both tables */ + addr_key_init(&akey, addr, name); + if (btree_put(addr_table, &akey, NULL) < 0 || + btree_put(sym_table, &skey, &addr) < 0) { + fprintf(stderr, "stab: can't set %s = 0x%04x\n", name, addr); + return -1; } - s = &v->syms[v->len++]; - s->name = name; - s->addr = addr; - return 0; } -int stab_add_symbol(int name, u_int16_t addr) +int stab_get(const char *name, u_int16_t *value) { - if (name < 0 || name > strtab_len) { - fprintf(stderr, "stab: symbol name out of bounds: %d\n", + struct sym_key skey; + + sym_key_init(&skey, name); + if (btree_get(sym_table, &skey, value)) { + fprintf(stderr, "stab: can't find symbol: %s\n", name); + return -1; + } + + return 0; +} + +int stab_del(const char *name) +{ + struct sym_key skey; + u_int16_t value; + struct addr_key akey; + + sym_key_init(&skey, name); + if (btree_get(sym_table, &skey, &value)) { + fprintf(stderr, "stab: can't delete nonexistent symbol: %s\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; + addr_key_init(&akey, value, name); + btree_delete(sym_table, &skey); + btree_delete(addr_table, &akey); return 0; } -static int cmp_by_name(const void *a, const void *b) +int stab_enum(stab_callback_t cb) { - const struct symbol *sa = (const struct symbol *)a; - const struct symbol *sb = (const struct symbol *)b; + struct sym_key skey; + u_int16_t value; + int ret; + int count = 0; - return strcmp(strtab + sa->name, strtab + sb->name); + ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value); + while (!ret) { + if (cb && cb(skey.name, value) < 0) + return -1; + count++; + ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value); + } + + return count; } -static int cmp_by_addr(const void *a, const void *b) +int stab_re_search(const char *regex, stab_callback_t cb) { - const struct symbol *sa = (const struct symbol *)a; - const struct symbol *sb = (const struct symbol *)b; + struct sym_key skey; + u_int16_t value; + int ret; + int count = 0; + regex_t preg; - if (sa->addr < sb->addr) + if (regcomp(&preg, regex, REG_EXTENDED | REG_NOSUB)) { + fprintf(stderr, "stab: failed to compile: %s\n", regex); return -1; - if (sa->addr > sb->addr) - return 1; - return 0; -} + } -static void sort_tables(void) -{ - if (!need_sort) - return; - need_sort = 0; + ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value); + while (!ret) { + if (!regexec(&preg, skey.name, 0, NULL, 0)) { + if (cb && cb(skey.name, value) < 0) { + regfree(&preg); + return -1; + } - 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); + count++; + } + + ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value); + } + + regfree(&preg); + return count; } static char token_buf[64]; @@ -185,9 +262,9 @@ static int token_sum; static int token_add(void) { - int low = 0; - int high = by_name.len - 1; int i; + struct sym_key skey; + u_int16_t value; if (!token_len) return 0; @@ -211,20 +288,10 @@ static int token_add(void) } /* 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 0; - } - - if (cmp < 0) - low = mid + 1; - else - high = mid - 1; + sym_key_init(&skey, token_buf); + if (!btree_get(sym_table, &skey, &value)) { + token_sum += token_mult * (int)value; + return 0; } fprintf(stderr, "stab: unknown token: %s\n", token_buf); @@ -237,8 +304,6 @@ int stab_parse(const char *text, int *addr) token_mult = 1; token_sum = 0; - sort_tables(); - while (*text) { if (isalnum(*text) || *text == '_' || *text == '$') { if (token_len + 1 < sizeof(token_buf)) @@ -258,32 +323,27 @@ int stab_parse(const char *text, int *addr) if (token_add() < 0) return -1; - *addr = token_sum; + *addr = token_sum & 0xffff; return 0; } -int stab_find(u_int16_t *addr, const char **name) +int stab_nearest(u_int16_t addr, char *ret_name, int max_len, + u_int16_t *ret_offset) { - int low = 0; - int high = by_addr.len - 1; + struct addr_key akey; + int i; - sort_tables(); + akey.addr = addr; + for (i = 0; i < sizeof(akey.name); i++) + akey.name[i] = 0xff; + akey.name[sizeof(akey.name) - 1] = 0xff; - 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; - } + if (!btree_select(addr_table, &akey, BTREE_LE, &akey, NULL)) { + strncpy(ret_name, akey.name, max_len); + ret_name[max_len - 1] = 0; + *ret_offset = addr - akey.addr; + return 0; } - return -1; + return 1; } diff --git a/stab.h b/stab.h index ccd3f59..49e7066 100644 --- a/stab.h +++ b/stab.h @@ -21,37 +21,61 @@ #include +/* Initialise/destroy the symbol table manager. If successful, init returns + * 0, or -1 on error. + */ +int stab_init(void); +void stab_exit(void); + /* 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); +/* Check to see if the symbol table has been modified, and clear + * modification flag. */ -int stab_add_string(const char *text, int len); +int stab_is_modified(void); +void stab_clear_modified(void); -/* Symbol types. Symbols are divided into a fixed number of classes for - * query purposes. +/* 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. */ -#define STAB_TYPE_CODE 0x01 -#define STAB_TYPE_DATA 0x02 -#define STAB_TYPE_ALL 0x03 +int stab_get(const char *name, u_int16_t *value); -/* Add a symbol to the table. The name is specified as an offset into - * the string table. +/* Delete a symbol from the symbol table. * - * Returns 0 on success, or -1 if an error occurs. + * Returns 0 if successful, -1 if no such symbol exists. */ -int stab_add_symbol(int name, u_int16_t addr); +int stab_del(const char *name); -/* Parse a symbol name and return an address. The text may be an address, - * a symbol name or a combination (using + or -). +/* 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); -/* Take an address and find the nearest symbol. */ -int stab_find(u_int16_t *addr, const char **name); +/* Take an address and find the nearest symbol and offset (always + * non-negative). + * + * Returns 0 if found, 1 otherwise. + */ +int stab_nearest(u_int16_t addr, char *ret_name, int max_len, + u_int16_t *ret_offset); #endif diff --git a/symmap.c b/symmap.c index 41060b6..5dc006e 100644 --- a/symmap.c +++ b/symmap.c @@ -60,12 +60,8 @@ int symmap_syms(FILE *in) if (addr && name) { int addr_val = strtoul(addr, NULL, 16); - int offset = stab_add_string(NULL, 0); - if (stab_add_string(name, strlen(name) + 1) < 0) - return -1; - - if (stab_add_symbol(offset, addr_val) < 0) + if (stab_set(name, addr_val) < 0) return -1; } }