aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/src/arch-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/arch-ppc.c')
-rw-r--r--trunk/src/arch-ppc.c54
1 files changed, 40 insertions, 14 deletions
diff --git a/trunk/src/arch-ppc.c b/trunk/src/arch-ppc.c
index 99dbea5..ad3961e 100644
--- a/trunk/src/arch-ppc.c
+++ b/trunk/src/arch-ppc.c
@@ -126,7 +126,8 @@ static int
ppc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
GElf_Addr adjust)
{
- if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE)
+ if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE
+ || GELF_R_TYPE (rela->r_info) == R_PPC_IRELATIVE)
{
if ((Elf32_Word) rela->r_addend >= start)
rela->r_addend += (Elf32_Sword) adjust;
@@ -206,7 +207,8 @@ ppc_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
DSO *dso = info->dso;
GElf_Addr value;
- if (GELF_R_TYPE (rela->r_info) == R_PPC_NONE)
+ if (GELF_R_TYPE (rela->r_info) == R_PPC_NONE
+ || GELF_R_TYPE (rela->r_info) == R_PPC_IRELATIVE)
return 0;
else if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE)
{
@@ -332,6 +334,7 @@ static int
ppc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
char *buf, GElf_Addr dest_addr)
{
+ GElf_Rela *ret;
switch (GELF_R_TYPE (rela->r_info))
{
case R_PPC_ADDR32:
@@ -342,6 +345,16 @@ ppc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
case R_PPC_UADDR16:
buf_write_be16 (buf, rela->r_addend);
break;
+ case R_PPC_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_PPC_IRELATIVE);
+ ret->r_addend = rela->r_addend;
+ break;
default:
abort ();
}
@@ -435,16 +448,13 @@ 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
- || info->dso == dso)
+ || GELF_R_TYPE (rela->r_info) == R_PPC_NONE)
/* 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)
{
- if (info->curtls == NULL)
- return 0;
switch (GELF_R_TYPE (rela->r_info))
{
/* Even local DTPMOD and TPREL relocs need conflicts. */
@@ -454,18 +464,19 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
+ if (info->curtls == NULL || info->dso == dso)
+ return 0;
+ break;
+ /* Similarly IRELATIVE relocations always need conflicts. */
+ case R_PPC_IRELATIVE:
break;
default:
return 0;
}
value = 0;
}
- else if (conflict->ifunc)
- {
- error (0, 0, "%s: STT_GNU_IFUNC not handled on PowerPC yet",
- dso->filename);
- return 1;
- }
+ else if (info->dso == dso && !conflict->ifunc)
+ return 0;
else
{
/* DTPREL wants to see only real conflicts, not lookups
@@ -494,13 +505,19 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
{
case R_PPC_GLOB_DAT:
r_type = R_PPC_ADDR32;
- break;
case R_PPC_ADDR32:
case R_PPC_UADDR32:
+ case R_PPC_IRELATIVE:
+ if (conflict != NULL && conflict->ifunc)
+ r_type = R_PPC_IRELATIVE;
break;
case R_PPC_JMP_SLOT:
if (dynamic_info_is_set (dso, DT_PPC_GOT_BIT))
- r_type = R_PPC_ADDR32;
+ {
+ r_type = R_PPC_ADDR32;
+ if (conflict != NULL && conflict->ifunc)
+ r_type = R_PPC_IRELATIVE;
+ }
break;
case R_PPC_ADDR16_HA:
value += 0x8000;
@@ -606,6 +623,12 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
r_type);
return 1;
}
+ if (conflict != NULL && conflict->ifunc && r_type != R_PPC_IRELATIVE)
+ {
+ error (0, 0, "%s: relocation %d against IFUNC symbol", dso->filename,
+ (int) GELF_R_TYPE (rela->r_info));
+ return 1;
+ }
ret->r_info = GELF_R_INFO (0, r_type);
ret->r_addend = (Elf32_Sword) value;
return 0;
@@ -777,6 +800,9 @@ ppc_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr)
otherwise .plt section will be unprelinked in
ppc_arch_undo_prelink. */
return 0;
+ case R_PPC_IRELATIVE:
+ /* .iplt section will become SHT_NOBITS. */
+ return 0;
case R_PPC_ADDR16:
case R_PPC_UADDR16:
case R_PPC_ADDR16_LO: