about summary refs log tree commit diff
path: root/ports/sysdeps/aarch64
diff options
context:
space:
mode:
Diffstat (limited to 'ports/sysdeps/aarch64')
-rw-r--r--ports/sysdeps/aarch64/dl-irel.h22
-rw-r--r--ports/sysdeps/aarch64/dl-machine.h20
2 files changed, 39 insertions, 3 deletions
diff --git a/ports/sysdeps/aarch64/dl-irel.h b/ports/sysdeps/aarch64/dl-irel.h
index 1a3811e7bb..f37ee399c8 100644
--- a/ports/sysdeps/aarch64/dl-irel.h
+++ b/ports/sysdeps/aarch64/dl-irel.h
@@ -22,15 +22,31 @@
 
 #include <stdio.h>
 #include <unistd.h>
+#include <ldsodefs.h>
 
-/* AArch64 does not yet implement IFUNC support.  However since
-   2011-06-20 provision of a elf_ifunc_invoke has been mandatory.  */
+#define ELF_MACHINE_IRELA	1
 
 static inline ElfW(Addr)
 __attribute ((always_inline))
 elf_ifunc_invoke (ElfW(Addr) addr)
 {
-  return ((ElfW(Addr) (*) (void)) (addr)) ();
+  return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+}
+
+static inline void
+__attribute ((always_inline))
+elf_irela (const ElfW(Rela) *reloc)
+{
+  ElfW(Addr) *const reloc_addr = (void *) reloc->r_offset;
+  const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);
+
+  if (__glibc_likely (r_type == R_AARCH64_IRELATIVE))
+    {
+      ElfW(Addr) value = elf_ifunc_invoke (reloc->r_addend);
+      *reloc_addr = value;
+    }
+  else
+    __libc_fatal ("unexpected reloc type in static binary");
 }
 
 #endif
diff --git a/ports/sysdeps/aarch64/dl-machine.h b/ports/sysdeps/aarch64/dl-machine.h
index 71dd6b3894..01a214fb6b 100644
--- a/ports/sysdeps/aarch64/dl-machine.h
+++ b/ports/sysdeps/aarch64/dl-machine.h
@@ -23,6 +23,7 @@
 
 #include <tls.h>
 #include <dl-tlsdesc.h>
+#include <dl-irel.h>
 
 /* Return nonzero iff ELF header is compatible with the running host.  */
 static inline int __attribute__ ((unused))
@@ -243,6 +244,12 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
       ElfW(Addr) value = sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value;
 
+      if (sym != NULL
+	  && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
+	  && __glibc_likely (sym->st_shndx != SHN_UNDEF)
+	  && __glibc_likely (!skip_ifunc))
+	value = elf_ifunc_invoke (value);
+
       switch (r_type)
 	{
 	case R_AARCH64_COPY:
@@ -331,6 +338,12 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
 	    }
 	  break;
 
+	case R_AARCH64_IRELATIVE:
+	  value = map->l_addr + reloc->r_addend;
+	  value = elf_ifunc_invoke (value);
+	  *reloc_addr = value;
+	  break;
+
 	default:
 	  _dl_reloc_bad_type (map, r_type, 0);
 	  break;
@@ -374,6 +387,13 @@ elf_machine_lazy_rel (struct link_map *map,
       td->entry = (void*)(D_PTR (map, l_info[ADDRIDX (DT_TLSDESC_PLT)])
 			  + map->l_addr);
     }
+  else if (__glibc_unlikely (r_type == R_AARCH64_IRELATIVE))
+    {
+      ElfW(Addr) value = map->l_addr + reloc->r_addend;
+      if (__glibc_likely (!skip_ifunc))
+	value = elf_ifunc_invoke (value);
+      *reloc_addr = value;
+    }
   else
     _dl_reloc_bad_type (map, r_type, 1);
 }