Subject: MIPS shared linker running at virtual address 0!
To: None <tech-kern@netbsd.org>
From: Simon Burge <simonb@wasabisystems.com>
List: tech-kern
Date: 07/29/2005 19:38:05
--mYCpIKhGyMATD0i+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Folks,

On the MIPS ports we have a problem when the ELF_INTERP_NON_RELOCATABLE
option is enabled (which is enabled by COMPAT_16 option).  Now that the
shared linker (ld.elf_so) is relocatable and linked at virtual address
0 instead of the the funny magic address of 0x5ffe0000, if we have
ELF_INTERP_NON_RELOCATABLE enabled then it will actually run at virtual
address 0.

Here's the output of pmap(6) for a kernel with COMPAT_16 enabled:

	rhone 1> pmap -p $$
	00000000     56K read/exec         /libexec/ld.elf_so
	0000E000    252K                     [ anon ]
	0004D000      4K read/write        /libexec/ld.elf_so
	0004E000      4K read/write          [ anon ]
	00400000    520K read/exec         /bin/tcsh
	10000000     16K read/write        /bin/tcsh
	10004000    840K read/write          [ anon ]
	7DE00000   1036K read/exec         /lib/libc.so.12.130
	7DF03000    256K                   /lib/libc.so.12.130
	7DF43000     28K read/write        /lib/libc.so.12.130
	7DF4A000     60K read/write          [ anon ]
	7DF60000     28K read/exec         /lib/libcrypt.so.0.2
	7DF67000    252K                   /lib/libcrypt.so.0.2
	7DFA6000      4K read/write        /lib/libcrypt.so.0.2
	7DFA7000     12K read/write          [ anon ]
	7DFB0000     12K read/exec         /lib/libtermcap.so.0.6
	7DFB3000    252K                   /lib/libtermcap.so.0.6
	7DFF2000      4K read/write        /lib/libtermcap.so.0.6
	7DFF6000     32K read/write          [ anon ]
	7DFFE000      4K read/exec           [ uvm_aobj ]
	7DFFF000  30720K                     [ stack ]
	7FDFF000   1924K read/write          [ stack ]
	7FFE0000    124K read/write          [ stack ]
	 total     4708K

This is bad because it means that a NULL pointer deref will hit a mapped
address and won't segfault!

The rather ugly patched attached checks to see if the interpreter (ie,
shared linker) is linked at a low address, and if so tells later code
to load it at a normal address chosen at the top of the address space
instead of the address of where it was linked at.  The shared linkers
that the ELF_INTERP_NON_RELOCATABLE option is used for are all linked
at the magic MIPS address of 0x5ffe0000, and so aren't effected by this
patch.

After this patch, and still with COMPAT_16 enabled, we now see:

	rhone 1> pmap -p $$
	00400000    520K read/exec         /bin/tcsh
	10000000     16K read/write        /bin/tcsh
	10004000    840K read/write          [ anon ]
	7DDB0000   1036K read/exec         /lib/libc.so.12.130
	7DEB3000    256K                   /lib/libc.so.12.130
	7DEF3000     28K read/write        /lib/libc.so.12.130
	7DEFA000     60K read/write          [ anon ]
	7DF10000     28K read/exec         /lib/libcrypt.so.0.2
	7DF17000    252K                   /lib/libcrypt.so.0.2
	7DF56000      4K read/write        /lib/libcrypt.so.0.2
	7DF57000     12K read/write          [ anon ]
	7DF60000     12K read/exec         /lib/libtermcap.so.0.6
	7DF63000    252K                   /lib/libtermcap.so.0.6
	7DFA2000      4K read/write        /lib/libtermcap.so.0.6
	7DFA7000     32K read/write          [ anon ]
	7DFAF000      4K read/exec           [ uvm_aobj ]
	7DFB0000     56K read/exec         /libexec/ld.elf_so
	7DFBE000    252K                     [ anon ]
	7DFFD000      4K read/write        /libexec/ld.elf_so
	7DFFE000      4K read/write          [ anon ]
	7DFFF000  30720K                     [ stack ]
	7FDFF000   1924K read/write          [ stack ]
	7FFE0000    124K read/write          [ stack ]
	 total     4708K

Notice how virtual address 0 is now unmapped, and the shared linker is
high up in the user address space.

No other ports use ELF_INTERP_NON_RELOCATABLE, so this doesn't break
anything else, but this patch is in MI code so I'm posting here for any
comments before committing.  I'd like to commit this and get it pulled
up to the 3.0 branch.  I'll ignore comments along the lines of "eww,
that's ugly" if no better fix is proposed :-)

Cheers,
Simon.
--
Simon Burge                                   <simonb@wasabisystems.com>
NetBSD Development, Support and Service:   http://www.wasabisystems.com/

--mYCpIKhGyMATD0i+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="exec_elf32.diff"

Index: sys/kern/exec_elf32.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf32.c,v
retrieving revision 1.106
diff -d -p -u -r1.106 exec_elf32.c
--- sys/kern/exec_elf32.c	17 Jul 2005 23:53:57 -0000	1.106
+++ sys/kern/exec_elf32.c	28 Jul 2005 16:01:39 -0000
@@ -405,6 +405,18 @@ elf_load_file(struct proc *p, struct exe
 	if ((error = exec_read_from(p, vp, eh.e_phoff, ph, phsize)) != 0)
 		goto bad;
 
+#ifdef ELF_INTERP_NON_RELOCATABLE
+	/*
+	 * Evil hack:  Only MIPS should be non-relocatable, and the
+	 * psections should have a high address (typically 0x5ffe0000).
+	 * If it's now relocatable, it should be linked at 0 and the
+	 * psections should have zeros in the upper part of the address.
+	 * Otherwise, force the load at the linked address.
+	 */
+	if (*last == ELF_LINK_ADDR && (ph->p_vaddr & 0xffff0000) == 0)
+		*last = ELFDEFNNAME(NO_ADDR);
+#endif
+
 	/*
 	 * If no position to load the interpreter was set by a probe
 	 * function, pick the same address that a non-fixed mmap(0, ..)

--mYCpIKhGyMATD0i+--