[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 07/12] linux-user: Load symbols from the interpr
From: |
Edgar E. Iglesias |
Subject: |
Re: [Qemu-devel] [PATCH 07/12] linux-user: Load symbols from the interpreter. |
Date: |
Thu, 29 Jul 2010 00:16:02 +0200 |
User-agent: |
Mutt/1.5.20 (2009-06-14) |
On Tue, Jul 27, 2010 at 10:25:33AM -0700, Richard Henderson wrote:
> First, adjust load_symbols to accept a load_bias parameter. At the same
> time, read the entire section header table in one go, use pread instead
> f lseek+read for the symbol and string tables, and properly free
> allocated structures on error exit paths.
>
> Second, adjust load_elf_interp to compute load_bias. This requires
> finding out the built-in load addresses. Which allows us to honor a
> pre-linked interpreter image when possible, and eliminate the hard-coded
> INTERP_MAP_SIZE value.
This was neat, I wish we've had this feature before :)
Cheers
>
> Signed-off-by: Richard Henderson <address@hidden>
> ---
> linux-user/elfload.c | 189
> +++++++++++++++++++++++++++-----------------------
> 1 files changed, 101 insertions(+), 88 deletions(-)
>
> diff --git a/linux-user/elfload.c b/linux-user/elfload.c
> index 3cbb1f4..6b57a91 100644
> --- a/linux-user/elfload.c
> +++ b/linux-user/elfload.c
> @@ -829,9 +829,6 @@ struct exec
> #define ZMAGIC 0413
> #define QMAGIC 0314
>
> -/* max code+data+bss space allocated to elf interpreter */
> -#define INTERP_MAP_SIZE (32 * 1024 * 1024)
> -
> /* max code+data+bss+brk space allocated to ET_DYN executables */
> #define ET_DYN_MAP_SIZE (128 * 1024 * 1024)
>
> @@ -920,6 +917,7 @@ static inline void bswap_sym(struct elf_sym *sym) { }
> #ifdef USE_ELF_CORE_DUMP
> static int elf_core_dump(int, const CPUState *);
> #endif /* USE_ELF_CORE_DUMP */
> +static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias);
>
> /*
> * 'copy_elf_strings()' copies argument/envelope strings from user
> @@ -1146,15 +1144,11 @@ static abi_ulong load_elf_interp(struct elfhdr *
> interp_elf_ex,
> char bprm_buf[BPRM_BUF_SIZE])
> {
> struct elf_phdr *elf_phdata = NULL;
> - struct elf_phdr *eppnt;
> - abi_ulong load_addr = 0;
> - int load_addr_set = 0;
> + abi_ulong load_addr, load_bias, loaddr, hiaddr;
> int retval;
> abi_ulong error;
> int i;
>
> - error = 0;
> -
> bswap_ehdr(interp_elf_ex);
> /* First of all, some simple consistency checks */
> if ((interp_elf_ex->e_type != ET_EXEC &&
> @@ -1163,7 +1157,6 @@ static abi_ulong load_elf_interp(struct elfhdr *
> interp_elf_ex,
> return ~((abi_ulong)0UL);
> }
>
> -
> /* Now read in all of the header information */
>
> if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE)
> @@ -1196,41 +1189,56 @@ static abi_ulong load_elf_interp(struct elfhdr *
> interp_elf_ex,
> }
> bswap_phdr(elf_phdata, interp_elf_ex->e_phnum);
>
> + /* Find the maximum size of the image and allocate an appropriate
> + amount of memory to handle that. */
> + loaddr = -1, hiaddr = 0;
> + for (i = 0; i < interp_elf_ex->e_phnum; ++i) {
> + if (elf_phdata[i].p_type == PT_LOAD) {
> + abi_ulong a = elf_phdata[i].p_vaddr;
> + if (a < loaddr) {
> + loaddr = a;
> + }
> + a += elf_phdata[i].p_memsz;
> + if (a > hiaddr) {
> + hiaddr = a;
> + }
> + }
> + }
> +
> + load_addr = loaddr;
> if (interp_elf_ex->e_type == ET_DYN) {
> - /* in order to avoid hardcoding the interpreter load
> - address in qemu, we allocate a big enough memory zone */
> - error = target_mmap(0, INTERP_MAP_SIZE,
> - PROT_NONE, MAP_PRIVATE | MAP_ANON,
> - -1, 0);
> - if (error == -1) {
> + /* The image indicates that it can be loaded anywhere. Find a
> + location that can hold the memory space required. If the
> + image is pre-linked, LOADDR will be non-zero. Since we do
> + not supply MAP_FIXED here we'll use that address if and
> + only if it remains available. */
> + load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE,
> + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
> + -1, 0);
> + if (load_addr == -1) {
> perror("mmap");
> exit(-1);
> }
> - load_addr = error;
> - load_addr_set = 1;
> }
> + load_bias = load_addr - loaddr;
>
> - eppnt = elf_phdata;
> - for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
> + for (i = 0; i < interp_elf_ex->e_phnum; i++) {
> + struct elf_phdr *eppnt = elf_phdata + i;
> if (eppnt->p_type == PT_LOAD) {
> - int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
> + abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em;
> int elf_prot = 0;
> - abi_ulong vaddr = 0;
>
> if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
> if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
> if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
> - if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
> - elf_type |= MAP_FIXED;
> - vaddr = eppnt->p_vaddr;
> - }
> - error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr),
> - eppnt->p_filesz +
> TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
> - elf_prot,
> - elf_type,
> - interpreter_fd,
> - eppnt->p_offset -
> TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
>
> + vaddr = load_bias + eppnt->p_vaddr;
> + vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr);
> + vaddr_ps = TARGET_ELF_PAGESTART(vaddr);
> +
> + error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
> + elf_prot, MAP_PRIVATE | MAP_FIXED,
> + interpreter_fd, eppnt->p_offset - vaddr_po);
> if (error == -1) {
> /* Real error */
> close(interpreter_fd);
> @@ -1238,26 +1246,25 @@ static abi_ulong load_elf_interp(struct elfhdr *
> interp_elf_ex,
> return ~((abi_ulong)0UL);
> }
>
> - if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
> - load_addr = error;
> - load_addr_set = 1;
> - }
> + vaddr_ef = vaddr + eppnt->p_filesz;
> + vaddr_em = vaddr + eppnt->p_memsz;
>
> /* If the load segment requests extra zeros (e.g. bss), map it.
> */
> - if (eppnt->p_filesz < eppnt->p_memsz) {
> - abi_ulong base = load_addr + eppnt->p_vaddr;
> - zero_bss(base + eppnt->p_filesz,
> - base + eppnt->p_memsz, elf_prot);
> + if (vaddr_ef < vaddr_em) {
> + zero_bss(vaddr_ef, vaddr_em, elf_prot);
> }
> }
> + }
>
> - /* Now use mmap to map the library into memory. */
> + if (qemu_log_enabled()) {
> + load_symbols(interp_elf_ex, interpreter_fd, load_bias);
> + }
>
> close(interpreter_fd);
> free(elf_phdata);
>
> *interp_load_addr = load_addr;
> - return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
> + return ((abi_ulong) interp_elf_ex->e_entry) + load_bias;
> }
>
> static int symfind(const void *s0, const void *s1)
> @@ -1306,82 +1313,87 @@ static int symcmp(const void *s0, const void *s1)
> }
>
> /* Best attempt to load symbols from this ELF object. */
> -static void load_symbols(struct elfhdr *hdr, int fd)
> +static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
> {
> - unsigned int i, nsyms;
> - struct elf_shdr sechdr, symtab, strtab;
> + int i, shnum, nsyms, sym_idx = 0, str_idx = 0;
> + struct elf_shdr *shdr;
> char *strings;
> struct syminfo *s;
> struct elf_sym *syms;
>
> - lseek(fd, hdr->e_shoff, SEEK_SET);
> - for (i = 0; i < hdr->e_shnum; i++) {
> - if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
> - return;
> - bswap_shdr(&sechdr, 1);
> - if (sechdr.sh_type == SHT_SYMTAB) {
> - symtab = sechdr;
> - lseek(fd, hdr->e_shoff
> - + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
> - if (read(fd, &strtab, sizeof(strtab))
> - != sizeof(strtab))
> - return;
> - bswap_shdr(&strtab, 1);
> + shnum = hdr->e_shnum;
> + i = shnum * sizeof(struct elf_shdr);
> + shdr = (struct elf_shdr *)alloca(i);
> + if (pread(fd, shdr, i, hdr->e_shoff) != i) {
> + return;
> + }
> +
> + bswap_shdr(shdr, shnum);
> + for (i = 0; i < shnum; ++i) {
> + if (shdr[i].sh_type == SHT_SYMTAB) {
> + sym_idx = i;
> + str_idx = shdr[i].sh_link;
> goto found;
> }
> }
> - return; /* Shouldn't happen... */
> +
> + /* There will be no symbol table if the file was stripped. */
> + return;
>
> found:
> - /* Now know where the strtab and symtab are. Snarf them. */
> + /* Now know where the strtab and symtab are. Snarf them. */
> s = malloc(sizeof(*s));
> - syms = malloc(symtab.sh_size);
> - if (!syms)
> - return;
> - s->disas_strtab = strings = malloc(strtab.sh_size);
> - if (!s->disas_strtab)
> + if (!s) {
> return;
> + }
>
> - lseek(fd, symtab.sh_offset, SEEK_SET);
> - if (read(fd, syms, symtab.sh_size) != symtab.sh_size)
> + i = shdr[str_idx].sh_size;
> + s->disas_strtab = strings = malloc(i);
> + if (!strings || pread(fd, strings, i, shdr[str_idx].sh_offset) != i) {
> + free(s);
> + free(strings);
> return;
> + }
>
> - nsyms = symtab.sh_size / sizeof(struct elf_sym);
> + i = shdr[sym_idx].sh_size;
> + syms = malloc(i);
> + if (!syms || pread(fd, syms, i, shdr[sym_idx].sh_offset) != i) {
> + free(s);
> + free(strings);
> + free(syms);
> + return;
> + }
>
> - i = 0;
> - while (i < nsyms) {
> + nsyms = i / sizeof(struct elf_sym);
> + for (i = 0; i < nsyms; ) {
> bswap_sym(syms + i);
> - // Throw away entries which we do not need.
> - 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) {
> + /* Throw away entries which we do not need. */
> + if (syms[i].st_shndx == SHN_UNDEF
> + || syms[i].st_shndx >= SHN_LORESERVE
> + || ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
> + if (i < --nsyms) {
> syms[i] = syms[nsyms];
> }
> - continue;
> - }
> + } else {
> #if defined(TARGET_ARM) || defined (TARGET_MIPS)
> - /* The bottom address bit marks a Thumb or MIPS16 symbol. */
> - syms[i].st_value &= ~(target_ulong)1;
> + /* The bottom address bit marks a Thumb or MIPS16 symbol. */
> + syms[i].st_value &= ~(target_ulong)1;
> #endif
> - i++;
> + syms[i].st_value += load_bias;
> + i++;
> + }
> }
> - syms = realloc(syms, nsyms * sizeof(*syms));
>
> + syms = realloc(syms, nsyms * sizeof(*syms));
> qsort(syms, nsyms, sizeof(*syms), symcmp);
>
> - lseek(fd, strtab.sh_offset, SEEK_SET);
> - if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
> - return;
> s->disas_num_syms = nsyms;
> #if ELF_CLASS == ELFCLASS32
> s->disas_symtab.elf32 = syms;
> - s->lookup_symbol = lookup_symbolxx;
> #else
> s->disas_symtab.elf64 = syms;
> - s->lookup_symbol = lookup_symbolxx;
> #endif
> + s->lookup_symbol = lookup_symbolxx;
> s->next = syminfos;
> syminfos = s;
> }
> @@ -1788,8 +1800,9 @@ int load_elf_binary(struct linux_binprm * bprm, struct
> target_pt_regs * regs,
>
> free(elf_phdata);
>
> - if (qemu_log_enabled())
> - load_symbols(&elf_ex, bprm->fd);
> + if (qemu_log_enabled()) {
> + load_symbols(&elf_ex, bprm->fd, load_bias);
> + }
>
> if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);
> info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
> --
> 1.7.1.1
>
>
[Qemu-devel] [PATCH 04/12] linux-user: Reduce lseek+reads while loading elf files., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 09/12] linux-user: Put the stack guard page at the top., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 01/12] linux-user: Handle filesz < memsz for any PT_LOAD segment., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 11/12] linux-user: Extract load_elf_image from load_elf_interp., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 12/12] linux-user: Re-use load_elf_image for the main binary., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 03/12] linux-user: Reindent elfload.c., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 05/12] linux-user: Define ELF_DATA generically., Richard Henderson, 2010/07/27
[Qemu-devel] [PATCH 08/12] linux-user: Improve consistency checking in elf headers., Richard Henderson, 2010/07/27