bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 1 | static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) |
| 2 | { |
| 3 | bswap16s(&ehdr->e_type); /* Object file type */ |
| 4 | bswap16s(&ehdr->e_machine); /* Architecture */ |
| 5 | bswap32s(&ehdr->e_version); /* Object file version */ |
| 6 | bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ |
| 7 | bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ |
| 8 | bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ |
| 9 | bswap32s(&ehdr->e_flags); /* Processor-specific flags */ |
| 10 | bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ |
| 11 | bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ |
| 12 | bswap16s(&ehdr->e_phnum); /* Program header table entry count */ |
| 13 | bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ |
| 14 | bswap16s(&ehdr->e_shnum); /* Section header table entry count */ |
| 15 | bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ |
| 16 | } |
| 17 | |
| 18 | static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) |
| 19 | { |
| 20 | bswap32s(&phdr->p_type); /* Segment type */ |
| 21 | bswapSZs(&phdr->p_offset); /* Segment file offset */ |
| 22 | bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ |
| 23 | bswapSZs(&phdr->p_paddr); /* Segment physical address */ |
| 24 | bswapSZs(&phdr->p_filesz); /* Segment size in file */ |
| 25 | bswapSZs(&phdr->p_memsz); /* Segment size in memory */ |
| 26 | bswap32s(&phdr->p_flags); /* Segment flags */ |
| 27 | bswapSZs(&phdr->p_align); /* Segment alignment */ |
| 28 | } |
| 29 | |
| 30 | static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) |
| 31 | { |
| 32 | bswap32s(&shdr->sh_name); |
| 33 | bswap32s(&shdr->sh_type); |
| 34 | bswapSZs(&shdr->sh_flags); |
| 35 | bswapSZs(&shdr->sh_addr); |
| 36 | bswapSZs(&shdr->sh_offset); |
| 37 | bswapSZs(&shdr->sh_size); |
| 38 | bswap32s(&shdr->sh_link); |
| 39 | bswap32s(&shdr->sh_info); |
| 40 | bswapSZs(&shdr->sh_addralign); |
| 41 | bswapSZs(&shdr->sh_entsize); |
| 42 | } |
| 43 | |
| 44 | static void glue(bswap_sym, SZ)(struct elf_sym *sym) |
| 45 | { |
| 46 | bswap32s(&sym->st_name); |
| 47 | bswapSZs(&sym->st_value); |
| 48 | bswapSZs(&sym->st_size); |
| 49 | bswap16s(&sym->st_shndx); |
| 50 | } |
| 51 | |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 52 | static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 53 | int n, int type) |
| 54 | { |
| 55 | int i; |
| 56 | for(i=0;i<n;i++) { |
| 57 | if (shdr_table[i].sh_type == type) |
| 58 | return shdr_table + i; |
| 59 | } |
| 60 | return NULL; |
| 61 | } |
| 62 | |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 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 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 101 | static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) |
| 102 | { |
| 103 | struct elf_shdr *symtab, *strtab, *shdr_table = NULL; |
| 104 | struct elf_sym *syms = NULL; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 105 | struct syminfo *s; |
| 106 | int nsyms, i; |
| 107 | char *str = NULL; |
| 108 | |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 109 | shdr_table = load_at(fd, ehdr->e_shoff, |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 110 | sizeof(struct elf_shdr) * ehdr->e_shnum); |
| 111 | if (!shdr_table) |
| 112 | return -1; |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 113 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 114 | if (must_swab) { |
| 115 | for (i = 0; i < ehdr->e_shnum; i++) { |
| 116 | glue(bswap_shdr, SZ)(shdr_table + i); |
| 117 | } |
| 118 | } |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 119 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 120 | symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); |
| 121 | if (!symtab) |
| 122 | goto fail; |
| 123 | syms = load_at(fd, symtab->sh_offset, symtab->sh_size); |
| 124 | if (!syms) |
| 125 | goto fail; |
| 126 | |
| 127 | nsyms = symtab->sh_size / sizeof(struct elf_sym); |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 128 | |
| 129 | i = 0; |
| 130 | while (i < nsyms) { |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 131 | if (must_swab) |
| 132 | glue(bswap_sym, SZ)(&syms[i]); |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 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; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 147 | #endif |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 148 | i++; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 149 | } |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 150 | syms = qemu_realloc(syms, nsyms * sizeof(*syms)); |
| 151 | |
| 152 | qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); |
| 153 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 154 | /* String table */ |
| 155 | if (symtab->sh_link >= ehdr->e_shnum) |
| 156 | goto fail; |
| 157 | strtab = &shdr_table[symtab->sh_link]; |
| 158 | |
| 159 | str = load_at(fd, strtab->sh_offset, strtab->sh_size); |
| 160 | if (!str) |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 161 | goto fail; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 162 | |
| 163 | /* Commit */ |
| 164 | s = qemu_mallocz(sizeof(*s)); |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 165 | s->lookup_symbol = glue(lookup_symbol, SZ); |
| 166 | glue(s->disas_symtab.elf, SZ) = syms; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 167 | s->disas_num_syms = nsyms; |
| 168 | s->disas_strtab = str; |
| 169 | s->next = syminfos; |
| 170 | syminfos = s; |
| 171 | qemu_free(shdr_table); |
| 172 | return 0; |
| 173 | fail: |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 174 | qemu_free(syms); |
| 175 | qemu_free(str); |
| 176 | qemu_free(shdr_table); |
| 177 | return -1; |
| 178 | } |
| 179 | |
pbrook | 83c1f87 | 2008-10-22 18:20:20 +0000 | [diff] [blame] | 180 | static int glue(load_elf, SZ)(int fd, int64_t address_offset, |
pbrook | 9596ebb | 2007-11-18 01:44:38 +0000 | [diff] [blame] | 181 | int must_swab, uint64_t *pentry, |
| 182 | uint64_t *lowaddr, uint64_t *highaddr) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 183 | { |
| 184 | struct elfhdr ehdr; |
| 185 | struct elf_phdr *phdr = NULL, *ph; |
| 186 | int size, i, total_size; |
blueswir1 | 9437454 | 2007-09-24 18:41:27 +0000 | [diff] [blame] | 187 | elf_word mem_size; |
blueswir1 | eb296a0 | 2007-09-25 17:30:09 +0000 | [diff] [blame] | 188 | uint64_t addr, low = 0, high = 0; |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 189 | uint8_t *data = NULL; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 190 | |
| 191 | if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) |
| 192 | goto fail; |
| 193 | if (must_swab) { |
| 194 | glue(bswap_ehdr, SZ)(&ehdr); |
| 195 | } |
| 196 | |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 197 | switch (ELF_MACHINE) { |
| 198 | case EM_PPC64: |
| 199 | if (EM_PPC64 != ehdr.e_machine) |
| 200 | if (EM_PPC != ehdr.e_machine) |
| 201 | goto fail; |
| 202 | break; |
| 203 | case EM_X86_64: |
| 204 | if (EM_X86_64 != ehdr.e_machine) |
| 205 | if (EM_386 != ehdr.e_machine) |
| 206 | goto fail; |
| 207 | break; |
| 208 | default: |
| 209 | if (ELF_MACHINE != ehdr.e_machine) |
| 210 | goto fail; |
| 211 | } |
ths | 9042c0e | 2006-12-23 14:18:40 +0000 | [diff] [blame] | 212 | |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 213 | if (pentry) |
ths | 8279006 | 2007-10-17 23:07:31 +0000 | [diff] [blame] | 214 | *pentry = (uint64_t)(elf_sword)ehdr.e_entry; |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 215 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 216 | glue(load_symbols, SZ)(&ehdr, fd, must_swab); |
| 217 | |
| 218 | size = ehdr.e_phnum * sizeof(phdr[0]); |
| 219 | lseek(fd, ehdr.e_phoff, SEEK_SET); |
| 220 | phdr = qemu_mallocz(size); |
| 221 | if (!phdr) |
| 222 | goto fail; |
| 223 | if (read(fd, phdr, size) != size) |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 224 | goto fail; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 225 | if (must_swab) { |
| 226 | for(i = 0; i < ehdr.e_phnum; i++) { |
| 227 | ph = &phdr[i]; |
| 228 | glue(bswap_phdr, SZ)(ph); |
| 229 | } |
| 230 | } |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 231 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 232 | total_size = 0; |
| 233 | for(i = 0; i < ehdr.e_phnum; i++) { |
| 234 | ph = &phdr[i]; |
| 235 | if (ph->p_type == PT_LOAD) { |
| 236 | mem_size = ph->p_memsz; |
| 237 | /* XXX: avoid allocating */ |
| 238 | data = qemu_mallocz(mem_size); |
| 239 | if (ph->p_filesz > 0) { |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 240 | if (lseek(fd, ph->p_offset, SEEK_SET) < 0) |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 241 | goto fail; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 242 | if (read(fd, data, ph->p_filesz) != ph->p_filesz) |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 243 | goto fail; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 244 | } |
pbrook | 83c1f87 | 2008-10-22 18:20:20 +0000 | [diff] [blame] | 245 | /* address_offset is hack for kernel images that are |
| 246 | linked at the wrong physical address. */ |
| 247 | addr = ph->p_paddr + address_offset; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 248 | |
| 249 | cpu_physical_memory_write_rom(addr, data, mem_size); |
| 250 | |
| 251 | total_size += mem_size; |
ths | 7428711 | 2007-04-01 17:56:37 +0000 | [diff] [blame] | 252 | if (!low || addr < low) |
| 253 | low = addr; |
| 254 | if (!high || (addr + mem_size) > high) |
| 255 | high = addr + mem_size; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 256 | |
| 257 | qemu_free(data); |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 258 | data = NULL; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 259 | } |
| 260 | } |
bellard | 3aee288 | 2006-05-02 20:54:12 +0000 | [diff] [blame] | 261 | qemu_free(phdr); |
ths | 7428711 | 2007-04-01 17:56:37 +0000 | [diff] [blame] | 262 | if (lowaddr) |
ths | 8279006 | 2007-10-17 23:07:31 +0000 | [diff] [blame] | 263 | *lowaddr = (uint64_t)(elf_sword)low; |
ths | 7428711 | 2007-04-01 17:56:37 +0000 | [diff] [blame] | 264 | if (highaddr) |
ths | 8279006 | 2007-10-17 23:07:31 +0000 | [diff] [blame] | 265 | *highaddr = (uint64_t)(elf_sword)high; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 266 | return total_size; |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 267 | fail: |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 268 | qemu_free(data); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 269 | qemu_free(phdr); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 270 | return -1; |
| 271 | } |