Implemented B+Tree symbol table.
This commit is contained in:
parent
4a298cdb3b
commit
0871041787
4
Makefile
4
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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
4
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)
|
||||
|
|
52
elf32.c
52
elf32.c
|
@ -16,6 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <elf.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
15
main.c
15
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;
|
||||
}
|
||||
|
|
6
sim.c
6
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);
|
||||
|
|
370
stab.c
370
stab.c
|
@ -17,165 +17,242 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
#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;
|
||||
if (!btree_get(sym_table, &skey, &old_addr)) {
|
||||
addr_key_init(&akey, old_addr, skey.name);
|
||||
btree_delete(addr_table, &akey);
|
||||
}
|
||||
|
||||
/* Reallocate if necessary */
|
||||
if (cap != strtab_cap) {
|
||||
char *n = realloc(strtab, cap);
|
||||
|
||||
if (!n) {
|
||||
perror("stab: can't allocate memory for string");
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value);
|
||||
while (!ret) {
|
||||
if (cb && cb(skey.name, value) < 0)
|
||||
return -1;
|
||||
if (sa->addr > sb->addr)
|
||||
return 1;
|
||||
return 0;
|
||||
count++;
|
||||
ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value);
|
||||
}
|
||||
|
||||
static void sort_tables(void)
|
||||
{
|
||||
if (!need_sort)
|
||||
return;
|
||||
need_sort = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
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);
|
||||
int stab_re_search(const char *regex, stab_callback_t cb)
|
||||
{
|
||||
struct sym_key skey;
|
||||
u_int16_t value;
|
||||
int ret;
|
||||
int count = 0;
|
||||
regex_t preg;
|
||||
|
||||
if (regcomp(&preg, regex, REG_EXTENDED | REG_NOSUB)) {
|
||||
fprintf(stderr, "stab: failed to compile: %s\n", regex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,22 +288,12 @@ 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;
|
||||
sym_key_init(&skey, token_buf);
|
||||
if (!btree_get(sym_table, &skey, &value)) {
|
||||
token_sum += token_mult * (int)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmp < 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid - 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "stab: unknown token: %s\n", token_buf);
|
||||
return -1;
|
||||
}
|
||||
|
@ -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;
|
||||
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;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
|
58
stab.h
58
stab.h
|
@ -21,37 +21,61 @@
|
|||
|
||||
#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) */
|
||||
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
|
||||
|
|
6
symmap.c
6
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue