* Use function pointers for symbol lookup (currently for elf32 and elf64,
could be expanded). This also fixes the bug with mips elf64 symbols in current Qemu trunk. * Use quicksort and binary search for symbol lookup. * Remove unneeded entries from symbol table. This reduced a typical table size (linux mips kernel) from 1764487 to 11656 entries. Signed-off-by: Stefan Weil <weil@mail.berlios.de> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5510 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
f16a0db323
commit
49918a752b
30
disas.c
30
disas.c
|
@ -303,33 +303,17 @@ void disas(FILE *out, void *code, unsigned long size)
|
||||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||||
const char *lookup_symbol(target_ulong orig_addr)
|
const char *lookup_symbol(target_ulong orig_addr)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
const char *symbol = "";
|
||||||
/* Hack, because we know this is x86. */
|
|
||||||
Elf32_Sym *sym;
|
|
||||||
struct syminfo *s;
|
struct syminfo *s;
|
||||||
target_ulong addr;
|
|
||||||
|
|
||||||
for (s = syminfos; s; s = s->next) {
|
for (s = syminfos; s; s = s->next) {
|
||||||
sym = s->disas_symtab;
|
symbol = s->lookup_symbol(s, orig_addr);
|
||||||
for (i = 0; i < s->disas_num_syms; i++) {
|
if (symbol[0] != '\0') {
|
||||||
if (sym[i].st_shndx == SHN_UNDEF
|
break;
|
||||||
|| sym[i].st_shndx >= SHN_LORESERVE)
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
addr = sym[i].st_value;
|
|
||||||
#if defined(TARGET_ARM) || defined (TARGET_MIPS)
|
|
||||||
/* The bottom address bit marks a Thumb or MIPS16 symbol. */
|
|
||||||
addr &= ~(target_ulong)1;
|
|
||||||
#endif
|
|
||||||
if (orig_addr >= addr
|
|
||||||
&& orig_addr < addr + sym[i].st_size)
|
|
||||||
return s->disas_strtab + sym[i].st_name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
|
20
disas.h
20
disas.h
|
@ -10,12 +10,24 @@ void monitor_disas(CPUState *env,
|
||||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||||
const char *lookup_symbol(target_ulong orig_addr);
|
const char *lookup_symbol(target_ulong orig_addr);
|
||||||
|
|
||||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
struct syminfo;
|
||||||
extern struct syminfo {
|
struct elf32_sym;
|
||||||
|
struct elf64_sym;
|
||||||
|
|
||||||
|
typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_ulong orig_addr);
|
||||||
|
|
||||||
|
struct syminfo {
|
||||||
|
lookup_symbol_t lookup_symbol;
|
||||||
unsigned int disas_num_syms;
|
unsigned int disas_num_syms;
|
||||||
void *disas_symtab;
|
union {
|
||||||
|
struct elf32_sym *elf32;
|
||||||
|
struct elf64_sym *elf64;
|
||||||
|
} disas_symtab;
|
||||||
const char *disas_strtab;
|
const char *disas_strtab;
|
||||||
struct syminfo *next;
|
struct syminfo *next;
|
||||||
} *syminfos;
|
};
|
||||||
|
|
||||||
|
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||||
|
extern struct syminfo *syminfos;
|
||||||
|
|
||||||
#endif /* _QEMU_DISAS_H */
|
#endif /* _QEMU_DISAS_H */
|
||||||
|
|
87
elf_ops.h
87
elf_ops.h
|
@ -60,13 +60,48 @@ static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int glue(symfind, SZ)(const void *s0, const void *s1)
|
||||||
|
{
|
||||||
|
struct elf_sym *key = (struct elf_sym *)s0;
|
||||||
|
struct elf_sym *sym = (struct elf_sym *)s1;
|
||||||
|
int result = 0;
|
||||||
|
if (key->st_value < sym->st_value) {
|
||||||
|
result = -1;
|
||||||
|
} else if (key->st_value > sym->st_value + sym->st_size) {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *glue(lookup_symbol, SZ)(struct syminfo *s, target_ulong orig_addr)
|
||||||
|
{
|
||||||
|
struct elf_sym *syms = glue(s->disas_symtab.elf, SZ);
|
||||||
|
struct elf_sym key;
|
||||||
|
struct elf_sym *sym;
|
||||||
|
|
||||||
|
key.st_value = orig_addr;
|
||||||
|
|
||||||
|
sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), glue(symfind, SZ));
|
||||||
|
if (sym != 0) {
|
||||||
|
return s->disas_strtab + sym->st_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int glue(symcmp, SZ)(const void *s0, const void *s1)
|
||||||
|
{
|
||||||
|
struct elf_sym *sym0 = (struct elf_sym *)s0;
|
||||||
|
struct elf_sym *sym1 = (struct elf_sym *)s1;
|
||||||
|
return (sym0->st_value < sym1->st_value)
|
||||||
|
? -1
|
||||||
|
: ((sym0->st_value > sym1->st_value) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
||||||
{
|
{
|
||||||
struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
|
struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
|
||||||
struct elf_sym *syms = NULL;
|
struct elf_sym *syms = NULL;
|
||||||
#if (SZ == 64)
|
|
||||||
struct elf32_sym *syms32 = NULL;
|
|
||||||
#endif
|
|
||||||
struct syminfo *s;
|
struct syminfo *s;
|
||||||
int nsyms, i;
|
int nsyms, i;
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
|
@ -90,21 +125,32 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
nsyms = symtab->sh_size / sizeof(struct elf_sym);
|
nsyms = symtab->sh_size / sizeof(struct elf_sym);
|
||||||
#if (SZ == 64)
|
|
||||||
syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym));
|
i = 0;
|
||||||
#endif
|
while (i < nsyms) {
|
||||||
for (i = 0; i < nsyms; i++) {
|
|
||||||
if (must_swab)
|
if (must_swab)
|
||||||
glue(bswap_sym, SZ)(&syms[i]);
|
glue(bswap_sym, SZ)(&syms[i]);
|
||||||
#if (SZ == 64)
|
/* We are only interested in function symbols.
|
||||||
syms32[i].st_name = syms[i].st_name;
|
Throw everything else away. */
|
||||||
syms32[i].st_info = syms[i].st_info;
|
if (syms[i].st_shndx == SHN_UNDEF ||
|
||||||
syms32[i].st_other = syms[i].st_other;
|
syms[i].st_shndx >= SHN_LORESERVE ||
|
||||||
syms32[i].st_shndx = syms[i].st_shndx;
|
ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
|
||||||
syms32[i].st_value = syms[i].st_value & 0xffffffff;
|
nsyms--;
|
||||||
syms32[i].st_size = syms[i].st_size & 0xffffffff;
|
if (i < nsyms) {
|
||||||
|
syms[i] = syms[nsyms];
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#if defined(TARGET_ARM) || defined (TARGET_MIPS)
|
||||||
|
/* The bottom address bit marks a Thumb or MIPS16 symbol. */
|
||||||
|
syms[i].st_value &= ~(target_ulong)1;
|
||||||
#endif
|
#endif
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
syms = qemu_realloc(syms, nsyms * sizeof(*syms));
|
||||||
|
|
||||||
|
qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ));
|
||||||
|
|
||||||
/* String table */
|
/* String table */
|
||||||
if (symtab->sh_link >= ehdr->e_shnum)
|
if (symtab->sh_link >= ehdr->e_shnum)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -112,16 +158,12 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
||||||
|
|
||||||
str = load_at(fd, strtab->sh_offset, strtab->sh_size);
|
str = load_at(fd, strtab->sh_offset, strtab->sh_size);
|
||||||
if (!str)
|
if (!str)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
s = qemu_mallocz(sizeof(*s));
|
s = qemu_mallocz(sizeof(*s));
|
||||||
#if (SZ == 64)
|
s->lookup_symbol = glue(lookup_symbol, SZ);
|
||||||
s->disas_symtab = syms32;
|
glue(s->disas_symtab.elf, SZ) = syms;
|
||||||
qemu_free(syms);
|
|
||||||
#else
|
|
||||||
s->disas_symtab = syms;
|
|
||||||
#endif
|
|
||||||
s->disas_num_syms = nsyms;
|
s->disas_num_syms = nsyms;
|
||||||
s->disas_strtab = str;
|
s->disas_strtab = str;
|
||||||
s->next = syminfos;
|
s->next = syminfos;
|
||||||
|
@ -129,9 +171,6 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
||||||
qemu_free(shdr_table);
|
qemu_free(shdr_table);
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
#if (SZ == 64)
|
|
||||||
qemu_free(syms32);
|
|
||||||
#endif
|
|
||||||
qemu_free(syms);
|
qemu_free(syms);
|
||||||
qemu_free(str);
|
qemu_free(str);
|
||||||
qemu_free(shdr_table);
|
qemu_free(shdr_table);
|
||||||
|
|
|
@ -984,80 +984,134 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
|
||||||
return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
|
return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int symfind(const void *s0, const void *s1)
|
||||||
|
{
|
||||||
|
struct elf_sym *key = (struct elf_sym *)s0;
|
||||||
|
struct elf_sym *sym = (struct elf_sym *)s1;
|
||||||
|
int result = 0;
|
||||||
|
if (key->st_value < sym->st_value) {
|
||||||
|
result = -1;
|
||||||
|
} else if (key->st_value > sym->st_value + sym->st_size) {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr)
|
||||||
|
{
|
||||||
|
#if ELF_CLASS == ELFCLASS32
|
||||||
|
struct elf_sym *syms = s->disas_symtab.elf32;
|
||||||
|
#else
|
||||||
|
struct elf_sym *syms = s->disas_symtab.elf64;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// binary search
|
||||||
|
struct elf_sym key;
|
||||||
|
struct elf_sym *sym;
|
||||||
|
|
||||||
|
key.st_value = orig_addr;
|
||||||
|
|
||||||
|
sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind);
|
||||||
|
if (sym != 0) {
|
||||||
|
return s->disas_strtab + sym->st_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: This should use elf_ops.h */
|
||||||
|
static int symcmp(const void *s0, const void *s1)
|
||||||
|
{
|
||||||
|
struct elf_sym *sym0 = (struct elf_sym *)s0;
|
||||||
|
struct elf_sym *sym1 = (struct elf_sym *)s1;
|
||||||
|
return (sym0->st_value < sym1->st_value)
|
||||||
|
? -1
|
||||||
|
: ((sym0->st_value > sym1->st_value) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Best attempt to load symbols from this ELF object. */
|
/* Best attempt to load symbols from this ELF object. */
|
||||||
static void load_symbols(struct elfhdr *hdr, int fd)
|
static void load_symbols(struct elfhdr *hdr, int fd)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i, nsyms;
|
||||||
struct elf_shdr sechdr, symtab, strtab;
|
struct elf_shdr sechdr, symtab, strtab;
|
||||||
char *strings;
|
char *strings;
|
||||||
struct syminfo *s;
|
struct syminfo *s;
|
||||||
#if (ELF_CLASS == ELFCLASS64)
|
struct elf_sym *syms;
|
||||||
// Disas uses 32 bit symbols
|
|
||||||
struct elf32_sym *syms32 = NULL;
|
|
||||||
struct elf_sym *sym;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
lseek(fd, hdr->e_shoff, SEEK_SET);
|
lseek(fd, hdr->e_shoff, SEEK_SET);
|
||||||
for (i = 0; i < hdr->e_shnum; i++) {
|
for (i = 0; i < hdr->e_shnum; i++) {
|
||||||
if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
|
if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
|
||||||
return;
|
return;
|
||||||
#ifdef BSWAP_NEEDED
|
#ifdef BSWAP_NEEDED
|
||||||
bswap_shdr(&sechdr);
|
bswap_shdr(&sechdr);
|
||||||
#endif
|
#endif
|
||||||
if (sechdr.sh_type == SHT_SYMTAB) {
|
if (sechdr.sh_type == SHT_SYMTAB) {
|
||||||
symtab = sechdr;
|
symtab = sechdr;
|
||||||
lseek(fd, hdr->e_shoff
|
lseek(fd, hdr->e_shoff
|
||||||
+ sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
|
+ sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
|
||||||
if (read(fd, &strtab, sizeof(strtab))
|
if (read(fd, &strtab, sizeof(strtab))
|
||||||
!= sizeof(strtab))
|
!= sizeof(strtab))
|
||||||
return;
|
return;
|
||||||
#ifdef BSWAP_NEEDED
|
#ifdef BSWAP_NEEDED
|
||||||
bswap_shdr(&strtab);
|
bswap_shdr(&strtab);
|
||||||
#endif
|
#endif
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return; /* Shouldn't happen... */
|
return; /* Shouldn't happen... */
|
||||||
|
|
||||||
found:
|
found:
|
||||||
/* Now know where the strtab and symtab are. Snarf them. */
|
/* Now know where the strtab and symtab are. Snarf them. */
|
||||||
s = malloc(sizeof(*s));
|
s = malloc(sizeof(*s));
|
||||||
s->disas_symtab = malloc(symtab.sh_size);
|
syms = malloc(symtab.sh_size);
|
||||||
#if (ELF_CLASS == ELFCLASS64)
|
if (!syms)
|
||||||
syms32 = malloc(symtab.sh_size / sizeof(struct elf_sym)
|
return;
|
||||||
* sizeof(struct elf32_sym));
|
|
||||||
#endif
|
|
||||||
s->disas_strtab = strings = malloc(strtab.sh_size);
|
s->disas_strtab = strings = malloc(strtab.sh_size);
|
||||||
if (!s->disas_symtab || !s->disas_strtab)
|
if (!s->disas_strtab)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lseek(fd, symtab.sh_offset, SEEK_SET);
|
lseek(fd, symtab.sh_offset, SEEK_SET);
|
||||||
if (read(fd, s->disas_symtab, symtab.sh_size) != symtab.sh_size)
|
if (read(fd, syms, symtab.sh_size) != symtab.sh_size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < symtab.sh_size / sizeof(struct elf_sym); i++) {
|
nsyms = symtab.sh_size / sizeof(struct elf_sym);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < nsyms) {
|
||||||
#ifdef BSWAP_NEEDED
|
#ifdef BSWAP_NEEDED
|
||||||
bswap_sym(s->disas_symtab + sizeof(struct elf_sym)*i);
|
bswap_sym(syms + i);
|
||||||
#endif
|
#endif
|
||||||
#if (ELF_CLASS == ELFCLASS64)
|
// Throw away entries which we do not need.
|
||||||
sym = s->disas_symtab + sizeof(struct elf_sym)*i;
|
if (syms[i].st_shndx == SHN_UNDEF ||
|
||||||
syms32[i].st_name = sym->st_name;
|
syms[i].st_shndx >= SHN_LORESERVE ||
|
||||||
syms32[i].st_info = sym->st_info;
|
ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
|
||||||
syms32[i].st_other = sym->st_other;
|
nsyms--;
|
||||||
syms32[i].st_shndx = sym->st_shndx;
|
if (i < nsyms) {
|
||||||
syms32[i].st_value = sym->st_value & 0xffffffff;
|
syms[i] = syms[nsyms];
|
||||||
syms32[i].st_size = sym->st_size & 0xffffffff;
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#if defined(TARGET_ARM) || defined (TARGET_MIPS)
|
||||||
|
/* The bottom address bit marks a Thumb or MIPS16 symbol. */
|
||||||
|
syms[i].st_value &= ~(target_ulong)1;
|
||||||
#endif
|
#endif
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
syms = realloc(syms, nsyms * sizeof(*syms));
|
||||||
|
|
||||||
|
qsort(syms, nsyms, sizeof(*syms), symcmp);
|
||||||
|
|
||||||
#if (ELF_CLASS == ELFCLASS64)
|
|
||||||
free(s->disas_symtab);
|
|
||||||
s->disas_symtab = syms32;
|
|
||||||
#endif
|
|
||||||
lseek(fd, strtab.sh_offset, SEEK_SET);
|
lseek(fd, strtab.sh_offset, SEEK_SET);
|
||||||
if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
|
if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
|
||||||
return;
|
return;
|
||||||
s->disas_num_syms = symtab.sh_size / sizeof(struct elf_sym);
|
s->disas_num_syms = nsyms;
|
||||||
|
#if ELF_CLASS == ELFCLASS32
|
||||||
|
s->disas_symtab.elf32 = syms;
|
||||||
|
s->lookup_symbol = lookup_symbolxx;
|
||||||
|
#else
|
||||||
|
s->disas_symtab.elf64 = syms;
|
||||||
|
s->lookup_symbol = lookup_symbolxx;
|
||||||
|
#endif
|
||||||
s->next = syminfos;
|
s->next = syminfos;
|
||||||
syminfos = s;
|
syminfos = s;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue