diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2006-04-23 17:12:42 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2006-04-23 17:12:42 +0000 |
commit | 5fe141fd30d35516eac3674f7b62be51ba34543f (patch) | |
tree | 731317581b49ea6baa75544180a38bcfb64dc7fe /elf_ops.h | |
parent | ce2f4b3cb9c1c542a6de6f0cdcd4771fe03a8bb6 (diff) |
generic ELF loader
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1831 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'elf_ops.h')
-rw-r--r-- | elf_ops.h | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/elf_ops.h b/elf_ops.h new file mode 100644 index 0000000000..a7d027e1ab --- /dev/null +++ b/elf_ops.h @@ -0,0 +1,197 @@ +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 struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, + int n, int type) +{ + int i; + for(i=0;i<n;i++) { + if (shdr_table[i].sh_type == type) + return shdr_table + i; + } + return NULL; +} + +static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) +{ + struct elf_shdr *symtab, *strtab, *shdr_table = NULL; + struct elf_sym *syms = NULL; +#if (SZ == 64) + struct elf32_sym *syms32 = NULL; +#endif + struct syminfo *s; + int nsyms, i; + char *str = NULL; + + 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); + } + } + + symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); + if (!symtab) + goto fail; + syms = load_at(fd, symtab->sh_offset, symtab->sh_size); + if (!syms) + goto fail; + + nsyms = symtab->sh_size / sizeof(struct elf_sym); +#if (SZ == 64) + syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym)); +#endif + for (i = 0; i < nsyms; i++) { + if (must_swab) + glue(bswap_sym, SZ)(&syms[i]); +#if (SZ == 64) + syms32[i].st_name = syms[i].st_name; + syms32[i].st_info = syms[i].st_info; + syms32[i].st_other = syms[i].st_other; + syms32[i].st_shndx = syms[i].st_shndx; + syms32[i].st_value = syms[i].st_value & 0xffffffff; + syms32[i].st_size = syms[i].st_size & 0xffffffff; +#endif + } + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) + goto fail; + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) + goto fail; + + /* Commit */ + s = qemu_mallocz(sizeof(*s)); +#if (SZ == 64) + s->disas_symtab = syms32; + qemu_free(syms); +#else + s->disas_symtab = syms; +#endif + s->disas_num_syms = nsyms; + s->disas_strtab = str; + s->next = syminfos; + syminfos = s; + qemu_free(shdr_table); + return 0; + fail: +#if (SZ == 64) + qemu_free(syms32); +#endif + qemu_free(syms); + qemu_free(str); + qemu_free(shdr_table); + return -1; +} + +int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend, int must_swab) +{ + struct elfhdr ehdr; + struct elf_phdr *phdr = NULL, *ph; + int size, i, total_size; + elf_word mem_size, addr; + uint8_t *data; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail; + if (must_swab) { + glue(bswap_ehdr, SZ)(&ehdr); + } + + glue(load_symbols, SZ)(&ehdr, fd, must_swab); + + size = ehdr.e_phnum * sizeof(phdr[0]); + lseek(fd, ehdr.e_phoff, SEEK_SET); + phdr = qemu_mallocz(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); + } + } + + 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; + /* XXX: avoid allocating */ + data = qemu_mallocz(mem_size); + if (ph->p_filesz > 0) { + lseek(fd, ph->p_offset, SEEK_SET); + if (read(fd, data, ph->p_filesz) != ph->p_filesz) + goto fail; + } + addr = ph->p_vaddr + virt_to_phys_addend; + + cpu_physical_memory_write_rom(addr, data, mem_size); + + total_size += mem_size; + + qemu_free(data); + } + } + return total_size; + fail: + qemu_free(phdr); + return -1; +} + |