summaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2010-02-10 07:42:17 -0800
committerUlrich Drepper <drepper@redhat.com>2010-02-10 07:42:17 -0800
commit311927f1bd58b6e2b36e3bbb83bbc4dbdb507155 (patch)
treeeae31d6ce51c8a6f279b6ad43787a4ffbc3a7a5f /sysdeps
parent57e065ae1eda898a42d9cb8e2ea344290d05578c (diff)
Sparc STT_GNU_IFUNC support
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/sparc/sparc32/dl-irel.h55
-rw-r--r--sysdeps/sparc/sparc32/dl-machine.h81
-rw-r--r--sysdeps/sparc/sparc32/dl-plt.h62
-rw-r--r--sysdeps/sparc/sparc64/dl-irel.h58
-rw-r--r--sysdeps/sparc/sparc64/dl-machine.h172
-rw-r--r--sysdeps/sparc/sparc64/dl-plt.h144
6 files changed, 384 insertions, 188 deletions
diff --git a/sysdeps/sparc/sparc32/dl-irel.h b/sysdeps/sparc/sparc32/dl-irel.h
new file mode 100644
index 0000000000..1891938d6d
--- /dev/null
+++ b/sysdeps/sparc/sparc32/dl-irel.h
@@ -0,0 +1,55 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+ SPARC 32-bit version.
+ Copyright (C) 2010 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. */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <dl-plt.h>
+
+#define ELF_MACHINE_IRELA 1
+
+static inline void
+__attribute ((always_inline))
+elf_irela (const Elf32_Rela *reloc)
+{
+ unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_SPARC_IRELATIVE, 1))
+ {
+ Elf32_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf32_Addr value = ((Elf32_Addr (*) (void)) reloc->r_addend) ();
+ *reloc_addr = value;
+ }
+ else if (__builtin_expect (r_type == R_SPARC_JMP_IREL, 1))
+ {
+ Elf32_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf32_Addr value = ((Elf32_Addr (*) (void)) reloc->r_addend) ();
+
+ sparc_fixup_plt (reloc, reloc_addr, value, 0, 1);
+ }
+ else if (r_type == R_SPARC_NONE)
+ ;
+ else
+ __libc_fatal ("unexpected reloc type in static binary");
+}
+
+#endif /* dl-irel.h */
diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
index b3b7852d87..e1385f7aca 100644
--- a/sysdeps/sparc/sparc32/dl-machine.h
+++ b/sysdeps/sparc/sparc32/dl-machine.h
@@ -1,5 +1,5 @@
/* Machine-dependent ELF dynamic relocation inline functions. SPARC version.
- Copyright (C) 1996-2003, 2004, 2005, 2006, 2007
+ Copyright (C) 1996-2003, 2004, 2005, 2006, 2007, 2010
Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -27,20 +27,13 @@
#include <sys/param.h>
#include <ldsodefs.h>
#include <tls.h>
+#include <dl-plt.h>
#ifndef VALIDX
# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ DT_EXTRANUM + DT_VALTAGIDX (tag))
#endif
-/* Some SPARC opcodes we need to use for self-modifying code. */
-#define OPCODE_NOP 0x01000000 /* nop */
-#define OPCODE_CALL 0x40000000 /* call ?; add PC-rel word address */
-#define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */
-#define OPCODE_JMP_G1 0x81c06000 /* jmp %g1+?; add lo 10 bits of value */
-#define OPCODE_SAVE_SP 0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */
-#define OPCODE_BA 0x30800000 /* b,a ?; add PC-rel word address */
-
/* Return nonzero iff ELF header is compatible with the running host. */
static inline int
elf_machine_matches_host (const Elf32_Ehdr *ehdr)
@@ -312,41 +305,6 @@ _dl_start_user:\n\
.size _dl_start_user, . - _dl_start_user\n\
.previous");
-static inline __attribute__ ((always_inline)) Elf32_Addr
-sparc_fixup_plt (const Elf32_Rela *reloc, Elf32_Addr *reloc_addr,
- Elf32_Addr value, int t, int do_flush)
-{
- Elf32_Sword disp = value - (Elf32_Addr) reloc_addr;
-
- if (0 && disp >= -0x800000 && disp < 0x800000)
- {
- /* Don't need to worry about thread safety. We're writing just one
- instruction. */
-
- reloc_addr[0] = OPCODE_BA | ((disp >> 2) & 0x3fffff);
- if (do_flush)
- __asm __volatile ("flush %0" : : "r"(reloc_addr));
- }
- else
- {
- /* For thread safety, write the instructions from the bottom and
- flush before we overwrite the critical "b,a". This of course
- need not be done during bootstrapping, since there are no threads.
- But we also can't tell if we _can_ use flush, so don't. */
-
- reloc_addr += t;
- reloc_addr[1] = OPCODE_JMP_G1 | (value & 0x3ff);
- if (do_flush)
- __asm __volatile ("flush %0+4" : : "r"(reloc_addr));
-
- reloc_addr[0] = OPCODE_SETHI_G1 | (value >> 10);
- if (do_flush)
- __asm __volatile ("flush %0" : : "r"(reloc_addr));
- }
-
- return value;
-}
-
static inline Elf32_Addr
elf_machine_fixup_plt (struct link_map *map, lookup_t t,
const Elf32_Rela *reloc,
@@ -433,6 +391,13 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
value += reloc->r_addend; /* Assume copy relocs have zero addend. */
+ if (sym != NULL
+ && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
+ && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1))
+ {
+ value = ((Elf32_Addr (*) (void)) value) ();
+ }
+
switch (r_type)
{
#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP
@@ -460,6 +425,13 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
case R_SPARC_32:
*reloc_addr = value;
break;
+ case R_SPARC_IRELATIVE:
+ value = ((Elf32_Addr (*) (void)) value) ();
+ *reloc_addr = value;
+ break;
+ case R_SPARC_JMP_IREL:
+ value = ((Elf32_Addr (*) (void)) value) ();
+ /* Fall thru */
case R_SPARC_JMP_SLOT:
{
#if !defined RTLD_BOOTSTRAP && !defined __sparc_v9__
@@ -578,16 +550,21 @@ __attribute__ ((always_inline))
elf_machine_lazy_rel (struct link_map *map,
Elf32_Addr l_addr, const Elf32_Rela *reloc)
{
- switch (ELF32_R_TYPE (reloc->r_info))
+ Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
+ const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_SPARC_JMP_SLOT, 1))
+ ;
+ else if (r_type == R_SPARC_JMP_IREL)
{
- case R_SPARC_NONE:
- break;
- case R_SPARC_JMP_SLOT:
- break;
- default:
- _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 1);
- break;
+ Elf32_Addr value = map->l_addr + reloc->r_addend;
+ value = ((Elf32_Addr (*) (void)) value) ();
+ sparc_fixup_plt (reloc, reloc_addr, value, 0, 1);
}
+ else if (r_type == R_SPARC_NONE)
+ ;
+ else
+ _dl_reloc_bad_type (map, r_type, 1);
}
#endif /* RESOLVE_MAP */
diff --git a/sysdeps/sparc/sparc32/dl-plt.h b/sysdeps/sparc/sparc32/dl-plt.h
new file mode 100644
index 0000000000..edcc5c1374
--- /dev/null
+++ b/sysdeps/sparc/sparc32/dl-plt.h
@@ -0,0 +1,62 @@
+/* PLT fixups. Sparc 32-bit version.
+ Copyright (C) 1996-2003, 2004, 2005, 2006, 2007, 2010
+ 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. */
+
+/* Some SPARC opcodes we need to use for self-modifying code. */
+#define OPCODE_NOP 0x01000000 /* nop */
+#define OPCODE_CALL 0x40000000 /* call ?; add PC-rel word address */
+#define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */
+#define OPCODE_JMP_G1 0x81c06000 /* jmp %g1+?; add lo 10 bits of value */
+#define OPCODE_SAVE_SP 0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */
+#define OPCODE_BA 0x30800000 /* b,a ?; add PC-rel word address */
+
+static inline __attribute__ ((always_inline)) Elf32_Addr
+sparc_fixup_plt (const Elf32_Rela *reloc, Elf32_Addr *reloc_addr,
+ Elf32_Addr value, int t, int do_flush)
+{
+ Elf32_Sword disp = value - (Elf32_Addr) reloc_addr;
+
+ if (0 && disp >= -0x800000 && disp < 0x800000)
+ {
+ /* Don't need to worry about thread safety. We're writing just one
+ instruction. */
+
+ reloc_addr[0] = OPCODE_BA | ((disp >> 2) & 0x3fffff);
+ if (do_flush)
+ __asm __volatile ("flush %0" : : "r"(reloc_addr));
+ }
+ else
+ {
+ /* For thread safety, write the instructions from the bottom and
+ flush before we overwrite the critical "b,a". This of course
+ need not be done during bootstrapping, since there are no threads.
+ But we also can't tell if we _can_ use flush, so don't. */
+
+ reloc_addr += t;
+ reloc_addr[1] = OPCODE_JMP_G1 | (value & 0x3ff);
+ if (do_flush)
+ __asm __volatile ("flush %0+4" : : "r"(reloc_addr));
+
+ reloc_addr[0] = OPCODE_SETHI_G1 | (value >> 10);
+ if (do_flush)
+ __asm __volatile ("flush %0" : : "r"(reloc_addr));
+ }
+
+ return value;
+}
diff --git a/sysdeps/sparc/sparc64/dl-irel.h b/sysdeps/sparc/sparc64/dl-irel.h
new file mode 100644
index 0000000000..1a2a0a3dd5
--- /dev/null
+++ b/sysdeps/sparc/sparc64/dl-irel.h
@@ -0,0 +1,58 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+ SPARC 64-bit version.
+ Copyright (C) 2010 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. */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <dl-plt.h>
+
+#define ELF_MACHINE_IRELA 1
+
+static inline void
+__attribute ((always_inline))
+elf_irela (const Elf64_Rela *reloc)
+{
+ unsigned int r_type = (reloc->r_info & 0xff);
+
+ if (__builtin_expect (r_type == R_SPARC_IRELATIVE, 1))
+ {
+ Elf64_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf64_Addr value = ((Elf64_Addr (*) (void)) reloc->r_addend) ();
+ *reloc_addr = value;
+ }
+ else if (__builtin_expect (r_type == R_SPARC_JMP_IREL, 1))
+ {
+ Elf64_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf64_Addr value = ((Elf64_Addr (*) (void)) reloc->r_addend) ();
+ struct link_map map = { .l_addr = 0 };
+
+ /* 'high' is always zero, for large PLT entries the linker
+ emits an R_SPARC_IRELATIVE. */
+ sparc64_fixup_plt (&map, reloc, reloc_addr, value, 0, 0);
+ }
+ else if (r_type == R_SPARC_NONE)
+ ;
+ else
+ __libc_fatal ("unexpected reloc type in static binary");
+}
+
+#endif /* dl-irel.h */
diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
index 3eee672912..b4f43e9cf5 100644
--- a/sysdeps/sparc/sparc64/dl-machine.h
+++ b/sysdeps/sparc/sparc64/dl-machine.h
@@ -1,6 +1,6 @@
/* Machine-dependent ELF dynamic relocation inline functions. Sparc64 version.
- Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ 2010 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
@@ -27,6 +27,7 @@
#include <sys/param.h>
#include <ldsodefs.h>
#include <sysdep.h>
+#include <dl-plt.h>
#ifndef VALIDX
# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
@@ -89,132 +90,6 @@ elf_machine_load_address (void)
return (Elf64_Addr) got - *got + (Elf32_Sword) ((pc[2] - pc[3]) * 4) - 4;
}
-/* We have 4 cases to handle. And we code different code sequences
- for each one. I love V9 code models... */
-static inline void __attribute__ ((always_inline))
-sparc64_fixup_plt (struct link_map *map, const Elf64_Rela *reloc,
- Elf64_Addr *reloc_addr, Elf64_Addr value,
- Elf64_Addr high, int t)
-{
- unsigned int *insns = (unsigned int *) reloc_addr;
- Elf64_Addr plt_vaddr = (Elf64_Addr) reloc_addr;
- Elf64_Sxword disp = value - plt_vaddr;
-
- /* Now move plt_vaddr up to the call instruction. */
- plt_vaddr += ((t + 1) * 4);
-
- /* PLT entries .PLT32768 and above look always the same. */
- if (__builtin_expect (high, 0) != 0)
- {
- *reloc_addr = value - map->l_addr;
- }
- /* Near destination. */
- else if (disp >= -0x800000 && disp < 0x800000)
- {
- /* As this is just one instruction, it is thread safe and so
- we can avoid the unnecessary sethi FOO, %g1.
- b,a target */
- insns[0] = 0x30800000 | ((disp >> 2) & 0x3fffff);
- __asm __volatile ("flush %0" : : "r" (insns));
- }
- /* 32-bit Sparc style, the target is in the lower 32-bits of
- address space. */
- else if (insns += t, (value >> 32) == 0)
- {
- /* sethi %hi(target), %g1
- jmpl %g1 + %lo(target), %g0 */
-
- insns[1] = 0x81c06000 | (value & 0x3ff);
- __asm __volatile ("flush %0 + 4" : : "r" (insns));
-
- insns[0] = 0x03000000 | ((unsigned int)(value >> 10));
- __asm __volatile ("flush %0" : : "r" (insns));
- }
- /* We can also get somewhat simple sequences if the distance between
- the target and the PLT entry is within +/- 2GB. */
- else if ((plt_vaddr > value
- && ((plt_vaddr - value) >> 31) == 0)
- || (value > plt_vaddr
- && ((value - plt_vaddr) >> 31) == 0))
- {
- unsigned int displacement;
-
- if (plt_vaddr > value)
- displacement = (0 - (plt_vaddr - value));
- else
- displacement = value - plt_vaddr;
-
- /* mov %o7, %g1
- call displacement
- mov %g1, %o7 */
-
- insns[2] = 0x9e100001;
- __asm __volatile ("flush %0 + 8" : : "r" (insns));
-
- insns[1] = 0x40000000 | (displacement >> 2);
- __asm __volatile ("flush %0 + 4" : : "r" (insns));
-
- insns[0] = 0x8210000f;
- __asm __volatile ("flush %0" : : "r" (insns));
- }
- /* Worst case, ho hum... */
- else
- {
- unsigned int high32 = (value >> 32);
- unsigned int low32 = (unsigned int) value;
-
- /* ??? Some tricks can be stolen from the sparc64 egcs backend
- constant formation code I wrote. -DaveM */
-
- if (__builtin_expect (high32 & 0x3ff, 0))
- {
- /* sethi %hh(value), %g1
- sethi %lm(value), %g5
- or %g1, %hm(value), %g1
- or %g5, %lo(value), %g5
- sllx %g1, 32, %g1
- jmpl %g1 + %g5, %g0
- nop */
-
- insns[5] = 0x81c04005;
- __asm __volatile ("flush %0 + 20" : : "r" (insns));
-
- insns[4] = 0x83287020;
- __asm __volatile ("flush %0 + 16" : : "r" (insns));
-
- insns[3] = 0x8a116000 | (low32 & 0x3ff);
- __asm __volatile ("flush %0 + 12" : : "r" (insns));
-
- insns[2] = 0x82106000 | (high32 & 0x3ff);
- }
- else
- {
- /* sethi %hh(value), %g1
- sethi %lm(value), %g5
- sllx %g1, 32, %g1
- or %g5, %lo(value), %g5
- jmpl %g1 + %g5, %g0
- nop */
-
- insns[4] = 0x81c04005;
- __asm __volatile ("flush %0 + 16" : : "r" (insns));
-
- insns[3] = 0x8a116000 | (low32 & 0x3ff);
- __asm __volatile ("flush %0 + 12" : : "r" (insns));
-
- insns[2] = 0x83287020;
- }
-
- __asm __volatile ("flush %0 + 8" : : "r" (insns));
-
- insns[1] = 0x0b000000 | (low32 >> 10);
- __asm __volatile ("flush %0 + 4" : : "r" (insns));
-
- insns[0] = 0x03000000 | (high32 >> 10);
- __asm __volatile ("flush %0" : : "r" (insns));
- }
-}
-
static inline Elf64_Addr __attribute__ ((always_inline))
elf_machine_fixup_plt (struct link_map *map, lookup_t t,
const Elf64_Rela *reloc,
@@ -549,6 +424,11 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
value += reloc->r_addend; /* Assume copy relocs have zero addend. */
+ if (sym != NULL
+ && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
+ && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1))
+ value = ((Elf64_Addr (*) (void)) value) ();
+
switch (r_type)
{
#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP
@@ -576,6 +456,13 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
case R_SPARC_GLOB_DAT:
*reloc_addr = value;
break;
+ case R_SPARC_IRELATIVE:
+ value = ((Elf64_Addr (*) (void)) value) ();
+ *reloc_addr = value;
+ break;
+ case R_SPARC_JMP_IREL:
+ value = ((Elf64_Addr (*) (void)) value) ();
+ /* Fall thru */
case R_SPARC_JMP_SLOT:
#ifdef RESOLVE_CONFLICT_FIND_MAP
/* R_SPARC_JMP_SLOT conflicts against .plt[32768+]
@@ -757,16 +644,29 @@ __attribute__ ((always_inline))
elf_machine_lazy_rel (struct link_map *map,
Elf64_Addr l_addr, const Elf64_Rela *reloc)
{
- switch (ELF64_R_TYPE (reloc->r_info))
+ Elf64_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
+ const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_SPARC_JMP_SLOT, 1))
+ ;
+ else if (r_type == R_SPARC_JMP_IREL
+ || r_type == R_SPARC_IRELATIVE)
{
- case R_SPARC_NONE:
- break;
- case R_SPARC_JMP_SLOT:
- break;
- default:
- _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 1);
- break;
+ Elf64_Addr value = map->l_addr + reloc->r_addend;
+ value = ((Elf64_Addr (*) (void)) value) ();
+ if (r_type == R_SPARC_JMP_IREL)
+ {
+ /* 'high' is always zero, for large PLT entries the linker
+ emits an R_SPARC_IRELATIVE. */
+ sparc64_fixup_plt (map, reloc, reloc_addr, value, 0, 0);
+ }
+ else
+ *reloc_addr = value;
}
+ else if (r_type == R_SPARC_NONE)
+ ;
+ else
+ _dl_reloc_bad_type (map, r_type, 1);
}
#endif /* RESOLVE_MAP */
diff --git a/sysdeps/sparc/sparc64/dl-plt.h b/sysdeps/sparc/sparc64/dl-plt.h
new file mode 100644
index 0000000000..e06be43a0a
--- /dev/null
+++ b/sysdeps/sparc/sparc64/dl-plt.h
@@ -0,0 +1,144 @@
+/* PLT fixups. Sparc 64-bit version.
+ Copyright (C) 1997-2006, 2010 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. */
+
+/* We have 4 cases to handle. And we code different code sequences
+ for each one. I love V9 code models... */
+static inline void __attribute__ ((always_inline))
+sparc64_fixup_plt (struct link_map *map, const Elf64_Rela *reloc,
+ Elf64_Addr *reloc_addr, Elf64_Addr value,
+ Elf64_Addr high, int t)
+{
+ unsigned int *insns = (unsigned int *) reloc_addr;
+ Elf64_Addr plt_vaddr = (Elf64_Addr) reloc_addr;
+ Elf64_Sxword disp = value - plt_vaddr;
+
+ /* Now move plt_vaddr up to the call instruction. */
+ plt_vaddr += ((t + 1) * 4);
+
+ /* PLT entries .PLT32768 and above look always the same. */
+ if (__builtin_expect (high, 0) != 0)
+ {
+ *reloc_addr = value - map->l_addr;
+ }
+ /* Near destination. */
+ else if (disp >= -0x800000 && disp < 0x800000)
+ {
+ /* As this is just one instruction, it is thread safe and so
+ we can avoid the unnecessary sethi FOO, %g1.
+ b,a target */
+ insns[0] = 0x30800000 | ((disp >> 2) & 0x3fffff);
+ __asm __volatile ("flush %0" : : "r" (insns));
+ }
+ /* 32-bit Sparc style, the target is in the lower 32-bits of
+ address space. */
+ else if (insns += t, (value >> 32) == 0)
+ {
+ /* sethi %hi(target), %g1
+ jmpl %g1 + %lo(target), %g0 */
+
+ insns[1] = 0x81c06000 | (value & 0x3ff);
+ __asm __volatile ("flush %0 + 4" : : "r" (insns));
+
+ insns[0] = 0x03000000 | ((unsigned int)(value >> 10));
+ __asm __volatile ("flush %0" : : "r" (insns));
+ }
+ /* We can also get somewhat simple sequences if the distance between
+ the target and the PLT entry is within +/- 2GB. */
+ else if ((plt_vaddr > value
+ && ((plt_vaddr - value) >> 31) == 0)
+ || (value > plt_vaddr
+ && ((value - plt_vaddr) >> 31) == 0))
+ {
+ unsigned int displacement;
+
+ if (plt_vaddr > value)
+ displacement = (0 - (plt_vaddr - value));
+ else
+ displacement = value - plt_vaddr;
+
+ /* mov %o7, %g1
+ call displacement
+ mov %g1, %o7 */
+
+ insns[2] = 0x9e100001;
+ __asm __volatile ("flush %0 + 8" : : "r" (insns));
+
+ insns[1] = 0x40000000 | (displacement >> 2);
+ __asm __volatile ("flush %0 + 4" : : "r" (insns));
+
+ insns[0] = 0x8210000f;
+ __asm __volatile ("flush %0" : : "r" (insns));
+ }
+ /* Worst case, ho hum... */
+ else
+ {
+ unsigned int high32 = (value >> 32);
+ unsigned int low32 = (unsigned int) value;
+
+ /* ??? Some tricks can be stolen from the sparc64 egcs backend
+ constant formation code I wrote. -DaveM */
+
+ if (__builtin_expect (high32 & 0x3ff, 0))
+ {
+ /* sethi %hh(value), %g1
+ sethi %lm(value), %g5
+ or %g1, %hm(value), %g1
+ or %g5, %lo(value), %g5
+ sllx %g1, 32, %g1
+ jmpl %g1 + %g5, %g0
+ nop */
+
+ insns[5] = 0x81c04005;
+ __asm __volatile ("flush %0 + 20" : : "r" (insns));
+
+ insns[4] = 0x83287020;
+ __asm __volatile ("flush %0 + 16" : : "r" (insns));
+
+ insns[3] = 0x8a116000 | (low32 & 0x3ff);
+ __asm __volatile ("flush %0 + 12" : : "r" (insns));
+
+ insns[2] = 0x82106000 | (high32 & 0x3ff);
+ }
+ else
+ {
+ /* sethi %hh(value), %g1
+ sethi %lm(value), %g5
+ sllx %g1, 32, %g1
+ or %g5, %lo(value), %g5
+ jmpl %g1 + %g5, %g0
+ nop */
+
+ insns[4] = 0x81c04005;
+ __asm __volatile ("flush %0 + 16" : : "r" (insns));
+
+ insns[3] = 0x8a116000 | (low32 & 0x3ff);
+ __asm __volatile ("flush %0 + 12" : : "r" (insns));
+
+ insns[2] = 0x83287020;
+ }
+
+ __asm __volatile ("flush %0 + 8" : : "r" (insns));
+
+ insns[1] = 0x0b000000 | (low32 >> 10);
+ __asm __volatile ("flush %0 + 4" : : "r" (insns));
+
+ insns[0] = 0x03000000 | (high32 >> 10);
+ __asm __volatile ("flush %0" : : "r" (insns));
+ }
+}