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 | } |
... | ... |