]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
aarch64: morello: rewrite start code in C
authorSzabolcs Nagy <szabolcs.nagy@arm.com>
Fri, 26 Aug 2022 08:06:27 +0000 (09:06 +0100)
committerSzabolcs Nagy <szabolcs.nagy@arm.com>
Wed, 12 Oct 2022 13:22:03 +0000 (14:22 +0100)
sysdeps/aarch64/morello/cheri-rel.h [new file with mode: 0644]
sysdeps/aarch64/morello/dl-start.S [deleted file]
sysdeps/aarch64/morello/dl-start.c [new file with mode: 0644]
sysdeps/aarch64/morello/start.S [deleted file]
sysdeps/aarch64/morello/start.c [new file with mode: 0644]

diff --git a/sysdeps/aarch64/morello/cheri-rel.h b/sysdeps/aarch64/morello/cheri-rel.h
new file mode 100644 (file)
index 0000000..e5522ce
--- /dev/null
@@ -0,0 +1,36 @@
+#include <stdint.h>
+#include <ldsodefs.h>
+#include <cheri_perms.h>
+
+static inline uintptr_t
+morello_relative (uintptr_t base,
+                 const ElfW(Rela) *reloc,
+                 void *reloc_addr)
+{
+  uint64_t *__attribute__((may_alias)) u64_reloc_addr = reloc_addr;
+
+  /* Fragment identified by r_offset has the following information:
+     | 64-bit: address | 56-bits: length | 8-bits: permissions | */
+  unsigned long loc = u64_reloc_addr[0];
+  unsigned long len = u64_reloc_addr[1] & ((1UL << 56) - 1);
+  unsigned long perm = u64_reloc_addr[1] >> 56;
+  uintptr_t value;
+
+  value = base + loc;
+
+  value = __builtin_cheri_bounds_set_exact (value, len);
+
+  value = value + reloc->r_addend;
+
+  if (perm == 1)
+    value = __builtin_cheri_perms_and (value, CAP_PERM_MASK_R);
+  if (perm == 2)
+    value = __builtin_cheri_perms_and (value, CAP_PERM_MASK_RW);
+  if (perm == 4)
+    value = __builtin_cheri_perms_and (value, CAP_PERM_MASK_RX);
+
+  /* Seal executable capabilities with MORELLO_RB.  */
+  if (perm == 4)
+    value = __builtin_cheri_seal_entry (value);
+  return value;
+}
diff --git a/sysdeps/aarch64/morello/dl-start.S b/sysdeps/aarch64/morello/dl-start.S
deleted file mode 100644 (file)
index ea8d435..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/* ld.so _start code.
-   Copyright (C) 2022 Free Software Foundation, Inc.
-
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <sysdep.h>
-
-ENTRY(_start)
-
-       /* Create an initial frame with 0 LR and FP */
-       cfi_undefined (c30)
-       mov     c29, czr
-       mov     c30, czr
-       mov     c0, csp
-       bl      _dl_start
-       /* Returns user entry point in x0.  */
-       mov     c21, c0
-.globl _dl_start_user
-.type _dl_start_user, %function
-_dl_start_user:
-       /* Get argc.  */
-       ldr     x1, [csp]
-       /* Get argv.  */
-       add     c2, csp, 16
-       /* Compute envp.  */
-       add     c3, c2, x1, lsl 4
-       add     c3, c3, 16
-
-       adrp    c16, capinit_rtld_local
-       ldr     c16, [c16, :lo12:capinit_rtld_local]
-       ldr     c0, [c16]
-       bl      _dl_init
-       /* Load the finalizer function.  */
-       adrp    c0, capinit_dl_fini
-       ldr     c0, [c0, :lo12:capinit_dl_fini]
-       /* Jump to the user's entry point.  */
-       mov     c16, c21
-       br      c16
-END(_start)
-
-       .section        .data.rel.ro.local,"aw"
-       .align  4
-       .type   capinit_rtld_local, %object
-       .size   capinit_rtld_local, 16
-capinit_rtld_local:
-       .capinit        _rtld_local
-       .xword  0
-       .xword  0
-       .type   capinit_dl_fini, %object
-       .size   capinit_dl_fini, 16
-capinit_dl_fini:
-       .capinit        _dl_fini
-       .xword  0
-       .xword  0
diff --git a/sysdeps/aarch64/morello/dl-start.c b/sysdeps/aarch64/morello/dl-start.c
new file mode 100644 (file)
index 0000000..b0bedc4
--- /dev/null
@@ -0,0 +1,82 @@
+/* ld.so _start code.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <ldsodefs.h>
+
+asm(""
+".global       _start\n"
+".type         _start, %function\n"
+"_start:\n"
+"      .cfi_startproc\n"
+"      .cfi_undefined c30\n"
+"      mov     c29, czr\n"
+"      mov     c30, czr\n"
+"      mov     c0, csp\n"
+"      bl      __real_start\n"
+       /* Jump to the user's entry point, with original csp.  */
+"      mov     c16, c0\n"
+"      mov     c0, c1\n"
+"      br      c16\n"
+"      .cfi_endproc\n"
+"      .size _start, .-_start\n");
+
+typedef void (entry_t) (void (*)(void));
+
+typedef struct user_entry {
+       entry_t *fun;
+       void (*arg)(void);
+};
+
+struct user_entry
+_dl_start_user (uintptr_t *args, entry_t *entry)
+{
+  /* Setup argv, envp, auxv for the application.  */
+  uintptr_t *p;
+  long n;
+  int argc = args[0];
+  p = args + 1;
+  n = argc + 1;
+  char **argv = (char **) __builtin_cheri_bounds_set (p, n * sizeof *p);
+  p += n;
+  for (n = 0; p[n]; n++);
+  n++;
+  char **envp = (char **) __builtin_cheri_bounds_set (p, n * sizeof *p);
+  p += n;
+  for (n = 0; p[n] != AT_NULL; n += 2);
+  n += 2;
+  uintptr_t *auxv = __builtin_cheri_bounds_set (p, n * sizeof *p);
+
+  _dl_init (GL(dl_ns)[LM_ID_BASE]._ns_loaded, argc, argv, envp);
+  struct user_entry e = {entry, _dl_fini};
+  return e;
+}
+
+/* Generic ld.so start code in rtld.c.  */
+uintptr_t
+_dl_start (void *) attribute_hidden;
+
+/* ld.so entry point.  */
+struct user_entry
+__real_start (uintptr_t *sp)
+{
+  /* Run ls.so setup.  */
+  entry_t *entry = (entry_t *) _dl_start (sp);
+  return _dl_start_user (sp, entry);
+}
diff --git a/sysdeps/aarch64/morello/start.S b/sysdeps/aarch64/morello/start.S
deleted file mode 100644 (file)
index 2706019..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-/* Copyright (C) 2022 Free Software Foundation, Inc.
-
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public License as
-   published by the Free Software Foundation; either version 2.1 of the
-   License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library.  If not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <sysdep.h>
-#include <linux/auxvec.h>
-#include <cheri_perms.h>
-
-weak_extern (__rela_dyn_start)
-weak_extern (__rela_dyn_end)
-
-/* This is the canonical entry point, usually the first thing in the text
-   segment.
-
-   Note that the code in the .init section has already been run.
-   This includes _init and _libc_init
-
-
-   At this entry point, most registers' values are unspecified, except:
-
-   x0/w0       Contains a function pointer to be registered with `atexit'.
-               This is how the dynamic linker arranges to have DT_FINI
-               functions called for shared libraries that have been loaded
-               before this code runs.
-
-   sp          The stack contains the arguments and environment:
-               0(sp)                   argc
-               8(sp)                   argv[0]
-               ...
-               (8*argc)(sp)            NULL
-               (8*(argc+1))(sp)        envp[0]
-               ...
-                                       NULL
- */
-
-       .text
-ENTRY(_start)
-
-       /* Create an initial frame with 0 LR and FP */
-       cfi_undefined (c30)
-       mov     c29, czr
-       mov     c30, czr
-
-       /* TODO: Use the presence of __rela_dyn_start.  */
-       mov     c5, c0
-       cbnz    x0, L(rtld_done)
-
-       /* Initialize capabilities.  */
-#ifndef SHARED
-       /* The GNU Linker emits a R_MORELLO_RELATIVE relocation along with
-          __rela_dyn_start and __rela_dyn_end symbols around the relocation section.
-          r_offset contains the location, size and permissions:
-
-               capability location             // offset 0 (64 bits)
-               length                          // offset 8 (56 bits)
-               permissions                     //           (8 bits)
-               addend (capability offset)      // offset 16
-
-          according to the ABI document in:
-          https://github.com/ARM-software/abi-aa/blob/main/aaelf64-morello/aaelf64-morello.rst#dynamic-linking-with-morello
-
-       The addend is the capability offset.
-
-          Note that this will not work with clang, since lld emits a __cap_relocs
-          struct that has a different layout.  If we need to support clang+lld, then
-          we need to copy the capability initialization steps from Bionic, in:
-          libc/arch-morello/bionic/__morello_init_static.S.  */
-
-       adrp    c0, __rela_dyn_start
-       add     c0, c0, #:lo12:__rela_dyn_start
-       adrp    c1, __rela_dyn_end
-       add     c1, c1, #:lo12:__rela_dyn_end
-
-1:
-       /* No capabilities to initialize. Skip to stack adjustment.  */
-       cmp     c0, c1
-       b.eq    3f
-
-       /* Load the address of capability.  */
-       ldr     x2, [c0, #0]
-
-       /* Construct capability location from DDC and r_offset.  */
-       cvtd    c2, x2
-
-       /* Base in x3.  */
-       ldr     x3, [c2]
-
-       /* Length + permissions in x5.  */
-       ldr     x5, [c2, #8]
-
-       /* Permissions in x6.  */
-       lsr     x6, x5, #56
-
-       /* Permissions encoded as:
-          4 = X (executable)
-          2 = W (read/write)
-          1 = R (read only)
-
-          Permissions to clear in x8 and x11.  */
-       mov     x8, (CAP_PERM_STORE_CAP | CAP_PERM_EXECUTE)
-       movk    x8, (CAP_PERM_STORE >> 16), lsl #16
-       mov     x9, (CAP_PERM_EXECUTE)
-       mov     x10, (CAP_PERM_STORE_CAP)
-       movk    x10, (CAP_PERM_STORE >> 16), lsl #16
-       mov     x11, (CAP_PERM_UNSEAL | CAP_PERM_SEAL | CAP_PERM_COMPARTMENT_ID)
-       cmp     x6, #0x2
-       csel    x8, x9, x8, eq
-       cmp     x6, #0x4
-       csel    x8, x10, x8, eq
-
-       /* Length in x5.  */
-       and     x5, x5, #0xffffffffffffff
-
-       /* Offset in x7.  */
-       ldr     x7, [c0, #16]
-
-       /* Construct capability from DDC with value equal to base.  */
-       cvtd    c3, x3
-
-       /* Set bounds to [base ; base + length].  */
-       scbndse c3, c3, x5
-
-       /* Compute the capability value from addend.  */
-       add     c3, c3, x7
-
-       /* Clear permissions.  */
-       clrperm c3, c3, x8
-       clrperm c3, c3, x11
-
-       /* Seal capabilities, which provide execute permission, with MORELLO_RB */
-       tst     x8, #CAP_PERM_EXECUTE
-       b.ne    2f
-
-       seal    c3, c3, rb
-
-2:
-       /* Store the constructed capability to the target location.  */
-       str     c3, [c2]
-
-       /* Move to the next __rela_dyn entry.  */
-       add     c0, c0, #24
-       b       1b
-
-3:
-#endif /* !SHARED */
-
-       /* Setup rtld_fini in argument register */
-       mov     c5, czr
-
-L(rtld_done):
-
-       /* Load argc and a pointer to argv */
-       ldr     c1, [csp, #0]
-       add     c2, csp, #16
-
-       /* Setup stack limit in argument register */
-       mov     c6, csp
-
-#ifdef PIC
-# ifdef SHARED
-       adrp    c0, :got:main
-       ldr     c0, [c0, #:got_lo12:main]
-# else
-       adrp    c0, __wrap_main
-       add     c0, c0, :lo12:__wrap_main
-# endif /* SHARED */
-#else
-       /* Set up the other arguments in registers */
-       MOVL (0, main)
-       cvtd    c0, x0
-#endif /* PIC */
-
-       mov     x3, #0          /* Used to be init.  */
-       mov     x4, #0          /* Used to be fini.  */
-
-       /* __libc_start_main (main, argc, argv, init, fini, rtld_fini,
-                             stack_end) */
-
-       /* Let the libc call main and exit with its return code.  */
-       bl      __libc_start_main
-
-       /* should never get here....*/
-       bl      abort
-END(_start)
-
-#if defined PIC && !defined SHARED
-       /* When main is not defined in the executable but in a shared library
-          then a wrapper is needed in crt1.o of the static-pie enabled libc,
-          because crt1.o and rcrt1.o share code and the later must avoid the
-          use of GOT relocations before __libc_start_main is called.  */
-ENTRY(__wrap_main)
-       b       main
-END(__wrap_main)
-#endif
-
-       /* Define a symbol for the first piece of initialized data.  */
-       .data
-       .globl __data_start
-__data_start:
-       .long 0
-       .weak data_start
-       data_start = __data_start
diff --git a/sysdeps/aarch64/morello/start.c b/sysdeps/aarch64/morello/start.c
new file mode 100644 (file)
index 0000000..5af010f
--- /dev/null
@@ -0,0 +1,169 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   In addition to the permissions in the GNU Lesser General Public
+   License, the Free Software Foundation gives you unlimited
+   permission to link the compiled version of this file with other
+   programs, and to distribute those programs without any restriction
+   coming from the use of this file. (The GNU Lesser General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into another program.)
+
+   Note that people who make modified versions of this file are not
+   obligated to grant this special exception for their modified
+   versions; it is their choice whether to do so. The GNU Lesser
+   General Public License gives permission to release a modified
+   version without this exception; this exception also makes it
+   possible to release a modified version which carries forward this
+   exception.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <ldsodefs.h>
+#include <cheri-rel.h>
+
+/* This is the canonical entry point, usually the first thing in the text
+   segment.
+
+   Note that in case of dynamic linked exe the code in the .init section
+   has already been run.  This includes _init and _libc_init.
+
+
+   At this entry point, most registers' values are unspecified, except:
+
+   x0/w0       Contains a function pointer to be registered with `atexit'.
+               This is how the dynamic linker arranges to have DT_FINI
+               functions called for shared libraries that have been loaded
+               before this code runs.
+
+   sp          The stack contains the arguments and environment:
+               0(sp)                   argc
+               8(sp)                   argv[0]
+               ...
+               (8*argc)(sp)            NULL
+               (8*(argc+1))(sp)        envp[0]
+               ...
+                                       NULL
+ */
+
+asm(""
+".global       _start\n"
+".type         _start, %function\n"
+"_start:\n"
+"      .cfi_startproc\n"
+"      .cfi_undefined c30\n"
+"      mov     c29, czr\n"
+"      mov     c30, czr\n"
+"      mov     c1, csp\n"
+"      b       __real_start\n"
+"      .cfi_endproc\n"
+"      .size _start, .-_start\n");
+
+#ifndef SHARED
+static int
+is_static_linked (void)
+{
+  unsigned long x;
+  asm (""
+    ".weak __rela_dyn_start\n"
+    ".hidden __rela_dyn_start\n"
+    "movz %0, #:abs_g3:__rela_dyn_start\n"
+    "movk %0, #:abs_g2_nc:__rela_dyn_start\n"
+    "movk %0, #:abs_g1_nc:__rela_dyn_start\n"
+    "movk %0, #:abs_g0_nc:__rela_dyn_start\n" : "=r"(x));
+  return x != 0;
+}
+
+static uintptr_t
+get_rela_dyn_start (void)
+{
+  uintptr_t p;
+  asm (""
+    ".weak __rela_dyn_start\n"
+    ".hidden __rela_dyn_start\n"
+    "adrp %0, __rela_dyn_start\n"
+    "add %0, %0, :lo12:__rela_dyn_start\n" : "=r"(p));
+  return p;
+}
+
+static uintptr_t
+get_rela_dyn_end (void)
+{
+  uintptr_t p;
+  asm (""
+    ".weak __rela_dyn_end\n"
+    ".hidden __rela_dyn_end\n"
+    "adrp %0, __rela_dyn_end\n"
+    "add %0, %0, :lo12:__rela_dyn_end\n" : "=r"(p));
+  return p;
+}
+
+static uintptr_t
+get_base (void)
+{
+  /* The base is always 0: only used for static linking and static pie
+     is not supported here.  */
+  uintptr_t p = 0;
+  asm volatile ("cvtd %0, %x0" : "+r"(p));
+  return p;
+}
+
+static void
+apply_rel (uintptr_t base, uintptr_t start, uintptr_t end)
+{
+  const ElfW(Rela) *r;
+  for (r = (const ElfW(Rela) *)start; r != (void *)end; r++)
+    {
+      uintptr_t *reloc_addr = base + r->r_offset;
+      uintptr_t value = morello_relative (base, r, reloc_addr);
+      *reloc_addr = value;
+    }
+}
+#endif /* !SHARED */
+
+int main (int argc, char **argv);
+
+void __libc_start_main (int main (int, char **),
+                       int argc, char **argv,
+                       void *init, void *fini,
+                       void rtld_fini (void), void *sp);
+
+void
+__real_start (void rtld_fini (void), uintptr_t *sp)
+{
+#ifndef SHARED
+  if (is_static_linked ())
+    {
+      uintptr_t start = get_rela_dyn_start ();
+      uintptr_t end = get_rela_dyn_end ();
+      uintptr_t base = get_base ();
+      apply_rel (base, start, end);
+      rtld_fini = 0;
+    }
+  /* Compiler barrier after relocs are processed.  */
+  asm volatile ("" ::: "memory");
+#endif
+
+  int argc = *sp;
+  char **argv = (char **) (sp + 1);
+  __libc_start_main (main, argc, argv, 0, 0, rtld_fini, sp);
+  __builtin_trap ();
+}
+
+int __data_start = 1;
+weak_alias (__data_start, data_start);