mspdebug/stab.c

290 lines
5.3 KiB
C

/* MSPDebug - debugging tool for the eZ430
* Copyright (C) 2009 Daniel Beer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "stab.h"
static char *strtab;
static int strtab_len;
static int strtab_cap;
struct symbol {
int name;
u_int16_t addr;
};
struct symvec {
struct symbol *syms;
int len;
int cap;
};
static struct symvec by_name;
static struct symvec by_addr;
static int need_sort;
static void vec_clear(struct symvec *v)
{
if (v->syms)
free(v->syms);
v->syms = NULL;
v->len = 0;
v->cap = 0;
}
void stab_clear(void)
{
if (strtab)
free(strtab);
strtab = NULL;
strtab_len = 0;
strtab_cap = 0;
need_sort = 0;
vec_clear(&by_name);
vec_clear(&by_addr);
}
int stab_add_string(const char *text, int len)
{
int cap = strtab_cap;
if (!text || !len)
return strtab_len;
/* Figure out how big the table needs to be after we add this
* string.
*/
if (!cap)
cap = 1024;
while (strtab_len + len + 1 > cap)
cap *= 2;
/* Reallocate if necessary */
if (cap != strtab_cap) {
char *n = realloc(strtab, cap);
if (!n) {
perror("stab: can't allocate memory for string");
return -1;
}
strtab = n;
strtab_cap = cap;
}
/* Copy it in */
memcpy(strtab + strtab_len, text, len);
strtab_len += len;
strtab[strtab_len] = 0;
return strtab_len;
}
static int vec_push(struct symvec *v, int name, u_int16_t addr)
{
int cap = v->cap;
struct symbol *s;
if (!cap)
cap = 64;
while (v->len + 1 > cap)
cap *= 2;
if (cap != v->cap) {
struct symbol *n = realloc(v->syms, cap * sizeof(v->syms[0]));
if (!n) {
perror("stab: can't allocate memory for symbol");
return -1;
}
v->syms = n;
v->cap = cap;
}
s = &v->syms[v->len++];
s->name = name;
s->addr = addr;
return 0;
}
int stab_add_symbol(int name, u_int16_t addr)
{
if (name < 0 || name > strtab_len) {
fprintf(stderr, "stab: symbol name out of bounds: %d\n",
name);
return -1;
}
need_sort = 1;
if (vec_push(&by_name, name, addr) < 0)
return -1;
if (vec_push(&by_addr, name, addr) < 0)
return -1;
return 0;
}
static int cmp_by_name(const void *a, const void *b)
{
const struct symbol *sa = (const struct symbol *)a;
const struct symbol *sb = (const struct symbol *)b;
return strcmp(strtab + sa->name, strtab + sb->name);
}
static int cmp_by_addr(const void *a, const void *b)
{
const struct symbol *sa = (const struct symbol *)a;
const struct symbol *sb = (const struct symbol *)b;
if (sa->addr < sb->addr)
return -1;
if (sa->addr > sb->addr)
return 1;
return 0;
}
static void sort_tables(void)
{
if (!need_sort)
return;
need_sort = 0;
qsort(by_name.syms, by_name.len, sizeof(by_name.syms[0]),
cmp_by_name);
qsort(by_addr.syms, by_addr.len, sizeof(by_addr.syms[0]),
cmp_by_addr);
}
static char token_buf[64];
static int token_len;
static int token_mult;
static int token_sum;
static int token_add(void)
{
int low = 0;
int high = by_name.len - 1;
int i;
if (!token_len)
return 0;
token_buf[token_len] = 0;
token_len = 0;
/* Is it a decimal? */
i = 0;
while (token_buf[i] && isdigit(token_buf[i]))
i++;
if (!token_buf[i]) {
token_sum += token_mult * atoi(token_buf);
return 0;
}
/* Is it hex? */
if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') {
token_sum += token_mult * strtol(token_buf + 2, NULL, 16);
return 0;
}
/* Look up the name in the symbol table */
while (low <= high) {
int mid = (low + high) / 2;
struct symbol *sym = &by_name.syms[mid];
int cmp = strcmp(strtab + sym->name, token_buf);
if (!cmp) {
token_sum += token_mult * (int)sym->addr;
return 0;
}
if (cmp < 0)
low = mid + 1;
else
high = mid - 1;
}
fprintf(stderr, "stab: unknown token: %s\n", token_buf);
return -1;
}
int stab_parse(const char *text, int *addr)
{
token_len = 0;
token_mult = 1;
token_sum = 0;
sort_tables();
while (*text) {
if (isalnum(*text) || *text == '_' || *text == '$') {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else {
if (token_add() < 0)
return -1;
if (*text == '+')
token_mult = 1;
if (*text == '-')
token_mult = -1;
}
text++;
}
if (token_add() < 0)
return -1;
*addr = token_sum;
return 0;
}
int stab_find(u_int16_t *addr, const char **name)
{
int low = 0;
int high = by_addr.len - 1;
sort_tables();
while (low <= high) {
int mid = (low + high) / 2;
struct symbol *sym = &by_addr.syms[mid];
if (sym->addr > *addr) {
high = mid - 1;
} else if (mid + 1 > by_addr.len ||
by_addr.syms[mid + 1].addr > *addr) {
*addr -= sym->addr;
*name = strtab + sym->name;
return 0;
} else {
low = mid + 1;
}
}
return -1;
}