bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 1 | static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) |
| 2 | { |
Yeqi Fu | 48805df | 2023-03-15 11:26:49 +0800 | [diff] [blame] | 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 */ |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 16 | } |
| 17 | |
| 18 | static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) |
| 19 | { |
Yeqi Fu | 48805df | 2023-03-15 11:26:49 +0800 | [diff] [blame] | 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 */ |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 | |
Thomas Huth | 5dce07e | 2015-03-09 11:12:52 +0100 | [diff] [blame] | 52 | static void glue(bswap_rela, SZ)(struct elf_rela *rela) |
| 53 | { |
| 54 | bswapSZs(&rela->r_offset); |
| 55 | bswapSZs(&rela->r_info); |
| 56 | bswapSZs((elf_word *)&rela->r_addend); |
| 57 | } |
| 58 | |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 59 | static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 60 | int n, int type) |
| 61 | { |
| 62 | int i; |
| 63 | for(i=0;i<n;i++) { |
| 64 | if (shdr_table[i].sh_type == type) |
| 65 | return shdr_table + i; |
| 66 | } |
| 67 | return NULL; |
| 68 | } |
| 69 | |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 70 | static int glue(symfind, SZ)(const void *s0, const void *s1) |
| 71 | { |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 72 | hwaddr addr = *(hwaddr *)s0; |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 73 | struct elf_sym *sym = (struct elf_sym *)s1; |
| 74 | int result = 0; |
Stefan Weil | c7c530c | 2012-01-05 15:39:39 +0100 | [diff] [blame] | 75 | if (addr < sym->st_value) { |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 76 | result = -1; |
Stefan Weil | c7c530c | 2012-01-05 15:39:39 +0100 | [diff] [blame] | 77 | } else if (addr >= sym->st_value + sym->st_size) { |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 78 | result = 1; |
| 79 | } |
| 80 | return result; |
| 81 | } |
| 82 | |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 83 | static const char *glue(lookup_symbol, SZ)(struct syminfo *s, |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 84 | hwaddr orig_addr) |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 85 | { |
| 86 | struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 87 | struct elf_sym *sym; |
| 88 | |
Stefan Weil | c7c530c | 2012-01-05 15:39:39 +0100 | [diff] [blame] | 89 | sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), |
| 90 | glue(symfind, SZ)); |
Blue Swirl | 660f11b | 2009-07-31 21:16:51 +0000 | [diff] [blame] | 91 | if (sym != NULL) { |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 92 | return s->disas_strtab + sym->st_name; |
| 93 | } |
| 94 | |
| 95 | return ""; |
| 96 | } |
| 97 | |
| 98 | static int glue(symcmp, SZ)(const void *s0, const void *s1) |
| 99 | { |
| 100 | struct elf_sym *sym0 = (struct elf_sym *)s0; |
| 101 | struct elf_sym *sym1 = (struct elf_sym *)s1; |
| 102 | return (sym0->st_value < sym1->st_value) |
| 103 | ? -1 |
| 104 | : ((sym0->st_value > sym1->st_value) ? 1 : 0); |
| 105 | } |
| 106 | |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 107 | static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, |
| 108 | int clear_lsb, symbol_fn_t sym_cb) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 109 | { |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 110 | struct elf_shdr *symtab, *strtab; |
| 111 | g_autofree struct elf_shdr *shdr_table = NULL; |
| 112 | g_autofree struct elf_sym *syms = NULL; |
| 113 | g_autofree char *str = NULL; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 114 | struct syminfo *s; |
| 115 | int nsyms, i; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 116 | |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 117 | shdr_table = load_at(fd, ehdr->e_shoff, |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 118 | sizeof(struct elf_shdr) * ehdr->e_shnum); |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 119 | if (!shdr_table) { |
Bin Meng | c1dadb8 | 2022-10-24 15:28:02 +0800 | [diff] [blame] | 120 | return; |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 121 | } |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 122 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 123 | if (must_swab) { |
| 124 | for (i = 0; i < ehdr->e_shnum; i++) { |
| 125 | glue(bswap_shdr, SZ)(shdr_table + i); |
| 126 | } |
| 127 | } |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 128 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 129 | symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 130 | if (!symtab) { |
| 131 | return; |
| 132 | } |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 133 | syms = load_at(fd, symtab->sh_offset, symtab->sh_size); |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 134 | if (!syms) { |
| 135 | return; |
| 136 | } |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 137 | |
| 138 | nsyms = symtab->sh_size / sizeof(struct elf_sym); |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 139 | |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 140 | /* String table */ |
| 141 | if (symtab->sh_link >= ehdr->e_shnum) { |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 142 | return; |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 143 | } |
| 144 | strtab = &shdr_table[symtab->sh_link]; |
| 145 | |
| 146 | str = load_at(fd, strtab->sh_offset, strtab->sh_size); |
| 147 | if (!str) { |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 148 | return; |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 149 | } |
| 150 | |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 151 | i = 0; |
| 152 | while (i < nsyms) { |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 153 | if (must_swab) { |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 154 | glue(bswap_sym, SZ)(&syms[i]); |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 155 | } |
| 156 | if (sym_cb) { |
| 157 | sym_cb(str + syms[i].st_name, syms[i].st_info, |
| 158 | syms[i].st_value, syms[i].st_size); |
| 159 | } |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 160 | /* We are only interested in function symbols. |
| 161 | Throw everything else away. */ |
| 162 | if (syms[i].st_shndx == SHN_UNDEF || |
| 163 | syms[i].st_shndx >= SHN_LORESERVE || |
| 164 | ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { |
| 165 | nsyms--; |
| 166 | if (i < nsyms) { |
| 167 | syms[i] = syms[nsyms]; |
| 168 | } |
| 169 | continue; |
| 170 | } |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 171 | if (clear_lsb) { |
| 172 | /* The bottom address bit marks a Thumb or MIPS16 symbol. */ |
| 173 | syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; |
| 174 | } |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 175 | i++; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 176 | } |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 177 | |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 178 | /* check we have symbols left */ |
| 179 | if (nsyms == 0) { |
| 180 | return; |
| 181 | } |
| 182 | |
| 183 | syms = g_realloc(syms, nsyms * sizeof(*syms)); |
Markus Armbruster | 8ce3c44 | 2014-08-20 20:38:11 +0200 | [diff] [blame] | 184 | qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); |
| 185 | for (i = 0; i < nsyms - 1; i++) { |
| 186 | if (syms[i].st_size == 0) { |
| 187 | syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; |
Stefan Weil | e403e43 | 2010-08-09 16:43:53 +0200 | [diff] [blame] | 188 | } |
Aurelien Jarno | 3e372cf | 2009-12-28 21:18:12 +0100 | [diff] [blame] | 189 | } |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 190 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 191 | /* Commit */ |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 192 | s = g_malloc0(sizeof(*s)); |
pbrook | 49918a7 | 2008-10-22 15:11:31 +0000 | [diff] [blame] | 193 | s->lookup_symbol = glue(lookup_symbol, SZ); |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 194 | glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 195 | s->disas_num_syms = nsyms; |
Alex Bennée | 040425f | 2020-04-03 20:11:39 +0100 | [diff] [blame] | 196 | s->disas_strtab = g_steal_pointer(&str); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 197 | s->next = syminfos; |
| 198 | syminfos = s; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 199 | } |
| 200 | |
Thomas Huth | 5dce07e | 2015-03-09 11:12:52 +0100 | [diff] [blame] | 201 | static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, |
| 202 | uint64_t (*translate_fn)(void *, uint64_t), |
| 203 | void *translate_opaque, uint8_t *data, |
| 204 | struct elf_phdr *ph, int elf_machine) |
| 205 | { |
| 206 | struct elf_shdr *reltab, *shdr_table = NULL; |
| 207 | struct elf_rela *rels = NULL; |
| 208 | int nrels, i, ret = -1; |
| 209 | elf_word wordval; |
| 210 | void *addr; |
| 211 | |
| 212 | shdr_table = load_at(fd, ehdr->e_shoff, |
| 213 | sizeof(struct elf_shdr) * ehdr->e_shnum); |
| 214 | if (!shdr_table) { |
| 215 | return -1; |
| 216 | } |
| 217 | if (must_swab) { |
| 218 | for (i = 0; i < ehdr->e_shnum; i++) { |
| 219 | glue(bswap_shdr, SZ)(&shdr_table[i]); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); |
| 224 | if (!reltab) { |
| 225 | goto fail; |
| 226 | } |
| 227 | rels = load_at(fd, reltab->sh_offset, reltab->sh_size); |
| 228 | if (!rels) { |
| 229 | goto fail; |
| 230 | } |
| 231 | nrels = reltab->sh_size / sizeof(struct elf_rela); |
| 232 | |
| 233 | for (i = 0; i < nrels; i++) { |
| 234 | if (must_swab) { |
| 235 | glue(bswap_rela, SZ)(&rels[i]); |
| 236 | } |
| 237 | if (rels[i].r_offset < ph->p_vaddr || |
| 238 | rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { |
| 239 | continue; |
| 240 | } |
| 241 | addr = &data[rels[i].r_offset - ph->p_vaddr]; |
| 242 | switch (elf_machine) { |
| 243 | case EM_S390: |
| 244 | switch (rels[i].r_info) { |
| 245 | case R_390_RELATIVE: |
| 246 | wordval = *(elf_word *)addr; |
| 247 | if (must_swab) { |
| 248 | bswapSZs(&wordval); |
| 249 | } |
| 250 | wordval = translate_fn(translate_opaque, wordval); |
| 251 | if (must_swab) { |
| 252 | bswapSZs(&wordval); |
| 253 | } |
| 254 | *(elf_word *)addr = wordval; |
| 255 | break; |
| 256 | default: |
| 257 | fprintf(stderr, "Unsupported relocation type %i!\n", |
| 258 | (int)rels[i].r_info); |
| 259 | } |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | ret = 0; |
| 264 | fail: |
| 265 | g_free(rels); |
| 266 | g_free(shdr_table); |
| 267 | return ret; |
| 268 | } |
| 269 | |
Liam Merwick | 696aa04 | 2019-01-15 12:18:04 +0000 | [diff] [blame] | 270 | /* |
| 271 | * Given 'nhdr', a pointer to a range of ELF Notes, search through them |
| 272 | * for a note matching type 'elf_note_type' and return a pointer to |
| 273 | * the matching ELF note. |
| 274 | */ |
| 275 | static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, |
| 276 | elf_word note_size, |
| 277 | elf_word phdr_align, |
| 278 | elf_word elf_note_type) |
| 279 | { |
| 280 | elf_word nhdr_size = sizeof(struct elf_note); |
| 281 | elf_word elf_note_entry_offset = 0; |
| 282 | elf_word note_type; |
| 283 | elf_word nhdr_namesz; |
| 284 | elf_word nhdr_descsz; |
| 285 | |
| 286 | if (nhdr == NULL) { |
| 287 | return NULL; |
| 288 | } |
| 289 | |
| 290 | note_type = nhdr->n_type; |
| 291 | while (note_type != elf_note_type) { |
| 292 | nhdr_namesz = nhdr->n_namesz; |
| 293 | nhdr_descsz = nhdr->n_descsz; |
| 294 | |
| 295 | elf_note_entry_offset = nhdr_size + |
| 296 | QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + |
| 297 | QEMU_ALIGN_UP(nhdr_descsz, phdr_align); |
| 298 | |
| 299 | /* |
| 300 | * If the offset calculated in this iteration exceeds the |
| 301 | * supplied size, we are done and no matching note was found. |
| 302 | */ |
| 303 | if (elf_note_entry_offset > note_size) { |
| 304 | return NULL; |
| 305 | } |
| 306 | |
| 307 | /* skip to the next ELF Note entry */ |
| 308 | nhdr = (void *)nhdr + elf_note_entry_offset; |
| 309 | note_type = nhdr->n_type; |
| 310 | } |
| 311 | |
| 312 | return nhdr; |
| 313 | } |
| 314 | |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 315 | static ssize_t glue(load_elf, SZ)(const char *name, int fd, |
| 316 | uint64_t (*elf_note_fn)(void *, void *, bool), |
| 317 | uint64_t (*translate_fn)(void *, uint64_t), |
| 318 | void *translate_opaque, |
| 319 | int must_swab, uint64_t *pentry, |
| 320 | uint64_t *lowaddr, uint64_t *highaddr, |
| 321 | uint32_t *pflags, int elf_machine, |
| 322 | int clear_lsb, int data_swab, |
| 323 | AddressSpace *as, bool load_rom, |
| 324 | symbol_fn_t sym_cb) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 325 | { |
| 326 | struct elfhdr ehdr; |
| 327 | struct elf_phdr *phdr = NULL, *ph; |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 328 | int size, i; |
| 329 | ssize_t total_size; |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 330 | elf_word mem_size, file_size, data_offset; |
Paul Brook | fd93a79 | 2009-06-05 15:16:41 +0100 | [diff] [blame] | 331 | uint64_t addr, low = (uint64_t)-1, high = 0; |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 332 | GMappedFile *mapped_file = NULL; |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 333 | uint8_t *data = NULL; |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 334 | ssize_t ret = ELF_LOAD_FAILED; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 335 | |
| 336 | if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) |
| 337 | goto fail; |
| 338 | if (must_swab) { |
| 339 | glue(bswap_ehdr, SZ)(&ehdr); |
| 340 | } |
| 341 | |
Alistair Francis | 8cf6e9d | 2016-09-22 18:13:07 +0100 | [diff] [blame] | 342 | if (elf_machine <= EM_NONE) { |
| 343 | /* The caller didn't specify an ARCH, we can figure it out */ |
| 344 | elf_machine = ehdr.e_machine; |
| 345 | } |
| 346 | |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 347 | switch (elf_machine) { |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 348 | case EM_PPC64: |
Peter Crosthwaite | 7cc4722 | 2015-08-17 21:53:16 -0700 | [diff] [blame] | 349 | if (ehdr.e_machine != EM_PPC64) { |
| 350 | if (ehdr.e_machine != EM_PPC) { |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 351 | ret = ELF_LOAD_WRONG_ARCH; |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 352 | goto fail; |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 353 | } |
Peter Crosthwaite | 7cc4722 | 2015-08-17 21:53:16 -0700 | [diff] [blame] | 354 | } |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 355 | break; |
| 356 | case EM_X86_64: |
Peter Crosthwaite | 7cc4722 | 2015-08-17 21:53:16 -0700 | [diff] [blame] | 357 | if (ehdr.e_machine != EM_X86_64) { |
| 358 | if (ehdr.e_machine != EM_386) { |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 359 | ret = ELF_LOAD_WRONG_ARCH; |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 360 | goto fail; |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 361 | } |
Peter Crosthwaite | 7cc4722 | 2015-08-17 21:53:16 -0700 | [diff] [blame] | 362 | } |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 363 | break; |
Edgar E. Iglesias | 16f0441 | 2010-05-24 11:14:04 +0200 | [diff] [blame] | 364 | case EM_MICROBLAZE: |
Peter Crosthwaite | 7cc4722 | 2015-08-17 21:53:16 -0700 | [diff] [blame] | 365 | if (ehdr.e_machine != EM_MICROBLAZE) { |
| 366 | if (ehdr.e_machine != EM_MICROBLAZE_OLD) { |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 367 | ret = ELF_LOAD_WRONG_ARCH; |
Edgar E. Iglesias | 16f0441 | 2010-05-24 11:14:04 +0200 | [diff] [blame] | 368 | goto fail; |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 369 | } |
Peter Crosthwaite | 7cc4722 | 2015-08-17 21:53:16 -0700 | [diff] [blame] | 370 | } |
Edgar E. Iglesias | 16f0441 | 2010-05-24 11:14:04 +0200 | [diff] [blame] | 371 | break; |
Aleksandar Rikalo | 56f2604 | 2018-08-07 13:03:13 +0200 | [diff] [blame] | 372 | case EM_MIPS: |
| 373 | case EM_NANOMIPS: |
| 374 | if ((ehdr.e_machine != EM_MIPS) && |
| 375 | (ehdr.e_machine != EM_NANOMIPS)) { |
| 376 | ret = ELF_LOAD_WRONG_ARCH; |
| 377 | goto fail; |
| 378 | } |
| 379 | break; |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 380 | default: |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 381 | if (elf_machine != ehdr.e_machine) { |
| 382 | ret = ELF_LOAD_WRONG_ARCH; |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 383 | goto fail; |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 384 | } |
blueswir1 | 7f70c93 | 2009-03-13 21:16:24 +0000 | [diff] [blame] | 385 | } |
ths | 9042c0e | 2006-12-23 14:18:40 +0000 | [diff] [blame] | 386 | |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 387 | if (pflags) { |
Richard Henderson | e1fee58 | 2023-09-19 16:25:54 +0200 | [diff] [blame] | 388 | *pflags = ehdr.e_flags; |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 389 | } |
Richard Henderson | e1fee58 | 2023-09-19 16:25:54 +0200 | [diff] [blame] | 390 | if (pentry) { |
| 391 | *pentry = ehdr.e_entry; |
| 392 | } |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 393 | |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 394 | glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 395 | |
| 396 | size = ehdr.e_phnum * sizeof(phdr[0]); |
Stefan Weil | 23bf2e7 | 2015-03-14 16:42:01 +0100 | [diff] [blame] | 397 | if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { |
| 398 | goto fail; |
| 399 | } |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 400 | phdr = g_malloc0(size); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 401 | if (!phdr) |
| 402 | goto fail; |
| 403 | if (read(fd, phdr, size) != size) |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 404 | goto fail; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 405 | if (must_swab) { |
| 406 | for(i = 0; i < ehdr.e_phnum; i++) { |
| 407 | ph = &phdr[i]; |
| 408 | glue(bswap_phdr, SZ)(ph); |
| 409 | } |
| 410 | } |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 411 | |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 412 | /* |
| 413 | * Since we want to be able to modify the mapped buffer, we set the |
Philippe Mathieu-Daudé | f413e51 | 2021-02-25 19:13:44 +0100 | [diff] [blame] | 414 | * 'writable' parameter to 'true'. Modifications to the buffer are not |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 415 | * written back to the file. |
| 416 | */ |
| 417 | mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); |
| 418 | if (!mapped_file) { |
| 419 | goto fail; |
| 420 | } |
| 421 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 422 | total_size = 0; |
| 423 | for(i = 0; i < ehdr.e_phnum; i++) { |
| 424 | ph = &phdr[i]; |
| 425 | if (ph->p_type == PT_LOAD) { |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 426 | mem_size = ph->p_memsz; /* Size of the ROM */ |
| 427 | file_size = ph->p_filesz; /* Size of the allocated data */ |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 428 | data_offset = ph->p_offset; /* Offset where the data is located */ |
| 429 | |
| 430 | if (file_size > 0) { |
| 431 | if (g_mapped_file_get_length(mapped_file) < |
| 432 | file_size + data_offset) { |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 433 | goto fail; |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 434 | } |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 435 | |
| 436 | data = (uint8_t *)g_mapped_file_get_contents(mapped_file); |
| 437 | data += data_offset; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 438 | } |
Peter Maydell | bf17333 | 2017-09-04 15:21:53 +0100 | [diff] [blame] | 439 | |
| 440 | /* The ELF spec is somewhat vague about the purpose of the |
| 441 | * physical address field. One common use in the embedded world |
| 442 | * is that physical address field specifies the load address |
| 443 | * and the virtual address field specifies the execution address. |
| 444 | * Segments are packed into ROM or flash, and the relocation |
| 445 | * and zero-initialization of data is done at runtime. This |
| 446 | * means that the memsz header represents the runtime size of the |
| 447 | * segment, but the filesz represents the loadtime size. If |
| 448 | * we try to honour the memsz value for an ELF file like this |
| 449 | * we will end up with overlapping segments (which the |
| 450 | * loader.c code will later reject). |
| 451 | * We support ELF files using this scheme by by checking whether |
| 452 | * paddr + memsz for this segment would overlap with any other |
| 453 | * segment. If so, then we assume it's using this scheme and |
| 454 | * truncate the loaded segment to the filesz size. |
| 455 | * If the segment considered as being memsz size doesn't overlap |
| 456 | * then we use memsz for the segment length, to handle ELF files |
| 457 | * which assume that the loader will do the zero-initialization. |
| 458 | */ |
| 459 | if (mem_size > file_size) { |
| 460 | /* If this segment's zero-init portion overlaps another |
| 461 | * segment's data or zero-init portion, then truncate this one. |
| 462 | * Invalid ELF files where the segments overlap even when |
| 463 | * only file_size bytes are loaded will be rejected by |
| 464 | * the ROM overlap check in loader.c, so we don't try to |
| 465 | * explicitly detect those here. |
| 466 | */ |
| 467 | int j; |
| 468 | elf_word zero_start = ph->p_paddr + file_size; |
| 469 | elf_word zero_end = ph->p_paddr + mem_size; |
| 470 | |
| 471 | for (j = 0; j < ehdr.e_phnum; j++) { |
| 472 | struct elf_phdr *jph = &phdr[j]; |
| 473 | |
| 474 | if (i != j && jph->p_type == PT_LOAD) { |
| 475 | elf_word other_start = jph->p_paddr; |
| 476 | elf_word other_end = jph->p_paddr + jph->p_memsz; |
| 477 | |
| 478 | if (!(other_start >= zero_end || |
| 479 | zero_start >= other_end)) { |
| 480 | mem_size = file_size; |
| 481 | break; |
| 482 | } |
| 483 | } |
| 484 | } |
| 485 | } |
| 486 | |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 487 | if (mem_size > SSIZE_MAX - total_size) { |
Stefano Garzarella | 41a2635 | 2019-09-10 16:22:23 +0200 | [diff] [blame] | 488 | ret = ELF_LOAD_TOO_BIG; |
| 489 | goto fail; |
| 490 | } |
| 491 | |
pbrook | 83c1f87 | 2008-10-22 18:20:20 +0000 | [diff] [blame] | 492 | /* address_offset is hack for kernel images that are |
| 493 | linked at the wrong physical address. */ |
Aurelien Jarno | 409dbce | 2010-03-14 21:20:59 +0100 | [diff] [blame] | 494 | if (translate_fn) { |
| 495 | addr = translate_fn(translate_opaque, ph->p_paddr); |
Thomas Huth | 5dce07e | 2015-03-09 11:12:52 +0100 | [diff] [blame] | 496 | glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, |
| 497 | translate_opaque, data, ph, elf_machine); |
Aurelien Jarno | 409dbce | 2010-03-14 21:20:59 +0100 | [diff] [blame] | 498 | } else { |
| 499 | addr = ph->p_paddr; |
| 500 | } |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 501 | |
Peter Crosthwaite | 7ef295e | 2016-03-04 11:30:21 +0000 | [diff] [blame] | 502 | if (data_swab) { |
Anastasia Belova | 410c2a4 | 2024-01-15 12:22:16 +0300 | [diff] [blame] | 503 | elf_word j; |
Peter Crosthwaite | 7ef295e | 2016-03-04 11:30:21 +0000 | [diff] [blame] | 504 | for (j = 0; j < file_size; j += (1 << data_swab)) { |
| 505 | uint8_t *dp = data + j; |
| 506 | switch (data_swab) { |
| 507 | case (1): |
| 508 | *(uint16_t *)dp = bswap16(*(uint16_t *)dp); |
| 509 | break; |
| 510 | case (2): |
| 511 | *(uint32_t *)dp = bswap32(*(uint32_t *)dp); |
| 512 | break; |
| 513 | case (3): |
| 514 | *(uint64_t *)dp = bswap64(*(uint64_t *)dp); |
| 515 | break; |
| 516 | default: |
| 517 | g_assert_not_reached(); |
| 518 | } |
| 519 | } |
| 520 | } |
| 521 | |
Henning Schild | 7e9c7ff | 2012-09-05 14:56:39 +0200 | [diff] [blame] | 522 | /* the entry pointer in the ELF header is a virtual |
| 523 | * address, if the text segments paddr and vaddr differ |
| 524 | * we need to adjust the entry */ |
| 525 | if (pentry && !translate_fn && |
| 526 | ph->p_vaddr != ph->p_paddr && |
| 527 | ehdr.e_entry >= ph->p_vaddr && |
| 528 | ehdr.e_entry < ph->p_vaddr + ph->p_filesz && |
| 529 | ph->p_flags & PF_X) { |
| 530 | *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; |
| 531 | } |
| 532 | |
Alex Bennée | 1fed4cd | 2024-02-07 16:38:02 +0000 | [diff] [blame] | 533 | /* Some ELF files really do have segments of zero size; |
| 534 | * just ignore them rather than trying to create empty |
| 535 | * ROM blobs, because the zero-length blob can falsely |
| 536 | * trigger the overlapping-ROM-blobs check. |
| 537 | */ |
| 538 | if (mem_size != 0) { |
| 539 | if (load_rom) { |
| 540 | g_autofree char *label = |
| 541 | g_strdup_printf("%s ELF program header segment %d", |
| 542 | name, i); |
Peter Maydell | f33e5e6 | 2017-09-04 15:21:53 +0100 | [diff] [blame] | 543 | |
Alex Bennée | 1fed4cd | 2024-02-07 16:38:02 +0000 | [diff] [blame] | 544 | /* |
| 545 | * rom_add_elf_program() takes its own reference to |
| 546 | * 'mapped_file'. |
| 547 | */ |
| 548 | rom_add_elf_program(label, mapped_file, data, file_size, |
| 549 | mem_size, addr, as); |
| 550 | } else { |
| 551 | MemTxResult res; |
Philippe Mathieu-Daudé | 5579b52 | 2020-05-18 17:53:05 +0200 | [diff] [blame] | 552 | |
Alex Bennée | 1fed4cd | 2024-02-07 16:38:02 +0000 | [diff] [blame] | 553 | res = address_space_write(as ? as : &address_space_memory, |
| 554 | addr, MEMTXATTRS_UNSPECIFIED, |
| 555 | data, file_size); |
Philippe Mathieu-Daudé | 5579b52 | 2020-05-18 17:53:05 +0200 | [diff] [blame] | 556 | if (res != MEMTX_OK) { |
| 557 | goto fail; |
| 558 | } |
Alex Bennée | 1fed4cd | 2024-02-07 16:38:02 +0000 | [diff] [blame] | 559 | /* |
| 560 | * We need to zero'ify the space that is not copied |
| 561 | * from file |
| 562 | */ |
| 563 | if (file_size < mem_size) { |
| 564 | res = address_space_set(as ? as : &address_space_memory, |
| 565 | addr + file_size, 0, |
| 566 | mem_size - file_size, |
| 567 | MEMTXATTRS_UNSPECIFIED); |
| 568 | if (res != MEMTX_OK) { |
| 569 | goto fail; |
| 570 | } |
| 571 | } |
Peter Maydell | f33e5e6 | 2017-09-04 15:21:53 +0100 | [diff] [blame] | 572 | } |
Farhan Ali | 34f1b23 | 2016-11-07 10:50:30 -0500 | [diff] [blame] | 573 | } |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 574 | |
| 575 | total_size += mem_size; |
Paul Brook | fd93a79 | 2009-06-05 15:16:41 +0100 | [diff] [blame] | 576 | if (addr < low) |
ths | 7428711 | 2007-04-01 17:56:37 +0000 | [diff] [blame] | 577 | low = addr; |
Paul Brook | fd93a79 | 2009-06-05 15:16:41 +0100 | [diff] [blame] | 578 | if ((addr + mem_size) > high) |
ths | 7428711 | 2007-04-01 17:56:37 +0000 | [diff] [blame] | 579 | high = addr + mem_size; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 580 | |
bellard | 9ee3c02 | 2006-04-26 22:05:26 +0000 | [diff] [blame] | 581 | data = NULL; |
Liam Merwick | 696aa04 | 2019-01-15 12:18:04 +0000 | [diff] [blame] | 582 | |
| 583 | } else if (ph->p_type == PT_NOTE && elf_note_fn) { |
| 584 | struct elf_note *nhdr = NULL; |
| 585 | |
| 586 | file_size = ph->p_filesz; /* Size of the range of ELF notes */ |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 587 | data_offset = ph->p_offset; /* Offset where the notes are located */ |
| 588 | |
| 589 | if (file_size > 0) { |
| 590 | if (g_mapped_file_get_length(mapped_file) < |
| 591 | file_size + data_offset) { |
Liam Merwick | 696aa04 | 2019-01-15 12:18:04 +0000 | [diff] [blame] | 592 | goto fail; |
| 593 | } |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 594 | |
| 595 | data = (uint8_t *)g_mapped_file_get_contents(mapped_file); |
| 596 | data += data_offset; |
Liam Merwick | 696aa04 | 2019-01-15 12:18:04 +0000 | [diff] [blame] | 597 | } |
| 598 | |
| 599 | /* |
| 600 | * Search the ELF notes to find one with a type matching the |
| 601 | * value passed in via 'translate_opaque' |
| 602 | */ |
| 603 | nhdr = (struct elf_note *)data; |
| 604 | assert(translate_opaque != NULL); |
| 605 | nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, |
| 606 | *(uint64_t *)translate_opaque); |
| 607 | if (nhdr != NULL) { |
David Edmondson | 64d70277 | 2021-03-02 09:03:14 +0000 | [diff] [blame] | 608 | elf_note_fn((void *)nhdr, (void *)&ph->p_align, SZ == 64); |
Liam Merwick | 696aa04 | 2019-01-15 12:18:04 +0000 | [diff] [blame] | 609 | } |
Liam Merwick | 696aa04 | 2019-01-15 12:18:04 +0000 | [diff] [blame] | 610 | data = NULL; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 611 | } |
| 612 | } |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 613 | |
Richard Henderson | e1fee58 | 2023-09-19 16:25:54 +0200 | [diff] [blame] | 614 | if (lowaddr) { |
| 615 | *lowaddr = low; |
| 616 | } |
| 617 | if (highaddr) { |
| 618 | *highaddr = high; |
| 619 | } |
Stefano Garzarella | 816b9fe | 2019-07-24 16:31:04 +0200 | [diff] [blame] | 620 | ret = total_size; |
ths | 04d4b0c | 2006-12-23 15:18:47 +0000 | [diff] [blame] | 621 | fail: |
Peter Maydell | a1ecb43 | 2020-04-23 21:20:11 +0100 | [diff] [blame] | 622 | if (mapped_file) { |
| 623 | g_mapped_file_unref(mapped_file); |
| 624 | } |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 625 | g_free(phdr); |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 626 | return ret; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 627 | } |