diff options
author | Christophe Lyon <christophe.lyon@st.com> | 2013-01-18 15:08:04 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbrodkorb@conet.de> | 2018-08-10 16:01:58 +0200 |
commit | 5ec586eb37f61e57f19ac60b58af61f167ca054a (patch) | |
tree | 6c72d03d43e76a33eb5d6360d9bdb2573a048c42 /libc/sysdeps/linux | |
parent | f88bb681ba22d77a19abfb4c1617c826298ed473 (diff) |
rtld: Add FDPIC code for arm
Add FDPIC dynamic relocations support, similar to what other FDPIC
targets do.
Lazy binding is implemented in a folllow-up patch.
Disable the SEND* macros because they involve relocations to
access constant strings that are unsupported by the existing
arm version.
Define DL_START, START, ARCH_NEEDS_BOOTSTRAP_RELOCS,
DL_CHECK_LIB_TYPE similarly to what other FDPIC targets do.
Define raise() because _dl_find_hash references __aeabi_uidivmod,
which uses __aeabi_idiv0 which in turn references raise.
* include/elf.h (R_ARM_FUNCDESC): Define.
(R_ARM_FUNCDESC_VALUE): Define.
* ldso/include/dl-string.h (SEND_STDERR, SEND_ADDRESS_STDERR)
(SEND_NUMBER_STDERR): Define empty for __FDPIC__.
* ldso/ldso/arm/dl-inlines.h: New file.
* ldso/ldso/arm/dl-startup.h (PERFORM_BOOTSTRAP_RELOC): Fix type
of load_addr. Fix handling of R_ARM_RELATIVE, add support for
R_ARM_FUNCDESC_VALUE.
(DL_START, START): Define for __FDPIC__.
(raise): Define.
* ldso/ldso/arm/dl-sysdep.h (ARCH_NEEDS_BOOTSTRAP_RELOCS): Define.
(DL_CHECK_LIB_TYPE): Define.
(elf_machine_type_class): Take into account FDPIC related
relocations.
(elf_machine_load_address): Support __FDPIC__.
(elf_machine_relative): Likewise.
* ldso/ldso/arm/elfinterp.c (_dl_linux_resolver): Dummy support
for __FDPIC__, implemented in a later patch.
(_dl_do_reloc): Fix reloc_adr computation for __FDPIC__, fix
handling of local symbols. Fix handling of R_ARM_RELATIVE, add
support for R_ARM_FUNCDESC_VALUE, R_ARM_FUNCDESC.
* ldso/ldso/arm/resolve.S: Make _dl_linux_resolve hidden.
* ldso/ldso/fdpic/dl-inlines.h (htab_delete): Declare.
* libc/sysdeps/linux/arm/bits/elf-fdpic.h: New file, similar to bfin's.
* libc/sysdeps/linux/arm/crtreloc.c: Likewise.
* libc/sysdeps/linux/arm/find_exidx.c (__dl_addr_in_loadaddr) Define.
(find_exidx_callback): Support __FDPIC__.
Signed-off-by: Mickaël Guêné <mickael.guene@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@st.com>
Diffstat (limited to 'libc/sysdeps/linux')
-rw-r--r-- | libc/sysdeps/linux/arm/bits/elf-fdpic.h | 114 | ||||
-rw-r--r-- | libc/sysdeps/linux/arm/crtreloc.c | 144 | ||||
-rw-r--r-- | libc/sysdeps/linux/arm/find_exidx.c | 38 |
3 files changed, 296 insertions, 0 deletions
diff --git a/libc/sysdeps/linux/arm/bits/elf-fdpic.h b/libc/sysdeps/linux/arm/bits/elf-fdpic.h new file mode 100644 index 000000000..3d6db54af --- /dev/null +++ b/libc/sysdeps/linux/arm/bits/elf-fdpic.h @@ -0,0 +1,114 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#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 <elf.h> + +/* 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 = p - (void*)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)) + +#endif /* _BITS_ELF_FDPIC_H */ diff --git a/libc/sysdeps/linux/arm/crtreloc.c b/libc/sysdeps/linux/arm/crtreloc.c new file mode 100644 index 000000000..560b4168b --- /dev/null +++ b/libc/sysdeps/linux/arm/crtreloc.c @@ -0,0 +1,144 @@ +/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. + written by Alexandre Oliva <aoliva@redhat.com> +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 <http://www.gnu.org/licenses/>. */ + +#ifdef __FDPIC__ + +#include <sys/types.h> +#include <link.h> + +/* This file is to be compiled into crt object files, to enable + executables to easily self-relocate. */ + +union word { + char c[4]; + void *v; +}; + +/* 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) + { + void *pt; + if ((long)ptr & 3) + { + unsigned char *c = ptr; + int i; + unsigned long v = 0; + for (i = 0; i < 4; i++) + v |= c[i] << 8 * i; + pt = (void *)v; + } + else + pt = *(void**)ptr; + pt = __reloc_pointer (pt, map); + if ((long)ptr & 3) + { + unsigned char *c = ptr; + int i; + unsigned long v = (unsigned long)pt; + for (i = 0; i < 4; i++, v >>= 8) + c[i] = v; + } + 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); +} + +#if 0 +/* These are other functions that might be useful, but that we don't + need. */ + +/* Remap pointers in [p,e). */ +static __always_inline void** +reloc_range (void **p, void **e, + const struct elf32_fdpic_loadmap *map) +{ + while (p < e) + { + *p = __reloc_pointer (*p, map); + p++; + } + return p; +} + +/* Remap p, adjust e by the same offset, then map the pointers in the + range determined by them. */ +void attribute_hidden +__reloc_range (const struct elf32_fdpic_loadmap *map, + void **p, void **e) +{ + void **old = p; + + p = __reloc_pointer (p, map); + e += p - old; + reloc_range (p, e, map); +} + +/* Remap p, adjust e by the same offset, then map pointers referenced + by the (unadjusted) pointers in the range. Return the relocated + value of the last pointer in the range. */ +void* attribute_hidden +__reloc_range_indirect (const struct elf32_fdpic_loadmap *map, + void ***p, void ***e) +{ + void ***old = p; + + p = __reloc_pointer (p, map); + e += p - old; + return reloc_range_indirect (p, e, map); +} +#endif + +#endif /* __FDPIC__ */ diff --git a/libc/sysdeps/linux/arm/find_exidx.c b/libc/sysdeps/linux/arm/find_exidx.c index 679d90c05..cd4d442cf 100644 --- a/libc/sysdeps/linux/arm/find_exidx.c +++ b/libc/sysdeps/linux/arm/find_exidx.c @@ -18,6 +18,23 @@ #include <link.h> #include <unwind.h> +#if __FDPIC__ +#include <bits/elf-fdpic.h> +static __always_inline int +__dl_addr_in_loadaddr(void *p, struct elf32_fdpic_loadaddr loadaddr) +{ + struct elf32_fdpic_loadmap *map = loadaddr.map; + int c; + + for (c = 0; c < map->nsegs; c++) + if ((void *)map->segs[c].addr <= p && + (char *)p < (char *)map->segs[c].addr + map->segs[c].p_memsz) + return 1; + + return 0; +} +#endif + struct unw_eh_callback_data { _Unwind_Ptr pc; @@ -32,6 +49,26 @@ struct unw_eh_callback_data static int find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr) { +#if __FDPIC__ + struct unw_eh_callback_data * data; + const ElfW(Phdr) *phdr; + int i; + int match = 0; + + data = (struct unw_eh_callback_data *) ptr; + if (__dl_addr_in_loadaddr((void *) data->pc, info->dlpi_addr)) { + match = 1; + phdr = info->dlpi_phdr; + for (i = info->dlpi_phnum; i > 0; i--, phdr++) { + if (phdr->p_type == PT_ARM_EXIDX) { + data->exidx_start = (_Unwind_Ptr) __RELOC_POINTER(phdr->p_vaddr, info->dlpi_addr); + data->exidx_len = phdr->p_memsz; + } + } + } + + return match; +#else struct unw_eh_callback_data * data; const ElfW(Phdr) *phdr; int i; @@ -59,6 +96,7 @@ find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr) } return match; +#endif } |