load_elf: match linux better for statically-linked binaries
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 6 Dec 2023 09:11:56 +0000 (01:11 -0800)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 13 Dec 2023 00:59:12 +0000 (16:59 -0800)
we still need more auxv entries, but the rest should match close
enough now.

src/openpower/decoder/isa/mem.py
src/openpower/syscalls/ppc_flags.py

index 2b8c52794c927377478618d062b53f789b074392..fc308cc8ab036538932f3cc26ac191add0875973 100644 (file)
@@ -1181,37 +1181,100 @@ def load_elf(mem, elf_file, args=(), env=(), stack_size=DEFAULT_INIT_STACK_SZ):
     result = mem.mmap_syscall(
         stack_low, stack_size, prot, flags, fd=-1, offset=0, is_mmap2=False)
     raise_if_syscall_err(result)
-    gprs = {}
-    if len(args):
-        raise NotImplementedError("allocate argv on the stack")
-    else:
-        argv = 0
-    if len(env):
-        raise NotImplementedError("allocate envp on the stack")
-    else:
-        envp = 0
 
-    if heap_start > 0:
-        mem.heap_range = range(heap_start, heap_start)  # empty heap to start
-
-    # FIXME: incorrect, should point to the aux vector allocated on the stack
+    env_bytes = bytearray()
+    env_offsets = []
+    for env_entry in env:
+        env_offsets.append(len(env_bytes))
+        if isinstance(env_entry, str):
+            env_entry = env_entry.encode()
+        else:
+            env_entry = bytes(env_entry)
+        if b"\0" in env_entry:
+            raise ValueError("env var byte-string can't contain NUL")
+        if b"=" not in env_entry:
+            raise ValueError("env var is missing =")
+        env_bytes += env_entry
+        env_bytes += b"\0"
+
+    stack_top -= len(env_bytes)
+    env_bytes_addr = stack_top
+    if len(env_bytes):
+        mem.get_ctypes(env_bytes_addr, len(env_bytes), True)[:] = env_bytes
+
+    args = tuple(args)
+    if len(args) == 0:
+        args = ("program",)  # glibc depends on argc != 0
+    args_bytes = bytearray()
+    arg_offsets = []
+    for arg in args:
+        arg_offsets.append(len(args_bytes))
+        if isinstance(arg, str):
+            arg = arg.encode()
+        else:
+            arg = bytes(arg)
+        if b"\0" in arg:
+            raise ValueError("argument byte-string can't contain NUL")
+        args_bytes += arg
+        args_bytes += b"\0"
+    args_bytes += b"\0"
+    stack_top -= len(args_bytes)
+    args_bytes_addr = stack_top
+    mem.get_ctypes(args_bytes_addr, len(args_bytes), True)[:] = args_bytes
+
+    stack_top -= stack_top % 8  # align stack top for auxv
+
+    auxv_t = struct.Struct("<QQ")
     auxv = 0
+    def write_auxv_entry(a_type, a_un):
+        nonlocal stack_top, auxv
+        stack_top -= auxv_t.size
+        auxv = stack_top
+        buf = mem.get_ctypes(stack_top, auxv_t.size, True)
+        auxv_t.pack_into(buf, 0, a_type, a_un)
+
+    # TODO: put more in auxv
+
+    write_auxv_entry(ppc_flags.AT_NULL, 0)  # final auxv entry
+
+    # final envp entry
+    stack_top -= 8
+    mem.get_ctypes(stack_top, 8, True)[:] = bytes(8)
 
-    # make space for red zone, 512 bytes specified in
-    # 64-bit ELF V2 ABI Specification v1.5 section 2.2.3.4
-    # https://files.openpower.foundation/s/cfA2oFPXbbZwEBK
-    stack_top -= 512
+    for env_offset in reversed(env_offsets):
+        stack_top -= 8
+        env_addr = env_offset + env_bytes_addr
+        mem.get_ctypes(stack_top, 8, True)[:] = env_addr.to_bytes(8, 'little')
 
-    # align stack_top
-    stack_top -= stack_top % 16
+    envp = stack_top
+
+    # final argv entry
+    stack_top -= 8
+    mem.get_ctypes(stack_top, 8, True)[:] = bytes(8)
+
+    for arg_offset in reversed(arg_offsets):
+        stack_top -= 8
+        arg_addr = arg_offset + args_bytes_addr
+        mem.get_ctypes(stack_top, 8, True)[:] = arg_addr.to_bytes(8, 'little')
+
+    argv = stack_top
+    argc = len(arg_offsets)
+
+    stack_top -= 8
+    mem.get_ctypes(stack_top, 8, True)[:] = argc.to_bytes(8, 'little')
+
+    gprs = {}
+
+    if heap_start > 0:
+        mem.heap_range = range(heap_start, heap_start)  # empty heap to start
 
     # TODO: dynamically-linked binaries need to use the entry-point of ld.so
     pc = elf_file.header['e_entry']
     gprs[1] = stack_top
-    gprs[3] = len(args)  # argc
-    gprs[4] = argv
-    gprs[5] = envp
-    gprs[5] = auxv
+    gprs[3] = 0  # argc -- apparently just zeroed by linux
+    gprs[4] = 0  # argv -- apparently just zeroed by linux
+    gprs[5] = 0  # envp -- apparently just zeroed by linux
+    gprs[6] = 0  # auxv -- apparently just zeroed by linux
     gprs[7] = 0  # termination function pointer
     gprs[12] = pc
     fpscr = 0
index 13da5ec2754a19e740f042243b27392253531e1e..7f2fe507efc5983f48d92759ba4f115525ae6af5 100644 (file)
@@ -21,6 +21,7 @@ def parse_defines(flags, compiler):
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <linux/utsname.h>
+#include <linux/auxvec.h>
 """
     if isinstance(compiler, str):
         compiler = [compiler]