summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--trunk/ChangeLog69
-rw-r--r--trunk/src/arch-alpha.c17
-rw-r--r--trunk/src/arch-arm.c32
-rw-r--r--trunk/src/arch-cris.c11
-rw-r--r--trunk/src/arch-i386.c49
-rw-r--r--trunk/src/arch-ia64.c11
-rw-r--r--trunk/src/arch-mips.c15
-rw-r--r--trunk/src/arch-ppc.c25
-rw-r--r--trunk/src/arch-ppc64.c17
-rw-r--r--trunk/src/arch-s390.c13
-rw-r--r--trunk/src/arch-s390x.c21
-rw-r--r--trunk/src/arch-sh.c23
-rw-r--r--trunk/src/arch-sparc.c17
-rw-r--r--trunk/src/arch-sparc64.c25
-rw-r--r--trunk/src/arch-x86_64.c37
-rw-r--r--trunk/src/conflict.c163
-rw-r--r--trunk/src/cxx.c4
-rw-r--r--trunk/src/get.c53
-rw-r--r--trunk/src/prelink.h15
-rw-r--r--trunk/testsuite/Makefile.am1
-rw-r--r--trunk/testsuite/Makefile.in1
-rw-r--r--trunk/testsuite/ifunc.h33
-rw-r--r--trunk/testsuite/ifunc1.c29
-rwxr-xr-xtrunk/testsuite/ifunc1.sh21
-rw-r--r--trunk/testsuite/ifunc1lib1.c32
-rw-r--r--trunk/testsuite/ifunc1lib2.c39
-rwxr-xr-xtrunk/testsuite/ifunc2.sh21
-rw-r--r--trunk/testsuite/ifunc3.c32
-rwxr-xr-xtrunk/testsuite/ifunc3.sh21
-rw-r--r--trunk/testsuite/ifunc3lib1.c37
-rw-r--r--trunk/testsuite/ifunctest.c16
31 files changed, 724 insertions, 176 deletions
diff --git a/trunk/ChangeLog b/trunk/ChangeLog
index e1870c4..8265782 100644
--- a/trunk/ChangeLog
+++ b/trunk/ChangeLog
@@ -1,5 +1,74 @@
2009-06-15 Jakub Jelinek <jakub@redhat.com>
+ * src/prelink.h (R_386_IRELATIVE, R_X86_64_IRELATIVE): Define
+ if not defined.
+ (struct PLArch): Add dest_addr argument to apply_conflict_rela
+ hook.
+ (struct prelink_conflict): Change used type from int to unsigned
+ char, add ifunc field.
+ (get_relocated_mem): Add dest_addr argument.
+ * src/get.c (prelink_record_relocations): Handle lookups resolving
+ to STT_GNU_IFUNC symbols.
+ * src/conflict.c (get_relocated_mem): Add dest_addr argument, pass
+ it through to the apply_conflict_rela hook. Fail if
+ apply_conflict_rela hook failed.
+ (prelink_build_conflicts): Build conflicts even in the executable,
+ and for libraries even if no conflicts were reported by the dynamic
+ linker. In the executable ignore any unused conflicts except for
+ ifunc conflicts. Adjust get_relocated_mem callers.
+ * src/cxx.c (remove_redundant_cxx_conflicts): Adjust
+ get_relocated_mem callers.
+ * arch-alpha.c (alpha_apply_conflict_rela): Add dest_addr argument.
+ (alpha_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-arm.c (arm_apply_conflict_rela): Add dest_addr argument.
+ (arm_prelink_conflict_rel, arm_prelink_conflict_rela): Return early
+ if info->dso == dso.
+ * arch-cris.c (cris_apply_conflict_rela): Add dest_addr argument.
+ (cris_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-ia64.c (ia64_apply_conflict_rela): Add dest_addr argument.
+ (ia64_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-ppc.c (ppc_apply_conflict_rela): Add dest_addr argument.
+ (ppc_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-ppc64.c (ppc64_apply_conflict_rela): Add dest_addr argument.
+ (ppc64_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-s390.c (s390_apply_conflict_rela): Add dest_addr argument.
+ (s390_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-s390x.c (s390x_apply_conflict_rela): Add dest_addr argument.
+ (s390x_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-sh.c (sh_apply_conflict_rela): Add dest_addr argument.
+ (sh_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-sparc.c (sparc_apply_conflict_rela): Add dest_addr argument.
+ (sparc_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-sparc64.c (sparc64_apply_conflict_rela): Add dest_addr argument.
+ (sparc64_prelink_conflict_rela): Return early if info->dso == dso.
+ * arch-mips.c (mipz_apply_conflict_rela): Add dest_addr argument.
+ (mips_prelink_conflict_reloc, mips_arch_prelink_conflict): Return
+ early if info->dso == dso.
+ * arch-i386.c (i386_adjust_rel, i386_adjust_rela, i386_prelink_rel,
+ i386_prelink_rela, i386_rel_to_rela, i386_undo_prelink_rel,
+ i386_rela_to_rel): Handle R_386_IRELATIVE.
+ (i386_apply_conflict_rela): Add dest_addr argument, handle
+ R_386_IRELATIVE.
+ (i386_prelink_conflict_rel, i386_prelink_conflict_rela): Handle
+ R_386_IRELATIVE and conflict->ifunc conflicts.
+ * arch-x86_64.c (x86_64_adjust_rela, x86_64_prelink_rela,
+ x86_64_undo_prelink_rela): Handle R_X86_64_IRELATIVE.
+ (x86_64_apply_conflict_rela): Handle R_X86_64_IRELATIVE and
+ conflict->ifunc conflicts.
+ * testsuite/Makefile.am (TESTS): Add ifunc1.sh, ifunc2.sh and
+ ifunc3.sh.
+ * testsuite/Makefile.in: Regenerated.
+ * testsuite/ifunc1.sh: New test.
+ * testsuite/ifunc2.sh: New test.
+ * testsuite/ifunc3.sh: New test.
+ * testsuite/ifunc1.c: New file.
+ * testsuite/ifunc1lib1.c: New file.
+ * testsuite/ifunc1lib2.c: New file.
+ * testsuite/ifunc3.c: New file.
+ * testsuite/ifunc3lib1.c: New file.
+ * testsuite/ifunc.h: New file.
+ * testsuite/ifunctest.c: New file.
+
* src/Makefile.am (DEFS, AM_CFLAGS): Add -Wno-pointer-sign.
* src/Makefile.in: Regenerated.
* src/data.c (reopen_dso): Initialize data variable to avoid
diff --git a/trunk/src/arch-alpha.c b/trunk/src/arch-alpha.c
index 31b03ae..b326b0b 100644
--- a/trunk/src/arch-alpha.c
+++ b/trunk/src/arch-alpha.c
@@ -30,14 +30,14 @@
static int
alpha_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
return 0;
}
static int
alpha_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename);
return 1;
@@ -45,7 +45,7 @@ alpha_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
alpha_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE
|| GELF_R_TYPE (rela->r_info) == R_ALPHA_JMP_SLOT)
@@ -195,7 +195,7 @@ alpha_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
alpha_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info) & 0xff)
{
@@ -260,7 +260,8 @@ alpha_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
GElf_Rela *ret;
if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_ALPHA_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_ALPHA_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -280,6 +281,12 @@ alpha_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on Alpha yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPREL64 wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-arm.c b/trunk/src/arch-arm.c
index 66d3ffb..47fde9b 100644
--- a/trunk/src/arch-arm.c
+++ b/trunk/src/arch-arm.c
@@ -30,7 +30,7 @@
static int
arm_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PLTGOT)
{
@@ -67,7 +67,7 @@ arm_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
arm_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
Elf32_Addr data;
switch (GELF_R_TYPE (rel->r_info))
@@ -84,7 +84,7 @@ arm_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
arm_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
Elf32_Addr data;
@@ -180,7 +180,7 @@ arm_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
static int
arm_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
- GElf_Addr relaaddr)
+ GElf_Addr relaaddr)
{
DSO *dso;
GElf_Addr value;
@@ -248,7 +248,7 @@ arm_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
arm_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -348,7 +348,7 @@ arm_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
static int
arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
- GElf_Addr reladdr)
+ GElf_Addr reladdr)
{
GElf_Addr value;
struct prelink_conflict *conflict;
@@ -356,7 +356,8 @@ arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
GElf_Rela *ret;
if (GELF_R_TYPE (rel->r_info) == R_ARM_RELATIVE
- || GELF_R_TYPE (rel->r_info) == R_ARM_NONE)
+ || GELF_R_TYPE (rel->r_info) == R_ARM_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rel->r_info),
@@ -378,6 +379,12 @@ arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on ARM yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPOFF32 wants to see only real conflicts, not lookups
@@ -446,7 +453,7 @@ arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
static int
arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
- GElf_Rela *rela, GElf_Addr relaaddr)
+ GElf_Rela *rela, GElf_Addr relaaddr)
{
GElf_Addr value;
struct prelink_conflict *conflict;
@@ -455,7 +462,8 @@ arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
Elf32_Sword val;
if (GELF_R_TYPE (rela->r_info) == R_ARM_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_ARM_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_ARM_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -478,6 +486,12 @@ arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on ARM yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPOFF32 wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-cris.c b/trunk/src/arch-cris.c
index 43f987a..d70260a 100644
--- a/trunk/src/arch-cris.c
+++ b/trunk/src/arch-cris.c
@@ -159,7 +159,7 @@ cris_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
cris_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -247,13 +247,20 @@ cris_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
GElf_Rela *ret;
if (GELF_R_TYPE (rela->r_info) == R_CRIS_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_CRIS_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_CRIS_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
GELF_R_TYPE (rela->r_info));
if (conflict == NULL)
return 0;
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on CRIS yet",
+ dso->filename);
+ return 1;
+ }
value = conflict_lookup_value (conflict);
ret = prelink_conflict_add_rela (info);
if (ret == NULL)
diff --git a/trunk/src/arch-i386.c b/trunk/src/arch-i386.c
index 34af1d2..67b12b5 100644
--- a/trunk/src/arch-i386.c
+++ b/trunk/src/arch-i386.c
@@ -75,6 +75,7 @@ i386_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
{
case R_386_RELATIVE:
case R_386_JMP_SLOT:
+ case R_386_IRELATIVE:
data = read_ule32 (dso, rel->r_offset);
if (data >= start)
write_le32 (dso, rel->r_offset, data + adjust);
@@ -92,6 +93,7 @@ i386_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
switch (GELF_R_TYPE (rela->r_info))
{
case R_386_RELATIVE:
+ case R_386_IRELATIVE:
if ((Elf32_Addr) rela->r_addend >= start)
{
rela->r_addend += (Elf32_Sword) adjust;
@@ -117,6 +119,7 @@ i386_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
GElf_Addr value;
if (GELF_R_TYPE (rel->r_info) == R_386_RELATIVE
+ || GELF_R_TYPE (rel->r_info) == R_386_IRELATIVE
|| GELF_R_TYPE (rel->r_info) == R_386_NONE)
/* Fast path: nothing to do. */
return 0;
@@ -187,6 +190,7 @@ i386_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
GElf_Addr value;
if (GELF_R_TYPE (rela->r_info) == R_386_RELATIVE
+ || GELF_R_TYPE (rela->r_info) == R_386_IRELATIVE
|| GELF_R_TYPE (rela->r_info) == R_386_NONE)
/* Fast path: nothing to do. */
return 0;
@@ -244,8 +248,10 @@ i386_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
i386_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
+ GElf_Rela *ret;
+
switch (GELF_R_TYPE (rela->r_info))
{
case R_386_GLOB_DAT:
@@ -253,6 +259,16 @@ i386_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
case R_386_32:
buf_write_le32 (buf, rela->r_addend);
break;
+ case R_386_IRELATIVE:
+ if (dest_addr == 0)
+ return 5;
+ ret = prelink_conflict_add_rela (info);
+ if (ret == NULL)
+ return 1;
+ ret->r_offset = dest_addr;
+ ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE);
+ ret->r_addend = rela->r_addend;
+ break;
default:
abort ();
}
@@ -339,20 +355,25 @@ i386_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
GELF_R_TYPE (rel->r_info));
if (conflict == NULL)
{
- if (info->curtls == NULL)
- return 0;
switch (GELF_R_TYPE (rel->r_info))
{
/* Even local DTPMOD and TPOFF relocs need conflicts. */
case R_386_TLS_DTPMOD32:
case R_386_TLS_TPOFF32:
case R_386_TLS_TPOFF:
+ if (info->curtls == NULL || info->dso == dso)
+ return 0;
+ break;
+ /* Similarly IRELATIVE relocations always need conflicts. */
+ case R_386_IRELATIVE:
break;
default:
return 0;
}
value = 0;
}
+ else if (info->dso == dso && !conflict->ifunc)
+ return 0;
else
{
/* DTPOFF32 wants to see only real conflicts, not lookups
@@ -376,6 +397,11 @@ i386_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
/* FALLTHROUGH */
case R_386_JMP_SLOT:
ret->r_addend = (Elf32_Sword) value;
+ if (conflict != NULL && conflict->ifunc)
+ ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE);
+ break;
+ case R_386_IRELATIVE:
+ ret->r_addend = (Elf32_Sword) read_ule32 (dso, rel->r_offset);
break;
case R_386_32:
case R_386_PC32:
@@ -441,20 +467,25 @@ i386_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
GELF_R_TYPE (rela->r_info));
if (conflict == NULL)
{
- if (info->curtls == NULL)
- return 0;
switch (GELF_R_TYPE (rela->r_info))
{
/* Even local DTPMOD and TPOFF relocs need conflicts. */
case R_386_TLS_DTPMOD32:
case R_386_TLS_TPOFF32:
case R_386_TLS_TPOFF:
+ if (info->curtls == NULL || info->dso == dso)
+ return 0;
+ break;
+ /* Similarly IRELATIVE relocations always need conflicts. */
+ case R_386_IRELATIVE:
break;
default:
return 0;
}
value = 0;
}
+ else if (info->dso == dso && !conflict->ifunc)
+ return 0;
else
{
/* DTPOFF32 wants to see only real conflicts, not lookups
@@ -477,11 +508,16 @@ i386_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
ret->r_info = GELF_R_INFO (0, R_386_32);
/* FALLTHROUGH */
case R_386_JMP_SLOT:
+ case R_386_IRELATIVE:
ret->r_addend = (Elf32_Sword) (value + rela->r_addend);
+ if (conflict != NULL && conflict->ifunc)
+ ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE);
break;
case R_386_32:
value += rela->r_addend;
ret->r_addend = (Elf32_Sword) value;
+ if (conflict != NULL && conflict->ifunc)
+ ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE);
break;
case R_386_PC32:
ret->r_addend = (Elf32_Sword) (value + rela->r_addend - rela->r_offset);
@@ -539,6 +575,7 @@ i386_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela)
/* We should be never converting .rel.plt into .rela.plt. */
abort ();
case R_386_RELATIVE:
+ case R_386_IRELATIVE:
case R_386_32:
case R_386_PC32:
case R_386_TLS_TPOFF32:
@@ -672,6 +709,7 @@ i386_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr)
{
case R_386_NONE:
case R_386_RELATIVE:
+ case R_386_IRELATIVE:
break;
case R_386_JMP_SLOT:
sec = addr_to_sec (dso, rel->r_offset);
@@ -746,6 +784,7 @@ i386_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel)
and thus never .rela.plt back to .rel.plt. */
abort ();
case R_386_RELATIVE:
+ case R_386_IRELATIVE:
case R_386_32:
case R_386_PC32:
case R_386_TLS_TPOFF32:
diff --git a/trunk/src/arch-ia64.c b/trunk/src/arch-ia64.c
index bdfe1b1..2573d69 100644
--- a/trunk/src/arch-ia64.c
+++ b/trunk/src/arch-ia64.c
@@ -196,7 +196,7 @@ ia64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
ia64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB)
{
@@ -312,13 +312,20 @@ ia64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
GElf_Rela *ret;
if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB
- || GELF_R_TYPE (rela->r_info) == R_IA64_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_IA64_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
GELF_R_TYPE (rela->r_info));
if (conflict == NULL)
return 0;
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on IA-64 yet",
+ dso->filename);
+ return 1;
+ }
value = conflict_lookup_value (conflict);
ret = prelink_conflict_add_rela (info);
if (ret == NULL)
diff --git a/trunk/src/arch-mips.c b/trunk/src/arch-mips.c
index 4be23c8..3e39f03 100644
--- a/trunk/src/arch-mips.c
+++ b/trunk/src/arch-mips.c
@@ -550,9 +550,12 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
{
GElf_Addr value;
struct prelink_conflict *conflict;
- struct prelink_tls *tls;
+ struct prelink_tls *tls = NULL;
GElf_Rela *entry;
+ if (info->dso == dso)
+ return 0;
+
conflict = prelink_conflict (info, GELF_R_SYM (r_info),
GELF_R_TYPE (r_info));
if (conflict == NULL)
@@ -573,6 +576,12 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
return 0;
}
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on MIPS yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPREL32 relocations just involve the symbol value; no other
@@ -652,7 +661,7 @@ mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info)
struct prelink_conflict *conflict;
GElf_Rela *entry;
- if (dso->info[DT_PLTGOT] == 0)
+ if (info->dso == dso || dso->info[DT_PLTGOT] == 0)
return 0;
/* Add a conflict for every global GOT entry that does not hold the
@@ -685,7 +694,7 @@ mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info)
static int
mips_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
diff --git a/trunk/src/arch-ppc.c b/trunk/src/arch-ppc.c
index 24a22b9..98aa78e 100644
--- a/trunk/src/arch-ppc.c
+++ b/trunk/src/arch-ppc.c
@@ -37,7 +37,7 @@
static int
ppc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PPC_GOT)
{
@@ -116,7 +116,7 @@ ppc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
ppc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename);
return 1;
@@ -124,7 +124,7 @@ ppc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
ppc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE)
{
@@ -143,7 +143,7 @@ ppc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
static int
ppc_prelink_rel (struct prelink_info *info, GElf_Rel *rel,
- GElf_Addr reladdr)
+ GElf_Addr reladdr)
{
error (0, 0, "%s: PowerPC doesn't support REL relocs", info->dso->filename);
return 1;
@@ -201,7 +201,7 @@ ppc_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value)
static int
ppc_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
- GElf_Addr relaaddr)
+ GElf_Addr relaaddr)
{
DSO *dso = info->dso;
GElf_Addr value;
@@ -330,7 +330,7 @@ ppc_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
ppc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -418,7 +418,7 @@ ppc_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
static int
ppc_prelink_conflict_rel (DSO *dso, struct prelink_info *info,
- GElf_Rel *rel, GElf_Addr reladdr)
+ GElf_Rel *rel, GElf_Addr reladdr)
{
error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename);
return 1;
@@ -426,7 +426,7 @@ ppc_prelink_conflict_rel (DSO *dso, struct prelink_info *info,
static int
ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
- GElf_Rela *rela, GElf_Addr relaaddr)
+ GElf_Rela *rela, GElf_Addr relaaddr)
{
GElf_Addr value;
struct prelink_conflict *conflict;
@@ -435,7 +435,8 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
int r_type;
if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_PPC_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_PPC_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -459,6 +460,12 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on PowerPC yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPREL wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-ppc64.c b/trunk/src/arch-ppc64.c
index f0195d2..73015a8 100644
--- a/trunk/src/arch-ppc64.c
+++ b/trunk/src/arch-ppc64.c
@@ -60,7 +60,7 @@ ppc64_adjust_section (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust)
static int
ppc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PPC64_GLINK && dyn->d_un.d_ptr >= start)
{
@@ -73,7 +73,7 @@ ppc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
ppc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename);
return 1;
@@ -81,7 +81,7 @@ ppc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
ppc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE)
{
@@ -306,7 +306,7 @@ ppc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
ppc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -439,7 +439,8 @@ ppc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
int r_type;
if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_PPC64_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_PPC64_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -463,6 +464,12 @@ ppc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on PowerPC64 yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPREL wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-s390.c b/trunk/src/arch-s390.c
index a0b33ba..b750beb 100644
--- a/trunk/src/arch-s390.c
+++ b/trunk/src/arch-s390.c
@@ -67,7 +67,7 @@ s390_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
s390_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename);
return 1;
@@ -168,7 +168,7 @@ s390_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
s390_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -238,7 +238,8 @@ s390_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
GElf_Rela *ret;
if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_390_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_390_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -258,6 +259,12 @@ s390_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on S390 yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPOFF wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-s390x.c b/trunk/src/arch-s390x.c
index 2027005..a0321ef 100644
--- a/trunk/src/arch-s390x.c
+++ b/trunk/src/arch-s390x.c
@@ -67,7 +67,7 @@ s390x_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
s390x_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename);
return 1;
@@ -75,7 +75,7 @@ s390x_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
s390x_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
Elf64_Addr addr;
@@ -108,7 +108,7 @@ s390x_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
static int
s390x_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
- GElf_Addr relaaddr)
+ GElf_Addr relaaddr)
{
DSO *dso = info->dso;
GElf_Addr value;
@@ -191,7 +191,7 @@ s390x_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
s390x_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -276,7 +276,7 @@ s390x_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
static int
s390x_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
- GElf_Addr reladdr)
+ GElf_Addr reladdr)
{
error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename);
return 1;
@@ -284,7 +284,7 @@ s390x_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
static int
s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
- GElf_Rela *rela, GElf_Addr relaaddr)
+ GElf_Rela *rela, GElf_Addr relaaddr)
{
GElf_Addr value;
struct prelink_conflict *conflict;
@@ -293,7 +293,8 @@ s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
int r_type;
if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_390_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_390_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -313,6 +314,12 @@ s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on S390x yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPOFF wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-sh.c b/trunk/src/arch-sh.c
index c5c9f52..095ed58 100644
--- a/trunk/src/arch-sh.c
+++ b/trunk/src/arch-sh.c
@@ -30,7 +30,7 @@
static int
sh_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PLTGOT)
{
@@ -67,7 +67,7 @@ sh_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
sh_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: SH doesn't support REL relocs", dso->filename);
return 1;
@@ -75,7 +75,7 @@ sh_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
sh_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
Elf32_Addr data;
@@ -107,7 +107,7 @@ sh_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
static int
sh_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
- GElf_Addr relaaddr)
+ GElf_Addr relaaddr)
{
DSO *dso;
GElf_Addr value;
@@ -151,7 +151,7 @@ sh_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
sh_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -206,7 +206,7 @@ sh_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
static int
sh_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
- GElf_Addr reladdr)
+ GElf_Addr reladdr)
{
error (0, 0, "%s: SH doesn't support REL relocs", dso->filename);
return 1;
@@ -214,20 +214,27 @@ sh_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
static int
sh_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
- GElf_Rela *rela, GElf_Addr relaaddr)
+ GElf_Rela *rela, GElf_Addr relaaddr)
{
GElf_Addr value;
struct prelink_conflict *conflict;
GElf_Rela *ret;
if (GELF_R_TYPE (rela->r_info) == R_SH_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_SH_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_SH_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
GELF_R_TYPE (rela->r_info));
if (conflict == NULL)
return 0;
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on SuperH yet",
+ dso->filename);
+ return 1;
+ }
value = conflict_lookup_value (conflict);
ret = prelink_conflict_add_rela (info);
if (ret == NULL)
diff --git a/trunk/src/arch-sparc.c b/trunk/src/arch-sparc.c
index f55d4f7..7a3c8db 100644
--- a/trunk/src/arch-sparc.c
+++ b/trunk/src/arch-sparc.c
@@ -30,7 +30,7 @@
static int
sparc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PLTGOT)
{
@@ -55,7 +55,7 @@ sparc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
sparc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename);
return 1;
@@ -63,7 +63,7 @@ sparc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
sparc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE)
{
@@ -235,7 +235,7 @@ sparc_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
sparc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (GELF_R_TYPE (rela->r_info))
{
@@ -333,7 +333,8 @@ sparc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
int r_type;
if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE
- || GELF_R_TYPE (rela->r_info) == R_SPARC_NONE)
+ || GELF_R_TYPE (rela->r_info) == R_SPARC_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -355,6 +356,12 @@ sparc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on SPARC yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPOFF32 wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-sparc64.c b/trunk/src/arch-sparc64.c
index 418bcac..63e107f 100644
--- a/trunk/src/arch-sparc64.c
+++ b/trunk/src/arch-sparc64.c
@@ -32,7 +32,7 @@
static int
sparc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PLTGOT)
{
@@ -57,7 +57,7 @@ sparc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
static int
sparc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename);
return 1;
@@ -65,7 +65,7 @@ sparc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
sparc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE)
{
@@ -84,7 +84,7 @@ sparc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
static int
sparc64_prelink_rel (struct prelink_info *info, GElf_Rel *rel,
- GElf_Addr reladdr)
+ GElf_Addr reladdr)
{
error (0, 0, "%s: Sparc doesn't support REL relocs", info->dso->filename);
return 1;
@@ -166,7 +166,7 @@ sparc64_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value)
static int
sparc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
- GElf_Addr relaaddr)
+ GElf_Addr relaaddr)
{
DSO *dso = info->dso;
GElf_Addr value;
@@ -309,7 +309,7 @@ sparc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
sparc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
switch (SPARC64_R_TYPE (rela->r_info))
{
@@ -425,7 +425,7 @@ sparc64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
static int
sparc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info,
- GElf_Rel *rel, GElf_Addr reladdr)
+ GElf_Rel *rel, GElf_Addr reladdr)
{
error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename);
return 1;
@@ -433,7 +433,7 @@ sparc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info,
static int
sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
- GElf_Rela *rela, GElf_Addr relaaddr)
+ GElf_Rela *rela, GElf_Addr relaaddr)
{
GElf_Addr value;
struct prelink_conflict *conflict;
@@ -442,7 +442,8 @@ sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
int r_type;
if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE
- || SPARC64_R_TYPE (rela->r_info) == R_SPARC_NONE)
+ || SPARC64_R_TYPE (rela->r_info) == R_SPARC_NONE
+ || info->dso == dso)
/* Fast path: nothing to do. */
return 0;
conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
@@ -464,6 +465,12 @@ sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
}
value = 0;
}
+ else if (conflict->ifunc)
+ {
+ error (0, 0, "%s: STT_GNU_IFUNC not handled on SPARC64 yet",
+ dso->filename);
+ return 1;
+ }
else
{
/* DTPOFF64 wants to see only real conflicts, not lookups
diff --git a/trunk/src/arch-x86_64.c b/trunk/src/arch-x86_64.c
index d5b88dd..a2c6412 100644
--- a/trunk/src/arch-x86_64.c
+++ b/trunk/src/arch-x86_64.c
@@ -30,7 +30,7 @@
static int
x86_64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
if (dyn->d_tag == DT_PLTGOT)
{
@@ -75,7 +75,7 @@ x86_64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
static int
x86_64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
- GElf_Addr adjust)
+ GElf_Addr adjust)
{
Elf64_Addr addr;
@@ -89,6 +89,10 @@ x86_64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
rela->r_addend += adjust;
}
break;
+ case R_X86_64_IRELATIVE:
+ if (rela->r_addend >= start)
+ rela->r_addend += adjust;
+ /* FALLTHROUGH */
case R_X86_64_JUMP_SLOT:
addr = read_ule64 (dso, rela->r_offset);
if (addr >= start)
@@ -113,7 +117,8 @@ x86_64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
GElf_Addr value;
dso = info->dso;
- if (GELF_R_TYPE (rela->r_info) == R_X86_64_NONE)
+ if (GELF_R_TYPE (rela->r_info) == R_X86_64_NONE
+ || GELF_R_TYPE (rela->r_info) == R_X86_64_IRELATIVE)
return 0;
else if (GELF_R_TYPE (rela->r_info) == R_X86_64_RELATIVE)
{
@@ -169,8 +174,9 @@ x86_64_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
static int
x86_64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
- char *buf)
+ char *buf, GElf_Addr dest_addr)
{
+ GElf_Rela *ret;
switch (GELF_R_TYPE (rela->r_info))
{
case R_X86_64_GLOB_DAT:
@@ -178,6 +184,16 @@ x86_64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
case R_X86_64_64:
buf_write_le64 (buf, rela->r_addend);
break;
+ case R_X86_64_IRELATIVE:
+ if (dest_addr == 0)
+ return 5;
+ ret = prelink_conflict_add_rela (info);
+ if (ret == NULL)
+ return 1;
+ ret->r_offset = dest_addr;
+ ret->r_info = GELF_R_INFO (0, R_X86_64_IRELATIVE);
+ ret->r_addend = rela->r_addend;
+ break;
case R_X86_64_32:
buf_write_le32 (buf, rela->r_addend);
break;
@@ -252,19 +268,24 @@ x86_64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
GELF_R_TYPE (rela->r_info));
if (conflict == NULL)
{
- if (info->curtls == NULL)
- return 0;
switch (GELF_R_TYPE (rela->r_info))
{
/* Even local DTPMOD and TPOFF relocs need conflicts. */
case R_X86_64_DTPMOD64:
case R_X86_64_TPOFF64:
+ if (info->curtls == NULL || info->dso == dso)
+ return 0;
+ break;
+ /* Similarly IRELATIVE relocations always need conflicts. */
+ case R_X86_64_IRELATIVE:
break;
default:
return 0;
}
value = 0;
}
+ else if (info->dso == dso && !conflict->ifunc)
+ return 0;
else
{
/* DTPOFF wants to see only real conflicts, not lookups
@@ -288,7 +309,10 @@ x86_64_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
/* FALLTHROUGH */
case R_X86_64_JUMP_SLOT:
case R_X86_64_64:
+ case R_X86_64_IRELATIVE:
ret->r_addend = value + rela->r_addend;
+ if (conflict != NULL && conflict->ifunc)
+ ret->r_info = GELF_R_INFO (0, R_X86_64_IRELATIVE);
break;
case R_X86_64_32:
value += rela->r_addend;
@@ -422,6 +446,7 @@ x86_64_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr)
{
case R_X86_64_NONE:
case R_X86_64_RELATIVE:
+ case R_X86_64_IRELATIVE:
break;
case R_X86_64_JUMP_SLOT:
sec = addr_to_sec (dso, rela->r_offset);
diff --git a/trunk/src/conflict.c b/trunk/src/conflict.c
index 82e07db..38d0830 100644
--- a/trunk/src/conflict.c
+++ b/trunk/src/conflict.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2002, 2003, 2004, 2007 Red Hat, Inc.
+/* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 Red Hat, Inc.
Written by Jakub Jelinek <jakub@redhat.com>, 2001.
This program is free software; you can redistribute it and/or modify
@@ -276,7 +276,7 @@ conflict_rela_cmp (const void *A, const void *B)
int
get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr,
- char *buf, GElf_Word size)
+ char *buf, GElf_Word size, GElf_Addr dest_addr)
{
int sec = addr_to_sec (dso, addr), j;
Elf_Scn *scn;
@@ -321,7 +321,7 @@ get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr,
reloc offset. */
for (j = 0; j < info->conflict_rela_size; ++j)
{
- int reloc_type, reloc_size;
+ int reloc_type, reloc_size, ret;
off_t off;
if (info->conflict_rela[j].r_offset >= addr + size)
@@ -343,8 +343,12 @@ get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr,
return 2;
/* Note that apply_conflict_rela shouldn't rely on R_SYM
field of conflict to be 0. */
- dso->arch->apply_conflict_rela (info, info->conflict_rela + j,
- buf + off);
+ ret
+ = dso->arch->apply_conflict_rela (info, info->conflict_rela + j,
+ buf + off,
+ dest_addr ? dest_addr + off : 0);
+ if (ret)
+ return ret;
}
}
else
@@ -467,15 +471,17 @@ prelink_build_conflicts (struct prelink_info *info)
}
}
- for (i = 1; i < ndeps; ++i)
+ for (i = 0; i < ndeps; ++i)
{
+ int j, sec, first_conflict, maxidx;
+ struct prelink_conflict *conflict;
+
dso = info->dsos[i];
- ent = info->ent->depends[i - 1];
+ ent = i ? info->ent->depends[i - 1] : info->ent;
/* Verify .gnu.liblist sections of all dependent libraries. */
- if (ent->ndepends > 0)
+ if (i && ent->ndepends > 0)
{
- int j;
const char *name;
int nliblist;
Elf32_Lib *liblist;
@@ -539,85 +545,78 @@ prelink_build_conflicts (struct prelink_info *info)
}
}
- if (info->conflicts[i].count || info->tls[i].modid)
+ info->curconflicts = &info->conflicts[i];
+ info->curtls = info->tls[i].modid ? info->tls + i : NULL;
+ first_conflict = info->conflict_rela_size;
+ sec = addr_to_sec (dso, dso->info[DT_SYMTAB]);
+ /* DT_SYMTAB should be found and should point to
+ start of .dynsym section. */
+ if (sec == -1 || dso->info[DT_SYMTAB] != dso->shdr[sec].sh_addr)
{
- int j, sec, first_conflict, maxidx;
- struct prelink_conflict *conflict;
-
- info->curconflicts = &info->conflicts[i];
- info->curtls = info->tls[i].modid ? info->tls + i : NULL;
- first_conflict = info->conflict_rela_size;
- sec = addr_to_sec (dso, dso->info[DT_SYMTAB]);
- /* DT_SYMTAB should be found and should point to
- start of .dynsym section. */
- if (sec == -1
- || dso->info[DT_SYMTAB] != dso->shdr[sec].sh_addr)
+ error (0, 0, "Bad symtab");
+ goto error_out;
+ }
+ info->symtab_start = dso->shdr[sec].sh_addr - dso->base;
+ info->symtab_end = info->symtab_start + dso->shdr[sec].sh_size;
+ for (j = 0; j < dso->ehdr.e_shnum; ++j)
+ {
+ if (! (dso->shdr[j].sh_flags & SHF_ALLOC))
+ continue;
+ switch (dso->shdr[j].sh_type)
{
- error (0, 0, "Bad symtab");
- goto error_out;
+ case SHT_REL:
+ if (prelink_conflict_rel (dso, j, info))
+ goto error_out;
+ break;
+ case SHT_RELA:
+ if (prelink_conflict_rela (dso, j, info))
+ goto error_out;
+ break;
}
- info->symtab_start = dso->shdr[sec].sh_addr - dso->base;
- info->symtab_end = info->symtab_start + dso->shdr[sec].sh_size;
- for (j = 0; j < dso->ehdr.e_shnum; ++j)
+ }
+
+ if (dso->arch->arch_prelink_conflict
+ && dso->arch->arch_prelink_conflict (dso, info))
+ goto error_out;
+
+ maxidx = 1;
+ if (info->curconflicts->hash != &info->curconflicts->first)
+ maxidx = 251;
+ for (j = 0; j < maxidx; j++)
+ for (conflict = info->curconflicts->hash[j]; conflict;
+ conflict = conflict->next)
+ if (! conflict->used && (i || conflict->ifunc))
{
- if (! (dso->shdr[j].sh_flags & SHF_ALLOC))
- continue;
- switch (dso->shdr[j].sh_type)
- {
- case SHT_REL:
- if (prelink_conflict_rel (dso, j, info))
- goto error_out;
- break;
- case SHT_RELA:
- if (prelink_conflict_rela (dso, j, info))
- goto error_out;
- break;
- }
+ error (0, 0, "%s: Conflict %08llx not found in any relocation",
+ dso->filename, (unsigned long long) conflict->symoff);
+ ret = 1;
}
- if (dso->arch->arch_prelink_conflict
- && dso->arch->arch_prelink_conflict (dso, info))
- goto error_out;
+ /* Record library's position in search scope into R_SYM field. */
+ for (j = first_conflict; j < info->conflict_rela_size; ++j)
+ info->conflict_rela[j].r_info
+ = GELF_R_INFO (i, GELF_R_TYPE (info->conflict_rela[j].r_info));
- maxidx = 1;
- if (info->curconflicts->hash != &info->curconflicts->first)
- maxidx = 251;
- for (j = 0; j < maxidx; j++)
- for (conflict = info->curconflicts->hash[j]; conflict;
- conflict = conflict->next)
- if (! conflict->used)
- {
- error (0, 0, "%s: Conflict %08llx not found in any relocation",
- dso->filename, (unsigned long long) conflict->symoff);
- ret = 1;
- }
+ if (dynamic_info_is_set (dso, DT_TEXTREL)
+ && info->conflict_rela_size > first_conflict)
+ {
+ /* We allow prelinking against non-PIC libraries, as long as
+ no conflict is against read-only segment. */
+ int k;
- /* Record library's position in search scope into R_SYM field. */
for (j = first_conflict; j < info->conflict_rela_size; ++j)
- info->conflict_rela[j].r_info
- = GELF_R_INFO (i, GELF_R_TYPE (info->conflict_rela[j].r_info));
-
- if (dynamic_info_is_set (dso, DT_TEXTREL)
- && info->conflict_rela_size > first_conflict)
- {
- /* We allow prelinking against non-PIC libraries, as long as
- no conflict is against read-only segment. */
- int k;
-
- for (j = first_conflict; j < info->conflict_rela_size; ++j)
- for (k = 0; k < dso->ehdr.e_phnum; ++k)
- if (dso->phdr[k].p_type == PT_LOAD
- && (dso->phdr[k].p_flags & PF_W) == 0
- && dso->phdr[k].p_vaddr
- <= info->conflict_rela[j].r_offset
- && dso->phdr[k].p_vaddr + dso->phdr[k].p_memsz
- > info->conflict_rela[j].r_offset)
- {
- error (0, 0, "%s: Cannot prelink against non-PIC shared library %s",
- info->dso->filename, dso->filename);
- goto error_out;
- }
- }
+ for (k = 0; k < dso->ehdr.e_phnum; ++k)
+ if (dso->phdr[k].p_type == PT_LOAD
+ && (dso->phdr[k].p_flags & PF_W) == 0
+ && dso->phdr[k].p_vaddr
+ <= info->conflict_rela[j].r_offset
+ && dso->phdr[k].p_vaddr + dso->phdr[k].p_memsz
+ > info->conflict_rela[j].r_offset)
+ {
+ error (0, 0, "%s: Cannot prelink against non-PIC shared library %s",
+ info->dso->filename, dso->filename);
+ goto error_out;
+ }
}
}
@@ -755,11 +754,13 @@ prelink_build_conflicts (struct prelink_info *info)
if (i < firstbss2)
j = get_relocated_mem (info, ndso, s->u.ent->base + s->value,
info->sdynbss + cr.rela[i].r_offset
- - info->sdynbss_base, cr.rela[i].r_addend);
+ - info->sdynbss_base, cr.rela[i].r_addend,
+ cr.rela[i].r_offset);
else
j = get_relocated_mem (info, ndso, s->u.ent->base + s->value,
info->dynbss + cr.rela[i].r_offset
- - info->dynbss_base, cr.rela[i].r_addend);
+ - info->dynbss_base, cr.rela[i].r_addend,
+ cr.rela[i].r_offset);
switch (j)
{
diff --git a/trunk/src/cxx.c b/trunk/src/cxx.c
index 5922a02..9a20b3c 100644
--- a/trunk/src/cxx.c
+++ b/trunk/src/cxx.c
@@ -451,9 +451,9 @@ remove_redundant_cxx_conflicts (struct prelink_info *info)
mem2 = mem1 + fcs1.sym.st_size;
if (get_relocated_mem (info, fcs1.dso, fcs1.sym.st_value, mem1,
- fcs1.sym.st_size)
+ fcs1.sym.st_size, 0)
|| get_relocated_mem (info, fcs2.dso, fcs2.sym.st_value, mem2,
- fcs1.sym.st_size)
+ fcs1.sym.st_size, 0)
|| memcmp (mem1, mem2, fcs1.sym.st_size) != 0)
{
free (mem1);
diff --git a/trunk/src/get.c b/trunk/src/get.c
index 05e0b54..fe32deb 100644
--- a/trunk/src/get.c
+++ b/trunk/src/get.c
@@ -241,7 +241,7 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
do
{
unsigned long long symstart, symoff, valstart[3], value[3];
- int reloc_class, len, type = 1;
+ int reloc_class, len, type = 1, ifunc = 0;
char *symname;
r = strchr (buffer, '\n');
@@ -276,7 +276,13 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
reloc_class = dso->arch->reloc_class (reloc_class);
else
{
- if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS)
+ if (reloc_class & 8)
+ {
+ reloc_class = ((reloc_class & ~8)
+ | dso->arch->rtype_class_valid);
+ ifunc = 1;
+ }
+ else if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS)
reloc_class |= RTYPE_CLASS_VALID;
else
reloc_class |= dso->arch->rtype_class_valid;
@@ -287,7 +293,8 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
ent = NULL;
tls = NULL;
if (symstart == deps[0].start
- || (reloc_class == RTYPE_CLASS_TLS && info->conflicts))
+ || ((reloc_class == RTYPE_CLASS_TLS || ifunc)
+ && info->conflicts))
{
for (i = 0; i < ndeps; i++)
if (deps[i].start == valstart[0])
@@ -316,7 +323,7 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
}
}
- if (symstart == deps[0].start)
+ if (symstart == deps[0].start && (!ifunc || info->conflicts == NULL))
{
/* Only interested in relocations from the current object. */
if (symoff < info->symtab_start || symoff >= info->symtab_end)
@@ -326,7 +333,8 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
goto error_out;
}
- if (ent == info->ent && reloc_class != RTYPE_CLASS_TLS)
+ if (ent == info->ent
+ && reloc_class != RTYPE_CLASS_TLS)
value[0] = adjust_old_to_new (info->dso, value[0]);
s = &info->symbols[(symoff - info->symtab_start)
@@ -371,13 +379,14 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
s->next = NULL;
}
}
- else if (reloc_class == RTYPE_CLASS_TLS && info->conflicts)
+ else if ((reloc_class == RTYPE_CLASS_TLS || ifunc)
+ && info->conflicts)
{
struct prelink_conflict *conflict;
int symowner;
size_t idx;
- for (symowner = 1; symowner < ndeps; symowner++)
+ for (symowner = 0; symowner < ndeps; symowner++)
if (deps[symowner].start == symstart)
break;
if (symowner == ndeps)
@@ -395,8 +404,12 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
if (conflict->symoff == symoff
&& conflict->reloc_class == reloc_class)
{
- if (conflict->lookup.tls != tls
- || conflict->conflict.tls != tls
+ if ((reloc_class != RTYPE_CLASS_TLS
+ && (conflict->lookup.ent != ent
+ || conflict->conflict.ent != ent))
+ || (reloc_class == RTYPE_CLASS_TLS
+ && (conflict->lookup.tls != tls
+ || conflict->conflict.tls != tls))
|| conflict->lookupval != value[0]
|| conflict->conflictval != value[0])
{
@@ -418,13 +431,22 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
conflict->next = info->conflicts[symowner].hash[idx];
conflict->next2 = NULL;
info->conflicts[symowner].hash[idx] = conflict;
- conflict->lookup.tls = tls;
- conflict->conflict.tls = tls;
+ if (reloc_class != RTYPE_CLASS_TLS)
+ {
+ conflict->lookup.ent = ent;
+ conflict->conflict.ent = ent;
+ }
+ else
+ {
+ conflict->lookup.tls = tls;
+ conflict->conflict.tls = tls;
+ }
conflict->lookupval = value[0];
conflict->conflictval = value[0];
conflict->symoff = symoff;
conflict->reloc_class = reloc_class;
conflict->used = 0;
+ conflict->ifunc = ifunc;
if (++info->conflicts[symowner].count == 16)
conflict_hash_init (&info->conflicts[symowner]);
}
@@ -458,7 +480,13 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
reloc_class = dso->arch->reloc_class (reloc_class);
else
{
- if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS)
+ if (reloc_class & 8)
+ {
+ reloc_class = ((reloc_class & ~8)
+ | dso->arch->rtype_class_valid);
+ ifunc = 1;
+ }
+ else if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS)
reloc_class |= RTYPE_CLASS_VALID;
else
reloc_class |= dso->arch->rtype_class_valid;
@@ -571,6 +599,7 @@ prelink_record_relocations (struct prelink_info *info, FILE *f,
conflict->symoff = symoff;
conflict->reloc_class = reloc_class;
conflict->used = 0;
+ conflict->ifunc = ifunc;
if (++info->conflicts[symowner].count == 16)
conflict_hash_init (&info->conflicts[symowner]);
}
diff --git a/trunk/src/prelink.h b/trunk/src/prelink.h
index 5de2f0e..61e25c2 100644
--- a/trunk/src/prelink.h
+++ b/trunk/src/prelink.h
@@ -85,6 +85,14 @@
#define R_ARM_TLS_TPOFF32 19
#endif
+#ifndef R_386_IRELATIVE
+#define R_386_IRELATIVE 42
+#endif
+
+#ifndef R_X86_64_IRELATIVE
+#define R_X86_64_IRELATIVE 37
+#endif
+
struct prelink_entry;
struct prelink_info;
struct PLArch;
@@ -179,7 +187,7 @@ struct PLArch
GElf_Rela *rela, GElf_Addr relaaddr);
int (*arch_prelink_conflict) (DSO *dso, struct prelink_info *info);
int (*apply_conflict_rela) (struct prelink_info *info, GElf_Rela *rela,
- char *buf);
+ char *buf, GElf_Addr dest_addr);
int (*apply_rel) (struct prelink_info *info, GElf_Rel *rel, char *buf);
int (*apply_rela) (struct prelink_info *info, GElf_Rela *rela, char *buf);
int (*rel_to_rela) (DSO *dso, GElf_Rel *rel, GElf_Rela *rela);
@@ -413,7 +421,8 @@ struct prelink_conflict
/* Value it has in conflict.ent. */
GElf_Addr conflictval;
int reloc_class;
- int used;
+ unsigned char used;
+ unsigned char ifunc;
};
struct prelink_conflicts
@@ -489,7 +498,7 @@ int execve_close (FILE *f);
int remove_redundant_cxx_conflicts (struct prelink_info *info);
int get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr,
- char *buf, GElf_Word size);
+ char *buf, GElf_Word size, GElf_Addr dest_addr);
int layout_libs (void);
diff --git a/trunk/testsuite/Makefile.am b/trunk/testsuite/Makefile.am
index 514cf8e..0de603c 100644
--- a/trunk/testsuite/Makefile.am
+++ b/trunk/testsuite/Makefile.am
@@ -14,6 +14,7 @@ TESTS = movelibs.sh \
tls1.sh tls2.sh tls3.sh tls4.sh tls5.sh tls6.sh tls7.sh \
cxx1.sh cxx2.sh quick1.sh quick2.sh quick3.sh cycle1.sh cycle2.sh \
deps1.sh deps2.sh \
+ ifunc1.sh ifunc2.sh ifunc3.sh \
undosyslibs.sh
TESTS_ENVIRONMENT = \
PRELINK="../src/prelink -c ./prelink.conf -C ./prelink.cache --ld-library-path=. --dynamic-linker=`echo ./ld*.so.*[0-9]`" \
diff --git a/trunk/testsuite/Makefile.in b/trunk/testsuite/Makefile.in
index 5dff524..30eeeb5 100644
--- a/trunk/testsuite/Makefile.in
+++ b/trunk/testsuite/Makefile.in
@@ -109,6 +109,7 @@ TESTS = movelibs.sh \
tls1.sh tls2.sh tls3.sh tls4.sh tls5.sh tls6.sh tls7.sh \
cxx1.sh cxx2.sh quick1.sh quick2.sh quick3.sh cycle1.sh cycle2.sh \
deps1.sh deps2.sh \
+ ifunc1.sh ifunc2.sh ifunc3.sh \
undosyslibs.sh
TESTS_ENVIRONMENT = \
diff --git a/trunk/testsuite/ifunc.h b/trunk/testsuite/ifunc.h
new file mode 100644
index 0000000..3593396
--- /dev/null
+++ b/trunk/testsuite/ifunc.h
@@ -0,0 +1,33 @@
+#ifndef PICKNO
+# define PICKNO 1
+#endif
+#if PICKNO == 2
+# define PICK(fn1, fn2) #fn2
+#else
+# define PICK(fn1, fn2) #fn1
+#endif
+#ifdef __x86_64__
+# define IFUNC_ASM(fn) "\tleaq " fn "(%rip), %rax\n\tretq\n"
+#elif defined __i386__
+# ifdef __PIC__
+# define IFUNC_ASM(fn) "\tcall 1f\n1:\tpopl %ecx\n" \
+ "\taddl $_GLOBAL_OFFSET_TABLE_+[.-1b], %ecx\n" \
+ "\tleal " fn "@GOTOFF(%ecx), %eax\n\tret\n"
+# else
+# define IFUNC_ASM(fn) "\tmovl $" fn ", %eax\n\tret\n"
+# endif
+#else
+# error Architecture not supported
+#endif
+#define IFUNC(name, hidden, fn1, fn2) \
+extern __typeof (fn1) fn1 __attribute__((used)); \
+extern __typeof (fn2) fn2 __attribute__((used)); \
+extern __typeof (fn1) name; \
+asm (".globl " #name "\n" \
+ "\t" hidden " " #name "\n" \
+ "\t.type " #name ", @gnu_indirect_function\n" \
+ #name ":\n" \
+ IFUNC_ASM (PICK (fn1, fn2)) \
+ "\t.size " #name ", .-" #name "\n")
+#define IFUNC_LOCAL(name, fn1, fn2) IFUNC(name, ".hidden", fn1, fn2)
+#define IFUNC_GLOBAL(name, fn1, fn2) IFUNC(name, ".globl", fn1, fn2)
diff --git a/trunk/testsuite/ifunc1.c b/trunk/testsuite/ifunc1.c
new file mode 100644
index 0000000..f5500df
--- /dev/null
+++ b/trunk/testsuite/ifunc1.c
@@ -0,0 +1,29 @@
+#include "ifunc.h"
+
+static int bint11 (void) { return 1; }
+static int bint12 (void) { return 2; }
+
+IFUNC_LOCAL (bint1, bint11, bint12);
+
+static int lib2t21 (void) { return 1; }
+static int lib2t22 (void) { return 2; }
+
+IFUNC_GLOBAL (lib2t2, lib2t21, lib2t22);
+
+extern int lib1t2 (void);
+extern int lib1test (void);
+extern int lib2test (void);
+
+extern void abort (void);
+
+int
+main (void)
+{
+ lib1test ();
+ lib2test ();
+ if (bint1 () != PICKNO)
+ abort ();
+ if (lib1t2 () != PICKNO)
+ abort ();
+ return 0;
+}
diff --git a/trunk/testsuite/ifunc1.sh b/trunk/testsuite/ifunc1.sh
new file mode 100755
index 0000000..7746234
--- /dev/null
+++ b/trunk/testsuite/ifunc1.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+. `dirname $0`/functions.sh
+# First check if __thread is supported by ld.so/gcc/ld/as:
+$CCLINK -o ifunctest $srcdir/ifunctest.c -Wl,--rpath-link,. > /dev/null 2>&1 || exit 77
+( ./ifunctest || { rm -f ifunctest; exit 77; } ) 2>/dev/null || exit 77
+rm -f ifunctest ifunc1 ifunc1lib*.so ifunc1.log
+rm -f prelink.cache
+$CC -shared -O2 -fpic -o ifunc1lib1.so $srcdir/ifunc1lib1.c
+$CC -shared -O2 -fpic -o ifunc1lib2.so $srcdir/ifunc1lib2.c ifunc1lib1.so
+BINS="ifunc1"
+LIBS="ifunc1lib1.so ifunc1lib2.so"
+$CCLINK -o ifunc1 $srcdir/ifunc1.c -Wl,--rpath-link,. ifunc1lib2.so
+savelibs
+echo $PRELINK ${PRELINK_OPTS--vm} ./ifunc1 >> ifunc1.log
+$PRELINK ${PRELINK_OPTS--vm} ./ifunc1 >> ifunc1.log 2>&1 || exit 1
+grep -q ^`echo $PRELINK | sed 's/ .*$/: /'` ifunc1.log && exit 2
+LD_LIBRARY_PATH=. ./ifunc1 || exit 3
+readelf -a ./ifunc1 >> ifunc1.log 2>&1 || exit 4
+# So that it is not prelinked again
+chmod -x ./ifunc1
+comparelibs >> ifunc1.log 2>&1 || exit 5
diff --git a/trunk/testsuite/ifunc1lib1.c b/trunk/testsuite/ifunc1lib1.c
new file mode 100644
index 0000000..27a3f03
--- /dev/null
+++ b/trunk/testsuite/ifunc1lib1.c
@@ -0,0 +1,32 @@
+#include "ifunc.h"
+
+static int lib1t11 (void) { return 11; }
+static int lib1t12 (void) { return 12; }
+
+IFUNC_LOCAL (lib1t1, lib1t11, lib1t12);
+
+static int lib1t21 (void) { return 1; }
+static int lib1t22 (void) { return 2; }
+
+IFUNC_GLOBAL (lib1t2, lib1t21, lib1t22);
+
+static int lib1t31 (void) { return 3; }
+static int lib1t32 (void) { return 4; }
+
+IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32);
+
+extern void abort (void);
+
+int (*lib1p1) (void) = lib1t1;
+
+int
+lib1test (void)
+{
+ if (lib1t1 () != PICKNO + 10)
+ abort ();
+ if (lib1t3 () != PICKNO)
+ abort ();
+ if (lib1p1 () != PICKNO + 10)
+ abort ();
+ return 0;
+}
diff --git a/trunk/testsuite/ifunc1lib2.c b/trunk/testsuite/ifunc1lib2.c
new file mode 100644
index 0000000..ca12e3d
--- /dev/null
+++ b/trunk/testsuite/ifunc1lib2.c
@@ -0,0 +1,39 @@
+#include "ifunc.h"
+
+static int lib2t11 (void) { return 1; }
+static int lib2t12 (void) { return 2; }
+
+IFUNC_LOCAL (lib2t1, lib2t11, lib2t12);
+
+static int lib2t21 (void) { return 3; }
+static int lib2t22 (void) { return 4; }
+
+IFUNC_GLOBAL (lib2t2, lib2t21, lib2t22);
+
+static int lib2t31 (void) { return 1; }
+static int lib2t32 (void) { return 2; }
+
+IFUNC_GLOBAL (lib2t3, lib2t31, lib2t32);
+
+static int lib1t31 (void) { return 1; }
+static int lib1t32 (void) { return 2; }
+
+IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32);
+
+int (*lib2p1) (void) = lib2t2;
+
+extern void abort (void);
+
+int
+lib2test (void)
+{
+ if (lib2t1 () != PICKNO)
+ abort ();
+ if (lib2t2 () != PICKNO)
+ abort ();
+ if (lib2t3 () != PICKNO)
+ abort ();
+ if (lib2p1 () != PICKNO)
+ abort ();
+ return 0;
+}
diff --git a/trunk/testsuite/ifunc2.sh b/trunk/testsuite/ifunc2.sh
new file mode 100755
index 0000000..dea5bf4
--- /dev/null
+++ b/trunk/testsuite/ifunc2.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+. `dirname $0`/functions.sh
+# First check if __thread is supported by ld.so/gcc/ld/as:
+$CCLINK -o ifunctest $srcdir/ifunctest.c -Wl,--rpath-link,. > /dev/null 2>&1 || exit 77
+( ./ifunctest || { rm -f ifunctest; exit 77; } ) 2>/dev/null || exit 77
+rm -f ifunctest ifunc2 ifunc2lib*.so ifunc2.log
+rm -f prelink.cache
+$CC -shared -O2 -fpic -o ifunc2lib1.so $srcdir/ifunc1lib1.c -DPICKNO=2
+$CC -shared -O2 -fpic -o ifunc2lib2.so $srcdir/ifunc1lib2.c ifunc2lib1.so -DPICKNO=2
+BINS="ifunc2"
+LIBS="ifunc2lib1.so ifunc2lib2.so"
+$CCLINK -o ifunc2 $srcdir/ifunc1.c -Wl,--rpath-link,. ifunc2lib2.so -DPICKNO=2
+savelibs
+echo $PRELINK ${PRELINK_OPTS--vm} ./ifunc2 >> ifunc2.log
+$PRELINK ${PRELINK_OPTS--vm} ./ifunc2 >> ifunc2.log 2>&1 || exit 1
+grep -q ^`echo $PRELINK | sed 's/ .*$/: /'` ifunc2.log && exit 2
+LD_LIBRARY_PATH=. ./ifunc2 || exit 3
+readelf -a ./ifunc2 >> ifunc2.log 2>&1 || exit 4
+# So that it is not prelinked again
+chmod -x ./ifunc2
+comparelibs >> ifunc2.log 2>&1 || exit 5
diff --git a/trunk/testsuite/ifunc3.c b/trunk/testsuite/ifunc3.c
new file mode 100644
index 0000000..1f243d8
--- /dev/null
+++ b/trunk/testsuite/ifunc3.c
@@ -0,0 +1,32 @@
+#include "ifunc.h"
+
+static int bint11 (void) { return 1; }
+static int bint12 (void) { return 2; }
+
+IFUNC_LOCAL (bint1, bint11, bint12);
+
+static int lib2t21 (void) { return 1; }
+static int lib2t22 (void) { return 2; }
+
+IFUNC_GLOBAL (lib2t2, lib2t21, lib2t22);
+
+extern int lib1t2 (void);
+extern int lib1test (void);
+extern int lib2test (void);
+extern int (*lib1p1) (void);
+
+extern void abort (void);
+
+int
+main (void)
+{
+ lib1test ();
+ lib2test ();
+ if (bint1 () != PICKNO)
+ abort ();
+ if (lib1t2 () != PICKNO)
+ abort ();
+ if (lib1p1 () != PICKNO + 10)
+ abort ();
+ return 0;
+}
diff --git a/trunk/testsuite/ifunc3.sh b/trunk/testsuite/ifunc3.sh
new file mode 100755
index 0000000..a54d4ec
--- /dev/null
+++ b/trunk/testsuite/ifunc3.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+. `dirname $0`/functions.sh
+# First check if __thread is supported by ld.so/gcc/ld/as:
+$CCLINK -o ifunctest $srcdir/ifunctest.c -Wl,--rpath-link,. > /dev/null 2>&1 || exit 77
+( ./ifunctest || { rm -f ifunctest; exit 77; } ) 2>/dev/null || exit 77
+rm -f ifunctest ifunc3 ifunc3lib*.so ifunc3.log
+rm -f prelink.cache
+$CC -shared -O2 -fpic -o ifunc3lib1.so $srcdir/ifunc3lib1.c
+$CC -shared -O2 -fpic -o ifunc3lib2.so $srcdir/ifunc1lib2.c ifunc3lib1.so
+BINS="ifunc3"
+LIBS="ifunc3lib1.so ifunc3lib2.so"
+$CCLINK -o ifunc3 $srcdir/ifunc3.c -Wl,--rpath-link,. ifunc3lib2.so
+savelibs
+echo $PRELINK ${PRELINK_OPTS--vm} ./ifunc3 >> ifunc3.log
+$PRELINK ${PRELINK_OPTS--vm} ./ifunc3 >> ifunc3.log 2>&1 || exit 1
+grep -q ^`echo $PRELINK | sed 's/ .*$/: /'` ifunc3.log && exit 2
+LD_LIBRARY_PATH=. ./ifunc3 || exit 3
+readelf -a ./ifunc3 >> ifunc3.log 2>&1 || exit 4
+# So that it is not prelinked again
+chmod -x ./ifunc3
+comparelibs >> ifunc3.log 2>&1 || exit 5
diff --git a/trunk/testsuite/ifunc3lib1.c b/trunk/testsuite/ifunc3lib1.c
new file mode 100644
index 0000000..133b389
--- /dev/null
+++ b/trunk/testsuite/ifunc3lib1.c
@@ -0,0 +1,37 @@
+#include "ifunc.h"
+
+static int lib1t11 (void) { return 11; }
+static int lib1t12 (void) { return 12; }
+
+IFUNC_LOCAL (lib1t1, lib1t11, lib1t12);
+
+static int lib1t21 (void) { return 1; }
+static int lib1t22 (void) { return 2; }
+
+IFUNC_GLOBAL (lib1t2, lib1t21, lib1t22);
+
+static int lib1t31 (void) { return 3; }
+static int lib1t32 (void) { return 4; }
+
+IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32);
+
+char lib1b1[4];
+char *lib1b2 = &lib1b1[2];
+
+extern void abort (void);
+
+int (*lib1p1) (void) = lib1t1;
+
+int
+lib1test (void)
+{
+ if (lib1t1 () != PICKNO + 10)
+ abort ();
+ if (lib1t3 () != PICKNO)
+ abort ();
+ if (lib1p1 () != PICKNO + 10)
+ abort ();
+ if (lib1b2 != lib1b1 + 2)
+ abort ();
+ return 0;
+}
diff --git a/trunk/testsuite/ifunctest.c b/trunk/testsuite/ifunctest.c
new file mode 100644
index 0000000..9ce7b1a
--- /dev/null
+++ b/trunk/testsuite/ifunctest.c
@@ -0,0 +1,16 @@
+#include "ifunc.h"
+
+static int foo1 (void) { return 1; }
+static int foo2 (void) { return 2; }
+
+IFUNC_LOCAL (foo, foo1, foo2);
+
+extern void abort (void);
+
+int
+main (void)
+{
+ if (foo () != PICKNO)
+ abort ();
+ return 0;
+}