Hey folks, I just commited this slightly intrusive cleanup to our "use topdown VA depending on the properties of the exec'd binary" support:
-- Log Message: We never exec(2) with a kernel vmspace, so do not test for that, but instead KASSERT() that we don't. When calculating the load address for the interpreter (e.g. ld.elf_so), we need to take into account wether the exec'd process will run with topdown memory or bottom up. We can not use the current vmspace's flags to test for that, as this happens too early. Luckily the execpack already knows what the new state will be later, so instead of testing the current vmspace, pass the info as additional argument to struct emul e_vm_default_addr. Fix all such functions and adopt all callers. -- (http://mail-index.netbsd.org/source-changes/2015/11/26/msg070635.html) and I would like to pull this change up - but unfortunately such a signature change and following kernel rev bump can not be done on a release branch. I came up with a working but quite ugly solution that I'd like to present below. But first some background: On most architectures the decision "we use topdown memory layout" is done once and for all binaries. Only exception that I am aware of is sparc64: we can not use topdown for some binaries, because they have been compiled with a restrictive compiler memory model. We had this mostly working right for a long time (and only recently noticed it was partly broken), since ld.elf_so is relocatable code and most installations had a new enough ld.elf_so (compiled with the proper memory model for full topdown VA). However, when trying to boot a -7 or -current kernel on an original 6.0 installation (or older), init would crash. This happened because ld.elf_so was placed in topdown fashion, even though (slightly later) all shared libs were loaded bottom up. AND at that time ld.elf_so had bugs AND was compiled with the wrong memory model, so it did not support relocation into the upper 2GB VA. To avoid changing the signature of emul::e_vm_default_addr I did a very evil hack: if a mismatch between the wanted topdown attribute and the current vmspace's flags is detected, the vmmap's VM_MAP_TOPDOWN flag (which is considered read-only) is toggled, e_vm_default_addr is called, and then the flag is restored. As I said above, this should only ever happen on sparc64 with old binaries. It has been tested and restores full binary compat, with no noticable fallout. I would like to send this as a pullup request for netbsd-7. Any objections (or even suggestions how to avoid the mess)? Martin
? arch/sparc64/compile/MODULAR Index: kern/exec_elf.c =================================================================== RCS file: /cvsroot/src/sys/kern/exec_elf.c,v retrieving revision 1.69.2.3 diff -u -p -r1.69.2.3 exec_elf.c --- kern/exec_elf.c 8 Nov 2015 00:57:09 -0000 1.69.2.3 +++ kern/exec_elf.c 25 Nov 2015 13:47:42 -0000 @@ -77,6 +77,7 @@ __KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v #include <sys/kauth.h> #include <sys/bitops.h> #include <sys/cprng.h> +#include <sys/atomic.h> #include <sys/cpu.h> #include <machine/reg.h> @@ -409,20 +410,18 @@ elf_load_interp(struct lwp *l, struct ex u_long phsize; Elf_Addr addr = *last; struct proc *p; - bool use_topdown; + bool use_topdown, restore_topdown; p = l->l_proc; KASSERT(p->p_vmspace); - if (__predict_true(p->p_vmspace != proc0.p_vmspace)) { - use_topdown = p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN; - } else { + KASSERT(p->p_vmspace != proc0.p_vmspace); + restore_topdown = false; #ifdef __USE_TOPDOWN_VM - use_topdown = epp->ep_flags & EXEC_TOPDOWN_VM; + use_topdown = epp->ep_flags & EXEC_TOPDOWN_VM; #else - use_topdown = false; + use_topdown = false; #endif - } /* * 1. open file @@ -537,9 +536,36 @@ elf_load_interp(struct lwp *l, struct ex /* * Now compute the size and load address. */ + if (__predict_false( + /* vmspace is marked as topdown */ + (((p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN) != 0) + != + /* but this differs from the topdown usage we need */ + use_topdown))) { + /* + * The vmmap might be shared, but this flag is + * considered r/o and we will restore it immediately + * after calculating the load address. + */ + int flags = p->p_vmspace->vm_map.flags; + int n = use_topdown + ? (flags | VM_MAP_TOPDOWN) + : (flags & ~VM_MAP_TOPDOWN); + + restore_topdown = true; + atomic_swap_32(&p->p_vmspace->vm_map.flags, n); + } addr = (*epp->ep_esch->es_emul->e_vm_default_addr)(p, epp->ep_daddr, round_page(limit) - trunc_page(base_ph->p_vaddr)); + if (__predict_false(restore_topdown)) { + int flags = p->p_vmspace->vm_map.flags; + int n = !use_topdown + ? (flags | VM_MAP_TOPDOWN) + : (flags & ~VM_MAP_TOPDOWN); + + atomic_swap_32(&p->p_vmspace->vm_map.flags, n); + } } else addr = *last; /* may be ELF_LINK_ADDR */