Patchwork [07/12] linux-user: Load symbols from the interpreter.

login
register
mail settings
Submitter Richard Henderson
Date July 27, 2010, 5:25 p.m.
Message ID <1280251538-6860-8-git-send-email-rth@twiddle.net>
Download mbox | patch
Permalink /patch/60018/
State New
Headers show

Comments

Richard Henderson - July 27, 2010, 5:25 p.m.
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.

Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 linux-user/elfload.c |  189 +++++++++++++++++++++++++++-----------------------
 1 files changed, 101 insertions(+), 88 deletions(-)
malc - July 27, 2010, 6:45 p.m.
On Tue, 27 Jul 2010, 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.
> 
> Signed-off-by: Richard Henderson <rth@twiddle.net>
> ---
>  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));

Realloc can fail here.

>      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);
>
Richard Henderson - July 27, 2010, 7:59 p.m.
On 07/27/2010 11:45 AM, malc wrote:
>> +    syms = realloc(syms, nsyms * sizeof(*syms));
> 
> Realloc can fail here.

I don't believe it can.  This (pre-existing) statement *reduces*
the existing allocation of syms.  I'd be surprised if any malloc
implementation fails on a size reduction.

That said, I'd be happy enough to eliminate the call entirely.


r~
malc - July 27, 2010, 8:39 p.m.
On Tue, 27 Jul 2010, Richard Henderson wrote:

> On 07/27/2010 11:45 AM, malc wrote:
> >> +    syms = realloc(syms, nsyms * sizeof(*syms));
> > 
> > Realloc can fail here.
> 
> I don't believe it can.  This (pre-existing) statement *reduces*
> the existing allocation of syms.  I'd be surprised if any malloc
> implementation fails on a size reduction.

Life is full of surprises.

realloc$ cat tr.c 
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    void *ptr;

    for (;;) {
        ptr = malloc (2);
        if (!ptr) break;
    }
    printf ("result = %p\n", realloc (ptr, 1));
    return 0;
}
realloc$ \time -v ./a.out 
result = (nil)
        Command being timed: "./a.out"
        User time (seconds): 5.62
        [..snip..]

> That said, I'd be happy enough to eliminate the call entirely.
malc - July 27, 2010, 8:44 p.m.
On Wed, 28 Jul 2010, malc wrote:

> On Tue, 27 Jul 2010, Richard Henderson wrote:
> 
> > On 07/27/2010 11:45 AM, malc wrote:
> > >> +    syms = realloc(syms, nsyms * sizeof(*syms));
> > > 
> > > Realloc can fail here.
> > 
> > I don't believe it can.  This (pre-existing) statement *reduces*
> > the existing allocation of syms.  I'd be surprised if any malloc
> > implementation fails on a size reduction.
> 
> Life is full of surprises.
> 
> realloc$ cat tr.c 
> #include <stdio.h>
> #include <stdlib.h>
> 
> int main (void)
> {
>     void *ptr;
> 
>     for (;;) {
>         ptr = malloc (2);
>         if (!ptr) break;
>     }
>     printf ("result = %p\n", realloc (ptr, 1));
>     return 0;
> }

Uh, appologies, this doesn't quite do what i thought it should,
final realloc allocates without freeing. Guess i have to spend
more time trying to prove that i'm the smartest kid on the block.
Edgar Iglesias - July 28, 2010, 10:16 p.m.
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 <rth@twiddle.net>
> ---
>  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
> 
>

Patch

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);