diff options
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; +} |