The zero_bss interface from linux-user is much better at doing this. Use it in preference to set_brk (badly named) and padzero. These both have issues with the new variable page size code, so it's best to just retire them and reuse the code from linux-user. Also start to use the error reporting code that linux-user uses to give better error messages on failure.
Signed-off-by: Warner Losh <i...@bsdimp.com> --- bsd-user/elfload.c | 110 +++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index dba03f17465..0a2f2379c93 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -22,6 +22,7 @@ #include "qemu.h" #include "disas/disas.h" #include "qemu/path.h" +#include "qapi/error.h" static abi_ulong target_auxents; /* Where the AUX entries are in target */ static size_t target_auxents_sz; /* Size of AUX entries including AT_NULL */ @@ -210,62 +211,63 @@ static void setup_arg_pages(struct bsd_binprm *bprm, struct image_info *info, } } -static void set_brk(abi_ulong start, abi_ulong end) +/** + * zero_bss: + * + * Map and zero the bss. We need to explicitly zero any fractional pages + * after the data section (i.e. bss). Return false on mapping failure. + */ +static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, + int prot, Error **errp) { - /* 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_ANON, -1, 0) == -1) { - perror("cannot mmap brk"); - exit(-1); + abi_ulong align_bss; + + /* We only expect writable bss; the code segment shouldn't need this. */ + if (!(prot & PROT_WRITE)) { + error_setg(errp, "PT_LOAD with non-writable bss"); + return false; } -} + align_bss = TARGET_PAGE_ALIGN(start_bss); + end_bss = TARGET_PAGE_ALIGN(end_bss); -/* - * 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 (start_bss < align_bss) { + int flags = page_get_flags(start_bss); - if (elf_bss >= last_bss) { - return; - } + if (!(flags & PAGE_RWX)) { + /* + * The whole address space of the executable was reserved + * at the start, therefore all pages will be VALID. + * But assuming there are no PROT_NONE PT_LOAD segments, + * a PROT_NONE page means no data all bss, and we can + * simply extend the new anon mapping back to the start + * of the page of bss. + */ + align_bss -= TARGET_PAGE_SIZE; + } else { + /* + * The start of the bss shares a page with something. + * The only thing that we expect is the data section, + * which would already be marked writable. + * Overlapping the RX code segment seems malformed. + */ + if (!(flags & PAGE_WRITE)) { + error_setg(errp, "PT_LOAD with bss overlapping " + "non-writable page"); + return false; + } - /* - * 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 = REAL_HOST_PAGE_ALIGN(elf_bss); - end_addr = HOST_PAGE_ALIGN(elf_bss); - if (end_addr1 < end_addr) { - mmap((void *)g2h_untagged(end_addr1), end_addr - end_addr1, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0); + /* The page is already mapped and writable. */ + memset(g2h_untagged(start_bss), 0, align_bss - start_bss); } } - - 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); + if (align_bss < end_bss && + target_mmap(align_bss, end_bss - align_bss, prot, + MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) { + error_setg_errno(errp, errno, "Error mapping bss"); + return false; } + return true; } static abi_ulong load_elf_interp(const char *elf_interpreter, @@ -535,6 +537,7 @@ load_elf_sections(const char *image_name, const struct elfhdr *hdr, abi_ulong baddr; int i; bool first; + Error *err = NULL; /* * Now we do a little grungy work by mmaping the ELF image into @@ -579,12 +582,10 @@ load_elf_sections(const char *image_name, const struct elfhdr *hdr, start_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_filesz; end_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - /* - * Calling set_brk effectively mmaps the pages that we need for the - * bss and break sections. - */ - set_brk(start_bss, end_bss); - padzero(start_bss, end_bss); + if (start_bss < end_bss && + !zero_bss(start_bss, end_bss, elf_prot, &err)) { + goto exit_errmsg; + } } if (first) { @@ -597,6 +598,9 @@ load_elf_sections(const char *image_name, const struct elfhdr *hdr, *baddrp = baddr; } return 0; +exit_errmsg: + error_reportf_err(err, "%s: ", image_name); + exit(-1); } int load_elf_binary(struct bsd_binprm *bprm, struct image_info *info) -- 2.45.1