summary refs log tree commit diff stats
path: root/linux-user/elfload.c
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2008-10-22 15:11:31 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2008-10-22 15:11:31 +0000
commit49918a752b739041c7afb205f7158e61c0874267 (patch)
treed8da8087010dc5f06f02e972e047505d284fefaf /linux-user/elfload.c
parentf16a0db323e1a8c0044696815cceeb98706f2243 (diff)
downloadfocaccia-qemu-49918a752b739041c7afb205f7158e61c0874267.tar.gz
focaccia-qemu-49918a752b739041c7afb205f7158e61c0874267.zip
* 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
Diffstat (limited to 'linux-user/elfload.c')
-rw-r--r--linux-user/elfload.c142
1 files changed, 98 insertions, 44 deletions
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index becf51593e..34334045df 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -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;
 }
 
+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. */
 static void load_symbols(struct elfhdr *hdr, int fd)
 {
-    unsigned int i;
+    unsigned int i, nsyms;
     struct elf_shdr sechdr, symtab, strtab;
     char *strings;
     struct syminfo *s;
-#if (ELF_CLASS == ELFCLASS64)
-    // Disas uses 32 bit symbols
-    struct elf32_sym *syms32 = NULL;
-    struct elf_sym *sym;
-#endif
+    struct elf_sym *syms;
 
     lseek(fd, hdr->e_shoff, SEEK_SET);
     for (i = 0; i < hdr->e_shnum; i++) {
-	if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
-	    return;
+        if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
+            return;
 #ifdef BSWAP_NEEDED
-	bswap_shdr(&sechdr);
+        bswap_shdr(&sechdr);
 #endif
-	if (sechdr.sh_type == SHT_SYMTAB) {
-	    symtab = sechdr;
-	    lseek(fd, hdr->e_shoff
-		  + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
-	    if (read(fd, &strtab, sizeof(strtab))
-		!= sizeof(strtab))
-		return;
+        if (sechdr.sh_type == SHT_SYMTAB) {
+            symtab = sechdr;
+            lseek(fd, hdr->e_shoff
+                  + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
+            if (read(fd, &strtab, sizeof(strtab))
+                != sizeof(strtab))
+                return;
 #ifdef BSWAP_NEEDED
-	    bswap_shdr(&strtab);
+            bswap_shdr(&strtab);
 #endif
-	    goto found;
-	}
+            goto found;
+        }
     }
     return; /* Shouldn't happen... */
 
  found:
     /* Now know where the strtab and symtab are.  Snarf them. */
     s = malloc(sizeof(*s));
-    s->disas_symtab = malloc(symtab.sh_size);
-#if (ELF_CLASS == ELFCLASS64)
-    syms32 = malloc(symtab.sh_size / sizeof(struct elf_sym)
-                    * sizeof(struct elf32_sym));
-#endif
+    syms = malloc(symtab.sh_size);
+    if (!syms)
+        return;
     s->disas_strtab = strings = malloc(strtab.sh_size);
-    if (!s->disas_symtab || !s->disas_strtab)
-	return;
+    if (!s->disas_strtab)
+        return;
 
     lseek(fd, symtab.sh_offset, SEEK_SET);
-    if (read(fd, s->disas_symtab, symtab.sh_size) != symtab.sh_size)
-	return;
+    if (read(fd, syms, symtab.sh_size) != symtab.sh_size)
+        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
-	bswap_sym(s->disas_symtab + sizeof(struct elf_sym)*i);
+        bswap_sym(syms + i);
 #endif
-#if (ELF_CLASS == ELFCLASS64)
-        sym = s->disas_symtab + sizeof(struct elf_sym)*i;
-        syms32[i].st_name = sym->st_name;
-        syms32[i].st_info = sym->st_info;
-        syms32[i].st_other = sym->st_other;
-        syms32[i].st_shndx = sym->st_shndx;
-        syms32[i].st_value = sym->st_value & 0xffffffff;
-        syms32[i].st_size = sym->st_size & 0xffffffff;
+        // Throw away entries which we do not need.
+        if (syms[i].st_shndx == SHN_UNDEF ||
+                syms[i].st_shndx >= SHN_LORESERVE ||
+                ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
+            nsyms--;
+            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
+        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);
     if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
-	return;
-    s->disas_num_syms = symtab.sh_size / sizeof(struct elf_sym);
+        return;
+    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;
     syminfos = s;
 }