tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Hack for pullup of e_vm_default_addr() change



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 */
 


Home | Main Index | Thread Index | Old Index