diff options
author | Alan Modra <amodra@gmail.com> | 2013-11-15 10:36:34 +1030 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2013-11-15 10:36:34 +1030 |
commit | 9055360d4a69313949c3535ec065080cb814367d (patch) | |
tree | c3f9040bc8ea9f010a9399427cb3145175d939c8 /gold | |
parent | ef1bc9e72fd2f0310ac3113acc41e1c115e3ac79 (diff) | |
download | gdb-9055360d4a69313949c3535ec065080cb814367d.zip gdb-9055360d4a69313949c3535ec065080cb814367d.tar.gz gdb-9055360d4a69313949c3535ec065080cb814367d.tar.bz2 |
Fixes to powerpc64 gold ELFv2 support
* powerpc.cc (Target_powerpc::glink_section): Provide non-const
accessor.
(Target_powerpc::Branch_info::make_stub): Make global entry stubs.
Only call ppc64_local_entry_offset for 64-bit. Restrict
symval_for_branch lookup to ELFv1.
(Stub_table::add_plt_call_entry): Use unsigned int off.
(Output_data_glink::Address, invalid_address): New.
(Output_data_glink::add_eh_frame): Move out of line. Add
support for ELFv2.
(Output_data_glink::add_global_entry, find_global_entry,
global_entry_address): New functions.
(Output_data_glink::global_entry_stubs_, end_branch_table_,
ge_size): New variables.
(Output_data_glink::set_final_data_size): Add global entry
stub sizing.
(Output_data_glink::do_write): Write global entry stubs.
(Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Add target
parameter. Return true for ELFv2. Adjust callers.
(Target_powerpc::Scan::local, global): Restrict opd lookup to
ELFv1. Similarly for ifunc and dynamic relocation processing
specific to ELFv1. Recognize that symbols are defined on
their plt entries for ELFv2.
(Target_powerpc::symval_for_branch): Assert if called for
ELFv2 or ppc32.
(Target_powerpc::Relocate::relocate): Use global entry plt
stub for symbol value if such exists on ELFv2.
(Target_powerpc::Relocate::relocate): Don't call
symval_for_branch when ELFv2. Do adjust for local entry
offset when ELFv2.
(Target_powerpc::do_dynsym_value): Set symbols to global entry
plt stub for ELFv2.
(Target_powerpc::do_plt_address_for_global): Similarly.
Diffstat (limited to 'gold')
-rw-r--r-- | gold/ChangeLog | 35 | ||||
-rw-r--r-- | gold/powerpc.cc | 469 |
2 files changed, 350 insertions, 154 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index d953d70..14e2824 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,38 @@ +2013-11-15 Alan Modra <amodra@gmail.com> + + * powerpc.cc (Target_powerpc::glink_section): Provide non-const + accessor. + (Target_powerpc::Branch_info::make_stub): Make global entry stubs. + Only call ppc64_local_entry_offset for 64-bit. Restrict + symval_for_branch lookup to ELFv1. + (Stub_table::add_plt_call_entry): Use unsigned int off. + (Output_data_glink::Address, invalid_address): New. + (Output_data_glink::add_eh_frame): Move out of line. Add + support for ELFv2. + (Output_data_glink::add_global_entry, find_global_entry, + global_entry_address): New functions. + (Output_data_glink::global_entry_stubs_, end_branch_table_, + ge_size): New variables. + (Output_data_glink::set_final_data_size): Add global entry + stub sizing. + (Output_data_glink::do_write): Write global entry stubs. + (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Add target + parameter. Return true for ELFv2. Adjust callers. + (Target_powerpc::Scan::local, global): Restrict opd lookup to + ELFv1. Similarly for ifunc and dynamic relocation processing + specific to ELFv1. Recognize that symbols are defined on + their plt entries for ELFv2. + (Target_powerpc::symval_for_branch): Assert if called for + ELFv2 or ppc32. + (Target_powerpc::Relocate::relocate): Use global entry plt + stub for symbol value if such exists on ELFv2. + (Target_powerpc::Relocate::relocate): Don't call + symval_for_branch when ELFv2. Do adjust for local entry + offset when ELFv2. + (Target_powerpc::do_dynsym_value): Set symbols to global entry + plt stub for ELFv2. + (Target_powerpc::do_plt_address_for_global): Similarly. + 2013-11-14 Cary Coutant <ccoutant@google.com> Revert patch -- this did not fix the problem, and there is diff --git a/gold/powerpc.cc b/gold/powerpc.cc index ada4d0c..1aa4791 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -687,6 +687,13 @@ class Target_powerpc : public Sized_target<size, big_endian> return this->glink_; } + Output_data_glink<size, big_endian>* + glink_section() + { + gold_assert(this->glink_ != NULL); + return this->glink_; + } + bool has_glink() const { return this->glink_ != NULL; } @@ -974,7 +981,8 @@ class Target_powerpc : public Sized_target<size, big_endian> } static bool - reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object, + reloc_needs_plt_for_ifunc(Target_powerpc<size, big_endian>* target, + Sized_relobj_file<size, big_endian>* object, unsigned int r_type, bool report_err); private: @@ -2530,20 +2538,29 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target)) : this->object_->local_has_plt_offset(this->r_sym_)) { - if (stub_table == NULL) - stub_table = this->object_->stub_table(this->shndx_); - if (stub_table == NULL) + if (size == 64 + && gsym != NULL + && target->abiversion() >= 2 + && !parameters->options().output_is_position_independent() + && !is_branch_reloc(this->r_type_)) + target->glink_section()->add_global_entry(gsym); + else { - // This is a ref from a data section to an ifunc symbol. - stub_table = ifunc_stub_table; + if (stub_table == NULL) + stub_table = this->object_->stub_table(this->shndx_); + if (stub_table == NULL) + { + // This is a ref from a data section to an ifunc symbol. + stub_table = ifunc_stub_table; + } + gold_assert(stub_table != NULL); + if (gsym != NULL) + stub_table->add_plt_call_entry(this->object_, gsym, + this->r_type_, this->addend_); + else + stub_table->add_plt_call_entry(this->object_, this->r_sym_, + this->r_type_, this->addend_); } - gold_assert(stub_table != NULL); - if (gsym != NULL) - stub_table->add_plt_call_entry(this->object_, gsym, - this->r_type_, this->addend_); - else - stub_table->add_plt_call_entry(this->object_, this->r_sym_, - this->r_type_, this->addend_); } else { @@ -2590,7 +2607,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( to = symtab->compute_final_value<size>(gsym, &status); if (status != Symbol_table::CFVS_OK) return; - to += this->object_->ppc64_local_entry_offset(gsym); + if (size == 64) + to += this->object_->ppc64_local_entry_offset(gsym); } else { @@ -2605,12 +2623,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( || !symval.has_output_value()) return; to = symval.value(this->object_, 0); - to += this->object_->ppc64_local_entry_offset(this->r_sym_); + if (size == 64) + to += this->object_->ppc64_local_entry_offset(this->r_sym_); } to += this->addend_; if (stub_table == NULL) stub_table = this->object_->stub_table(this->shndx_); - if (size == 64 && is_branch_reloc(this->r_type_)) + if (size == 64 && target->abiversion() < 2) { unsigned int dest_shndx; to = target->symval_for_branch(symtab, to, gsym, @@ -3036,6 +3055,7 @@ 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 ld_12_11 = 0xe98b0000; +static const uint32_t ld_12_12 = 0xe98c0000; static const uint32_t lfd_0_1 = 0xc8010000; static const uint32_t li_0_0 = 0x38000000; static const uint32_t li_12_0 = 0x39800000; @@ -3802,7 +3822,7 @@ Stub_table<size, big_endian>::add_plt_call_entry( Address addend) { Plt_stub_ent ent(object, gsym, r_type, addend); - Address off = this->plt_size_; + unsigned int off = this->plt_size_; std::pair<typename Plt_stub_entries::iterator, bool> p = this->plt_call_stubs_.insert(std::make_pair(ent, off)); if (p.second) @@ -3818,7 +3838,7 @@ Stub_table<size, big_endian>::add_plt_call_entry( Address addend) { Plt_stub_ent ent(object, locsym_index, r_type, addend); - Address off = this->plt_size_; + unsigned int off = this->plt_size_; std::pair<typename Plt_stub_entries::iterator, bool> p = this->plt_call_stubs_.insert(std::make_pair(ent, off)); if (p.second) @@ -3913,50 +3933,30 @@ template<int size, bool big_endian> class Output_data_glink : public Output_section_data { public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + static const Address invalid_address = static_cast<Address>(0) - 1; static const int pltresolve_size = 16*4; Output_data_glink(Target_powerpc<size, big_endian>* targ) - : Output_section_data(16), targ_(targ) + : Output_section_data(16), targ_(targ), global_entry_stubs_(), + end_branch_table_(), ge_size_(0) { } void - add_eh_frame(Layout* layout) - { - if (!parameters->options().ld_generated_unwind_info()) - return; + add_eh_frame(Layout* layout); - if (size == 64) - { - if (this->targ_->abiversion() < 2) - layout->add_eh_frame_for_plt(this, - Eh_cie<64>::eh_frame_cie, - sizeof (Eh_cie<64>::eh_frame_cie), - glink_eh_frame_fde_64v1, - sizeof (glink_eh_frame_fde_64v1)); - else - layout->add_eh_frame_for_plt(this, - Eh_cie<64>::eh_frame_cie, - sizeof (Eh_cie<64>::eh_frame_cie), - glink_eh_frame_fde_64v2, - sizeof (glink_eh_frame_fde_64v2)); - } - else - { - // 32-bit .glink can use the default since the CIE return - // address reg, LR, is valid. - layout->add_eh_frame_for_plt(this, - Eh_cie<32>::eh_frame_cie, - sizeof (Eh_cie<32>::eh_frame_cie), - default_fde, - sizeof (default_fde)); - // Except where LR is used in a PIC __glink_PLTresolve. - if (parameters->options().output_is_position_independent()) - layout->add_eh_frame_for_plt(this, - Eh_cie<32>::eh_frame_cie, - sizeof (Eh_cie<32>::eh_frame_cie), - glink_eh_frame_fde_32, - sizeof (glink_eh_frame_fde_32)); - } + void + add_global_entry(const Symbol*); + + Address + find_global_entry(const Symbol*) const; + + Address + global_entry_address() const + { + gold_assert(this->is_data_size_valid()); + unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16; + return this->address() + global_entry_off; } protected: @@ -3975,10 +3975,76 @@ class Output_data_glink : public Output_section_data // Allows access to .got and .plt for do_write. Target_powerpc<size, big_endian>* targ_; + + // Map sym to stub offset. + typedef Unordered_map<const Symbol*, unsigned int> Global_entry_stub_entries; + Global_entry_stub_entries global_entry_stubs_; + + unsigned int end_branch_table_, ge_size_; }; template<int size, bool big_endian> void +Output_data_glink<size, big_endian>::add_eh_frame(Layout* layout) +{ + if (!parameters->options().ld_generated_unwind_info()) + return; + + if (size == 64) + { + if (this->targ_->abiversion() < 2) + layout->add_eh_frame_for_plt(this, + Eh_cie<64>::eh_frame_cie, + sizeof (Eh_cie<64>::eh_frame_cie), + glink_eh_frame_fde_64v1, + sizeof (glink_eh_frame_fde_64v1)); + else + layout->add_eh_frame_for_plt(this, + Eh_cie<64>::eh_frame_cie, + sizeof (Eh_cie<64>::eh_frame_cie), + glink_eh_frame_fde_64v2, + sizeof (glink_eh_frame_fde_64v2)); + } + else + { + // 32-bit .glink can use the default since the CIE return + // address reg, LR, is valid. + layout->add_eh_frame_for_plt(this, + Eh_cie<32>::eh_frame_cie, + sizeof (Eh_cie<32>::eh_frame_cie), + default_fde, + sizeof (default_fde)); + // Except where LR is used in a PIC __glink_PLTresolve. + if (parameters->options().output_is_position_independent()) + layout->add_eh_frame_for_plt(this, + Eh_cie<32>::eh_frame_cie, + sizeof (Eh_cie<32>::eh_frame_cie), + glink_eh_frame_fde_32, + sizeof (glink_eh_frame_fde_32)); + } +} + +template<int size, bool big_endian> +void +Output_data_glink<size, big_endian>::add_global_entry(const Symbol* gsym) +{ + std::pair<typename Global_entry_stub_entries::iterator, bool> p + = this->global_entry_stubs_.insert(std::make_pair(gsym, this->ge_size_)); + if (p.second) + this->ge_size_ += 16; +} + +template<int size, bool big_endian> +typename Output_data_glink<size, big_endian>::Address +Output_data_glink<size, big_endian>::find_global_entry(const Symbol* gsym) const +{ + typename Global_entry_stub_entries::const_iterator p + = this->global_entry_stubs_.find(gsym); + return p == this->global_entry_stubs_.end() ? invalid_address : p->second; +} + +template<int size, bool big_endian> +void Output_data_glink<size, big_endian>::set_final_data_size() { unsigned int count = this->targ_->plt_entry_count(); @@ -4008,6 +4074,9 @@ Output_data_glink<size, big_endian>::set_final_data_size() } } } + this->end_branch_table_ = total; + total = (total + 15) & -16; + total += this->ge_size_; this->set_data_size(total); } @@ -4341,64 +4410,101 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of) if (size == 64) { - // Write pltresolve stub. - p = oview; - Address after_bcl = this->address() + 16; - Address pltoff = plt_base - after_bcl; - - elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8; - - if (this->targ_->abiversion() < 2) - { - write_insn<big_endian>(p, mflr_12), p += 4; - write_insn<big_endian>(p, bcl_20_31), p += 4; - write_insn<big_endian>(p, mflr_11), p += 4; - write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4; - write_insn<big_endian>(p, mtlr_12), p += 4; - write_insn<big_endian>(p, add_11_2_11), p += 4; - write_insn<big_endian>(p, ld_12_11 + 0), p += 4; - write_insn<big_endian>(p, ld_2_11 + 8), p += 4; - write_insn<big_endian>(p, mtctr_12), p += 4; - write_insn<big_endian>(p, ld_11_11 + 16), p += 4; - } - else + if (this->end_branch_table_ != 0) { - write_insn<big_endian>(p, mflr_0), p += 4; - write_insn<big_endian>(p, bcl_20_31), p += 4; - write_insn<big_endian>(p, mflr_11), p += 4; - write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4; - write_insn<big_endian>(p, mtlr_0), p += 4; - write_insn<big_endian>(p, sub_12_12_11), p += 4; - write_insn<big_endian>(p, add_11_2_11), p += 4; - write_insn<big_endian>(p, addi_0_12 + l(-48)), p += 4; - write_insn<big_endian>(p, ld_12_11 + 0), p += 4; - write_insn<big_endian>(p, srdi_0_0_2), p += 4; - write_insn<big_endian>(p, mtctr_12), p += 4; - write_insn<big_endian>(p, ld_11_11 + 8), p += 4; - } - write_insn<big_endian>(p, bctr), p += 4; - while (p < oview + this->pltresolve_size) - write_insn<big_endian>(p, nop), p += 4; + // Write pltresolve stub. + p = oview; + Address after_bcl = this->address() + 16; + Address pltoff = plt_base - after_bcl; + + elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8; - // Write lazy link call stubs. - uint32_t indx = 0; - while (p < oview + oview_size) - { if (this->targ_->abiversion() < 2) { - if (indx < 0x8000) - { - write_insn<big_endian>(p, li_0_0 + indx), p += 4; - } - else + write_insn<big_endian>(p, mflr_12), p += 4; + write_insn<big_endian>(p, bcl_20_31), p += 4; + write_insn<big_endian>(p, mflr_11), p += 4; + write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4; + write_insn<big_endian>(p, mtlr_12), p += 4; + write_insn<big_endian>(p, add_11_2_11), p += 4; + write_insn<big_endian>(p, ld_12_11 + 0), p += 4; + write_insn<big_endian>(p, ld_2_11 + 8), p += 4; + write_insn<big_endian>(p, mtctr_12), p += 4; + write_insn<big_endian>(p, ld_11_11 + 16), p += 4; + } + else + { + write_insn<big_endian>(p, mflr_0), p += 4; + write_insn<big_endian>(p, bcl_20_31), p += 4; + write_insn<big_endian>(p, mflr_11), p += 4; + write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4; + write_insn<big_endian>(p, mtlr_0), p += 4; + write_insn<big_endian>(p, sub_12_12_11), p += 4; + write_insn<big_endian>(p, add_11_2_11), p += 4; + write_insn<big_endian>(p, addi_0_12 + l(-48)), p += 4; + write_insn<big_endian>(p, ld_12_11 + 0), p += 4; + write_insn<big_endian>(p, srdi_0_0_2), p += 4; + write_insn<big_endian>(p, mtctr_12), p += 4; + write_insn<big_endian>(p, ld_11_11 + 8), p += 4; + } + write_insn<big_endian>(p, bctr), p += 4; + while (p < oview + this->pltresolve_size) + write_insn<big_endian>(p, nop), p += 4; + + // Write lazy link call stubs. + uint32_t indx = 0; + while (p < oview + this->end_branch_table_) + { + if (this->targ_->abiversion() < 2) { - write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4; - write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4; + if (indx < 0x8000) + { + write_insn<big_endian>(p, li_0_0 + indx), p += 4; + } + else + { + write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4; + write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4; + } } + uint32_t branch_off = 8 - (p - oview); + write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4; + indx++; } - uint32_t branch_off = 8 - (p - oview); - write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4; - indx++; + } + + Address plt_base = this->targ_->plt_section()->address(); + Address iplt_base = invalid_address; + unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16; + Address global_entry_base = this->address() + global_entry_off; + typename Global_entry_stub_entries::const_iterator ge; + for (ge = this->global_entry_stubs_.begin(); + ge != this->global_entry_stubs_.end(); + ++ge) + { + p = oview + global_entry_off + ge->second; + Address plt_addr = ge->first->plt_offset(); + if (ge->first->type() == elfcpp::STT_GNU_IFUNC + && ge->first->can_use_relative_reloc(false)) + { + if (iplt_base == invalid_address) + iplt_base = this->targ_->iplt_section()->address(); + plt_addr += iplt_base; + } + else + plt_addr += plt_base; + Address my_addr = global_entry_base + ge->second; + Address off = plt_addr - my_addr; + + if (off + 0x80008000 > 0xffffffff || (off & 3) != 0) + gold_error(_("%s: linkage table error against `%s'"), + ge->first->object()->name().c_str(), + ge->first->demangled_name().c_str()); + + write_insn<big_endian>(p, addis_12_12 + ha(off)), p += 4; + write_insn<big_endian>(p, ld_12_12 + l(off)), p += 4; + write_insn<big_endian>(p, mtctr_12), p += 4; + write_insn<big_endian>(p, bctr); } } else @@ -5119,13 +5225,15 @@ Target_powerpc<size, big_endian>::Scan::check_non_pic(Relobj* object, template<int size, bool big_endian> bool Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc( + Target_powerpc<size, big_endian>* target, Sized_relobj_file<size, big_endian>* object, unsigned int r_type, bool report_err) { // In non-pic code any reference will resolve to the plt call stub // for the ifunc symbol. - if (size == 32 && !parameters->options().output_is_position_independent()) + if ((size == 32 || target->abiversion() >= 2) + && !parameters->options().output_is_position_independent()) return true; switch (r_type) @@ -5232,7 +5340,7 @@ Target_powerpc<size, big_endian>::Scan::local( // A local STT_GNU_IFUNC symbol may require a PLT entry. bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC; - if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true)) + if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true)) { unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), @@ -5257,6 +5365,7 @@ Target_powerpc<size, big_endian>::Scan::local( { Address off = reloc.get_r_offset(); if (size == 64 + && target->abiversion() < 2 && data_shndx == ppc_object->opd_shndx() && ppc_object->get_opd_discard(off - 8)) break; @@ -5297,7 +5406,7 @@ Target_powerpc<size, big_endian>::Scan::local( // executable), we need to create a dynamic relocation for // this location. if (parameters->options().output_is_position_independent() - || (size == 64 && is_ifunc)) + || (size == 64 && is_ifunc && target->abiversion() < 2)) { Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout, is_ifunc); @@ -5389,7 +5498,8 @@ Target_powerpc<size, big_endian>::Scan::local( if (!parameters->options().output_is_position_independent()) { - if (size == 32 && is_ifunc) + if ((size == 32 && is_ifunc) + || (size == 64 && target->abiversion() >= 2)) got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD); else got->add_local(object, r_sym, GOT_TYPE_STANDARD); @@ -5587,12 +5697,14 @@ Target_powerpc<size, big_endian>::Scan::global( // A STT_GNU_IFUNC symbol may require a PLT entry. bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC; - if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true)) + bool pushed_ifunc = false; + if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true)) { target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), reloc.get_r_addend()); target->make_plt_entry(symtab, layout, gsym); + pushed_ifunc = true; } switch (r_type) @@ -5632,6 +5744,7 @@ Target_powerpc<size, big_endian>::Scan::global( case elfcpp::R_PPC64_ADDR64: if (size == 64 + && target->abiversion() < 2 && data_shndx == ppc_object->opd_shndx() && (gsym->is_defined_in_discarded_section() || gsym->object() != object)) @@ -5664,7 +5777,19 @@ Target_powerpc<size, big_endian>::Scan::global( // Make a PLT entry if necessary. if (gsym->needs_plt_entry()) { - if (!is_ifunc) + // Since this is not a PC-relative relocation, we may be + // taking the address of a function. In that case we need to + // set the entry in the dynamic symbol table to the address of + // the PLT call stub. + bool need_ifunc_plt = false; + if ((size == 32 || target->abiversion() >= 2) + && gsym->is_from_dynobj() + && !parameters->options().output_is_position_independent()) + { + gsym->set_needs_dynsym_value(); + need_ifunc_plt = true; + } + if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt)) { target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), r_type, @@ -5672,31 +5797,27 @@ Target_powerpc<size, big_endian>::Scan::global( reloc.get_r_addend()); target->make_plt_entry(symtab, layout, gsym); } - // Since this is not a PC-relative relocation, we may be - // taking the address of a function. In that case we need to - // set the entry in the dynamic symbol table to the address of - // the PLT call stub. - if (size == 32 - && gsym->is_from_dynobj() - && !parameters->options().output_is_position_independent()) - gsym->set_needs_dynsym_value(); } // Make a dynamic relocation if necessary. if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type, target)) - || (size == 64 && is_ifunc)) + || (size == 64 && is_ifunc && target->abiversion() < 2)) { if (gsym->may_need_copy_reloc()) { target->copy_reloc(symtab, layout, object, data_shndx, output_section, gsym, reloc); } - else if ((size == 32 - && r_type == elfcpp::R_POWERPC_ADDR32 + else if ((((size == 32 + && r_type == elfcpp::R_POWERPC_ADDR32) + || (size == 64 + && r_type == elfcpp::R_PPC64_ADDR64 + && target->abiversion() >= 2)) && gsym->can_use_relative_reloc(false) && !(gsym->visibility() == elfcpp::STV_PROTECTED && parameters->options().shared())) || (size == 64 && r_type == elfcpp::R_PPC64_ADDR64 + && target->abiversion() < 2 && (gsym->can_use_relative_reloc(false) || data_shndx == ppc_object->opd_shndx()))) { @@ -5822,7 +5943,8 @@ Target_powerpc<size, big_endian>::Scan::global( got = target->got_section(symtab, layout); if (gsym->final_value_is_known()) { - if (size == 32 && is_ifunc) + if ((size == 32 && is_ifunc) + || (size == 64 && target->abiversion() >= 2)) got->add_global_plt(gsym, GOT_TYPE_STANDARD); else got->add_global(gsym, GOT_TYPE_STANDARD); @@ -5838,7 +5960,8 @@ Target_powerpc<size, big_endian>::Scan::global( = target->rela_dyn_section(symtab, layout, is_ifunc); if (gsym->can_use_relative_reloc(false) - && !(size == 32 + && !((size == 32 + || target->abiversion() >= 2) && gsym->visibility() == elfcpp::STV_PROTECTED && parameters->options().shared())) { @@ -6438,9 +6561,9 @@ Target_powerpc<size, big_endian>::symval_for_branch( Powerpc_relobj<size, big_endian>* object, unsigned int *dest_shndx) { + if (size == 32 || this->abiversion() >= 2) + gold_unreachable(); *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 @@ -6522,26 +6645,39 @@ Target_powerpc<size, big_endian>::Relocate::relocate( ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target)) : object->local_has_plt_offset(r_sym)) && (!psymval->is_ifunc_symbol() - || Scan::reloc_needs_plt_for_ifunc(object, r_type, false))) + || Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false))) { - Stub_table<size, big_endian>* stub_table - = object->stub_table(relinfo->data_shndx); - if (stub_table == NULL) + if (size == 64 + && gsym != NULL + && target->abiversion() >= 2 + && !parameters->options().output_is_position_independent() + && !is_branch_reloc(r_type)) { - // This is a ref from a data section to an ifunc symbol. - if (target->stub_tables().size() != 0) - stub_table = target->stub_tables()[0]; + unsigned int off = target->glink_section()->find_global_entry(gsym); + gold_assert(off != (unsigned int)-1); + value = target->glink_section()->global_entry_address() + off; } - gold_assert(stub_table != NULL); - Address off; - if (gsym != NULL) - off = stub_table->find_plt_call_entry(object, gsym, r_type, - rela.get_r_addend()); else - off = stub_table->find_plt_call_entry(object, r_sym, r_type, - rela.get_r_addend()); - gold_assert(off != invalid_address); - value = stub_table->stub_address() + off; + { + Stub_table<size, big_endian>* stub_table + = object->stub_table(relinfo->data_shndx); + if (stub_table == NULL) + { + // This is a ref from a data section to an ifunc symbol. + if (target->stub_tables().size() != 0) + stub_table = target->stub_tables()[0]; + } + gold_assert(stub_table != NULL); + Address off; + if (gsym != NULL) + off = stub_table->find_plt_call_entry(object, gsym, r_type, + rela.get_r_addend()); + else + off = stub_table->find_plt_call_entry(object, r_sym, r_type, + rela.get_r_addend()); + gold_assert(off != invalid_address); + value = stub_table->stub_address() + off; + } has_plt_value = true; } @@ -6616,11 +6752,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate( if (gsym->source() == Symbol::FROM_OBJECT && gsym->object() == object) { - Address addend = rela.get_r_addend(); - unsigned int dest_shndx; - Address opdent = psymval->value(object, addend); - code = target->symval_for_branch(relinfo->symtab, opdent, - gsym, object, &dest_shndx); + unsigned int dest_shndx = 0; + if (target->abiversion() < 2) + { + Address addend = rela.get_r_addend(); + Address opdent = psymval->value(object, addend); + code = target->symval_for_branch(relinfo->symtab, + opdent, gsym, object, + &dest_shndx); + } bool is_ordinary; if (dest_shndx == 0) dest_shndx = gsym->shndx(&is_ordinary); @@ -6881,13 +7021,19 @@ Target_powerpc<size, big_endian>::Relocate::relocate( if (r_type != elfcpp::R_PPC_PLTREL24) addend = rela.get_r_addend(); value = psymval->value(object, addend); - if (gsym != NULL) - value += object->ppc64_local_entry_offset(gsym); - else - value += object->ppc64_local_entry_offset(r_sym); if (size == 64 && is_branch_reloc(r_type)) - value = target->symval_for_branch(relinfo->symtab, value, - gsym, object, &dest_shndx); + { + if (target->abiversion() >= 2) + { + if (gsym != NULL) + value += object->ppc64_local_entry_offset(gsym); + else + value += object->ppc64_local_entry_offset(r_sym); + } + else + value = target->symval_for_branch(relinfo->symtab, value, + gsym, object, &dest_shndx); + } unsigned int max_branch_offset = 0; if (r_type == elfcpp::R_POWERPC_REL24 || r_type == elfcpp::R_PPC_PLTREL24 @@ -7846,6 +7992,12 @@ Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const return (*p)->stub_address() + off; } } + else if (this->abiversion() >= 2) + { + unsigned int off = this->glink_section()->find_global_entry(gsym); + if (off != (unsigned int)-1) + return this->glink_section()->global_entry_address() + off; + } gold_unreachable(); } @@ -7890,6 +8042,12 @@ Target_powerpc<size, big_endian>::do_plt_address_for_global( return (*p)->stub_address() + off; } } + else if (this->abiversion() >= 2) + { + unsigned int off = this->glink_section()->find_global_entry(gsym); + if (off != (unsigned int)-1) + return this->glink_section()->global_entry_address() + off; + } gold_unreachable(); } @@ -7987,6 +8145,9 @@ Target_selector_powerpc<64, false> target_selector_ppc64le; template<int size, bool big_endian> const int Output_data_glink<size, big_endian>::pltresolve_size; template<int size, bool big_endian> +const typename Output_data_glink<size, big_endian>::Address + Output_data_glink<size, big_endian>::invalid_address; +template<int size, bool big_endian> const typename Stub_table<size, big_endian>::Address Stub_table<size, big_endian>::invalid_address; template<int size, bool big_endian> |