From 138274e92a01be8a98392489b1a2f497ab11c201 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sat, 1 Jun 2024 03:39:03 -0700 Subject: xtensa: add FDPIC support This change implements Xtensa FDPIC ABI as specified in the first version of the following document: https://github.com/jcmvbkbc/xtensa-abi/blob/master/fdpic-xtensa.txt Signed-off-by: Max Filippov --- libc/sysdeps/linux/xtensa/__start_context.S | 6 +- libc/sysdeps/linux/xtensa/bits/elf-fdpic.h | 117 ++++++++++++++++++++++++++++ libc/sysdeps/linux/xtensa/clone.S | 6 ++ libc/sysdeps/linux/xtensa/crt1.S | 81 +++++++++++++++++++ libc/sysdeps/linux/xtensa/crti.S | 8 ++ libc/sysdeps/linux/xtensa/crtn.S | 6 ++ libc/sysdeps/linux/xtensa/crtreloc.c | 105 +++++++++++++++++++++++++ libc/sysdeps/linux/xtensa/getcontext.S | 1 + libc/sysdeps/linux/xtensa/makecontext.c | 5 ++ libc/sysdeps/linux/xtensa/setcontext.S | 1 + libc/sysdeps/linux/xtensa/setjmp.S | 3 +- libc/sysdeps/linux/xtensa/swapcontext.S | 1 + libc/sysdeps/linux/xtensa/sysdep.h | 48 +++++++++++- 13 files changed, 383 insertions(+), 5 deletions(-) create mode 100644 libc/sysdeps/linux/xtensa/bits/elf-fdpic.h create mode 100644 libc/sysdeps/linux/xtensa/crtreloc.c (limited to 'libc/sysdeps') diff --git a/libc/sysdeps/linux/xtensa/__start_context.S b/libc/sysdeps/linux/xtensa/__start_context.S index a30d7b618..e6ce93347 100644 --- a/libc/sysdeps/linux/xtensa/__start_context.S +++ b/libc/sysdeps/linux/xtensa/__start_context.S @@ -22,9 +22,10 @@ * There's no entry instruction, makecontext sets up ucontext_t as if * getcontext was called above and is about to return here. * Registers on entry to this function: - * a12: func to call + * a12: func to call (function descriptor in case of FDPIC) * a13: ucp->uc_link, next context to activate if func returns * a14: func argc + * a15: current GOT pointer (in case of FDPIC) */ .literal_position @@ -46,14 +47,17 @@ ENTRY_PREFIX(__start_context) addi a1, a1, 16 /* func arguments 6..argc - 1 are now at the top of the stack */ 1: + FDPIC_LOAD_FUNCDESC (a12, a12) callx0 a12 beqz a13, 1f mov a2, a13 movi a4, JUMPTARGET (setcontext) + FDPIC_LOAD_JUMPTARGET (a4, a15, a4) callx0 a4 1: movi a4, JUMPTARGET (_exit) movi a2, 0 + FDPIC_LOAD_JUMPTARGET (a4, a15, a4) callx0 a4 ill END(__start_context) diff --git a/libc/sysdeps/linux/xtensa/bits/elf-fdpic.h b/libc/sysdeps/linux/xtensa/bits/elf-fdpic.h new file mode 100644 index 000000000..19bb247b8 --- /dev/null +++ b/libc/sysdeps/linux/xtensa/bits/elf-fdpic.h @@ -0,0 +1,117 @@ +/* Copyright 2003, 2004 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.) + +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 +Library 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; see the file COPYING.LIB. If +not, see . */ + +#ifndef _BITS_ELF_FDPIC_H +#define _BITS_ELF_FDPIC_H + +/* These data structures are described in the FDPIC ABI extension. + The kernel passes a process a memory map, such that for every LOAD + segment there is an elf32_fdpic_loadseg entry. A pointer to an + elf32_fdpic_loadmap is passed in r7 at start-up, and a pointer to + an additional such map is passed in r8 for the interpreter, when + there is one. */ + +#include + +/* This data structure represents a PT_LOAD segment. */ +struct elf32_fdpic_loadseg +{ + /* Core address to which the segment is mapped. */ + Elf32_Addr addr; + /* VMA recorded in the program header. */ + Elf32_Addr p_vaddr; + /* Size of this segment in memory. */ + Elf32_Word p_memsz; +}; + +struct elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + Elf32_Half version; + /* Number of segments in this map. */ + Elf32_Half nsegs; + /* The actual memory map. */ + struct elf32_fdpic_loadseg segs[/*nsegs*/]; +}; + +struct elf32_fdpic_loadaddr { + struct elf32_fdpic_loadmap *map; + void *got_value; +}; + +/* Map a pointer's VMA to its corresponding address according to the + load map. */ +static __always_inline void * +__reloc_pointer (void *p, + const struct elf32_fdpic_loadmap *map) +{ + int c; + +#if 0 + if (map->version != 0) + /* Crash. */ + ((void(*)())0)(); +#endif + + /* No special provision is made for NULL. We don't want NULL + addresses to go through relocation, so they shouldn't be in + .rofixup sections, and, if they're present in dynamic + relocations, they shall be mapped to the NULL address without + undergoing relocations. */ + + for (c = 0; + /* Take advantage of the fact that the loadmap is ordered by + virtual addresses. In general there will only be 2 entries, + so it's not profitable to do a binary search. */ + c < map->nsegs && p >= (void*)map->segs[c].p_vaddr; + c++) + { + /* This should be computed as part of the pointer comparison + above, but we want to use the carry in the comparison, so we + can't convert it to an integer type beforehand. */ + unsigned long offset = (char*)p - (char*)map->segs[c].p_vaddr; + /* We only check for one-past-the-end for the last segment, + assumed to be the data segment, because other cases are + ambiguous in the absence of padding between segments, and + rofixup already serves as padding between text and data. + Unfortunately, unless we special-case the last segment, we + fail to relocate the _end symbol. */ + if (offset < map->segs[c].p_memsz + || (offset == map->segs[c].p_memsz && c + 1 == map->nsegs)) + return (char*)map->segs[c].addr + offset; + } + + /* We might want to crash instead. */ + return (void*)-1; +} + +# define __RELOC_POINTER(ptr, loadaddr) \ + (__reloc_pointer ((void*)(ptr), \ + (loadaddr).map)) + +void* +__self_reloc (const struct elf32_fdpic_loadmap *map, void ***p, void ***e); + +#endif /* _BITS_ELF_FDPIC_H */ diff --git a/libc/sysdeps/linux/xtensa/clone.S b/libc/sysdeps/linux/xtensa/clone.S index ebfdcc1f6..a11044cd0 100644 --- a/libc/sysdeps/linux/xtensa/clone.S +++ b/libc/sysdeps/linux/xtensa/clone.S @@ -81,11 +81,17 @@ ENTRY (__clone) callx4 a2 #elif defined(__XTENSA_CALL0_ABI__) mov a2, a9 /* load up the 'arg' parameter */ +#ifdef __FDPIC__ + mov a12, a11 + l32i a11, a7, 4 + l32i a7, a7, 0 +#endif callx0 a7 /* call the user's function */ /* Call _exit. Note that any return parameter from the user's function in a2 is seen as inputs to _exit. */ movi a0, JUMPTARGET(_exit) + FDPIC_LOAD_JUMPTARGET(a0, a12, a0) callx0 a0 #else #error Unsupported Xtensa ABI diff --git a/libc/sysdeps/linux/xtensa/crt1.S b/libc/sysdeps/linux/xtensa/crt1.S index 3fa14ae58..a12f82dd6 100644 --- a/libc/sysdeps/linux/xtensa/crt1.S +++ b/libc/sysdeps/linux/xtensa/crt1.S @@ -35,6 +35,86 @@ #include +#if defined(__FDPIC__) + +/* This is the canonical entry point, usually the first thing in the text + segment. When the entry point runs, most register values are unspecified, + except for: + + a6 Address of .dynamic section + a5 Interpreter map + a4 Executable map + + a2 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. + + a1 The stack (i.e., a1+16) contains the arguments and environment: + a1+0 argc + a1+4 argv[0] + ... + a1+(4*argc) NULL + a1+(4*(argc+1)) envp[0] + ... + NULL + */ + .text + .align 4 + .literal_position + .global _start + .type _start, @function +_start: +#if defined(__XTENSA_CALL0_ABI__) + + .begin no-transform + call0 1f +2: + .end no-transform + .align 4 + .literal_position +1: + movi a15, 2b + sub a15, a0, a15 + + mov a12, a4 + mov a13, a5 + mov a14, a6 + mov a2, a4 + movi a3, __ROFIXUP_LIST__ + add a3, a3, a15 + movi a4, __ROFIXUP_END__ + add a4, a4, a15 + movi a0, __self_reloc + add a0, a0, a15 + callx0 a0 + + mov a11, a2 + movi a2, main@GOTOFFFUNCDESC + add a2, a2, a11 + l32i a3, sp, 0 /* argc */ + addi a4, sp, 4 /* argv */ + /* a5 is either 0 when static or set by the RTLD to the rtld_fini */ + mov a7, a13 + /* unused stack_end argument is what used to be argc */ + movi a5, _init@GOTOFFFUNCDESC + add a5, a5, a11 + movi a6, _fini@GOTOFFFUNCDESC + add a6, a6, a11 + + movi a0, __uClibc_main@GOTOFFFUNCDESC + add a0, a0, a11 + l32i a11, a0, 4 + l32i a0, a0, 0 + callx0 a0 + ill + +#else +#error Unsupported Xtensa ABI +#endif + +#else /* defined(__FDPIC__) */ + #ifndef __UCLIBC_CTOR_DTOR__ .weak _init .weak _fini @@ -173,3 +253,4 @@ __data_start: .long 0 .weak data_start data_start = __data_start +#endif /* defined(__FDPIC__) */ diff --git a/libc/sysdeps/linux/xtensa/crti.S b/libc/sysdeps/linux/xtensa/crti.S index 43e66e308..2923ff09d 100644 --- a/libc/sysdeps/linux/xtensa/crti.S +++ b/libc/sysdeps/linux/xtensa/crti.S @@ -11,6 +11,10 @@ _init: #elif defined(__XTENSA_CALL0_ABI__) addi sp, sp, -16 s32i a0, sp, 0 +#ifdef __FDPIC__ + s32i a12, sp, 4 + mov a12, a11 +#endif #else #error Unsupported Xtensa ABI #endif @@ -26,6 +30,10 @@ _fini: #elif defined(__XTENSA_CALL0_ABI__) addi sp, sp, -16 s32i a0, sp, 0 +#ifdef __FDPIC__ + s32i a12, sp, 4 + mov a12, a11 +#endif #else #error Unsupported Xtensa ABI #endif diff --git a/libc/sysdeps/linux/xtensa/crtn.S b/libc/sysdeps/linux/xtensa/crtn.S index a3598da1a..6f797e8bd 100644 --- a/libc/sysdeps/linux/xtensa/crtn.S +++ b/libc/sysdeps/linux/xtensa/crtn.S @@ -4,6 +4,9 @@ #if defined(__XTENSA_WINDOWED_ABI__) retw #elif defined(__XTENSA_CALL0_ABI__) +#ifdef __FDPIC__ + l32i a12, sp, 4 +#endif l32i a0, sp, 0 addi sp, sp, 16 ret @@ -15,6 +18,9 @@ #if defined(__XTENSA_WINDOWED_ABI__) retw #elif defined(__XTENSA_CALL0_ABI__) +#ifdef __FDPIC__ + l32i a12, sp, 4 +#endif l32i a0, sp, 0 addi sp, sp, 16 ret diff --git a/libc/sysdeps/linux/xtensa/crtreloc.c b/libc/sysdeps/linux/xtensa/crtreloc.c new file mode 100644 index 000000000..697ef91ab --- /dev/null +++ b/libc/sysdeps/linux/xtensa/crtreloc.c @@ -0,0 +1,105 @@ +/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. + written by Alexandre Oliva +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.) + +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 +Library 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; see the file COPYING.LIB. If +not, see . */ + +#ifdef __FDPIC__ + +#include +#include + +/* This file is to be compiled into crt object files, to enable + executables to easily self-relocate. */ + +/* Compute the runtime address of pointer in the range [p,e), and then + map the pointer pointed by it. */ +static __always_inline void *** +reloc_range_indirect (void ***p, void ***e, + const struct elf32_fdpic_loadmap *map) +{ + while (p < e) + { + if (*p != (void **)-1) + { + void *ptr = __reloc_pointer (*p, map); + + if (ptr != (void *)-1) + { + unsigned long off = ((unsigned long)ptr & 3) * 8; + unsigned long *pa = (unsigned long *)((unsigned long)ptr & -4); + unsigned long v2; + void *pt; + + if (off) + { + unsigned long v0, v1; +#ifdef __XTENSA_EB__ + v0 = pa[1]; v1 = pa[0]; + v2 = (v1 >> (32 - off)) | (v0 << off); +#else /* __XTENSA_EL__ */ + v0 = pa[0]; v1 = pa[1]; + v2 = (v0 << (32 - off)) | (v1 >> off); +#endif + pt = (void *)((v1 << (32 - off)) | (v0 >> off)); + } + else + pt = *(void**)ptr; + pt = __reloc_pointer (pt, map); + if (off) + { + unsigned long v = (unsigned long)pt; +#ifdef __XTENSA_EB__ + pa[0] = (v2 << (32 - off)) | (v >> off); + pa[1] = (v << (32 - off)) | (v2 >> off); +#else /* __XTENSA_EL__ */ + pa[0] = (v2 >> (32 - off)) | (v << off); + pa[1] = (v >> (32 - off)) | (v2 << off); +#endif + } + else + *(void**)ptr = pt; + } + } + p++; + } + return p; +} + +/* Call __reloc_range_indirect for the given range except for the last + entry, whose contents are only relocated. It's expected to hold + the GOT value. */ +attribute_hidden void* +__self_reloc (const struct elf32_fdpic_loadmap *map, + void ***p, void ***e) +{ + p = reloc_range_indirect (p, e-1, map); + + if (p >= e) + return (void*)-1; + + return __reloc_pointer (*p, map); +} + +#endif /* __FDPIC__ */ diff --git a/libc/sysdeps/linux/xtensa/getcontext.S b/libc/sysdeps/linux/xtensa/getcontext.S index 7588a91b3..4cc644552 100644 --- a/libc/sysdeps/linux/xtensa/getcontext.S +++ b/libc/sysdeps/linux/xtensa/getcontext.S @@ -33,6 +33,7 @@ ENTRY(__getcontext) addi a4, a2, UCONTEXT_SIGMASK movi a2, SIG_BLOCK movi a5, JUMPTARGET (sigprocmask) + FDPIC_LOAD_JUMPTARGET (a5, a11, a5) jx a5 END(__getcontext) #elif defined(__XTENSA_WINDOWED_ABI__) diff --git a/libc/sysdeps/linux/xtensa/makecontext.c b/libc/sysdeps/linux/xtensa/makecontext.c index da26a0130..0a8f7116f 100644 --- a/libc/sysdeps/linux/xtensa/makecontext.c +++ b/libc/sysdeps/linux/xtensa/makecontext.c @@ -73,7 +73,12 @@ __makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...) sp -= 4 * (argc + 2); sp &= -16; +#ifdef __FDPIC__ + ucp->uc_mcontext.sc_pc = ((unsigned long *) __start_context)[0]; + ucp->uc_mcontext.sc_a[15] = ((unsigned long *) __start_context)[1]; +#else ucp->uc_mcontext.sc_pc = (unsigned long) __start_context; +#endif ucp->uc_mcontext.sc_a[1] = sp; ucp->uc_mcontext.sc_a[12] = (unsigned long) func; ucp->uc_mcontext.sc_a[13] = (unsigned long) ucp->uc_link; diff --git a/libc/sysdeps/linux/xtensa/setcontext.S b/libc/sysdeps/linux/xtensa/setcontext.S index 4df7cc049..72915ef8d 100644 --- a/libc/sysdeps/linux/xtensa/setcontext.S +++ b/libc/sysdeps/linux/xtensa/setcontext.S @@ -28,6 +28,7 @@ ENTRY(__setcontext) movi a4, 0 movi a2, SIG_SETMASK movi a5, JUMPTARGET (sigprocmask) + FDPIC_LOAD_JUMPTARGET (a5, a11, a5) callx0 a5 bnez a2, .Lerror diff --git a/libc/sysdeps/linux/xtensa/setjmp.S b/libc/sysdeps/linux/xtensa/setjmp.S index b8152fdd8..d629c11a8 100644 --- a/libc/sysdeps/linux/xtensa/setjmp.S +++ b/libc/sysdeps/linux/xtensa/setjmp.S @@ -155,7 +155,8 @@ ENTRY (__sigsetjmp) s32i a14, a2, 16 s32i a15, a2, 20 mov a12, a2 - movi a0, __sigjmp_save + movi a0, JUMPTARGET(__sigjmp_save) + FDPIC_LOAD_JUMPTARGET(a0, a11, a0) callx0 a0 l32i a0, a12, 0 l32i a12, a12, 8 diff --git a/libc/sysdeps/linux/xtensa/swapcontext.S b/libc/sysdeps/linux/xtensa/swapcontext.S index a215edc6d..40b38e98c 100644 --- a/libc/sysdeps/linux/xtensa/swapcontext.S +++ b/libc/sysdeps/linux/xtensa/swapcontext.S @@ -36,6 +36,7 @@ ENTRY(__swapcontext) addi a4, a2, UCONTEXT_SIGMASK movi a2, SIG_SETMASK movi a5, JUMPTARGET (sigprocmask) + FDPIC_LOAD_JUMPTARGET (a5, a11, a5) callx0 a5 bnez a2, .Lerror diff --git a/libc/sysdeps/linux/xtensa/sysdep.h b/libc/sysdeps/linux/xtensa/sysdep.h index 80b3f30fc..d05741027 100644 --- a/libc/sysdeps/linux/xtensa/sysdep.h +++ b/libc/sysdeps/linux/xtensa/sysdep.h @@ -75,13 +75,27 @@ #define LITERAL_POSITION .literal_position #undef JUMPTARGET -#ifdef __PIC__ +#if defined(__FDPIC__) +#define JUMPTARGET(name) name##@GOTOFFFUNCDESC +#define FDPIC_LOAD_FUNCDESC(call_target, funcdesc) \ + l32i a11, funcdesc, 4; \ + l32i call_target, funcdesc, 0 + +#define FDPIC_LOAD_JUMPTARGET(call_target, got_base, jumptarget)\ + add call_target, got_base, jumptarget; \ + FDPIC_LOAD_FUNCDESC(call_target, call_target) + +#elif defined(__PIC__) /* The "@PLT" suffix is currently a no-op for non-shared linking, but it doesn't hurt to use it conditionally for PIC code in case that changes someday. */ #define JUMPTARGET(name) name##@PLT +#define FDPIC_LOAD_FUNCDESC(call_target, funcdesc) +#define FDPIC_LOAD_JUMPTARGET(call_target, got_base, jumptarget) #else #define JUMPTARGET(name) name +#define FDPIC_LOAD_FUNCDESC(call_target, funcdesc) +#define FDPIC_LOAD_JUMPTARGET(call_target, got_base, jumptarget) #endif #ifndef FRAMESIZE @@ -153,6 +167,21 @@ #if defined _LIBC_REENTRANT # if defined USE___THREAD +#ifdef __FDPIC__ +# define SYSCALL_ERROR_ERRNO errno +# define SYSCALL_ERROR_HANDLER \ +0: rur a4, THREADPTR; \ + movi a3, SYSCALL_ERROR_ERRNO@GOTTPOFF; \ + .reloc ., R_XTENSA_TLS_TPOFF_PTR, SYSCALL_ERROR_ERRNO; \ + add a3, a3, a11; \ + .reloc ., R_XTENSA_TLS_TPOFF_LOAD, SYSCALL_ERROR_ERRNO; \ + l32i a3, a3, 0; \ + neg a2, a2; \ + add a4, a4, a3; \ + s32i a2, a4, 0; \ + movi a2, -1; \ + j .Lpseudo_end; +#else # define SYSCALL_ERROR_ERRNO errno # define SYSCALL_ERROR_HANDLER \ 0: rur a4, THREADPTR; \ @@ -162,13 +191,14 @@ s32i a2, a4, 0; \ movi a2, -1; \ j .Lpseudo_end; +#endif # else /* !USE___THREAD */ #if defined(__XTENSA_WINDOWED_ABI__) # define SYSCALL_ERROR_HANDLER \ 0: neg a2, a2; \ mov a6, a2; \ - movi a4, __errno_location@PLT; \ + movi a4, JUMPTARGET(__errno_location); \ callx4 a4; \ s32i a2, a6, 0; \ movi a2, -1; \ @@ -179,7 +209,8 @@ addi a1, a1, -16; \ s32i a0, a1, 0; \ s32i a2, a1, 4; \ - movi a0, __errno_location@PLT; \ + movi a0, JUMPTARGET(__errno_location); \ + FDPIC_LOAD_JUMPTARGET(a0, a11, a0); \ callx0 a0; \ l32i a0, a1, 0; \ l32i a3, a1, 4; \ @@ -193,12 +224,23 @@ # endif /* !USE___THREAD */ #else /* !_LIBC_REENTRANT */ +#ifdef __FDPIC__ +#define SYSCALL_ERROR_HANDLER \ +0: movi a4, errno@GOT; \ + add a4, a4, a11; \ + l32i a4, a4, 0; \ + neg a2, a2; \ + s32i a2, a4, 0; \ + movi a2, -1; \ + j .Lpseudo_end; +#else #define SYSCALL_ERROR_HANDLER \ 0: movi a4, errno; \ neg a2, a2; \ s32i a2, a4, 0; \ movi a2, -1; \ j .Lpseudo_end; +#endif /* __FDPIC__ */ #endif /* _LIBC_REENTRANT */ #endif /* __ASSEMBLER__ */ -- cgit v1.2.3