From: Philippe Mathieu-Daudé Date: Thu, 18 Apr 2024 14:49:16 +0000 (+0200) Subject: hw/elf_ops: Rename elf_ops.h -> elf_ops.h.inc X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=159fb790e48992f74644b0b0876615e8caacbfe4;p=qemu.git hw/elf_ops: Rename elf_ops.h -> elf_ops.h.inc Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented in the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore rename "hw/elf_ops.h" as "hw/elf_ops.h.inc". Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20240424173333.96148-2-philmd@linaro.org> --- diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index baf2f63d2f..833fa3bd05 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -383,7 +383,7 @@ static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) return ""; } -/* FIXME: This should use elf_ops.h */ +/* FIXME: This should use elf_ops.h.inc */ static int symcmp(const void *s0, const void *s1) { struct elf_sym *sym0 = (struct elf_sym *)s0; diff --git a/hw/core/loader.c b/hw/core/loader.c index b8e52f3fb0..2f8105d7de 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -305,7 +305,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define elf_word uint32_t #define elf_sword int32_t #define bswapSZs bswap32s -#include "hw/elf_ops.h" +#include "hw/elf_ops.h.inc" #undef elfhdr #undef elf_phdr @@ -327,7 +327,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define elf_sword int64_t #define bswapSZs bswap64s #define SZ 64 -#include "hw/elf_ops.h" +#include "hw/elf_ops.h.inc" const char *load_elf_strerror(ssize_t error) { diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h deleted file mode 100644 index 9c35d1b9da..0000000000 --- a/include/hw/elf_ops.h +++ /dev/null @@ -1,627 +0,0 @@ -static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) -{ - bswap16s(&ehdr->e_type); /* Object file type */ - bswap16s(&ehdr->e_machine); /* Architecture */ - bswap32s(&ehdr->e_version); /* Object file version */ - bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ - bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ - bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ - bswap32s(&ehdr->e_flags); /* Processor-specific flags */ - bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ - bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ - bswap16s(&ehdr->e_phnum); /* Program header table entry count */ - bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ - bswap16s(&ehdr->e_shnum); /* Section header table entry count */ - bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ -} - -static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) -{ - bswap32s(&phdr->p_type); /* Segment type */ - bswapSZs(&phdr->p_offset); /* Segment file offset */ - bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ - bswapSZs(&phdr->p_paddr); /* Segment physical address */ - bswapSZs(&phdr->p_filesz); /* Segment size in file */ - bswapSZs(&phdr->p_memsz); /* Segment size in memory */ - bswap32s(&phdr->p_flags); /* Segment flags */ - bswapSZs(&phdr->p_align); /* Segment alignment */ -} - -static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) -{ - bswap32s(&shdr->sh_name); - bswap32s(&shdr->sh_type); - bswapSZs(&shdr->sh_flags); - bswapSZs(&shdr->sh_addr); - bswapSZs(&shdr->sh_offset); - bswapSZs(&shdr->sh_size); - bswap32s(&shdr->sh_link); - bswap32s(&shdr->sh_info); - bswapSZs(&shdr->sh_addralign); - bswapSZs(&shdr->sh_entsize); -} - -static void glue(bswap_sym, SZ)(struct elf_sym *sym) -{ - bswap32s(&sym->st_name); - bswapSZs(&sym->st_value); - bswapSZs(&sym->st_size); - bswap16s(&sym->st_shndx); -} - -static void glue(bswap_rela, SZ)(struct elf_rela *rela) -{ - bswapSZs(&rela->r_offset); - bswapSZs(&rela->r_info); - bswapSZs((elf_word *)&rela->r_addend); -} - -static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, - int n, int type) -{ - int i; - for(i=0;ist_value) { - result = -1; - } else if (addr >= sym->st_value + sym->st_size) { - result = 1; - } - return result; -} - -static const char *glue(lookup_symbol, SZ)(struct syminfo *s, - hwaddr orig_addr) -{ - struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); - struct elf_sym *sym; - - sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), - glue(symfind, SZ)); - if (sym != NULL) { - return s->disas_strtab + sym->st_name; - } - - return ""; -} - -static int glue(symcmp, SZ)(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); -} - -static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, - int clear_lsb, symbol_fn_t sym_cb) -{ - struct elf_shdr *symtab, *strtab; - g_autofree struct elf_shdr *shdr_table = NULL; - g_autofree struct elf_sym *syms = NULL; - g_autofree char *str = NULL; - struct syminfo *s; - int nsyms, i; - - shdr_table = load_at(fd, ehdr->e_shoff, - sizeof(struct elf_shdr) * ehdr->e_shnum); - if (!shdr_table) { - return; - } - - if (must_swab) { - for (i = 0; i < ehdr->e_shnum; i++) { - glue(bswap_shdr, SZ)(shdr_table + i); - } - } - - symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); - if (!symtab) { - return; - } - syms = load_at(fd, symtab->sh_offset, symtab->sh_size); - if (!syms) { - return; - } - - nsyms = symtab->sh_size / sizeof(struct elf_sym); - - /* String table */ - if (symtab->sh_link >= ehdr->e_shnum) { - return; - } - strtab = &shdr_table[symtab->sh_link]; - - str = load_at(fd, strtab->sh_offset, strtab->sh_size); - if (!str) { - return; - } - - i = 0; - while (i < nsyms) { - if (must_swab) { - glue(bswap_sym, SZ)(&syms[i]); - } - if (sym_cb) { - sym_cb(str + syms[i].st_name, syms[i].st_info, - syms[i].st_value, syms[i].st_size); - } - /* We are only interested in function symbols. - Throw everything else away. */ - 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 (clear_lsb) { - /* The bottom address bit marks a Thumb or MIPS16 symbol. */ - syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; - } - i++; - } - - /* check we have symbols left */ - if (nsyms == 0) { - return; - } - - syms = g_realloc(syms, nsyms * sizeof(*syms)); - qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); - for (i = 0; i < nsyms - 1; i++) { - if (syms[i].st_size == 0) { - syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; - } - } - - /* Commit */ - s = g_malloc0(sizeof(*s)); - s->lookup_symbol = glue(lookup_symbol, SZ); - glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); - s->disas_num_syms = nsyms; - s->disas_strtab = g_steal_pointer(&str); - s->next = syminfos; - syminfos = s; -} - -static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint8_t *data, - struct elf_phdr *ph, int elf_machine) -{ - struct elf_shdr *reltab, *shdr_table = NULL; - struct elf_rela *rels = NULL; - int nrels, i, ret = -1; - elf_word wordval; - void *addr; - - shdr_table = load_at(fd, ehdr->e_shoff, - sizeof(struct elf_shdr) * ehdr->e_shnum); - if (!shdr_table) { - return -1; - } - if (must_swab) { - for (i = 0; i < ehdr->e_shnum; i++) { - glue(bswap_shdr, SZ)(&shdr_table[i]); - } - } - - reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); - if (!reltab) { - goto fail; - } - rels = load_at(fd, reltab->sh_offset, reltab->sh_size); - if (!rels) { - goto fail; - } - nrels = reltab->sh_size / sizeof(struct elf_rela); - - for (i = 0; i < nrels; i++) { - if (must_swab) { - glue(bswap_rela, SZ)(&rels[i]); - } - if (rels[i].r_offset < ph->p_vaddr || - rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { - continue; - } - addr = &data[rels[i].r_offset - ph->p_vaddr]; - switch (elf_machine) { - case EM_S390: - switch (rels[i].r_info) { - case R_390_RELATIVE: - wordval = *(elf_word *)addr; - if (must_swab) { - bswapSZs(&wordval); - } - wordval = translate_fn(translate_opaque, wordval); - if (must_swab) { - bswapSZs(&wordval); - } - *(elf_word *)addr = wordval; - break; - default: - fprintf(stderr, "Unsupported relocation type %i!\n", - (int)rels[i].r_info); - } - } - } - - ret = 0; -fail: - g_free(rels); - g_free(shdr_table); - return ret; -} - -/* - * Given 'nhdr', a pointer to a range of ELF Notes, search through them - * for a note matching type 'elf_note_type' and return a pointer to - * the matching ELF note. - */ -static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, - elf_word note_size, - elf_word phdr_align, - elf_word elf_note_type) -{ - elf_word nhdr_size = sizeof(struct elf_note); - elf_word elf_note_entry_offset = 0; - elf_word note_type; - elf_word nhdr_namesz; - elf_word nhdr_descsz; - - if (nhdr == NULL) { - return NULL; - } - - note_type = nhdr->n_type; - while (note_type != elf_note_type) { - nhdr_namesz = nhdr->n_namesz; - nhdr_descsz = nhdr->n_descsz; - - elf_note_entry_offset = nhdr_size + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + - QEMU_ALIGN_UP(nhdr_descsz, phdr_align); - - /* - * If the offset calculated in this iteration exceeds the - * supplied size, we are done and no matching note was found. - */ - if (elf_note_entry_offset > note_size) { - return NULL; - } - - /* skip to the next ELF Note entry */ - nhdr = (void *)nhdr + elf_note_entry_offset; - note_type = nhdr->n_type; - } - - return nhdr; -} - -static ssize_t glue(load_elf, SZ)(const char *name, int fd, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, - int must_swab, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, - symbol_fn_t sym_cb) -{ - struct elfhdr ehdr; - struct elf_phdr *phdr = NULL, *ph; - int size, i; - ssize_t total_size; - elf_word mem_size, file_size, data_offset; - uint64_t addr, low = (uint64_t)-1, high = 0; - GMappedFile *mapped_file = NULL; - uint8_t *data = NULL; - ssize_t ret = ELF_LOAD_FAILED; - - if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) - goto fail; - if (must_swab) { - glue(bswap_ehdr, SZ)(&ehdr); - } - - if (elf_machine <= EM_NONE) { - /* The caller didn't specify an ARCH, we can figure it out */ - elf_machine = ehdr.e_machine; - } - - switch (elf_machine) { - case EM_PPC64: - if (ehdr.e_machine != EM_PPC64) { - if (ehdr.e_machine != EM_PPC) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - break; - case EM_X86_64: - if (ehdr.e_machine != EM_X86_64) { - if (ehdr.e_machine != EM_386) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - break; - case EM_MICROBLAZE: - if (ehdr.e_machine != EM_MICROBLAZE) { - if (ehdr.e_machine != EM_MICROBLAZE_OLD) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - break; - case EM_MIPS: - case EM_NANOMIPS: - if ((ehdr.e_machine != EM_MIPS) && - (ehdr.e_machine != EM_NANOMIPS)) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - break; - default: - if (elf_machine != ehdr.e_machine) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - - if (pflags) { - *pflags = ehdr.e_flags; - } - if (pentry) { - *pentry = ehdr.e_entry; - } - - glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); - - size = ehdr.e_phnum * sizeof(phdr[0]); - if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { - goto fail; - } - phdr = g_malloc0(size); - if (!phdr) - goto fail; - if (read(fd, phdr, size) != size) - goto fail; - if (must_swab) { - for(i = 0; i < ehdr.e_phnum; i++) { - ph = &phdr[i]; - glue(bswap_phdr, SZ)(ph); - } - } - - /* - * Since we want to be able to modify the mapped buffer, we set the - * 'writable' parameter to 'true'. Modifications to the buffer are not - * written back to the file. - */ - mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); - if (!mapped_file) { - goto fail; - } - - total_size = 0; - for(i = 0; i < ehdr.e_phnum; i++) { - ph = &phdr[i]; - if (ph->p_type == PT_LOAD) { - mem_size = ph->p_memsz; /* Size of the ROM */ - file_size = ph->p_filesz; /* Size of the allocated data */ - data_offset = ph->p_offset; /* Offset where the data is located */ - - if (file_size > 0) { - if (g_mapped_file_get_length(mapped_file) < - file_size + data_offset) { - goto fail; - } - - data = (uint8_t *)g_mapped_file_get_contents(mapped_file); - data += data_offset; - } - - /* The ELF spec is somewhat vague about the purpose of the - * physical address field. One common use in the embedded world - * is that physical address field specifies the load address - * and the virtual address field specifies the execution address. - * Segments are packed into ROM or flash, and the relocation - * and zero-initialization of data is done at runtime. This - * means that the memsz header represents the runtime size of the - * segment, but the filesz represents the loadtime size. If - * we try to honour the memsz value for an ELF file like this - * we will end up with overlapping segments (which the - * loader.c code will later reject). - * We support ELF files using this scheme by by checking whether - * paddr + memsz for this segment would overlap with any other - * segment. If so, then we assume it's using this scheme and - * truncate the loaded segment to the filesz size. - * If the segment considered as being memsz size doesn't overlap - * then we use memsz for the segment length, to handle ELF files - * which assume that the loader will do the zero-initialization. - */ - if (mem_size > file_size) { - /* If this segment's zero-init portion overlaps another - * segment's data or zero-init portion, then truncate this one. - * Invalid ELF files where the segments overlap even when - * only file_size bytes are loaded will be rejected by - * the ROM overlap check in loader.c, so we don't try to - * explicitly detect those here. - */ - int j; - elf_word zero_start = ph->p_paddr + file_size; - elf_word zero_end = ph->p_paddr + mem_size; - - for (j = 0; j < ehdr.e_phnum; j++) { - struct elf_phdr *jph = &phdr[j]; - - if (i != j && jph->p_type == PT_LOAD) { - elf_word other_start = jph->p_paddr; - elf_word other_end = jph->p_paddr + jph->p_memsz; - - if (!(other_start >= zero_end || - zero_start >= other_end)) { - mem_size = file_size; - break; - } - } - } - } - - if (mem_size > SSIZE_MAX - total_size) { - ret = ELF_LOAD_TOO_BIG; - goto fail; - } - - /* address_offset is hack for kernel images that are - linked at the wrong physical address. */ - if (translate_fn) { - addr = translate_fn(translate_opaque, ph->p_paddr); - glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, - translate_opaque, data, ph, elf_machine); - } else { - addr = ph->p_paddr; - } - - if (data_swab) { - elf_word j; - for (j = 0; j < file_size; j += (1 << data_swab)) { - uint8_t *dp = data + j; - switch (data_swab) { - case (1): - *(uint16_t *)dp = bswap16(*(uint16_t *)dp); - break; - case (2): - *(uint32_t *)dp = bswap32(*(uint32_t *)dp); - break; - case (3): - *(uint64_t *)dp = bswap64(*(uint64_t *)dp); - break; - default: - g_assert_not_reached(); - } - } - } - - /* the entry pointer in the ELF header is a virtual - * address, if the text segments paddr and vaddr differ - * we need to adjust the entry */ - if (pentry && !translate_fn && - ph->p_vaddr != ph->p_paddr && - ehdr.e_entry >= ph->p_vaddr && - ehdr.e_entry < ph->p_vaddr + ph->p_filesz && - ph->p_flags & PF_X) { - *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; - } - - /* Some ELF files really do have segments of zero size; - * just ignore them rather than trying to create empty - * ROM blobs, because the zero-length blob can falsely - * trigger the overlapping-ROM-blobs check. - */ - if (mem_size != 0) { - if (load_rom) { - g_autofree char *label = - g_strdup_printf("%s ELF program header segment %d", - name, i); - - /* - * rom_add_elf_program() takes its own reference to - * 'mapped_file'. - */ - rom_add_elf_program(label, mapped_file, data, file_size, - mem_size, addr, as); - } else { - MemTxResult res; - - res = address_space_write(as ? as : &address_space_memory, - addr, MEMTXATTRS_UNSPECIFIED, - data, file_size); - if (res != MEMTX_OK) { - goto fail; - } - /* - * We need to zero'ify the space that is not copied - * from file - */ - if (file_size < mem_size) { - res = address_space_set(as ? as : &address_space_memory, - addr + file_size, 0, - mem_size - file_size, - MEMTXATTRS_UNSPECIFIED); - if (res != MEMTX_OK) { - goto fail; - } - } - } - } - - total_size += mem_size; - if (addr < low) - low = addr; - if ((addr + mem_size) > high) - high = addr + mem_size; - - data = NULL; - - } else if (ph->p_type == PT_NOTE && elf_note_fn) { - struct elf_note *nhdr = NULL; - - file_size = ph->p_filesz; /* Size of the range of ELF notes */ - data_offset = ph->p_offset; /* Offset where the notes are located */ - - if (file_size > 0) { - if (g_mapped_file_get_length(mapped_file) < - file_size + data_offset) { - goto fail; - } - - data = (uint8_t *)g_mapped_file_get_contents(mapped_file); - data += data_offset; - } - - /* - * Search the ELF notes to find one with a type matching the - * value passed in via 'translate_opaque' - */ - nhdr = (struct elf_note *)data; - assert(translate_opaque != NULL); - nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, - *(uint64_t *)translate_opaque); - if (nhdr != NULL) { - elf_note_fn((void *)nhdr, (void *)&ph->p_align, SZ == 64); - } - data = NULL; - } - } - - if (lowaddr) { - *lowaddr = low; - } - if (highaddr) { - *highaddr = high; - } - ret = total_size; - fail: - if (mapped_file) { - g_mapped_file_unref(mapped_file); - } - g_free(phdr); - return ret; -} diff --git a/include/hw/elf_ops.h.inc b/include/hw/elf_ops.h.inc new file mode 100644 index 0000000000..9c35d1b9da --- /dev/null +++ b/include/hw/elf_ops.h.inc @@ -0,0 +1,627 @@ +static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ + bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ + bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswapSZs(&phdr->p_offset); /* Segment file offset */ + bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ + bswapSZs(&phdr->p_paddr); /* Segment physical address */ + bswapSZs(&phdr->p_filesz); /* Segment size in file */ + bswapSZs(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswapSZs(&phdr->p_align); /* Segment alignment */ +} + +static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswapSZs(&shdr->sh_flags); + bswapSZs(&shdr->sh_addr); + bswapSZs(&shdr->sh_offset); + bswapSZs(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswapSZs(&shdr->sh_addralign); + bswapSZs(&shdr->sh_entsize); +} + +static void glue(bswap_sym, SZ)(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswapSZs(&sym->st_value); + bswapSZs(&sym->st_size); + bswap16s(&sym->st_shndx); +} + +static void glue(bswap_rela, SZ)(struct elf_rela *rela) +{ + bswapSZs(&rela->r_offset); + bswapSZs(&rela->r_info); + bswapSZs((elf_word *)&rela->r_addend); +} + +static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, + int n, int type) +{ + int i; + for(i=0;ist_value) { + result = -1; + } else if (addr >= sym->st_value + sym->st_size) { + result = 1; + } + return result; +} + +static const char *glue(lookup_symbol, SZ)(struct syminfo *s, + hwaddr orig_addr) +{ + struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); + struct elf_sym *sym; + + sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), + glue(symfind, SZ)); + if (sym != NULL) { + return s->disas_strtab + sym->st_name; + } + + return ""; +} + +static int glue(symcmp, SZ)(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); +} + +static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + int clear_lsb, symbol_fn_t sym_cb) +{ + struct elf_shdr *symtab, *strtab; + g_autofree struct elf_shdr *shdr_table = NULL; + g_autofree struct elf_sym *syms = NULL; + g_autofree char *str = NULL; + struct syminfo *s; + int nsyms, i; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) { + return; + } + + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(shdr_table + i); + } + } + + symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); + if (!symtab) { + return; + } + syms = load_at(fd, symtab->sh_offset, symtab->sh_size); + if (!syms) { + return; + } + + nsyms = symtab->sh_size / sizeof(struct elf_sym); + + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) { + return; + } + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) { + return; + } + + i = 0; + while (i < nsyms) { + if (must_swab) { + glue(bswap_sym, SZ)(&syms[i]); + } + if (sym_cb) { + sym_cb(str + syms[i].st_name, syms[i].st_info, + syms[i].st_value, syms[i].st_size); + } + /* We are only interested in function symbols. + Throw everything else away. */ + 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 (clear_lsb) { + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ + syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; + } + i++; + } + + /* check we have symbols left */ + if (nsyms == 0) { + return; + } + + syms = g_realloc(syms, nsyms * sizeof(*syms)); + qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); + for (i = 0; i < nsyms - 1; i++) { + if (syms[i].st_size == 0) { + syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; + } + } + + /* Commit */ + s = g_malloc0(sizeof(*s)); + s->lookup_symbol = glue(lookup_symbol, SZ); + glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); + s->disas_num_syms = nsyms; + s->disas_strtab = g_steal_pointer(&str); + s->next = syminfos; + syminfos = s; +} + +static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint8_t *data, + struct elf_phdr *ph, int elf_machine) +{ + struct elf_shdr *reltab, *shdr_table = NULL; + struct elf_rela *rels = NULL; + int nrels, i, ret = -1; + elf_word wordval; + void *addr; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) { + return -1; + } + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(&shdr_table[i]); + } + } + + reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); + if (!reltab) { + goto fail; + } + rels = load_at(fd, reltab->sh_offset, reltab->sh_size); + if (!rels) { + goto fail; + } + nrels = reltab->sh_size / sizeof(struct elf_rela); + + for (i = 0; i < nrels; i++) { + if (must_swab) { + glue(bswap_rela, SZ)(&rels[i]); + } + if (rels[i].r_offset < ph->p_vaddr || + rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { + continue; + } + addr = &data[rels[i].r_offset - ph->p_vaddr]; + switch (elf_machine) { + case EM_S390: + switch (rels[i].r_info) { + case R_390_RELATIVE: + wordval = *(elf_word *)addr; + if (must_swab) { + bswapSZs(&wordval); + } + wordval = translate_fn(translate_opaque, wordval); + if (must_swab) { + bswapSZs(&wordval); + } + *(elf_word *)addr = wordval; + break; + default: + fprintf(stderr, "Unsupported relocation type %i!\n", + (int)rels[i].r_info); + } + } + } + + ret = 0; +fail: + g_free(rels); + g_free(shdr_table); + return ret; +} + +/* + * Given 'nhdr', a pointer to a range of ELF Notes, search through them + * for a note matching type 'elf_note_type' and return a pointer to + * the matching ELF note. + */ +static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, + elf_word note_size, + elf_word phdr_align, + elf_word elf_note_type) +{ + elf_word nhdr_size = sizeof(struct elf_note); + elf_word elf_note_entry_offset = 0; + elf_word note_type; + elf_word nhdr_namesz; + elf_word nhdr_descsz; + + if (nhdr == NULL) { + return NULL; + } + + note_type = nhdr->n_type; + while (note_type != elf_note_type) { + nhdr_namesz = nhdr->n_namesz; + nhdr_descsz = nhdr->n_descsz; + + elf_note_entry_offset = nhdr_size + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + + QEMU_ALIGN_UP(nhdr_descsz, phdr_align); + + /* + * If the offset calculated in this iteration exceeds the + * supplied size, we are done and no matching note was found. + */ + if (elf_note_entry_offset > note_size) { + return NULL; + } + + /* skip to the next ELF Note entry */ + nhdr = (void *)nhdr + elf_note_entry_offset; + note_type = nhdr->n_type; + } + + return nhdr; +} + +static ssize_t glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, + symbol_fn_t sym_cb) +{ + struct elfhdr ehdr; + struct elf_phdr *phdr = NULL, *ph; + int size, i; + ssize_t total_size; + elf_word mem_size, file_size, data_offset; + uint64_t addr, low = (uint64_t)-1, high = 0; + GMappedFile *mapped_file = NULL; + uint8_t *data = NULL; + ssize_t ret = ELF_LOAD_FAILED; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail; + if (must_swab) { + glue(bswap_ehdr, SZ)(&ehdr); + } + + if (elf_machine <= EM_NONE) { + /* The caller didn't specify an ARCH, we can figure it out */ + elf_machine = ehdr.e_machine; + } + + switch (elf_machine) { + case EM_PPC64: + if (ehdr.e_machine != EM_PPC64) { + if (ehdr.e_machine != EM_PPC) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + break; + case EM_X86_64: + if (ehdr.e_machine != EM_X86_64) { + if (ehdr.e_machine != EM_386) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + break; + case EM_MICROBLAZE: + if (ehdr.e_machine != EM_MICROBLAZE) { + if (ehdr.e_machine != EM_MICROBLAZE_OLD) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + break; + case EM_MIPS: + case EM_NANOMIPS: + if ((ehdr.e_machine != EM_MIPS) && + (ehdr.e_machine != EM_NANOMIPS)) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + break; + default: + if (elf_machine != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + + if (pflags) { + *pflags = ehdr.e_flags; + } + if (pentry) { + *pentry = ehdr.e_entry; + } + + glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); + + size = ehdr.e_phnum * sizeof(phdr[0]); + if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { + goto fail; + } + phdr = g_malloc0(size); + if (!phdr) + goto fail; + if (read(fd, phdr, size) != size) + goto fail; + if (must_swab) { + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + glue(bswap_phdr, SZ)(ph); + } + } + + /* + * Since we want to be able to modify the mapped buffer, we set the + * 'writable' parameter to 'true'. Modifications to the buffer are not + * written back to the file. + */ + mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); + if (!mapped_file) { + goto fail; + } + + total_size = 0; + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + if (ph->p_type == PT_LOAD) { + mem_size = ph->p_memsz; /* Size of the ROM */ + file_size = ph->p_filesz; /* Size of the allocated data */ + data_offset = ph->p_offset; /* Offset where the data is located */ + + if (file_size > 0) { + if (g_mapped_file_get_length(mapped_file) < + file_size + data_offset) { + goto fail; + } + + data = (uint8_t *)g_mapped_file_get_contents(mapped_file); + data += data_offset; + } + + /* The ELF spec is somewhat vague about the purpose of the + * physical address field. One common use in the embedded world + * is that physical address field specifies the load address + * and the virtual address field specifies the execution address. + * Segments are packed into ROM or flash, and the relocation + * and zero-initialization of data is done at runtime. This + * means that the memsz header represents the runtime size of the + * segment, but the filesz represents the loadtime size. If + * we try to honour the memsz value for an ELF file like this + * we will end up with overlapping segments (which the + * loader.c code will later reject). + * We support ELF files using this scheme by by checking whether + * paddr + memsz for this segment would overlap with any other + * segment. If so, then we assume it's using this scheme and + * truncate the loaded segment to the filesz size. + * If the segment considered as being memsz size doesn't overlap + * then we use memsz for the segment length, to handle ELF files + * which assume that the loader will do the zero-initialization. + */ + if (mem_size > file_size) { + /* If this segment's zero-init portion overlaps another + * segment's data or zero-init portion, then truncate this one. + * Invalid ELF files where the segments overlap even when + * only file_size bytes are loaded will be rejected by + * the ROM overlap check in loader.c, so we don't try to + * explicitly detect those here. + */ + int j; + elf_word zero_start = ph->p_paddr + file_size; + elf_word zero_end = ph->p_paddr + mem_size; + + for (j = 0; j < ehdr.e_phnum; j++) { + struct elf_phdr *jph = &phdr[j]; + + if (i != j && jph->p_type == PT_LOAD) { + elf_word other_start = jph->p_paddr; + elf_word other_end = jph->p_paddr + jph->p_memsz; + + if (!(other_start >= zero_end || + zero_start >= other_end)) { + mem_size = file_size; + break; + } + } + } + } + + if (mem_size > SSIZE_MAX - total_size) { + ret = ELF_LOAD_TOO_BIG; + goto fail; + } + + /* address_offset is hack for kernel images that are + linked at the wrong physical address. */ + if (translate_fn) { + addr = translate_fn(translate_opaque, ph->p_paddr); + glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, + translate_opaque, data, ph, elf_machine); + } else { + addr = ph->p_paddr; + } + + if (data_swab) { + elf_word j; + for (j = 0; j < file_size; j += (1 << data_swab)) { + uint8_t *dp = data + j; + switch (data_swab) { + case (1): + *(uint16_t *)dp = bswap16(*(uint16_t *)dp); + break; + case (2): + *(uint32_t *)dp = bswap32(*(uint32_t *)dp); + break; + case (3): + *(uint64_t *)dp = bswap64(*(uint64_t *)dp); + break; + default: + g_assert_not_reached(); + } + } + } + + /* the entry pointer in the ELF header is a virtual + * address, if the text segments paddr and vaddr differ + * we need to adjust the entry */ + if (pentry && !translate_fn && + ph->p_vaddr != ph->p_paddr && + ehdr.e_entry >= ph->p_vaddr && + ehdr.e_entry < ph->p_vaddr + ph->p_filesz && + ph->p_flags & PF_X) { + *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; + } + + /* Some ELF files really do have segments of zero size; + * just ignore them rather than trying to create empty + * ROM blobs, because the zero-length blob can falsely + * trigger the overlapping-ROM-blobs check. + */ + if (mem_size != 0) { + if (load_rom) { + g_autofree char *label = + g_strdup_printf("%s ELF program header segment %d", + name, i); + + /* + * rom_add_elf_program() takes its own reference to + * 'mapped_file'. + */ + rom_add_elf_program(label, mapped_file, data, file_size, + mem_size, addr, as); + } else { + MemTxResult res; + + res = address_space_write(as ? as : &address_space_memory, + addr, MEMTXATTRS_UNSPECIFIED, + data, file_size); + if (res != MEMTX_OK) { + goto fail; + } + /* + * We need to zero'ify the space that is not copied + * from file + */ + if (file_size < mem_size) { + res = address_space_set(as ? as : &address_space_memory, + addr + file_size, 0, + mem_size - file_size, + MEMTXATTRS_UNSPECIFIED); + if (res != MEMTX_OK) { + goto fail; + } + } + } + } + + total_size += mem_size; + if (addr < low) + low = addr; + if ((addr + mem_size) > high) + high = addr + mem_size; + + data = NULL; + + } else if (ph->p_type == PT_NOTE && elf_note_fn) { + struct elf_note *nhdr = NULL; + + file_size = ph->p_filesz; /* Size of the range of ELF notes */ + data_offset = ph->p_offset; /* Offset where the notes are located */ + + if (file_size > 0) { + if (g_mapped_file_get_length(mapped_file) < + file_size + data_offset) { + goto fail; + } + + data = (uint8_t *)g_mapped_file_get_contents(mapped_file); + data += data_offset; + } + + /* + * Search the ELF notes to find one with a type matching the + * value passed in via 'translate_opaque' + */ + nhdr = (struct elf_note *)data; + assert(translate_opaque != NULL); + nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, + *(uint64_t *)translate_opaque); + if (nhdr != NULL) { + elf_note_fn((void *)nhdr, (void *)&ph->p_align, SZ == 64); + } + data = NULL; + } + } + + if (lowaddr) { + *lowaddr = low; + } + if (highaddr) { + *highaddr = high; + } + ret = total_size; + fail: + if (mapped_file) { + g_mapped_file_unref(mapped_file); + } + g_free(phdr); + return ret; +} diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f4a0b78c75..a0999dac15 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3572,7 +3572,7 @@ static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) return ""; } -/* FIXME: This should use elf_ops.h */ +/* FIXME: This should use elf_ops.h.inc */ static int symcmp(const void *s0, const void *s1) { struct elf_sym *sym0 = (struct elf_sym *)s0;