// Return section and offset of function entry for .opd + R_OFF.
void
- get_opd_ent(Address r_off, unsigned int* shndx, Address* value)
+ get_opd_ent(Address r_off, unsigned int* shndx, Address* value) const
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_shndx_.size());
const unsigned char* plocal_symbols,
Relocatable_relocs*);
- // Relocate a section during a relocatable link.
+ // Emit relocations for a section.
void
- relocate_for_relocatable(const Relocate_info<size, big_endian>*,
- unsigned int sh_type,
- const unsigned char* prelocs,
- size_t reloc_count,
- Output_section* output_section,
- off_t offset_in_output_section,
- const Relocatable_relocs*,
- unsigned char*,
- Address view_address,
- section_size_type,
- unsigned char* reloc_view,
- section_size_type reloc_view_size);
+ relocate_relocs(const Relocate_info<size, big_endian>*,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ off_t offset_in_output_section,
+ const Relocatable_relocs*,
+ unsigned char*,
+ Address view_address,
+ section_size_type,
+ unsigned char* reloc_view,
+ section_size_type reloc_view_size);
// Return whether SYM is defined by the ABI.
bool
bool issued_non_pic_error_;
};
+ Address
+ symval_for_branch(Address value, const Sized_symbol<size>* gsym,
+ Powerpc_relobj<size, big_endian>* object,
+ unsigned int *dest_shndx);
+
// The class which implements relocation.
class Relocate
{
class Powerpc_relocate_functions
{
public:
- enum overflow_check
+ enum Overflow_check
{
- check_none,
- check_signed,
- check_bitfield
+ CHECK_NONE,
+ CHECK_SIGNED,
+ CHECK_BITFIELD
};
- enum overflow_status
+ enum Status
{
- status_ok,
- status_overflow
+ STATUS_OK,
+ STATUS_OVERFLOW
};
private:
}
template<int valsize>
- static inline enum overflow_status
- overflowed(Address value, enum overflow_check overflow)
+ static inline Status
+ overflowed(Address value, Overflow_check overflow)
{
- if (overflow == check_signed)
+ if (overflow == CHECK_SIGNED)
{
if (has_overflow_signed<valsize>(value))
- return status_overflow;
+ return STATUS_OVERFLOW;
}
- else if (overflow == check_bitfield)
+ else if (overflow == CHECK_BITFIELD)
{
if (has_overflow_bitfield<valsize>(value))
- return status_overflow;
+ return STATUS_OVERFLOW;
}
- return status_ok;
+ return STATUS_OK;
}
// Do a simple RELA relocation
template<int valsize>
- static inline enum overflow_status
- rela(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ rela(unsigned char* view, Address value, Overflow_check overflow)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
}
template<int valsize>
- static inline enum overflow_status
+ static inline Status
rela(unsigned char* view,
unsigned int right_shift,
typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
Address value,
- enum overflow_check overflow)
+ Overflow_check overflow)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
// Do a simple RELA relocation, unaligned.
template<int valsize>
- static inline enum overflow_status
- rela_ua(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ rela_ua(unsigned char* view, Address value, Overflow_check overflow)
{
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, value);
return overflowed<valsize>(value, overflow);
}
template<int valsize>
- static inline enum overflow_status
+ static inline Status
rela_ua(unsigned char* view,
unsigned int right_shift,
typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
Address value,
- enum overflow_check overflow)
+ Overflow_check overflow)
{
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
Valtype;
// R_PPC64_ADDR64: (Symbol + Addend)
static inline void
addr64(unsigned char* view, Address value)
- { This::template rela<64>(view, value, check_none); }
+ { This::template rela<64>(view, value, CHECK_NONE); }
// R_PPC64_UADDR64: (Symbol + Addend) unaligned
static inline void
addr64_u(unsigned char* view, Address value)
- { This::template rela_ua<64>(view, value, check_none); }
+ { This::template rela_ua<64>(view, value, CHECK_NONE); }
// R_POWERPC_ADDR32: (Symbol + Addend)
- static inline enum overflow_status
- addr32(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr32(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela<32>(view, value, overflow); }
// R_POWERPC_UADDR32: (Symbol + Addend) unaligned
- static inline enum overflow_status
- addr32_u(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr32_u(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela_ua<32>(view, value, overflow); }
// R_POWERPC_ADDR24: (Symbol + Addend) & 0x3fffffc
- static inline enum overflow_status
- addr24(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr24(unsigned char* view, Address value, Overflow_check overflow)
{
- enum overflow_status stat
- = This::template rela<32>(view, 0, 0x03fffffc, value, overflow);
- if (overflow != check_none && (value & 3) != 0)
- stat = status_overflow;
+ Status stat = This::template rela<32>(view, 0, 0x03fffffc, value, overflow);
+ if (overflow != CHECK_NONE && (value & 3) != 0)
+ stat = STATUS_OVERFLOW;
return stat;
}
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff
- static inline enum overflow_status
- addr16(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr16(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela<16>(view, value, overflow); }
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff, unaligned
- static inline enum overflow_status
- addr16_u(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr16_u(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela_ua<16>(view, value, overflow); }
// R_POWERPC_ADDR16_DS: (Symbol + Addend) & 0xfffc
- static inline enum overflow_status
- addr16_ds(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr16_ds(unsigned char* view, Address value, Overflow_check overflow)
{
- enum overflow_status stat
- = This::template rela<16>(view, 0, 0xfffc, value, overflow);
- if (overflow != check_none && (value & 3) != 0)
- stat = status_overflow;
+ Status stat = This::template rela<16>(view, 0, 0xfffc, value, overflow);
+ if (overflow != CHECK_NONE && (value & 3) != 0)
+ stat = STATUS_OVERFLOW;
return stat;
}
// R_POWERPC_ADDR16_HI: ((Symbol + Addend) >> 16) & 0xffff
static inline void
addr16_hi(unsigned char* view, Address value)
- { This::template rela<16>(view, 16, 0xffff, value, check_none); }
+ { This::template rela<16>(view, 16, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff
static inline void
// R_POWERPC_ADDR16_HIGHER: ((Symbol + Addend) >> 32) & 0xffff
static inline void
addr16_hi2(unsigned char* view, Address value)
- { This::template rela<16>(view, 32, 0xffff, value, check_none); }
+ { This::template rela<16>(view, 32, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHERA: ((Symbol + Addend + 0x8000) >> 32) & 0xffff
static inline void
// R_POWERPC_ADDR16_HIGHEST: ((Symbol + Addend) >> 48) & 0xffff
static inline void
addr16_hi3(unsigned char* view, Address value)
- { This::template rela<16>(view, 48, 0xffff, value, check_none); }
+ { This::template rela<16>(view, 48, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHESTA: ((Symbol + Addend + 0x8000) >> 48) & 0xffff
static inline void
{ This::addr16_hi3(view, value + 0x8000); }
// R_POWERPC_ADDR14: (Symbol + Addend) & 0xfffc
- static inline enum overflow_status
- addr14(unsigned char* view, Address value, enum overflow_check overflow)
+ static inline Status
+ addr14(unsigned char* view, Address value, Overflow_check overflow)
{
- enum overflow_status stat
- = This::template rela<32>(view, 0, 0xfffc, value, overflow);
- if (overflow != check_none && (value & 3) != 0)
- stat = status_overflow;
+ Status stat = This::template rela<32>(view, 0, 0xfffc, value, overflow);
+ if (overflow != CHECK_NONE && (value & 3) != 0)
+ stat = STATUS_OVERFLOW;
return stat;
}
};
off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
{
int et = ehdr.get_e_type();
- if (et == elfcpp::ET_REL)
+ // ET_EXEC files are valid input for --just-symbols/-R,
+ // and we treat them as relocatable objects.
+ if (et == elfcpp::ET_REL
+ || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
{
Powerpc_relobj<size, big_endian>* obj =
new Powerpc_relobj<size, big_endian>(name, input_file, offset, ehdr);
// and 64-bit powerpc.
switch (r_type)
{
+ case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_RELATIVE:
case elfcpp::R_POWERPC_GLOB_DAT:
case elfcpp::R_POWERPC_DTPMOD:
case elfcpp::R_POWERPC_TPREL:
case elfcpp::R_POWERPC_JMP_SLOT:
case elfcpp::R_POWERPC_COPY:
+ case elfcpp::R_POWERPC_IRELATIVE:
case elfcpp::R_POWERPC_ADDR32:
+ case elfcpp::R_POWERPC_UADDR32:
case elfcpp::R_POWERPC_ADDR24:
+ case elfcpp::R_POWERPC_ADDR16:
+ case elfcpp::R_POWERPC_UADDR16:
+ case elfcpp::R_POWERPC_ADDR16_LO:
+ case elfcpp::R_POWERPC_ADDR16_HI:
+ case elfcpp::R_POWERPC_ADDR16_HA:
+ case elfcpp::R_POWERPC_ADDR14:
+ 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:
+ case elfcpp::R_POWERPC_TPREL16_HA:
return;
default:
{
// These are the relocation types supported only on 64-bit.
case elfcpp::R_PPC64_ADDR64:
- case elfcpp::R_PPC64_TPREL16_LO_DS:
- case elfcpp::R_PPC64_TPREL16_DS:
- 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_HIGHER:
- case elfcpp::R_PPC64_TPREL16_HIGHEST:
- case elfcpp::R_PPC64_TPREL16_HIGHERA:
- case elfcpp::R_PPC64_TPREL16_HIGHESTA:
- case elfcpp::R_PPC64_ADDR16_LO_DS:
- case elfcpp::R_POWERPC_ADDR16_LO:
- case elfcpp::R_POWERPC_ADDR16_HI:
- case elfcpp::R_POWERPC_ADDR16_HA:
- case elfcpp::R_POWERPC_ADDR30:
case elfcpp::R_PPC64_UADDR64:
- case elfcpp::R_POWERPC_UADDR32:
- case elfcpp::R_POWERPC_ADDR16:
- case elfcpp::R_POWERPC_UADDR16:
+ case elfcpp::R_PPC64_JMP_IREL:
case elfcpp::R_PPC64_ADDR16_DS:
+ case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
- case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
- case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
- case elfcpp::R_POWERPC_REL32:
case elfcpp::R_PPC64_REL64:
+ case elfcpp::R_POWERPC_ADDR30:
+ case elfcpp::R_PPC64_TPREL16_DS:
+ case elfcpp::R_PPC64_TPREL16_LO_DS:
+ case elfcpp::R_PPC64_TPREL16_HIGHER:
+ case elfcpp::R_PPC64_TPREL16_HIGHEST:
+ case elfcpp::R_PPC64_TPREL16_HIGHERA:
+ case elfcpp::R_PPC64_TPREL16_HIGHESTA:
return;
default:
switch (r_type)
{
// These are the relocation types supported only on 32-bit.
+ // ??? glibc ld.so doesn't need to support these.
+ case elfcpp::R_POWERPC_DTPREL16:
+ case elfcpp::R_POWERPC_DTPREL16_LO:
+ case elfcpp::R_POWERPC_DTPREL16_HI:
+ case elfcpp::R_POWERPC_DTPREL16_HA:
+ return;
default:
break;
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC64_TOCSAVE:
case elfcpp::R_PPC_EMB_MRKREF:
+ case elfcpp::R_POWERPC_TLS:
break;
case elfcpp::R_PPC64_TOC:
}
break;
+ case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_LOCAL24PC:
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
+ if (parameters->options().emit_relocs())
+ {
+ Output_section* os = layout->tls_segment()->first_section();
+ gold_assert(os != NULL);
+ os->set_needs_symtab_index();
+ }
}
else
gold_unreachable();
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_PPC_EMB_MRKREF:
+ case elfcpp::R_POWERPC_TLS:
break;
case elfcpp::R_PPC64_TOC:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL24:
- {
- if (gsym->needs_plt_entry()
- || (!gsym->final_value_is_known()
- && !(gsym->is_defined()
- && !gsym->is_from_dynobj()
- && !gsym->is_preemptible())))
- target->make_plt_entry(layout, gsym, reloc, object);
- // Make a dynamic relocation if necessary.
- if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type)))
- {
- if (gsym->may_need_copy_reloc())
- {
- target->copy_reloc(symtab, layout, object,
- data_shndx, output_section, gsym,
- reloc);
- }
- else
- {
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- check_non_pic(object, r_type);
- rela_dyn->add_global(gsym, r_type, output_section, object,
- data_shndx, reloc.get_r_offset(),
- reloc.get_r_addend());
- }
- }
- }
- break;
+ if (gsym->needs_plt_entry()
+ || (!gsym->final_value_is_known()
+ && (gsym->is_undefined()
+ || gsym->is_from_dynobj()
+ || gsym->is_preemptible())))
+ target->make_plt_entry(layout, gsym, reloc, object);
+ // Fall thru
+ case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL32:
+ // Make a dynamic relocation if necessary.
+ if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type)))
+ {
+ if (gsym->may_need_copy_reloc())
+ {
+ target->copy_reloc(symtab, layout, object,
+ data_shndx, output_section, gsym,
+ reloc);
+ }
+ else
+ {
+ Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ check_non_pic(object, r_type);
+ rela_dyn->add_global(gsym, r_type, output_section, object,
+ data_shndx, reloc.get_r_offset(),
+ reloc.get_r_addend());
+ }
+ }
+ break;
+
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
+ if (parameters->options().emit_relocs())
+ {
+ Output_section* os = layout->tls_segment()->first_section();
+ gold_assert(os != NULL);
+ os->set_needs_symtab_index();
+ }
}
else
gold_unreachable();
this->copy_relocs_.emit(this->rela_dyn_section(layout));
}
+// Return the value to use for a branch relocation.
+
+template<int size, bool big_endian>
+typename elfcpp::Elf_types<size>::Elf_Addr
+Target_powerpc<size, big_endian>::symval_for_branch(
+ Address value,
+ const Sized_symbol<size>* gsym,
+ Powerpc_relobj<size, big_endian>* object,
+ unsigned int *dest_shndx)
+{
+ *dest_shndx = 0;
+ if (size == 32)
+ return value;
+
+ // If the symbol is defined in an opd section, ie. is a function
+ // descriptor, use the function descriptor code entry address
+ Powerpc_relobj<size, big_endian>* symobj = object;
+ if (gsym != NULL)
+ symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
+ unsigned int shndx = symobj->opd_shndx();
+ if (shndx == 0)
+ return value;
+ Address opd_addr = symobj->get_output_section_offset(shndx);
+ gold_assert(opd_addr != invalid_address);
+ opd_addr += symobj->output_section(shndx)->address();
+ if (value >= opd_addr && value < opd_addr + symobj->section_size(shndx))
+ {
+ Address sec_off;
+ symobj->get_opd_ent(value - opd_addr, dest_shndx, &sec_off);
+ Address sec_addr = symobj->get_output_section_offset(*dest_shndx);
+ gold_assert(sec_addr != invalid_address);
+ sec_addr += symobj->output_section(*dest_shndx)->address();
+ value = sec_addr + sec_off;
+ }
+ return value;
+}
+
// Perform a relocation.
template<int size, bool big_endian>
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
- const Powerpc_relobj<size, big_endian>* const object
- = static_cast<const Powerpc_relobj<size, big_endian>*>(relinfo->object);
+ Powerpc_relobj<size, big_endian>* const object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
bool has_plt_value = false;
if (gsym != NULL
bool can_plt_call = false;
if (rela.get_r_offset() + 8 <= view_size)
{
+ Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
Valtype insn2 = elfcpp::Swap<32, big_endian>::readval(wv + 1);
- if (insn2 == nop
- || insn2 == cror_15_15_15 || insn2 == cror_31_31_31)
+ if ((insn & 1) != 0
+ && (insn2 == nop
+ || insn2 == cror_15_15_15 || insn2 == cror_31_31_31))
{
elfcpp::Swap<32, big_endian>::writeval(wv + 1, ld_2_1 + 40);
can_plt_call = true;
}
}
if (!can_plt_call)
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("call lacks nop, can't restore toc"));
+ {
+ // If we don't have a branch and link followed by a nop,
+ // we can't go via the plt because there is no place to
+ // put a toc restoring instruction.
+ // Unless we know we won't be returning.
+ if (strcmp(gsym->name(), "__libc_start_main") == 0)
+ can_plt_call = true;
+ }
+ if (!can_plt_call)
+ {
+ // This is not an error in one special case: A self
+ // call. It isn't possible to cheaply verify we have
+ // such a call so just check for a call to the same
+ // section.
+ bool ok = false;
+ if (gsym->source() == Symbol::FROM_OBJECT
+ && gsym->object() == object)
+ {
+ Address addend = rela.get_r_addend();
+ unsigned int dest_shndx;
+ value = psymval->value(object, addend);
+ value = target->symval_for_branch(value, gsym, object,
+ &dest_shndx);
+ bool is_ordinary;
+ if (dest_shndx == 0)
+ dest_shndx = gsym->shndx(&is_ordinary);
+ ok = dest_shndx == relinfo->data_shndx;
+ }
+ if (!ok)
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("call lacks nop, can't restore toc; "
+ "recompile with -fPIC"));
+ }
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
insn = addis_3_2;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_HA;
- value = relinfo->layout->tls_segment()->vaddr() + dtp_offset;
+ value = dtp_offset;
}
else
{
this->call_tls_get_addr_ = CALL_SKIP;
r_type = elfcpp::R_POWERPC_TPREL16_LO;
view += 2 * big_endian;
- value = relinfo->layout->tls_segment()->vaddr() + dtp_offset;
+ value = dtp_offset;
}
}
else if (r_type == elfcpp::R_POWERPC_TLS)
else
{
Address addend = 0;
+ unsigned int dest_shndx;
if (r_type != elfcpp::R_PPC_PLTREL24)
addend = rela.get_r_addend();
if (size == 64 || !has_plt_value)
value = psymval->value(object, addend);
if (size == 64 && is_branch_reloc(r_type))
- {
- // If the symbol is defined in an opd section, ie. is a function
- // descriptor, use the function descriptor code entry address
- Powerpc_relobj<size, big_endian>* symobj = const_cast
- <Powerpc_relobj<size, big_endian>*>(object);
- if (gsym != NULL)
- symobj = static_cast
- <Powerpc_relobj<size, big_endian>*>(gsym->object());
- unsigned int shndx = symobj->opd_shndx();
- Address opd_addr = symobj->get_output_section_offset(shndx);
- gold_assert(opd_addr != invalid_address);
- opd_addr += symobj->output_section(shndx)->address();
- if (value >= opd_addr
- && value < opd_addr + symobj->section_size(shndx))
- {
- Address sec_off;
- symobj->get_opd_ent(value - opd_addr, &shndx, &sec_off);
- Address sec_addr = symobj->get_output_section_offset(shndx);
- gold_assert(sec_addr != invalid_address);
- sec_addr += symobj->output_section(shndx)->address();
- value = sec_addr + sec_off;
- }
- }
+ value = target->symval_for_branch(value, gsym, object, &dest_shndx);
}
switch (r_type)
break;
}
- enum Reloc::overflow_check overflow = Reloc::check_none;
+ typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE;
switch (r_type)
{
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
if (size == 64)
- overflow = Reloc::check_bitfield;
+ overflow = Reloc::CHECK_BITFIELD;
break;
case elfcpp::R_POWERPC_REL32:
if (size == 64)
- overflow = Reloc::check_signed;
+ overflow = Reloc::CHECK_SIGNED;
break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
- overflow = Reloc::check_bitfield;
+ overflow = Reloc::CHECK_BITFIELD;
break;
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16:
- overflow = Reloc::check_signed;
+ overflow = Reloc::CHECK_SIGNED;
break;
}
+ typename Powerpc_relocate_functions<size, big_endian>::Status status
+ = Powerpc_relocate_functions<size, big_endian>::STATUS_OK;
switch (r_type)
{
case elfcpp::R_POWERPC_NONE:
if (size == 64)
Reloc::addr64(view, value);
else
- Reloc::addr32(view, value, overflow);
+ status = Reloc::addr32(view, value, overflow);
break;
case elfcpp::R_PPC64_UADDR64:
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_REL32:
- Reloc::addr32(view, value, overflow);
+ status = Reloc::addr32(view, value, overflow);
break;
case elfcpp::R_POWERPC_UADDR32:
- Reloc::addr32_u(view, value, overflow);
+ status = Reloc::addr32_u(view, value, overflow);
break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
- Reloc::addr24(view, value, overflow);
+ status = Reloc::addr24(view, value, overflow);
break;
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
if (size == 64)
{
- Reloc::addr16_ds(view, value, overflow);
+ status = Reloc::addr16_ds(view, value, overflow);
break;
}
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
- Reloc::addr16(view, value, overflow);
+ status = Reloc::addr16(view, value, overflow);
break;
case elfcpp::R_POWERPC_UADDR16:
- Reloc::addr16_u(view, value, overflow);
+ status = Reloc::addr16_u(view, value, overflow);
break;
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_PPC64_GOT16_LO_DS:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
- Reloc::addr16_ds(view, value, overflow);
+ status = Reloc::addr16_ds(view, value, overflow);
break;
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
- Reloc::addr14(view, value, overflow);
+ status = Reloc::addr14(view, value, overflow);
break;
case elfcpp::R_POWERPC_COPY:
r_type);
break;
}
+ if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK)
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow"));
return true;
}
rr);
}
-// Relocate a section during a relocatable link.
+// Emit relocations for a section.
// This is a modified version of the function by the same name in
// target-reloc.h. Using relocate_special_relocatable for
// R_PPC_PLTREL24 would require duplication of the entire body of the
template<int size, bool big_endian>
void
-Target_powerpc<size, big_endian>::relocate_for_relocatable(
+Target_powerpc<size, big_endian>::relocate_relocs(
const Relocate_info<size, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
}
unsigned char* pwrite = reloc_view;
-
+ bool zap_next = false;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
Relocatable_relocs::Reloc_strategy strategy = rr->strategy(i);
Reltype reloc(prelocs);
Reltype_write reloc_write(pwrite);
+ Address offset = reloc.get_r_offset();
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
- const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
- const unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+ unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+ const unsigned int orig_r_sym = r_sym;
+ typename elfcpp::Elf_types<size>::Elf_Swxword addend
+ = reloc.get_r_addend();
+ const Symbol* gsym = NULL;
+
+ if (zap_next)
+ {
+ // We could arrange to discard these and other relocs for
+ // tls optimised sequences in the strategy methods, but for
+ // now do as BFD ld does.
+ r_type = elfcpp::R_POWERPC_NONE;
+ zap_next = false;
+ }
// Get the new symbol index.
-
- unsigned int new_symndx;
if (r_sym < local_count)
{
switch (strategy)
{
case Relocatable_relocs::RELOC_COPY:
case Relocatable_relocs::RELOC_SPECIAL:
- if (r_sym == 0)
- new_symndx = 0;
- else
+ if (r_sym != 0)
{
- new_symndx = object->symtab_index(r_sym);
- gold_assert(new_symndx != -1U);
+ r_sym = object->symtab_index(r_sym);
+ gold_assert(r_sym != -1U);
}
break;
Output_section* os = object->output_section(shndx);
gold_assert(os != NULL);
gold_assert(os->needs_symtab_index());
- new_symndx = os->symtab_index();
+ r_sym = os->symtab_index();
}
break;
}
else
{
- const Symbol* gsym = object->global_symbol(r_sym);
+ gsym = object->global_symbol(r_sym);
gold_assert(gsym != NULL);
if (gsym->is_forwarder())
gsym = relinfo->symtab->resolve_forwards(gsym);
gold_assert(gsym->has_symtab_index());
- new_symndx = gsym->symtab_index();
+ r_sym = gsym->symtab_index();
}
// Get the new offset--the location in the output section where
// this relocation should be applied.
-
- Address offset = reloc.get_r_offset();
- Address new_offset;
if (static_cast<Address>(offset_in_output_section) != invalid_address)
- new_offset = offset + offset_in_output_section;
+ offset += offset_in_output_section;
else
{
section_offset_type sot_offset =
output_section->output_offset(object, relinfo->data_shndx,
sot_offset);
gold_assert(new_sot_offset != -1);
- new_offset = new_sot_offset;
+ offset = new_sot_offset;
}
// In an object file, r_offset is an offset within the section.
// In an executable or dynamic object, generated by
// --emit-relocs, r_offset is an absolute address.
- // FIXME: Arrange to call this function for --emit-relocs too,
- // so that we can make emitted relocs match edited TLS code.
- if (0 && !parameters->options().relocatable())
+ if (!parameters->options().relocatable())
{
- new_offset += view_address;
+ offset += view_address;
if (static_cast<Address>(offset_in_output_section) != invalid_address)
- new_offset -= offset_in_output_section;
+ offset -= offset_in_output_section;
}
- reloc_write.put_r_offset(new_offset);
- reloc_write.put_r_info(elfcpp::elf_r_info<size>(new_symndx, r_type));
-
// Handle the reloc addend based on the strategy.
- typename elfcpp::Elf_types<size>::Elf_Swxword addend;
- addend = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::
- get_reloc_addend(&reloc);
-
if (strategy == Relocatable_relocs::RELOC_COPY)
;
else if (strategy == Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA)
{
- const Symbol_value<size>* psymval = object->local_symbol(r_sym);
+ const Symbol_value<size>* psymval = object->local_symbol(orig_r_sym);
addend = psymval->value(object, addend);
}
else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
else
gold_unreachable();
- Reloc_types<elfcpp::SHT_RELA, size, big_endian>::
- set_reloc_addend(&reloc_write, addend);
+ if (!parameters->options().relocatable())
+ {
+ 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)
+ {
+ // First instruction of a global dynamic sequence,
+ // arg setup insn.
+ const bool final = gsym == NULL || gsym->final_value_is_known();
+ switch (this->optimize_tls_gd(final))
+ {
+ case tls::TLSOPT_TO_IE:
+ r_type += (elfcpp::R_POWERPC_GOT_TPREL16
+ - elfcpp::R_POWERPC_GOT_TLSGD16);
+ break;
+ case tls::TLSOPT_TO_LE:
+ if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+ r_type = elfcpp::R_POWERPC_TPREL16_HA;
+ else
+ {
+ r_type = elfcpp::R_POWERPC_NONE;
+ offset -= 2 * big_endian;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ 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)
+ {
+ // First instruction of a local dynamic sequence,
+ // arg setup insn.
+ if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+ {
+ if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
+ || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
+ {
+ r_type = elfcpp::R_POWERPC_TPREL16_HA;
+ const Output_section* os = relinfo->layout->tls_segment()
+ ->first_section();
+ gold_assert(os != NULL);
+ gold_assert(os->needs_symtab_index());
+ r_sym = os->symtab_index();
+ addend = dtp_offset;
+ }
+ else
+ {
+ r_type = elfcpp::R_POWERPC_NONE;
+ offset -= 2 * big_endian;
+ }
+ }
+ }
+ 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)
+ {
+ // First instruction of initial exec sequence.
+ const bool final = gsym == NULL || gsym->final_value_is_known();
+ if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
+ {
+ if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
+ || r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
+ r_type = elfcpp::R_POWERPC_TPREL16_HA;
+ else
+ {
+ r_type = elfcpp::R_POWERPC_NONE;
+ offset -= 2 * big_endian;
+ }
+ }
+ }
+ else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
+ || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
+ {
+ // Second instruction of a global dynamic sequence,
+ // the __tls_get_addr call
+ const bool final = gsym == NULL || gsym->final_value_is_known();
+ switch (this->optimize_tls_gd(final))
+ {
+ case tls::TLSOPT_TO_IE:
+ r_type = elfcpp::R_POWERPC_NONE;
+ zap_next = true;
+ break;
+ case tls::TLSOPT_TO_LE:
+ r_type = elfcpp::R_POWERPC_TPREL16_LO;
+ offset += 2 * big_endian;
+ zap_next = true;
+ break;
+ default:
+ break;
+ }
+ }
+ else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
+ || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
+ {
+ // Second instruction of a local dynamic sequence,
+ // the __tls_get_addr call
+ if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+ {
+ const Output_section* os = relinfo->layout->tls_segment()
+ ->first_section();
+ gold_assert(os != NULL);
+ gold_assert(os->needs_symtab_index());
+ r_sym = os->symtab_index();
+ addend = dtp_offset;
+ r_type = elfcpp::R_POWERPC_TPREL16_LO;
+ offset += 2 * big_endian;
+ zap_next = true;
+ }
+ }
+ else if (r_type == elfcpp::R_POWERPC_TLS)
+ {
+ // Second instruction of an initial exec sequence
+ const bool final = gsym == NULL || gsym->final_value_is_known();
+ if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
+ {
+ r_type = elfcpp::R_POWERPC_TPREL16_LO;
+ offset += 2 * big_endian;
+ }
+ }
+ }
+
+ reloc_write.put_r_offset(offset);
+ reloc_write.put_r_info(elfcpp::elf_r_info<size>(r_sym, r_type));
+ reloc_write.put_r_addend(addend);
pwrite += reloc_size;
}