Implemented B+Tree symbol table.

This commit is contained in:
Daniel Beer 2010-03-27 22:50:02 +13:00
parent 4a298cdb3b
commit 0871041787
10 changed files with 1173 additions and 201 deletions

View File

@ -40,8 +40,8 @@ install: mspdebug mspdebug.man
.SUFFIXES: .c .o .SUFFIXES: .c .o
mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o stab.o util.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) $(CC) $(LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o: .c.o:
$(CC) $(CFLAGS) $(READLINE_CFLAGS) -O1 -Wall -o $@ -c $*.c $(CC) $(CFLAGS) $(READLINE_CFLAGS) -O1 -Wall -ggdb -o $@ -c $*.c

767
btree.c Normal file
View File

@ -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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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;
}

94
btree.h Normal file
View File

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

4
dis.c
View File

@ -519,9 +519,9 @@ static const char *const msp430_reg_names[] = {
static int format_addr(char *buf, int max_len, const char *prefix, static int format_addr(char *buf, int max_len, const char *prefix,
u_int16_t addr) 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); return snprintf(buf, max_len, "%s0x%04x", prefix, addr);
if (addr) if (addr)

52
elf32.c
View File

@ -16,6 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <elf.h> #include <elf.h>
@ -35,6 +36,9 @@ static Elf32_Ehdr file_ehdr;
static Elf32_Phdr file_phdrs[MAX_PHDRS]; static Elf32_Phdr file_phdrs[MAX_PHDRS];
static Elf32_Shdr file_shdrs[MAX_SHDRS]; static Elf32_Shdr file_shdrs[MAX_SHDRS];
static char *string_tab;
static int string_len;
static int read_ehdr(FILE *in) static int read_ehdr(FILE *in)
{ {
int i; int i;
@ -217,14 +221,26 @@ static Elf32_Shdr *find_shdr(Elf32_Word type)
static int syms_load_strings(FILE *in, Elf32_Shdr *s) static int syms_load_strings(FILE *in, Elf32_Shdr *s)
{ {
int len = s->sh_size; int len = s->sh_size;
char buf[1024]; int offset = 0;
if (!len)
return 0;
if (fseek(in, s->sh_offset, SEEK_SET) < 0) { if (fseek(in, s->sh_offset, SEEK_SET) < 0) {
perror("elf32: can't seek to strings"); perror("elf32: can't seek to strings");
return -1; 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) { while (len) {
char buf[1024];
int req = sizeof(buf) > len ? len : sizeof(buf); int req = sizeof(buf) > len ? len : sizeof(buf);
int count = fread(buf, 1, req, in); int count = fread(buf, 1, req, in);
@ -238,17 +254,18 @@ static int syms_load_strings(FILE *in, Elf32_Shdr *s)
return -1; return -1;
} }
if (stab_add_string(buf, count) < 0) memcpy(string_tab + offset, buf, count);
return -1; offset += count;
len -= count; len -= count;
} }
string_tab[string_len] = 0;
return 0; return 0;
} }
#define N_SYMS 128 #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]; Elf32_Sym syms[N_SYMS];
int len = s->sh_size / sizeof(syms[0]); 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++) { for (i = 0; i < count; i++) {
Elf32_Sym *y = &syms[i]; Elf32_Sym *y = &syms[i];
if (stab_add_symbol(y->st_name + soff, if (y->st_name > string_len) {
y->st_value) < 0) fprintf(stderr, "elf32: symbol out of "
"bounds\n");
return -1;
}
if (stab_set(string_tab + y->st_name, y->st_value) < 0)
return -1; return -1;
} }
len -= count; len -= count;
} }
@ -289,7 +312,6 @@ static int syms_load_syms(FILE *in, Elf32_Shdr *s, int soff)
int elf32_syms(FILE *in) int elf32_syms(FILE *in)
{ {
Elf32_Shdr *s; Elf32_Shdr *s;
int soff;
if (read_all(in) < 0) if (read_all(in) < 0)
return -1; return -1;
@ -305,11 +327,17 @@ int elf32_syms(FILE *in)
return -1; return -1;
} }
soff = stab_add_string(NULL, 0); string_tab = NULL;
if (syms_load_strings(in, &file_shdrs[s->sh_link]) < 0) string_len = 0;
return -1;
if (syms_load_syms(in, s, soff) < 0)
return -1;
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; return 0;
} }

15
main.c
View File

@ -233,10 +233,10 @@ static void disassemble(u_int16_t offset, u_int8_t *data, int length)
int retval; int retval;
int count; int count;
int i; int i;
u_int16_t oboff = offset; u_int16_t oboff;
const char *obname; char obname[64];
if (stab_find(&oboff, &obname) >= 0) { if (!stab_nearest(offset, obname, sizeof(obname), &oboff)) {
if (!oboff) if (!oboff)
printf("%s:\n", obname); printf("%s:\n", obname);
else if (first_line) else if (first_line)
@ -686,7 +686,7 @@ static int cmd_eval(char **arg)
{ {
int addr; int addr;
u_int16_t offset; u_int16_t offset;
const char *name; char name[64];
if (stab_parse(*arg, &addr) < 0) { if (stab_parse(*arg, &addr) < 0) {
fprintf(stderr, "=: can't parse: %s\n", *arg); fprintf(stderr, "=: can't parse: %s\n", *arg);
@ -694,8 +694,7 @@ static int cmd_eval(char **arg)
} }
printf("0x%04x", addr); printf("0x%04x", addr);
offset = addr; if (!stab_nearest(addr, name, sizeof(name), &offset)) {
if (!stab_find(&offset, &name)) {
printf(" = %s", name); printf(" = %s", name);
if (offset) if (offset)
printf("+0x%x", offset); printf("+0x%x", offset);
@ -1049,6 +1048,8 @@ int main(int argc, char **argv)
} }
ctrlc_init(); ctrlc_init();
if (stab_init() < 0)
return -1;
/* Open a device */ /* Open a device */
if (mode == MODE_SIM) { if (mode == MODE_SIM) {
@ -1086,5 +1087,7 @@ int main(int argc, char **argv)
} }
msp430_dev->close(); msp430_dev->close();
stab_exit();
return 0; return 0;
} }

6
sim.c
View File

@ -47,10 +47,10 @@ static u_int16_t current_insn;
static void io_prefix(const char *prefix, u_int16_t addr, int is_byte) 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; u_int16_t pc = current_insn;
if (!stab_find(&pc, &name)) { if (!stab_nearest(pc, name, sizeof(name), &pc)) {
printf("%s", name); printf("%s", name);
if (pc) if (pc)
printf("+0x%x", addr); 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); 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); printf(" (%s", name);
if (addr) if (addr)
printf("+0x%x", addr); printf("+0x%x", addr);

368
stab.c
View File

@ -17,165 +17,242 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include "stab.h" #include "stab.h"
#include "btree.h"
static char *strtab; struct sym_key {
static int strtab_len; char name[64];
static int strtab_cap;
struct symbol {
int name;
u_int16_t addr;
}; };
struct symvec { static const struct sym_key sym_key_zero;
struct symbol *syms;
int len;
int cap;
};
static struct symvec by_name; static int sym_key_compare(const void *left, const void *right)
static struct symvec by_addr;
static int need_sort;
static void vec_clear(struct symvec *v)
{ {
if (v->syms) return strcmp(((const struct sym_key *)left)->name,
free(v->syms); ((const struct sym_key *)right)->name);
}
v->syms = NULL; static void sym_key_init(struct sym_key *key, const char *text)
v->len = 0; {
v->cap = 0; 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) void stab_clear(void)
{ {
if (strtab) btree_clear(sym_table);
free(strtab); btree_clear(addr_table);
strtab = NULL; is_modified = 0;
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 stab_is_modified(void)
{ {
int cap = strtab_cap; return is_modified;
}
if (!text || !len) void stab_clear_modified(void)
return strtab_len; {
is_modified = 0;
}
/* Figure out how big the table needs to be after we add this int stab_set(const char *name, u_int16_t addr)
* string. {
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) if (!btree_get(sym_table, &skey, &old_addr)) {
cap = 1024; addr_key_init(&akey, old_addr, skey.name);
while (strtab_len + len + 1 > cap) btree_delete(addr_table, &akey);
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 */ /* Put the new mapping into both tables */
memcpy(strtab + strtab_len, text, len); addr_key_init(&akey, addr, name);
strtab_len += len; if (btree_put(addr_table, &akey, NULL) < 0 ||
strtab[strtab_len] = 0; btree_put(sym_table, &skey, &addr) < 0) {
fprintf(stderr, "stab: can't set %s = 0x%04x\n", name, addr);
return strtab_len; return -1;
}
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; 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) { struct sym_key skey;
fprintf(stderr, "stab: symbol name out of bounds: %d\n",
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); name);
return -1; return -1;
} }
need_sort = 1; addr_key_init(&akey, value, name);
if (vec_push(&by_name, name, addr) < 0) btree_delete(sym_table, &skey);
return -1; btree_delete(addr_table, &akey);
if (vec_push(&by_addr, name, addr) < 0)
return -1;
return 0; 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; struct sym_key skey;
const struct symbol *sb = (const struct symbol *)b; 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; struct sym_key skey;
const struct symbol *sb = (const struct symbol *)b; 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; return -1;
if (sa->addr > sb->addr) }
return 1;
return 0;
}
static void sort_tables(void) ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value);
{ while (!ret) {
if (!need_sort) if (!regexec(&preg, skey.name, 0, NULL, 0)) {
return; if (cb && cb(skey.name, value) < 0) {
need_sort = 0; regfree(&preg);
return -1;
}
qsort(by_name.syms, by_name.len, sizeof(by_name.syms[0]), count++;
cmp_by_name); }
qsort(by_addr.syms, by_addr.len, sizeof(by_addr.syms[0]),
cmp_by_addr); ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value);
}
regfree(&preg);
return count;
} }
static char token_buf[64]; static char token_buf[64];
@ -185,9 +262,9 @@ static int token_sum;
static int token_add(void) static int token_add(void)
{ {
int low = 0;
int high = by_name.len - 1;
int i; int i;
struct sym_key skey;
u_int16_t value;
if (!token_len) if (!token_len)
return 0; return 0;
@ -211,20 +288,10 @@ static int token_add(void)
} }
/* Look up the name in the symbol table */ /* Look up the name in the symbol table */
while (low <= high) { sym_key_init(&skey, token_buf);
int mid = (low + high) / 2; if (!btree_get(sym_table, &skey, &value)) {
struct symbol *sym = &by_name.syms[mid]; token_sum += token_mult * (int)value;
int cmp = strcmp(strtab + sym->name, token_buf); return 0;
if (!cmp) {
token_sum += token_mult * (int)sym->addr;
return 0;
}
if (cmp < 0)
low = mid + 1;
else
high = mid - 1;
} }
fprintf(stderr, "stab: unknown token: %s\n", token_buf); 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_mult = 1;
token_sum = 0; token_sum = 0;
sort_tables();
while (*text) { while (*text) {
if (isalnum(*text) || *text == '_' || *text == '$') { if (isalnum(*text) || *text == '_' || *text == '$') {
if (token_len + 1 < sizeof(token_buf)) if (token_len + 1 < sizeof(token_buf))
@ -258,32 +323,27 @@ int stab_parse(const char *text, int *addr)
if (token_add() < 0) if (token_add() < 0)
return -1; return -1;
*addr = token_sum; *addr = token_sum & 0xffff;
return 0; 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; struct addr_key akey;
int high = by_addr.len - 1; 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) { if (!btree_select(addr_table, &akey, BTREE_LE, &akey, NULL)) {
int mid = (low + high) / 2; strncpy(ret_name, akey.name, max_len);
struct symbol *sym = &by_addr.syms[mid]; ret_name[max_len - 1] = 0;
*ret_offset = addr - akey.addr;
if (sym->addr > *addr) { return 0;
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; return 1;
} }

58
stab.h
View File

@ -21,37 +21,61 @@
#include <sys/types.h> #include <sys/types.h>
/* 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) */ /* Reset the symbol table (delete all symbols) */
void stab_clear(void); void stab_clear(void);
/* Add a block of text to the string table. On success, returns the new /* Check to see if the symbol table has been modified, and clear
* size of the string table. Returns -1 on error. You can fetch the table's * modification flag.
* current size by calling stab_add_string(NULL, 0);
*/ */
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 /* Set a symbol in the table. Returns 0 on success, or -1 on error. */
* query purposes. 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 int stab_get(const char *name, u_int16_t *value);
#define STAB_TYPE_DATA 0x02
#define STAB_TYPE_ALL 0x03
/* Add a symbol to the table. The name is specified as an offset into /* Delete a symbol from the symbol table.
* the string 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, /* Enumerate all symbols. Returns total symbol count, or -1 if a callback
* a symbol name or a combination (using + or -). * 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. * Returns 0 if parsed successfully, -1 if an error occurs.
*/ */
int stab_parse(const char *text, int *addr); int stab_parse(const char *text, int *addr);
/* Take an address and find the nearest symbol. */ /* Take an address and find the nearest symbol and offset (always
int stab_find(u_int16_t *addr, const char **name); * 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 #endif

View File

@ -60,12 +60,8 @@ int symmap_syms(FILE *in)
if (addr && name) { if (addr && name) {
int addr_val = strtoul(addr, NULL, 16); int addr_val = strtoul(addr, NULL, 16);
int offset = stab_add_string(NULL, 0);
if (stab_add_string(name, strlen(name) + 1) < 0) if (stab_set(name, addr_val) < 0)
return -1;
if (stab_add_symbol(offset, addr_val) < 0)
return -1; return -1;
} }
} }