diff options
author | Richard Henderson <rth@twiddle.net> | 2010-07-27 10:25:27 -0700 |
---|---|---|
committer | Edgar E. Iglesias <edgar.iglesias@gmail.com> | 2010-07-29 08:32:27 +0200 |
commit | cf129f3a8eea76a34bc748762c31bbc27eabe540 (patch) | |
tree | 25f67db1b11263eaa4088c63953cf1319e8a5491 | |
parent | 36500de674a3caa37cab5b4665458a318d0c2173 (diff) |
linux-user: Handle filesz < memsz for any PT_LOAD segment.
I caught padzero not properly initializing the .bss segment
on a statically linked Alpha program. Rather than a minimal
patch, replace the gross code with a single mmap+memset.
Share more code between load_elf_interp and load_elf_binary.
Legally, an ELF program need not have just a single .bss;
and PT_LOAD segment can have memsz > filesz.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
-rw-r--r-- | linux-user/elfload.c | 152 |
1 files changed, 55 insertions, 97 deletions
diff --git a/linux-user/elfload.c b/linux-user/elfload.c index accb44d9de..5a0d3d41c9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1034,60 +1034,47 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, return p; } -static void set_brk(abi_ulong start, abi_ulong end) -{ - /* page-align the start and end addresses... */ - start = HOST_PAGE_ALIGN(start); - end = HOST_PAGE_ALIGN(end); - if (end <= start) - return; - if(target_mmap(start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) { - perror("cannot mmap brk"); - exit(-1); - } -} - - -/* We need to explicitly zero any fractional pages after the data - section (i.e. bss). This would contain the junk from the file that - should not be in memory. */ -static void padzero(abi_ulong elf_bss, abi_ulong last_bss) -{ - abi_ulong nbyte; - - if (elf_bss >= last_bss) - return; - - /* XXX: this is really a hack : if the real host page size is - smaller than the target page size, some pages after the end - of the file may not be mapped. A better fix would be to - patch target_mmap(), but it is more complicated as the file - size must be known */ - if (qemu_real_host_page_size < qemu_host_page_size) { - abi_ulong end_addr, end_addr1; - end_addr1 = (elf_bss + qemu_real_host_page_size - 1) & - ~(qemu_real_host_page_size - 1); - end_addr = HOST_PAGE_ALIGN(elf_bss); - if (end_addr1 < end_addr) { - mmap((void *)g2h(end_addr1), end_addr - end_addr1, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - } +/* Map and zero the bss. We need to explicitly zero any fractional pages + after the data section (i.e. bss). */ +static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) +{ + uintptr_t host_start, host_map_start, host_end; + + last_bss = TARGET_PAGE_ALIGN(last_bss); + + /* ??? There is confusion between qemu_real_host_page_size and + qemu_host_page_size here and elsewhere in target_mmap, which + may lead to the end of the data section mapping from the file + not being mapped. At least there was an explicit test and + comment for that here, suggesting that "the file size must + be known". The comment probably pre-dates the introduction + of the fstat system call in target_mmap which does in fact + find out the size. What isn't clear is if the workaround + here is still actually needed. For now, continue with it, + but merge it with the "normal" mmap that would allocate the bss. */ + + host_start = (uintptr_t) g2h(elf_bss); + host_end = (uintptr_t) g2h(last_bss); + host_map_start = (host_start + qemu_real_host_page_size - 1); + host_map_start &= -qemu_real_host_page_size; + + if (host_map_start < host_end) { + void *p = mmap((void *)host_map_start, host_end - host_map_start, + prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { + perror("cannot mmap brk"); + exit(-1); } - nbyte = elf_bss & (qemu_host_page_size-1); - if (nbyte) { - nbyte = qemu_host_page_size - nbyte; - do { - /* FIXME - what to do if put_user() fails? */ - put_user_u8(0, elf_bss); - elf_bss++; - } while (--nbyte); - } -} + /* Since we didn't use target_mmap, make sure to record + the validity of the pages with qemu. */ + page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot|PAGE_VALID); + } + if (host_start < host_map_start) { + memset((void *)host_start, 0, host_map_start - host_start); + } +} static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr * exec, @@ -1179,12 +1166,9 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex, abi_ulong load_addr = 0; int load_addr_set = 0; int retval; - abi_ulong last_bss, elf_bss; abi_ulong error; int i; - elf_bss = 0; - last_bss = 0; error = 0; #ifdef BSWAP_NEEDED @@ -1257,7 +1241,6 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex, int elf_type = MAP_PRIVATE | MAP_DENYWRITE; int elf_prot = 0; abi_ulong vaddr = 0; - abi_ulong k; if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; @@ -1285,40 +1268,17 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex, load_addr_set = 1; } - /* - * Find the end of the file mapping for this phdr, and keep - * track of the largest address we see for this. - */ - k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; - if (k > elf_bss) elf_bss = k; - - /* - * Do the same thing for the memory mapping - between - * elf_bss and last_bss is the bss section. - */ - k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; - if (k > last_bss) last_bss = k; + /* 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); + } } /* Now use mmap to map the library into memory. */ close(interpreter_fd); - - /* - * Now fill out the bss section. First pad the last page up - * to the page boundary, and then perform a mmap to make sure - * that there are zeromapped pages up to and including the last - * bss page. - */ - padzero(elf_bss, last_bss); - elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */ - - /* Map the last of the bss segment */ - if (last_bss > elf_bss) { - target_mmap(elf_bss, last_bss-elf_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - } free(elf_phdata); *interp_load_addr = load_addr; @@ -1472,7 +1432,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, abi_ulong mapped_addr; struct elf_phdr * elf_ppnt; struct elf_phdr *elf_phdata; - abi_ulong elf_bss, k, elf_brk; + abi_ulong k, elf_brk; int retval; char * elf_interpreter; abi_ulong elf_entry, interp_load_addr = 0; @@ -1531,10 +1491,8 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, #endif elf_ppnt = elf_phdata; - elf_bss = 0; elf_brk = 0; - elf_stack = ~((abi_ulong)0UL); elf_interpreter = NULL; start_code = ~((abi_ulong)0UL); @@ -1838,18 +1796,24 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, if (start_data < k) start_data = k; k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; - if (k > elf_bss) - elf_bss = k; if ((elf_ppnt->p_flags & PF_X) && end_code < k) end_code = k; if (end_data < k) end_data = k; k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - if (k > elf_brk) elf_brk = k; + if (k > elf_brk) { + elf_brk = TARGET_PAGE_ALIGN(k); + } + + /* If the load segment requests extra zeros (e.g. bss), map it. */ + if (elf_ppnt->p_filesz < elf_ppnt->p_memsz) { + abi_ulong base = load_bias + elf_ppnt->p_vaddr; + zero_bss(base + elf_ppnt->p_filesz, + base + elf_ppnt->p_memsz, elf_prot); + } } elf_entry += load_bias; - elf_bss += load_bias; elf_brk += load_bias; start_code += load_bias; end_code += load_bias; @@ -1904,12 +1868,6 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, info->end_data = end_data; info->start_stack = bprm->p; - /* Calling set_brk effectively mmaps the pages that we need for the bss and break - sections */ - set_brk(elf_bss, elf_brk); - - padzero(elf_bss, elf_brk); - #if 0 printf("(start_brk) %x\n" , info->start_brk); printf("(end_code) %x\n" , info->end_code); |