Commit 49918a752b739041c7afb205f7158e61c0874267
1 parent
f16a0db3
* 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
Showing
4 changed files
with
184 additions
and
95 deletions
disas.c
| ... | ... | @@ -303,33 +303,17 @@ void disas(FILE *out, void *code, unsigned long size) |
| 303 | 303 | /* Look up symbol for debugging purpose. Returns "" if unknown. */ |
| 304 | 304 | const char *lookup_symbol(target_ulong orig_addr) |
| 305 | 305 | { |
| 306 | - unsigned int i; | |
| 307 | - /* Hack, because we know this is x86. */ | |
| 308 | - Elf32_Sym *sym; | |
| 306 | + const char *symbol = ""; | |
| 309 | 307 | struct syminfo *s; |
| 310 | - target_ulong addr; | |
| 311 | 308 | |
| 312 | 309 | for (s = syminfos; s; s = s->next) { |
| 313 | - sym = s->disas_symtab; | |
| 314 | - for (i = 0; i < s->disas_num_syms; i++) { | |
| 315 | - if (sym[i].st_shndx == SHN_UNDEF | |
| 316 | - || sym[i].st_shndx >= SHN_LORESERVE) | |
| 317 | - continue; | |
| 318 | - | |
| 319 | - if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC) | |
| 320 | - continue; | |
| 321 | - | |
| 322 | - addr = sym[i].st_value; | |
| 323 | -#if defined(TARGET_ARM) || defined (TARGET_MIPS) | |
| 324 | - /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
| 325 | - addr &= ~(target_ulong)1; | |
| 326 | -#endif | |
| 327 | - if (orig_addr >= addr | |
| 328 | - && orig_addr < addr + sym[i].st_size) | |
| 329 | - return s->disas_strtab + sym[i].st_name; | |
| 330 | - } | |
| 310 | + symbol = s->lookup_symbol(s, orig_addr); | |
| 311 | + if (symbol[0] != '\0') { | |
| 312 | + break; | |
| 313 | + } | |
| 331 | 314 | } |
| 332 | - return ""; | |
| 315 | + | |
| 316 | + return symbol; | |
| 333 | 317 | } |
| 334 | 318 | |
| 335 | 319 | #if !defined(CONFIG_USER_ONLY) | ... | ... |
disas.h
| ... | ... | @@ -10,12 +10,24 @@ void monitor_disas(CPUState *env, |
| 10 | 10 | /* Look up symbol for debugging purpose. Returns "" if unknown. */ |
| 11 | 11 | const char *lookup_symbol(target_ulong orig_addr); |
| 12 | 12 | |
| 13 | -/* Filled in by elfload.c. Simplistic, but will do for now. */ | |
| 14 | -extern struct syminfo { | |
| 13 | +struct syminfo; | |
| 14 | +struct elf32_sym; | |
| 15 | +struct elf64_sym; | |
| 16 | + | |
| 17 | +typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_ulong orig_addr); | |
| 18 | + | |
| 19 | +struct syminfo { | |
| 20 | + lookup_symbol_t lookup_symbol; | |
| 15 | 21 | unsigned int disas_num_syms; |
| 16 | - void *disas_symtab; | |
| 22 | + union { | |
| 23 | + struct elf32_sym *elf32; | |
| 24 | + struct elf64_sym *elf64; | |
| 25 | + } disas_symtab; | |
| 17 | 26 | const char *disas_strtab; |
| 18 | 27 | struct syminfo *next; |
| 19 | -} *syminfos; | |
| 28 | +}; | |
| 29 | + | |
| 30 | +/* Filled in by elfload.c. Simplistic, but will do for now. */ | |
| 31 | +extern struct syminfo *syminfos; | |
| 20 | 32 | |
| 21 | 33 | #endif /* _QEMU_DISAS_H */ | ... | ... |
elf_ops.h
| ... | ... | @@ -60,13 +60,48 @@ static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, |
| 60 | 60 | return NULL; |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | +static int glue(symfind, SZ)(const void *s0, const void *s1) | |
| 64 | +{ | |
| 65 | + struct elf_sym *key = (struct elf_sym *)s0; | |
| 66 | + struct elf_sym *sym = (struct elf_sym *)s1; | |
| 67 | + int result = 0; | |
| 68 | + if (key->st_value < sym->st_value) { | |
| 69 | + result = -1; | |
| 70 | + } else if (key->st_value > sym->st_value + sym->st_size) { | |
| 71 | + result = 1; | |
| 72 | + } | |
| 73 | + return result; | |
| 74 | +} | |
| 75 | + | |
| 76 | +static const char *glue(lookup_symbol, SZ)(struct syminfo *s, target_ulong orig_addr) | |
| 77 | +{ | |
| 78 | + struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); | |
| 79 | + struct elf_sym key; | |
| 80 | + struct elf_sym *sym; | |
| 81 | + | |
| 82 | + key.st_value = orig_addr; | |
| 83 | + | |
| 84 | + sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), glue(symfind, SZ)); | |
| 85 | + if (sym != 0) { | |
| 86 | + return s->disas_strtab + sym->st_name; | |
| 87 | + } | |
| 88 | + | |
| 89 | + return ""; | |
| 90 | +} | |
| 91 | + | |
| 92 | +static int glue(symcmp, SZ)(const void *s0, const void *s1) | |
| 93 | +{ | |
| 94 | + struct elf_sym *sym0 = (struct elf_sym *)s0; | |
| 95 | + struct elf_sym *sym1 = (struct elf_sym *)s1; | |
| 96 | + return (sym0->st_value < sym1->st_value) | |
| 97 | + ? -1 | |
| 98 | + : ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
| 99 | +} | |
| 100 | + | |
| 63 | 101 | static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) |
| 64 | 102 | { |
| 65 | 103 | struct elf_shdr *symtab, *strtab, *shdr_table = NULL; |
| 66 | 104 | struct elf_sym *syms = NULL; |
| 67 | -#if (SZ == 64) | |
| 68 | - struct elf32_sym *syms32 = NULL; | |
| 69 | -#endif | |
| 70 | 105 | struct syminfo *s; |
| 71 | 106 | int nsyms, i; |
| 72 | 107 | char *str = NULL; |
| ... | ... | @@ -90,21 +125,32 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) |
| 90 | 125 | goto fail; |
| 91 | 126 | |
| 92 | 127 | nsyms = symtab->sh_size / sizeof(struct elf_sym); |
| 93 | -#if (SZ == 64) | |
| 94 | - syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym)); | |
| 95 | -#endif | |
| 96 | - for (i = 0; i < nsyms; i++) { | |
| 128 | + | |
| 129 | + i = 0; | |
| 130 | + while (i < nsyms) { | |
| 97 | 131 | if (must_swab) |
| 98 | 132 | glue(bswap_sym, SZ)(&syms[i]); |
| 99 | -#if (SZ == 64) | |
| 100 | - syms32[i].st_name = syms[i].st_name; | |
| 101 | - syms32[i].st_info = syms[i].st_info; | |
| 102 | - syms32[i].st_other = syms[i].st_other; | |
| 103 | - syms32[i].st_shndx = syms[i].st_shndx; | |
| 104 | - syms32[i].st_value = syms[i].st_value & 0xffffffff; | |
| 105 | - syms32[i].st_size = syms[i].st_size & 0xffffffff; | |
| 133 | + /* We are only interested in function symbols. | |
| 134 | + Throw everything else away. */ | |
| 135 | + if (syms[i].st_shndx == SHN_UNDEF || | |
| 136 | + syms[i].st_shndx >= SHN_LORESERVE || | |
| 137 | + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
| 138 | + nsyms--; | |
| 139 | + if (i < nsyms) { | |
| 140 | + syms[i] = syms[nsyms]; | |
| 141 | + } | |
| 142 | + continue; | |
| 143 | + } | |
| 144 | +#if defined(TARGET_ARM) || defined (TARGET_MIPS) | |
| 145 | + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
| 146 | + syms[i].st_value &= ~(target_ulong)1; | |
| 106 | 147 | #endif |
| 148 | + i++; | |
| 107 | 149 | } |
| 150 | + syms = qemu_realloc(syms, nsyms * sizeof(*syms)); | |
| 151 | + | |
| 152 | + qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); | |
| 153 | + | |
| 108 | 154 | /* String table */ |
| 109 | 155 | if (symtab->sh_link >= ehdr->e_shnum) |
| 110 | 156 | goto fail; |
| ... | ... | @@ -112,16 +158,12 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) |
| 112 | 158 | |
| 113 | 159 | str = load_at(fd, strtab->sh_offset, strtab->sh_size); |
| 114 | 160 | if (!str) |
| 115 | - goto fail; | |
| 161 | + goto fail; | |
| 116 | 162 | |
| 117 | 163 | /* Commit */ |
| 118 | 164 | s = qemu_mallocz(sizeof(*s)); |
| 119 | -#if (SZ == 64) | |
| 120 | - s->disas_symtab = syms32; | |
| 121 | - qemu_free(syms); | |
| 122 | -#else | |
| 123 | - s->disas_symtab = syms; | |
| 124 | -#endif | |
| 165 | + s->lookup_symbol = glue(lookup_symbol, SZ); | |
| 166 | + glue(s->disas_symtab.elf, SZ) = syms; | |
| 125 | 167 | s->disas_num_syms = nsyms; |
| 126 | 168 | s->disas_strtab = str; |
| 127 | 169 | s->next = syminfos; |
| ... | ... | @@ -129,9 +171,6 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) |
| 129 | 171 | qemu_free(shdr_table); |
| 130 | 172 | return 0; |
| 131 | 173 | fail: |
| 132 | -#if (SZ == 64) | |
| 133 | - qemu_free(syms32); | |
| 134 | -#endif | |
| 135 | 174 | qemu_free(syms); |
| 136 | 175 | qemu_free(str); |
| 137 | 176 | qemu_free(shdr_table); | ... | ... |
linux-user/elfload.c
| ... | ... | @@ -984,80 +984,134 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex, |
| 984 | 984 | return ((abi_ulong) interp_elf_ex->e_entry) + load_addr; |
| 985 | 985 | } |
| 986 | 986 | |
| 987 | +static int symfind(const void *s0, const void *s1) | |
| 988 | +{ | |
| 989 | + struct elf_sym *key = (struct elf_sym *)s0; | |
| 990 | + struct elf_sym *sym = (struct elf_sym *)s1; | |
| 991 | + int result = 0; | |
| 992 | + if (key->st_value < sym->st_value) { | |
| 993 | + result = -1; | |
| 994 | + } else if (key->st_value > sym->st_value + sym->st_size) { | |
| 995 | + result = 1; | |
| 996 | + } | |
| 997 | + return result; | |
| 998 | +} | |
| 999 | + | |
| 1000 | +static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) | |
| 1001 | +{ | |
| 1002 | +#if ELF_CLASS == ELFCLASS32 | |
| 1003 | + struct elf_sym *syms = s->disas_symtab.elf32; | |
| 1004 | +#else | |
| 1005 | + struct elf_sym *syms = s->disas_symtab.elf64; | |
| 1006 | +#endif | |
| 1007 | + | |
| 1008 | + // binary search | |
| 1009 | + struct elf_sym key; | |
| 1010 | + struct elf_sym *sym; | |
| 1011 | + | |
| 1012 | + key.st_value = orig_addr; | |
| 1013 | + | |
| 1014 | + sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind); | |
| 1015 | + if (sym != 0) { | |
| 1016 | + return s->disas_strtab + sym->st_name; | |
| 1017 | + } | |
| 1018 | + | |
| 1019 | + return ""; | |
| 1020 | +} | |
| 1021 | + | |
| 1022 | +/* FIXME: This should use elf_ops.h */ | |
| 1023 | +static int symcmp(const void *s0, const void *s1) | |
| 1024 | +{ | |
| 1025 | + struct elf_sym *sym0 = (struct elf_sym *)s0; | |
| 1026 | + struct elf_sym *sym1 = (struct elf_sym *)s1; | |
| 1027 | + return (sym0->st_value < sym1->st_value) | |
| 1028 | + ? -1 | |
| 1029 | + : ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
| 1030 | +} | |
| 1031 | + | |
| 987 | 1032 | /* Best attempt to load symbols from this ELF object. */ |
| 988 | 1033 | static void load_symbols(struct elfhdr *hdr, int fd) |
| 989 | 1034 | { |
| 990 | - unsigned int i; | |
| 1035 | + unsigned int i, nsyms; | |
| 991 | 1036 | struct elf_shdr sechdr, symtab, strtab; |
| 992 | 1037 | char *strings; |
| 993 | 1038 | struct syminfo *s; |
| 994 | -#if (ELF_CLASS == ELFCLASS64) | |
| 995 | - // Disas uses 32 bit symbols | |
| 996 | - struct elf32_sym *syms32 = NULL; | |
| 997 | - struct elf_sym *sym; | |
| 998 | -#endif | |
| 1039 | + struct elf_sym *syms; | |
| 999 | 1040 | |
| 1000 | 1041 | lseek(fd, hdr->e_shoff, SEEK_SET); |
| 1001 | 1042 | for (i = 0; i < hdr->e_shnum; i++) { |
| 1002 | - if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) | |
| 1003 | - return; | |
| 1043 | + if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) | |
| 1044 | + return; | |
| 1004 | 1045 | #ifdef BSWAP_NEEDED |
| 1005 | - bswap_shdr(&sechdr); | |
| 1046 | + bswap_shdr(&sechdr); | |
| 1006 | 1047 | #endif |
| 1007 | - if (sechdr.sh_type == SHT_SYMTAB) { | |
| 1008 | - symtab = sechdr; | |
| 1009 | - lseek(fd, hdr->e_shoff | |
| 1010 | - + sizeof(sechdr) * sechdr.sh_link, SEEK_SET); | |
| 1011 | - if (read(fd, &strtab, sizeof(strtab)) | |
| 1012 | - != sizeof(strtab)) | |
| 1013 | - return; | |
| 1048 | + if (sechdr.sh_type == SHT_SYMTAB) { | |
| 1049 | + symtab = sechdr; | |
| 1050 | + lseek(fd, hdr->e_shoff | |
| 1051 | + + sizeof(sechdr) * sechdr.sh_link, SEEK_SET); | |
| 1052 | + if (read(fd, &strtab, sizeof(strtab)) | |
| 1053 | + != sizeof(strtab)) | |
| 1054 | + return; | |
| 1014 | 1055 | #ifdef BSWAP_NEEDED |
| 1015 | - bswap_shdr(&strtab); | |
| 1056 | + bswap_shdr(&strtab); | |
| 1016 | 1057 | #endif |
| 1017 | - goto found; | |
| 1018 | - } | |
| 1058 | + goto found; | |
| 1059 | + } | |
| 1019 | 1060 | } |
| 1020 | 1061 | return; /* Shouldn't happen... */ |
| 1021 | 1062 | |
| 1022 | 1063 | found: |
| 1023 | 1064 | /* Now know where the strtab and symtab are. Snarf them. */ |
| 1024 | 1065 | s = malloc(sizeof(*s)); |
| 1025 | - s->disas_symtab = malloc(symtab.sh_size); | |
| 1026 | -#if (ELF_CLASS == ELFCLASS64) | |
| 1027 | - syms32 = malloc(symtab.sh_size / sizeof(struct elf_sym) | |
| 1028 | - * sizeof(struct elf32_sym)); | |
| 1029 | -#endif | |
| 1066 | + syms = malloc(symtab.sh_size); | |
| 1067 | + if (!syms) | |
| 1068 | + return; | |
| 1030 | 1069 | s->disas_strtab = strings = malloc(strtab.sh_size); |
| 1031 | - if (!s->disas_symtab || !s->disas_strtab) | |
| 1032 | - return; | |
| 1070 | + if (!s->disas_strtab) | |
| 1071 | + return; | |
| 1033 | 1072 | |
| 1034 | 1073 | lseek(fd, symtab.sh_offset, SEEK_SET); |
| 1035 | - if (read(fd, s->disas_symtab, symtab.sh_size) != symtab.sh_size) | |
| 1036 | - return; | |
| 1074 | + if (read(fd, syms, symtab.sh_size) != symtab.sh_size) | |
| 1075 | + return; | |
| 1037 | 1076 | |
| 1038 | - for (i = 0; i < symtab.sh_size / sizeof(struct elf_sym); i++) { | |
| 1077 | + nsyms = symtab.sh_size / sizeof(struct elf_sym); | |
| 1078 | + | |
| 1079 | + i = 0; | |
| 1080 | + while (i < nsyms) { | |
| 1039 | 1081 | #ifdef BSWAP_NEEDED |
| 1040 | - bswap_sym(s->disas_symtab + sizeof(struct elf_sym)*i); | |
| 1082 | + bswap_sym(syms + i); | |
| 1041 | 1083 | #endif |
| 1042 | -#if (ELF_CLASS == ELFCLASS64) | |
| 1043 | - sym = s->disas_symtab + sizeof(struct elf_sym)*i; | |
| 1044 | - syms32[i].st_name = sym->st_name; | |
| 1045 | - syms32[i].st_info = sym->st_info; | |
| 1046 | - syms32[i].st_other = sym->st_other; | |
| 1047 | - syms32[i].st_shndx = sym->st_shndx; | |
| 1048 | - syms32[i].st_value = sym->st_value & 0xffffffff; | |
| 1049 | - syms32[i].st_size = sym->st_size & 0xffffffff; | |
| 1084 | + // Throw away entries which we do not need. | |
| 1085 | + if (syms[i].st_shndx == SHN_UNDEF || | |
| 1086 | + syms[i].st_shndx >= SHN_LORESERVE || | |
| 1087 | + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
| 1088 | + nsyms--; | |
| 1089 | + if (i < nsyms) { | |
| 1090 | + syms[i] = syms[nsyms]; | |
| 1091 | + } | |
| 1092 | + continue; | |
| 1093 | + } | |
| 1094 | +#if defined(TARGET_ARM) || defined (TARGET_MIPS) | |
| 1095 | + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
| 1096 | + syms[i].st_value &= ~(target_ulong)1; | |
| 1050 | 1097 | #endif |
| 1098 | + i++; | |
| 1051 | 1099 | } |
| 1100 | + syms = realloc(syms, nsyms * sizeof(*syms)); | |
| 1101 | + | |
| 1102 | + qsort(syms, nsyms, sizeof(*syms), symcmp); | |
| 1052 | 1103 | |
| 1053 | -#if (ELF_CLASS == ELFCLASS64) | |
| 1054 | - free(s->disas_symtab); | |
| 1055 | - s->disas_symtab = syms32; | |
| 1056 | -#endif | |
| 1057 | 1104 | lseek(fd, strtab.sh_offset, SEEK_SET); |
| 1058 | 1105 | if (read(fd, strings, strtab.sh_size) != strtab.sh_size) |
| 1059 | - return; | |
| 1060 | - s->disas_num_syms = symtab.sh_size / sizeof(struct elf_sym); | |
| 1106 | + return; | |
| 1107 | + s->disas_num_syms = nsyms; | |
| 1108 | +#if ELF_CLASS == ELFCLASS32 | |
| 1109 | + s->disas_symtab.elf32 = syms; | |
| 1110 | + s->lookup_symbol = lookup_symbolxx; | |
| 1111 | +#else | |
| 1112 | + s->disas_symtab.elf64 = syms; | |
| 1113 | + s->lookup_symbol = lookup_symbolxx; | |
| 1114 | +#endif | |
| 1061 | 1115 | s->next = syminfos; |
| 1062 | 1116 | syminfos = s; |
| 1063 | 1117 | } | ... | ... |