aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjakub <jakub@94c539fb-cf18-0410-b60f-edeeb537fa16>2009-06-15 12:00:56 +0000
committerjakub <jakub@94c539fb-cf18-0410-b60f-edeeb537fa16>2009-06-15 12:00:56 +0000
commite75d2ea7e57cf638a1b0a16b414e528433d4bbee (patch)
treee48e8b98424b4b480640c16657bc9ead2384520e
parent9ce5995832cdfaf78ee9ba8374c46762513c92b6 (diff)
downloadprelink-cross-e75d2ea7e57cf638a1b0a16b414e528433d4bbee.tar.gz
prelink-cross-e75d2ea7e57cf638a1b0a16b414e528433d4bbee.tar.bz2
prelink-cross-e75d2ea7e57cf638a1b0a16b414e528433d4bbee.zip
* 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. git-svn-id: http://sourceware.org/svn/prelink@172 94c539fb-cf18-0410-b60f-edeeb537fa16
-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;
+}