void
do_calls_non_split(Relobj* object, unsigned int shndx,
section_offset_type fnoffset, section_size_type fnsize,
+ const unsigned char* prelocs, size_t reloc_count,
unsigned char* view, section_size_type view_size,
std::string* from, std::string* to) const;
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off
offset_in_output_section,
- const Relocatable_relocs*,
unsigned char*,
Address view_address,
section_size_type,
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
- relocate(const Relocate_info<size, big_endian>*, Target_powerpc*,
- Output_section*, size_t relnum,
- const elfcpp::Rela<size, big_endian>&,
- unsigned int r_type, const Sized_symbol<size>*,
- const Symbol_value<size>*,
- unsigned char*,
- typename elfcpp::Elf_types<size>::Elf_Addr,
+ relocate(const Relocate_info<size, big_endian>*, unsigned int,
+ Target_powerpc*, Output_section*, size_t, const unsigned char*,
+ const Sized_symbol<size>*, const Symbol_value<size>*,
+ unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
};
symtab_(symtab), layout_(layout),
header_ent_cnt_(size == 32 ? 3 : 1),
header_index_(size == 32 ? 0x2000 : 0)
- { }
+ {
+ if (size == 64)
+ this->set_addralign(256);
+ }
// Override all the Output_data_got methods we use so as to first call
// reserve_ent().
static const uint32_t add_0_11_11 = 0x7c0b5a14;
static const uint32_t add_2_2_11 = 0x7c425a14;
+static const uint32_t add_2_2_12 = 0x7c426214;
static const uint32_t add_3_3_2 = 0x7c631214;
static const uint32_t add_3_3_13 = 0x7c636a14;
static const uint32_t add_11_0_11 = 0x7d605a14;
static const uint32_t ld_2_1 = 0xe8410000;
static const uint32_t ld_2_2 = 0xe8420000;
static const uint32_t ld_2_11 = 0xe84b0000;
+static const uint32_t ld_2_12 = 0xe84c0000;
static const uint32_t ld_11_2 = 0xe9620000;
static const uint32_t ld_11_11 = 0xe96b0000;
static const uint32_t ld_12_2 = 0xe9820000;
static const uint32_t li_0_0 = 0x38000000;
static const uint32_t li_12_0 = 0x39800000;
static const uint32_t lis_0 = 0x3c000000;
+static const uint32_t lis_2 = 0x3c400000;
static const uint32_t lis_11 = 0x3d600000;
static const uint32_t lis_12 = 0x3d800000;
static const uint32_t lvx_0_12_0 = 0x7c0c00ce;
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
- // Absolute in GOT.
- ref = Symbol::ABSOLUTE_REF;
+ ref = Symbol::RELATIVE_REF;
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC64_TOCSAVE:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_ENTRY:
break;
case elfcpp::R_PPC64_TOC:
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_ENTRY:
break;
case elfcpp::R_PPC64_TOC:
unsigned int shndx,
section_offset_type fnoffset,
section_size_type fnsize,
+ const unsigned char* prelocs,
+ size_t reloc_count,
unsigned char* view,
section_size_type view_size,
std::string* from,
{
// warn
Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
- view, view_size, from, to);
+ prelocs, reloc_count, view, view_size,
+ from, to);
return;
}
inline bool
Target_powerpc<size, big_endian>::Relocate::relocate(
const Relocate_info<size, big_endian>* relinfo,
+ unsigned int,
Target_powerpc* target,
Output_section* os,
size_t relnum,
- const elfcpp::Rela<size, big_endian>& rela,
- unsigned int r_type,
+ const unsigned char* preloc,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
if (view == NULL)
return true;
+ const elfcpp::Rela<size, big_endian> rela(preloc);
+ unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
switch (this->maybe_skip_tls_get_addr_call(r_type, gsym))
{
case Track_tls::NOT_EXPECTED:
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
+ typedef typename Reloc_types<elfcpp::SHT_RELA,
+ size, big_endian>::Reloc Reltype;
+ // Offset from start of insn to d-field reloc.
+ const int d_offset = big_endian ? 2 : 0;
+
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ 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)
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ 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)
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ 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_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ 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)
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ 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_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 21); // extract rt from ld
if (size == 32)
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ 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;
Insn insn = addi_3_3;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = psymval->value(object, rela.get_r_addend());
}
this->skip_next_tls_get_addr_call();
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
this->skip_next_tls_get_addr_call();
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = dtp_offset;
}
}
gold_assert(insn != 0);
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = psymval->value(object, rela.get_r_addend());
}
}
case elfcpp::R_PPC64_TOC16_HA:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
if ((insn & ((0x3f << 26) | 0x1f << 16))
!= ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
case elfcpp::R_PPC64_TOC16_LO_DS:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
if (!ok_lo_toc_insn(insn))
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
}
}
break;
+
+ case elfcpp::R_PPC64_ENTRY:
+ value = (target->got_section()->output_section()->address()
+ + object->toc_base_offset());
+ if (value + 0x80008000 <= 0xffffffff
+ && !parameters->options().output_is_position_independent())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = lis_2 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ else
+ {
+ value -= address;
+ if (value + 0x80008000 <= 0xffffffff)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = addis_2_12 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ }
+ break;
+
+ case elfcpp::R_POWERPC_REL16_LO:
+ // If we are generating a non-PIC executable, edit
+ // 0: addis 2,12,.TOC.-0b@ha
+ // addi 2,2,.TOC.-0b@l
+ // used by ELFv2 global entry points to set up r2, to
+ // lis 2,.TOC.@ha
+ // addi 2,2,.TOC.@l
+ // if .TOC. is in range. */
+ if (value + address - 4 + 0x80008000 <= 0xffffffff
+ && relnum != 0
+ && preloc != NULL
+ && target->abiversion() >= 2
+ && !parameters->options().output_is_position_independent()
+ && gsym != NULL
+ && strcmp(gsym->name(), ".TOC.") == 0)
+ {
+ const int reloc_size
+ = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ Reltype prev_rela(preloc - reloc_size);
+ if ((prev_rela.get_r_info()
+ == elfcpp::elf_r_info<size>(r_sym,
+ elfcpp::R_POWERPC_REL16_HA))
+ && prev_rela.get_r_offset() + 4 == rela.get_r_offset()
+ && prev_rela.get_r_addend() + 4 == rela.get_r_addend())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview - 1);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ if ((insn1 & 0xffff0000) == addis_2_12
+ && (insn2 & 0xffff0000) == addi_2_2)
+ {
+ insn1 = lis_2 + ha(value + address - 4);
+ elfcpp::Swap<32, big_endian>::writeval(iview - 1, insn1);
+ insn2 = addi_2_2 + l(value + address - 4);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn2);
+ if (relinfo->rr)
+ {
+ relinfo->rr->set_strategy(relnum - 1,
+ Relocatable_relocs::RELOC_SPECIAL);
+ relinfo->rr->set_strategy(relnum,
+ Relocatable_relocs::RELOC_SPECIAL);
+ }
+ return true;
+ }
+ }
+ }
+ break;
}
}
break;
}
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = 0;
if (overflow == Reloc::CHECK_LOW_INSN
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
- const Relocatable_relocs* rr,
unsigned char*,
Address view_address,
section_size_type,
Reltype_write;
const int reloc_size
= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ // Offset from start of insn to d-field reloc.
+ const int d_offset = big_endian ? 2 : 0;
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
bool zap_next = false;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
- Relocatable_relocs::Reloc_strategy strategy = rr->strategy(i);
+ Relocatable_relocs::Reloc_strategy strategy = relinfo->rr->strategy(i);
if (strategy == Relocatable_relocs::RELOC_DISCARD)
continue;
}
else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
{
- if (addend >= 32768)
- addend += got2_addend;
+ if (size == 32)
+ {
+ if (addend >= 32768)
+ addend += got2_addend;
+ }
+ else if (r_type == elfcpp::R_POWERPC_REL16_HA)
+ {
+ r_type = elfcpp::R_POWERPC_ADDR16_HA;
+ addend -= d_offset;
+ }
+ else if (r_type == elfcpp::R_POWERPC_REL16_LO)
+ {
+ r_type = elfcpp::R_POWERPC_ADDR16_LO;
+ addend -= d_offset + 4;
+ }
}
else
gold_unreachable();
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
break;
default:
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
}
}
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
}
}
break;
case tls::TLSOPT_TO_LE:
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
zap_next = true;
break;
default:
r_sym = os->symtab_index();
addend = dtp_offset;
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
zap_next = true;
}
}
if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
{
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
}
}
}