tech-kern archive

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

Fixing the ELF priorities



Hi,
I would like to improve the priorities of the binary loader. When the kernel
loads a binary, it basically loops and calls different loaders (for aout, ELF,
...). There are several ELF loaders, for native and emulated binaries. This
loop has a particular order: the 32bit compat loaders are called *before*
the native ones. Which means that when you launch a simple 64bit binary on a
64bit system, the kernel first tries to load it through the netbsd32 and linux32
compat loaders. The priority is obviously wrong, the native loaders should be
called first; it also has a non-negligible performance impact when executing
many binaries (when compiling something, for example).

 32bit system:
   - exec_elf32 ------> 32bit code and module, handles native binaries.
 64bit system:
   - exec_elf64 ------> 64bit code and module, handles native binaries.
   - exec_elf32 ------> 32bit code and module. This module loads 32bit binaries
                        properly, but they won't work in any case since it will
                        then use the native 64bit environment.
   - netbsd32   ------> this module loads 32bit binaries through the native
                        32bit code, but then uses its own 32bit syscalls. That's
                        how a 32bit binary can work on a 64bit system.

Let's consider this order:
  1. exec_elf64
  2. exec_elf32
  3. netbsd32

That's how a 32bit binary will be processed:
 exec_elf64 will reject the binary, because it's not a 64bit one (ELF header).
 exec_elf32 will accept the binary, as it is a 32bit one, and will try to
            launch it, which of course will fail because it will use the native
            64bit environment. So the binary will crash.
 netbsd32   is never reached.

That's why netbsd32 is currently the first: it will use the 32bit code to load
the binary (as well as exec_elf32 would), but it will then use its own 32bit
environment.

Now, a correct fix would be, on 64bit systems, to compile the 32bit code without
setting up the exec_elf32 module. That way, on those systems, there's no more
exec_elf32 module, which means that there's no module that will crash a binary
before it actually reaches the intended compat layer, but there's still the
32bit code usable by netbsd32 and linux32.

Then the order does not matter anymore, and we can give the native loader the
highest priority. Attached are two patches (the former to disable the 32bit
module, the latter to change the priorities).

But I wonder if there wouldn't be a cleaner way to fix this. The problem with
these patches is that exec_elf32 is a dependency of linux32:

------------------------- linux32/common/linux32_mod.c -------------------------
#if defined(EXEC_ELF32)
# define        MD1     ",exec_elf32,compat_netbsd32"
#else
# define        MD1     ""
#endif

MODULE(MODULE_CLASS_EXEC, compat_linux32, "compat_linux" MD1);
--------------------------------------------------------------------------------

I think there's another issue with the dependency here: whether the exec_elf32
module is enabled or not does not matter, what matters is if the kernel is
compiled with EXEC_ELF32. But as EXEC_ELF32 on 64bit systems will now disable
exec_elf32, we have a problem.

Beyond that, there's an inconsistency: if exec_elf32 is a dependency of linux32,
then it should also be a dependency of netbsd32 (not the case currently).

Do we just delete this dependency?

This should really be fixed. I've made a small benchmark: on average, we save 30
seconds when doing "./build.sh kernel=GENERIC" with the attached fixes on amd64.


-------------------------------------- 1 ---------------------------------------

Index: exec_elf32.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf32.c,v
retrieving revision 1.140
diff -u -r1.140 exec_elf32.c
--- exec_elf32.c        7 Apr 2014 17:02:15 -0000       1.140
+++ exec_elf32.c        8 Jun 2014 11:54:23 -0000
@@ -34,10 +34,17 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: exec_elf32.c,v 1.140 2014/04/07 17:02:15 rjs Exp 
$");
 
+/* Compile the 32bit code */
 #define        ELFSIZE 32
-
 #include "exec_elf.c"
 
+#if ARCH_ELFSIZE != 64
+/*
+ * If we are on a 64bit system, we don't need the exec_elf32 module. However,
+ * we need the 32bit code to be compiled, because netbsd32 and linux32 will
+ * use it.
+ */
+
 #include <sys/module.h>
 
 #define ELF32_AUXSIZE (howmany(ELF_AUX_ENTRIES * sizeof(Aux32Info), \
@@ -101,3 +108,5 @@
                return ENOTTY;
         }
 }
+
+#endif /* ARCH_ELFSIZE != 64 */

--------------------------------------------------------------------------------

-------------------------------------- 2 ---------------------------------------

Index: compat/linux32/common/linux32_mod.c
===================================================================
RCS file: /cvsroot/src/sys/compat/linux32/common/linux32_mod.c,v
retrieving revision 1.5
diff -u -r1.5 linux32_mod.c
--- compat/linux32/common/linux32_mod.c 7 Mar 2014 01:33:43 -0000       1.5
+++ compat/linux32/common/linux32_mod.c 8 Jun 2014 12:18:23 -0000
@@ -67,7 +67,7 @@
                        .elf_probe_func = linux32_elf32_probe, 
                },
                .es_emul = &emul_linux32,
-               .es_prio = EXECSW_PRIO_FIRST, 
+               .es_prio = EXECSW_PRIO_ANY, 
                .es_arglen = LINUX32_ELF_AUX_ARGSIZ,
                .es_copyargs = linux32_elf32_copyargs,
                .es_setregs = NULL,
Index: compat/netbsd32/netbsd32_mod.c
===================================================================
RCS file: /cvsroot/src/sys/compat/netbsd32/netbsd32_mod.c,v
retrieving revision 1.3
diff -u -r1.3 netbsd32_mod.c
--- compat/netbsd32/netbsd32_mod.c      7 Mar 2014 01:33:43 -0000       1.3
+++ compat/netbsd32/netbsd32_mod.c      8 Jun 2014 12:18:23 -0000
@@ -81,7 +81,7 @@
                        .elf_probe_func = netbsd32_elf32_probe,
                },
                .es_emul = &emul_netbsd32,
-               .es_prio = EXECSW_PRIO_FIRST,
+               .es_prio = EXECSW_PRIO_ANY,
                .es_arglen = ELF32_AUXSIZE,
                .es_copyargs = netbsd32_elf32_copyargs,
                .es_setregs = NULL,
Index: kern/exec_elf32.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf32.c,v
retrieving revision 1.140
diff -u -r1.140 exec_elf32.c
--- kern/exec_elf32.c   7 Apr 2014 17:02:15 -0000       1.140
+++ kern/exec_elf32.c   8 Jun 2014 12:18:23 -0000
@@ -59,7 +59,7 @@
                        .elf_probe_func = netbsd_elf32_probe,
                },
                .es_emul = &emul_netbsd,
-               .es_prio = EXECSW_PRIO_ANY,
+               .es_prio = EXECSW_PRIO_FIRST,
                .es_arglen = ELF32_AUXSIZE,
                .es_copyargs = elf32_copyargs,
                .es_setregs = NULL,
Index: kern/exec_elf64.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf64.c,v
retrieving revision 1.5
diff -u -r1.5 exec_elf64.c
--- kern/exec_elf64.c   7 Mar 2014 01:34:29 -0000       1.5
+++ kern/exec_elf64.c   8 Jun 2014 12:18:23 -0000
@@ -60,7 +60,7 @@
                        .elf_probe_func = netbsd_elf64_probe,
                },
                .es_emul = &emul_netbsd,
-               .es_prio = EXECSW_PRIO_ANY,
+               .es_prio = EXECSW_PRIO_FIRST,
                .es_arglen = ELF64_AUXSIZE,
                .es_copyargs = elf64_copyargs,
                .es_setregs = NULL,

--------------------------------------------------------------------------------


Home | Main Index | Thread Index | Old Index