// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2019 Free Software Foundation, Inc.
+// Copyright (C) 2008-2020 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>
// and David Edelsohn <edelsohn@gnu.org>
typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv);
- Valtype reloc = value >> right_shift;
+ if (overflow == CHECK_SIGNED)
+ value = static_cast<SignedAddress>(value) >> right_shift;
+ else
+ value = value >> right_shift;
+ Valtype reloc = value;
val &= ~dst_mask;
reloc &= dst_mask;
elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc);
- return overflowed<valsize>(value >> right_shift, overflow);
+ return overflowed<valsize>(value, overflow);
}
// Do a simple RELA relocation, unaligned.
typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype
Valtype;
Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view);
- Valtype reloc = value >> right_shift;
+ if (overflow == CHECK_SIGNED)
+ value = static_cast<SignedAddress>(value) >> right_shift;
+ else
+ value = value >> right_shift;
+ Valtype reloc = value;
val &= ~dst_mask;
reloc &= dst_mask;
elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc);
- return overflowed<valsize>(value >> right_shift, overflow);
+ return overflowed<valsize>(value, overflow);
}
public:
case elfcpp::R_POWERPC_ADDR16_LO:
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_ADDR16_HA:
+ case elfcpp::R_PPC64_ADDR16_HIGHER34:
+ case elfcpp::R_PPC64_ADDR16_HIGHERA34:
+ case elfcpp::R_PPC64_ADDR16_HIGHEST34:
+ case elfcpp::R_PPC64_ADDR16_HIGHESTA34:
+ case elfcpp::R_PPC64_D34:
+ case elfcpp::R_PPC64_D34_LO:
+ case elfcpp::R_PPC64_D34_HI30:
+ case elfcpp::R_PPC64_D34_HA30:
+ case elfcpp::R_PPC64_D28:
ref = Symbol::ABSOLUTE_REF;
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_TLSGD:
+ case elfcpp::R_PPC64_TLSLD:
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
+ case elfcpp::R_PPC64_GOT_TLSGD34:
+ case elfcpp::R_PPC64_GOT_TLSLD34:
+ case elfcpp::R_PPC64_GOT_TPREL34:
+ case elfcpp::R_PPC64_GOT_DTPREL34:
ref = Symbol::TLS_REF;
break;
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_POWERPC_REL32:
- case elfcpp::R_POWERPC_REL24:
case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_TPREL16_HI:
{
// These are the relocation types supported only on 32-bit.
// ??? glibc ld.so doesn't need to support these.
+ case elfcpp::R_POWERPC_REL24:
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_PPC64_REL16_HIGHERA34:
case elfcpp::R_PPC64_REL16_HIGHEST34:
case elfcpp::R_PPC64_REL16_HIGHESTA34:
- break;
-
case elfcpp::R_PPC64_D34:
case elfcpp::R_PPC64_D34_LO:
case elfcpp::R_PPC64_D34_HI30:
case elfcpp::R_PPC64_D28:
case elfcpp::R_PPC64_PCREL34:
case elfcpp::R_PPC64_PCREL28:
- target->set_powerxx_stubs();
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
break;
case elfcpp::R_PPC64_TOC:
case elfcpp::R_PPC64_PLT_PCREL34:
case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
- target->set_powerxx_stubs();
- // Fall through.
case elfcpp::R_POWERPC_PLT16_LO:
case elfcpp::R_POWERPC_PLT16_HI:
case elfcpp::R_POWERPC_PLT16_HA:
break;
case elfcpp::R_PPC64_GOT_PCREL34:
- target->set_powerxx_stubs();
- // Fall through.
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_GOT16_LO:
case elfcpp::R_POWERPC_GOT16_HI:
target->got_section(symtab, layout);
break;
+ case elfcpp::R_PPC64_GOT_TLSGD34:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
}
break;
+ case elfcpp::R_PPC64_GOT_TLSLD34:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
}
break;
+ case elfcpp::R_PPC64_GOT_DTPREL34:
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
}
break;
+ case elfcpp::R_PPC64_GOT_TPREL34:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_DS:
ppc_object->set_has_small_toc_reloc();
+ break;
+ default:
+ break;
+ }
+
+ switch (r_type)
+ {
+ case elfcpp::R_POWERPC_TPREL16:
+ case elfcpp::R_POWERPC_TPREL16_LO:
+ case elfcpp::R_POWERPC_TPREL16_HI:
+ case elfcpp::R_POWERPC_TPREL16_HA:
+ case elfcpp::R_PPC64_TPREL16_DS:
+ case elfcpp::R_PPC64_TPREL16_LO_DS:
+ case elfcpp::R_PPC64_TPREL16_HIGH:
+ case elfcpp::R_PPC64_TPREL16_HIGHA:
+ case elfcpp::R_PPC64_TPREL16_HIGHER:
+ case elfcpp::R_PPC64_TPREL16_HIGHERA:
+ case elfcpp::R_PPC64_TPREL16_HIGHEST:
+ case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+ case elfcpp::R_PPC64_TPREL34:
+ layout->set_has_static_tls();
+ break;
+ default:
+ break;
+ }
+
+ switch (r_type)
+ {
+ case elfcpp::R_PPC64_D34:
+ case elfcpp::R_PPC64_D34_LO:
+ case elfcpp::R_PPC64_D34_HI30:
+ case elfcpp::R_PPC64_D34_HA30:
+ case elfcpp::R_PPC64_D28:
+ case elfcpp::R_PPC64_PCREL34:
+ case elfcpp::R_PPC64_PCREL28:
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
+ case elfcpp::R_PPC64_PLT_PCREL34:
+ case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
+ case elfcpp::R_PPC64_GOT_PCREL34:
+ case elfcpp::R_PPC64_GOT_TLSGD34:
+ case elfcpp::R_PPC64_GOT_TLSLD34:
+ case elfcpp::R_PPC64_GOT_DTPREL34:
+ case elfcpp::R_PPC64_GOT_TPREL34:
+ target->set_powerxx_stubs();
+ break;
default:
break;
}
case elfcpp::R_PPC64_REL16_HIGHERA34:
case elfcpp::R_PPC64_REL16_HIGHEST34:
case elfcpp::R_PPC64_REL16_HIGHESTA34:
- break;
-
case elfcpp::R_PPC64_D34:
case elfcpp::R_PPC64_D34_LO:
case elfcpp::R_PPC64_D34_HI30:
case elfcpp::R_PPC64_D28:
case elfcpp::R_PPC64_PCREL34:
case elfcpp::R_PPC64_PCREL28:
- target->set_powerxx_stubs();
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
break;
case elfcpp::R_PPC64_TOC:
case elfcpp::R_PPC64_PLT_PCREL34:
case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
- target->set_powerxx_stubs();
- // Fall through.
case elfcpp::R_POWERPC_PLT16_LO:
case elfcpp::R_POWERPC_PLT16_HI:
case elfcpp::R_POWERPC_PLT16_HA:
break;
case elfcpp::R_PPC64_GOT_PCREL34:
- target->set_powerxx_stubs();
- // Fall through.
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_GOT16_LO:
case elfcpp::R_POWERPC_GOT16_HI:
target->got_section(symtab, layout);
break;
+ case elfcpp::R_PPC64_GOT_TLSGD34:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
}
break;
+ case elfcpp::R_PPC64_GOT_TLSLD34:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
}
break;
+ case elfcpp::R_PPC64_GOT_DTPREL34:
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
}
break;
+ case elfcpp::R_PPC64_GOT_TPREL34:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_DS:
ppc_object->set_has_small_toc_reloc();
+ break;
+ default:
+ break;
+ }
+
+ switch (r_type)
+ {
+ case elfcpp::R_POWERPC_TPREL16:
+ case elfcpp::R_POWERPC_TPREL16_LO:
+ case elfcpp::R_POWERPC_TPREL16_HI:
+ case elfcpp::R_POWERPC_TPREL16_HA:
+ case elfcpp::R_PPC64_TPREL16_DS:
+ case elfcpp::R_PPC64_TPREL16_LO_DS:
+ case elfcpp::R_PPC64_TPREL16_HIGH:
+ case elfcpp::R_PPC64_TPREL16_HIGHA:
+ case elfcpp::R_PPC64_TPREL16_HIGHER:
+ case elfcpp::R_PPC64_TPREL16_HIGHERA:
+ case elfcpp::R_PPC64_TPREL16_HIGHEST:
+ case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+ case elfcpp::R_PPC64_TPREL34:
+ layout->set_has_static_tls();
+ break;
+ default:
+ break;
+ }
+
+ switch (r_type)
+ {
+ case elfcpp::R_PPC64_D34:
+ case elfcpp::R_PPC64_D34_LO:
+ case elfcpp::R_PPC64_D34_HI30:
+ case elfcpp::R_PPC64_D34_HA30:
+ case elfcpp::R_PPC64_D28:
+ case elfcpp::R_PPC64_PCREL34:
+ case elfcpp::R_PPC64_PCREL28:
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
+ case elfcpp::R_PPC64_PLT_PCREL34:
+ case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
+ case elfcpp::R_PPC64_GOT_PCREL34:
+ case elfcpp::R_PPC64_GOT_TLSGD34:
+ case elfcpp::R_PPC64_GOT_TLSLD34:
+ case elfcpp::R_PPC64_GOT_DTPREL34:
+ case elfcpp::R_PPC64_GOT_TPREL34:
+ target->set_powerxx_stubs();
+ break;
default:
break;
}
return is_ordinary && shndx != elfcpp::SHN_UNDEF;
}
+// PCREL_OPT in one instance flags to the linker that a pair of insns:
+// pld ra,symbol@got@pcrel
+// load/store rt,0(ra)
+// or
+// pla ra,symbol@pcrel
+// load/store rt,0(ra)
+// may be translated to
+// pload/pstore rt,symbol@pcrel
+// nop.
+// This function returns true if the optimization is possible, placing
+// the prefix insn in *PINSN1 and a NOP in *PINSN2.
+//
+// On entry to this function, the linker has already determined that
+// the pld can be replaced with pla: *PINSN1 is that pla insn,
+// while *PINSN2 is the second instruction.
+
+inline bool
+xlate_pcrel_opt(uint64_t *pinsn1, uint64_t *pinsn2)
+{
+ uint32_t insn2 = *pinsn2 >> 32;
+ uint64_t i1new;
+
+ // Check that regs match.
+ if (((insn2 >> 16) & 31) != ((*pinsn1 >> 21) & 31))
+ return false;
+
+ switch ((insn2 >> 26) & 63)
+ {
+ default:
+ return false;
+
+ case 32: // lwz
+ case 34: // lbz
+ case 36: // stw
+ case 38: // stb
+ case 40: // lhz
+ case 42: // lha
+ case 44: // sth
+ case 48: // lfs
+ case 50: // lfd
+ case 52: // stfs
+ case 54: // stfd
+ // These are the PMLS cases, where we just need to tack a prefix
+ // on the insn. Check that the D field is zero.
+ if ((insn2 & 0xffff) != 0)
+ return false;
+ i1new = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+ | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+ break;
+
+ case 58: // lwa, ld
+ if ((insn2 & 0xfffd) != 0)
+ return false;
+ i1new = ((1ULL << 58) | (1ULL << 52)
+ | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26)
+ | (insn2 & (31ULL << 21)));
+ break;
+
+ case 57: // lxsd, lxssp
+ if ((insn2 & 0xfffc) != 0 || (insn2 & 3) < 2)
+ return false;
+ i1new = ((1ULL << 58) | (1ULL << 52)
+ | ((40ULL | (insn2 & 3)) << 26)
+ | (insn2 & (31ULL << 21)));
+ break;
+
+ case 61: // stxsd, stxssp, lxv, stxv
+ if ((insn2 & 3) == 0)
+ return false;
+ else if ((insn2 & 3) >= 2)
+ {
+ if ((insn2 & 0xfffc) != 0)
+ return false;
+ i1new = ((1ULL << 58) | (1ULL << 52)
+ | ((44ULL | (insn2 & 3)) << 26)
+ | (insn2 & (31ULL << 21)));
+ }
+ else
+ {
+ if ((insn2 & 0xfff0) != 0)
+ return false;
+ i1new = ((1ULL << 58) | (1ULL << 52)
+ | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26)
+ | (insn2 & (31ULL << 21)));
+ }
+ break;
+
+ case 56: // lq
+ if ((insn2 & 0xffff) != 0)
+ return false;
+ i1new = ((1ULL << 58) | (1ULL << 52)
+ | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+ break;
+
+ case 62: // std, stq
+ if ((insn2 & 0xfffd) != 0)
+ return false;
+ i1new = ((1ULL << 58) | (1ULL << 52)
+ | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26)
+ | (insn2 & (31ULL << 21)));
+ break;
+ }
+
+ *pinsn1 = i1new;
+ *pinsn2 = (uint64_t) nop << 32;
+ return true;
+}
+
// Perform a relocation.
template<int size, bool big_endian>
else if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HI
- || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HA)
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HA
+ || r_type == elfcpp::R_PPC64_GOT_TLSGD34)
{
// First instruction of a global dynamic sequence, arg setup insn.
const bool final = gsym == NULL || gsym->final_value_is_known();
gold_assert(object->local_has_got_offset(r_sym, got_type));
value = object->local_got_offset(r_sym, got_type);
}
- value -= target->got_section()->got_base_offset(object);
+ if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
+ value += target->got_section()->address();
+ else
+ value -= target->got_section()->got_base_offset(object);
}
if (tls_type == tls::TLSOPT_TO_IE)
{
- if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
- || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+ if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
{
- Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
- Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
- insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
- if (size == 32)
- insn |= 32 << 26; // lwz
- else
- insn |= 58 << 26; // ld
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ // pla -> pld
+ pinsn += (-2ULL << 56) + (57ULL << 26) - (14ULL << 26);
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ pinsn & 0xffffffff);
+ r_type = elfcpp::R_PPC64_GOT_TPREL34;
+ }
+ else
+ {
+ if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
+ if (size == 32)
+ insn |= 32 << 26; // lwz
+ else
+ insn |= 58 << 26; // ld
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ }
+ r_type += (elfcpp::R_POWERPC_GOT_TPREL16
+ - elfcpp::R_POWERPC_GOT_TLSGD16);
}
- r_type += (elfcpp::R_POWERPC_GOT_TPREL16
- - elfcpp::R_POWERPC_GOT_TLSGD16);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
- if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
- || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+ if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
{
- Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
- Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
- insn &= (1 << 26) - (1 << 21); // extract rt
- if (size == 32)
- insn |= addis_0_2;
- else
- insn |= addis_0_13;
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
- r_type = elfcpp::R_POWERPC_TPREL16_HA;
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ // pla pcrel -> paddi r13
+ pinsn += (-1ULL << 52) + (13ULL << 16);
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ pinsn & 0xffffffff);
+ r_type = elfcpp::R_PPC64_TPREL34;
value = psymval->value(object, rela.get_r_addend());
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
- Insn insn = nop;
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
- r_type = elfcpp::R_POWERPC_NONE;
+ if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= (1 << 26) - (1 << 21); // extract rt
+ if (size == 32)
+ insn |= addis_0_2;
+ else
+ insn |= addis_0_13;
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ r_type = elfcpp::R_POWERPC_TPREL16_HA;
+ value = psymval->value(object, rela.get_r_addend());
+ }
+ else
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = nop;
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ r_type = elfcpp::R_POWERPC_NONE;
+ }
}
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HI
- || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HA)
+ || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HA
+ || r_type == elfcpp::R_PPC64_GOT_TLSLD34)
{
// First instruction of a local dynamic sequence, arg setup insn.
const tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
{
value = target->tlsld_got_offset();
- value -= target->got_section()->got_base_offset(object);
+ if (r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+ value += target->got_section()->address();
+ else
+ value -= target->got_section()->got_base_offset(object);
}
else
{
gold_assert(tls_type == tls::TLSOPT_TO_LE);
- if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
- || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
+ if (r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ // pla pcrel -> paddi r13
+ pinsn += (-1ULL << 52) + (13ULL << 16);
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ pinsn & 0xffffffff);
+ r_type = elfcpp::R_PPC64_TPREL34;
+ value = dtp_offset;
+ }
+ else if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
+ || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
{
Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
else if (r_type == elfcpp::R_POWERPC_GOT_DTPREL16
|| r_type == elfcpp::R_POWERPC_GOT_DTPREL16_LO
|| r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HI
- || r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HA)
+ || r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HA
+ || r_type == elfcpp::R_PPC64_GOT_DTPREL34)
{
// Accesses relative to a local dynamic sequence address,
// no optimisation here.
gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_DTPREL));
value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL);
}
- value -= target->got_section()->got_base_offset(object);
+ if (r_type == elfcpp::R_PPC64_GOT_DTPREL34)
+ value += target->got_section()->address();
+ else
+ value -= target->got_section()->got_base_offset(object);
}
else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_HI
- || r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA)
+ || r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA
+ || r_type == elfcpp::R_PPC64_GOT_TPREL34)
{
// First instruction of initial exec sequence.
const bool final = gsym == NULL || gsym->final_value_is_known();
gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_TPREL));
value = object->local_got_offset(r_sym, GOT_TYPE_TPREL);
}
- value -= target->got_section()->got_base_offset(object);
+ if (r_type == elfcpp::R_PPC64_GOT_TPREL34)
+ value += target->got_section()->address();
+ else
+ value -= target->got_section()->got_base_offset(object);
}
else
{
gold_assert(tls_type == tls::TLSOPT_TO_LE);
- if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
- || r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
+ if (r_type == elfcpp::R_PPC64_GOT_TPREL34)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ // pld ra,sym@got@tprel@pcrel -> paddi ra,r13,sym@tprel
+ pinsn += ((2ULL << 56) + (-1ULL << 52)
+ + (14ULL << 26) - (57ULL << 26) + (13ULL << 16));
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ pinsn & 0xffffffff);
+ r_type = elfcpp::R_PPC64_TPREL34;
+ value = psymval->value(object, rela.get_r_addend());
+ }
+ else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
+ || r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
{
Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
}
else
{
+ bool is_pcrel = false;
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+ size_t reloc_count = shdr.get_sh_size() / reloc_size;
+ if (relnum < reloc_count - 1)
+ {
+ Reltype next_rela(preloc + reloc_size);
+ unsigned int r_type2
+ = elfcpp::elf_r_type<size>(next_rela.get_r_info());
+ if ((r_type2 == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
+ && next_rela.get_r_offset() == rela.get_r_offset())
+ is_pcrel = true;
+ }
Insn* iview = reinterpret_cast<Insn*>(view);
- Insn insn = addi_3_3;
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
- r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += d_offset;
- value = psymval->value(object, rela.get_r_addend());
+ if (is_pcrel)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ r_type = elfcpp::R_POWERPC_NONE;
+ }
+ else
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, addi_3_3);
+ r_type = elfcpp::R_POWERPC_TPREL16_LO;
+ view += d_offset;
+ value = psymval->value(object, rela.get_r_addend());
+ }
}
this->skip_next_tls_get_addr_call();
}
const tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_TO_LE)
{
+ bool is_pcrel = false;
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+ size_t reloc_count = shdr.get_sh_size() / reloc_size;
+ if (relnum < reloc_count - 1)
+ {
+ Reltype next_rela(preloc + reloc_size);
+ unsigned int r_type2
+ = elfcpp::elf_r_type<size>(next_rela.get_r_info());
+ if ((r_type2 == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
+ && next_rela.get_r_offset() == rela.get_r_offset())
+ is_pcrel = true;
+ }
Insn* iview = reinterpret_cast<Insn*>(view);
- Insn insn = addi_3_3;
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ if (is_pcrel)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ r_type = elfcpp::R_POWERPC_NONE;
+ }
+ else
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, addi_3_3);
+ r_type = elfcpp::R_POWERPC_TPREL16_LO;
+ view += d_offset;
+ value = dtp_offset;
+ }
this->skip_next_tls_get_addr_call();
- r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += d_offset;
- value = dtp_offset;
}
}
else if (r_type == elfcpp::R_POWERPC_TLS)
const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_TO_LE)
{
- Insn* iview = reinterpret_cast<Insn*>(view);
+ Address roff = rela.get_r_offset() & 3;
+ Insn* iview = reinterpret_cast<Insn*>(view - roff);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
unsigned int reg = size == 32 ? 2 : 13;
insn = at_tls_transform(insn, reg);
gold_assert(insn != 0);
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
- r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += d_offset;
- value = psymval->value(object, rela.get_r_addend());
+ if (roff == 0)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ r_type = elfcpp::R_POWERPC_TPREL16_LO;
+ view += d_offset;
+ value = psymval->value(object, rela.get_r_addend());
+ }
+ else if (roff == 1)
+ {
+ // For pcrel IE to LE we already have the full offset
+ // and thus don't need an addi here. A nop or mr will do.
+ if ((insn & (0x3f << 26)) == 14 << 26)
+ {
+ // Extract regs from addi rt,ra,si.
+ unsigned int rt = (insn >> 21) & 0x1f;
+ unsigned int ra = (insn >> 16) & 0x1f;
+ if (rt == ra)
+ insn = nop;
+ else
+ {
+ // Build or ra,rs,rb with rb==rs, ie. mr ra,rs.
+ insn = (rt << 16) | (ra << 21) | (ra << 11);
+ insn |= (31u << 26) | (444u << 1);
+ }
+ }
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ r_type = elfcpp::R_POWERPC_NONE;
+ }
}
}
else if (!has_stub_value)
case elfcpp::R_PPC64_PLT_PCREL34:
case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
case elfcpp::R_PPC64_PCREL28:
+ case elfcpp::R_PPC64_GOT_TLSGD34:
+ case elfcpp::R_PPC64_GOT_TLSLD34:
+ case elfcpp::R_PPC64_GOT_TPREL34:
+ case elfcpp::R_PPC64_GOT_DTPREL34:
case elfcpp::R_PPC64_REL16_HIGHER34:
case elfcpp::R_PPC64_REL16_HIGHERA34:
case elfcpp::R_PPC64_REL16_HIGHEST34:
case elfcpp::R_PPC64_TPREL16_HIGHERA:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+ case elfcpp::R_PPC64_TPREL34:
// tls symbol values are relative to tls_segment()->vaddr()
value -= tp_offset;
break;
case elfcpp::R_POWERPC_DTPREL:
case elfcpp::R_PPC64_DTPREL16_HIGH:
case elfcpp::R_PPC64_DTPREL16_HIGHA:
+ case elfcpp::R_PPC64_DTPREL34:
// tls symbol values are relative to tls_segment()->vaddr()
value -= dtp_offset;
break;
? relative_value_is_known(gsym)
: relative_value_is_known(psymval)))
{
+ Insn* iview;
+ Insn* iview2;
+ Insn insn;
+ uint64_t pinsn, pinsn2;
+
switch (r_type)
{
default:
case elfcpp::R_PPC64_TOC16_HA:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
- Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ iview = reinterpret_cast<Insn*>(view - d_offset);
+ insn = elfcpp::Swap<32, big_endian>::readval(iview);
if ((r_type == elfcpp::R_PPC64_TOC16_HA
&& object->make_toc_relative(target, &value))
|| (r_type == elfcpp::R_POWERPC_GOT16_HA
case elfcpp::R_PPC64_TOC16_LO_DS:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
- Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ iview = reinterpret_cast<Insn*>(view - d_offset);
+ insn = elfcpp::Swap<32, big_endian>::readval(iview);
bool changed = false;
if ((r_type == elfcpp::R_PPC64_TOC16_LO_DS
&& object->make_toc_relative(target, &value))
case elfcpp::R_PPC64_GOT_PCREL34:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view);
- uint64_t insn = elfcpp::Swap<32, big_endian>::readval(iview);
- insn <<= 32;
- insn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
- if ((insn & ((-1ULL << 50) | (63ULL << 26)))
+ iview = reinterpret_cast<Insn*>(view);
+ pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
!= ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
break;
{
value = relval;
// Replace with paddi
- insn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
- elfcpp::Swap<32, big_endian>::writeval(iview, insn >> 32);
+ pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
elfcpp::Swap<32, big_endian>::writeval(iview + 1,
- insn & 0xffffffff);
+ pinsn & 0xffffffff);
+ goto pcrelopt;
}
}
break;
+ case elfcpp::R_PPC64_PCREL34:
+ {
+ iview = reinterpret_cast<Insn*>(view);
+ pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+ != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+ | (14ULL << 26) /* paddi */))
+ break;
+
+ pcrelopt:
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+ size_t reloc_count = shdr.get_sh_size() / reloc_size;
+ if (relnum >= reloc_count - 1)
+ break;
+
+ Reltype next_rela(preloc + reloc_size);
+ if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
+ != elfcpp::R_PPC64_PCREL_OPT)
+ || next_rela.get_r_offset() != rela.get_r_offset())
+ break;
+
+ Address off = next_rela.get_r_addend();
+ if (off == 0)
+ off = 8; // zero means next insn.
+ if (off + rela.get_r_offset() + 4 > view_size)
+ break;
+
+ iview2 = reinterpret_cast<Insn*>(view + off);
+ pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
+ pinsn2 <<= 32;
+ if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+ break;
+ if (xlate_pcrel_opt(&pinsn, &pinsn2))
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ pinsn & 0xffffffff);
+ elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
+ }
+ }
+ break;
+
case elfcpp::R_POWERPC_TPREL16_HA:
if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
{
case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
case elfcpp::R_PPC64_D28:
case elfcpp::R_PPC64_PCREL28:
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
+ case elfcpp::R_PPC64_GOT_TLSGD34:
+ case elfcpp::R_PPC64_GOT_TLSLD34:
+ case elfcpp::R_PPC64_GOT_TPREL34:
+ case elfcpp::R_PPC64_GOT_DTPREL34:
overflow = Reloc::CHECK_SIGNED;
break;
}
case elfcpp::R_PPC64_GOT_PCREL34:
case elfcpp::R_PPC64_PLT_PCREL34:
case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
+ case elfcpp::R_PPC64_TPREL34:
+ case elfcpp::R_PPC64_DTPREL34:
+ case elfcpp::R_PPC64_GOT_TLSGD34:
+ case elfcpp::R_PPC64_GOT_TLSLD34:
+ case elfcpp::R_PPC64_GOT_TPREL34:
+ case elfcpp::R_PPC64_GOT_DTPREL34:
if (size == 32)
goto unsupp;
status = Reloc::addr34(view, value, overflow);