Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/sys/linux/dl/dl-runtime.c')
-rw-r--r--newlib/libc/sys/linux/dl/dl-runtime.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/newlib/libc/sys/linux/dl/dl-runtime.c b/newlib/libc/sys/linux/dl/dl-runtime.c
new file mode 100644
index 000000000..111acfb71
--- /dev/null
+++ b/newlib/libc/sys/linux/dl/dl-runtime.c
@@ -0,0 +1,231 @@
+/* On-demand PLT fixup for shared objects.
+ Copyright (C) 1995-1999, 2000, 2001 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, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <alloca.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include "dynamic-link.h"
+
+#define __attribute_used__
+
+#if !defined ELF_MACHINE_NO_RELA || ELF_MACHINE_NO_REL
+# define PLTREL ElfW(Rela)
+#else
+# define PLTREL ElfW(Rel)
+#endif
+
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
+
+
+/* This function is called through a special trampoline from the PLT the
+ first time each PLT entry is called. We must perform the relocation
+ specified in the PLT of the given shared object, and return the resolved
+ function address to the trampoline, which will restart the original call
+ to that address. Future calls will bounce directly from the PLT to the
+ function. */
+
+#ifndef ELF_MACHINE_NO_PLT
+static ElfW(Addr) __attribute_used__
+fixup (
+# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+ ELF_MACHINE_RUNTIME_FIXUP_ARGS,
+# endif
+ /* GKM FIXME: Fix trampoline to pass bounds so we can do
+ without the `__unbounded' qualifier. */
+ struct link_map *__unbounded l, ElfW(Word) reloc_offset)
+{
+ const ElfW(Sym) *const symtab
+ = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
+ const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+
+ const PLTREL *const reloc
+ = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
+ const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+ void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
+ lookup_t result;
+ ElfW(Addr) value;
+
+ /* The use of `alloca' here looks ridiculous but it helps. The goal is
+ to prevent the function from being inlined and thus optimized out.
+ There is no official way to do this so we use this trick. gcc never
+ inlines functions which use `alloca'. */
+ alloca (sizeof (int));
+
+ /* Sanity check that we're really looking at a PLT relocation. */
+ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
+
+ /* Look up the target symbol. If the normal lookup rules are not
+ used don't look in the global scope. */
+ if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
+ {
+ switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
+ {
+ default:
+ {
+ const ElfW(Half) *vernum =
+ (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
+ ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)];
+ const struct r_found_version *version = &l->l_versions[ndx];
+
+ if (version->hash != 0)
+ {
+ result = _dl_lookup_versioned_symbol (strtab + sym->st_name,
+ l, &sym, l->l_scope,
+ version,
+ ELF_RTYPE_CLASS_PLT, 0);
+ break;
+ }
+ }
+ case 0:
+ result = _dl_lookup_symbol (strtab + sym->st_name, l, &sym,
+ l->l_scope, ELF_RTYPE_CLASS_PLT, 0);
+ }
+
+ /* Currently result contains the base load address (or link map)
+ of the object that defines sym. Now add in the symbol
+ offset. */
+ value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0);
+ }
+ else
+ {
+ /* We already found the symbol. The module (and therefore its load
+ address) is also known. */
+ value = l->l_addr + sym->st_value;
+#ifdef DL_LOOKUP_RETURNS_MAP
+ result = l;
+#endif
+ }
+
+ /* And now perhaps the relocation addend. */
+ value = elf_machine_plt_value (l, reloc, value);
+
+ /* Finally, fix up the plt itself. */
+ if (__builtin_expect (_dl_bind_not, 0))
+ return value;
+
+ return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
+}
+#endif
+
+#if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__
+
+static ElfW(Addr) __attribute_used__
+profile_fixup (
+#ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+ ELF_MACHINE_RUNTIME_FIXUP_ARGS,
+#endif
+ struct link_map *l, ElfW(Word) reloc_offset, ElfW(Addr) retaddr)
+{
+ void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = _dl_mcount;
+ ElfW(Addr) *resultp;
+ lookup_t result;
+ ElfW(Addr) value;
+
+ /* The use of `alloca' here looks ridiculous but it helps. The goal is
+ to prevent the function from being inlined, and thus optimized out.
+ There is no official way to do this so we use this trick. gcc never
+ inlines functions which use `alloca'. */
+ alloca (sizeof (int));
+
+ /* This is the address in the array where we store the result of previous
+ relocations. */
+ resultp = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)];
+
+ value = *resultp;
+ if (value == 0)
+ {
+ /* This is the first time we have to relocate this object. */
+ const ElfW(Sym) *const symtab
+ = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
+ const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+
+ const PLTREL *const reloc
+ = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
+ const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+
+ /* Sanity check that we're really looking at a PLT relocation. */
+ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
+
+ /* Look up the target symbol. If the symbol is marked STV_PROTECTED
+ don't look in the global scope. */
+ if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
+ {
+ switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
+ {
+ default:
+ {
+ const ElfW(Half) *vernum =
+ (const void *) D_PTR (l,l_info[VERSYMIDX (DT_VERSYM)]);
+ ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)];
+ const struct r_found_version *version = &l->l_versions[ndx];
+
+ if (version->hash != 0)
+ {
+ result = _dl_lookup_versioned_symbol(strtab + sym->st_name,
+ l, &sym, l->l_scope,
+ version,
+ ELF_RTYPE_CLASS_PLT,
+ 0);
+ break;
+ }
+ }
+ case 0:
+ result = _dl_lookup_symbol (strtab + sym->st_name, l, &sym,
+ l->l_scope, ELF_RTYPE_CLASS_PLT, 0);
+ }
+
+ /* Currently result contains the base load address (or link map)
+ of the object that defines sym. Now add in the symbol
+ offset. */
+ value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0);
+ }
+ else
+ {
+ /* We already found the symbol. The module (and therefore its load
+ address) is also known. */
+ value = l->l_addr + sym->st_value;
+#ifdef DL_LOOKUP_RETURNS_MAP
+ result = l;
+#endif
+ }
+ /* And now perhaps the relocation addend. */
+ value = elf_machine_plt_value (l, reloc, value);
+
+ /* Store the result for later runs. */
+ if (__builtin_expect (! _dl_bind_not, 1))
+ *resultp = value;
+ }
+
+ (*mcount_fct) (retaddr, value);
+
+ return value;
+}
+
+#endif /* PROF && ELF_MACHINE_NO_PLT */
+
+
+/* This macro is defined in dl-machine.h to define the entry point called
+ by the PLT. The `fixup' function above does the real work, but a little
+ more twiddling is needed to get the stack right and jump to the address
+ finally resolved. */
+
+ELF_MACHINE_RUNTIME_TRAMPOLINE