diff options
author | Alan Modra <amodra@gmail.com> | 2012-12-12 08:09:41 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2012-12-12 08:09:41 +0000 |
commit | e3deeb9c3acc713e427d589e8cb771bc38e73987 (patch) | |
tree | 429c2fa184461503aac33e9658160de4816098e0 /gold/powerpc.cc | |
parent | d8f5a2749d0409237cb681bb47c4a625c410f238 (diff) | |
download | fsf-binutils-gdb-e3deeb9c3acc713e427d589e8cb771bc38e73987.zip fsf-binutils-gdb-e3deeb9c3acc713e427d589e8cb771bc38e73987.tar.gz fsf-binutils-gdb-e3deeb9c3acc713e427d589e8cb771bc38e73987.tar.bz2 |
* powerpc.cc (class Track_tls): New.
(class Relocate, class Scan): Inherit Track_tls.
(Target_powerpc::Scan::local, global): Track tls optimization
and avoid creating plt entry for __tls_get_addr if all uses
are optimized away.
(Target_powerpc::Relocate::relocate): Update to use Track_tls.
Diffstat (limited to 'gold/powerpc.cc')
-rw-r--r-- | gold/powerpc.cc | 191 |
1 files changed, 148 insertions, 43 deletions
diff --git a/gold/powerpc.cc b/gold/powerpc.cc index edd48e7..2a2277c 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -623,14 +623,102 @@ class Target_powerpc : public Sized_target<size, big_endian> private: + class Track_tls + { + public: + enum Tls_get_addr + { + NOT_EXPECTED = 0, + EXPECTED = 1, + SKIP = 2, + NORMAL = 3 + }; + + Track_tls() + : tls_get_addr_(NOT_EXPECTED), + relinfo_(NULL), relnum_(0), r_offset_(0) + { } + + ~Track_tls() + { + if (this->tls_get_addr_ != NOT_EXPECTED) + this->missing(); + } + + void + missing(void) + { + if (this->relinfo_ != NULL) + gold_error_at_location(this->relinfo_, this->relnum_, this->r_offset_, + _("missing expected __tls_get_addr call")); + } + + void + expect_tls_get_addr_call( + const Relocate_info<size, big_endian>* relinfo, + size_t relnum, + Address r_offset) + { + this->tls_get_addr_ = EXPECTED; + this->relinfo_ = relinfo; + this->relnum_ = relnum; + this->r_offset_ = r_offset; + } + + void + expect_tls_get_addr_call() + { this->tls_get_addr_ = EXPECTED; } + + void + skip_next_tls_get_addr_call() + {this->tls_get_addr_ = SKIP; } + + Tls_get_addr + maybe_skip_tls_get_addr_call(unsigned int r_type, const Symbol* gsym) + { + bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24 + || r_type == elfcpp::R_PPC_PLTREL24) + && gsym != NULL + && strcmp(gsym->name(), "__tls_get_addr") == 0); + Tls_get_addr last_tls = this->tls_get_addr_; + this->tls_get_addr_ = NOT_EXPECTED; + if (is_tls_call && last_tls != EXPECTED) + return last_tls; + else if (!is_tls_call && last_tls != NOT_EXPECTED) + { + this->missing(); + return EXPECTED; + } + return NORMAL; + } + + private: + // What we're up to regarding calls to __tls_get_addr. + // On powerpc, the branch and link insn making a call to + // __tls_get_addr is marked with a relocation, R_PPC64_TLSGD, + // R_PPC64_TLSLD, R_PPC_TLSGD or R_PPC_TLSLD, in addition to the + // usual R_POWERPC_REL24 or R_PPC_PLTREL25 relocation on a call. + // The marker relocation always comes first, and has the same + // symbol as the reloc on the insn setting up the __tls_get_addr + // argument. This ties the arg setup insn with the call insn, + // allowing ld to safely optimize away the call. We check that + // every call to __tls_get_addr has a marker relocation, and that + // every marker relocation is on a call to __tls_get_addr. + Tls_get_addr tls_get_addr_; + // Info about the last reloc for error message. + const Relocate_info<size, big_endian>* relinfo_; + size_t relnum_; + Address r_offset_; + }; + // The class which scans relocations. - class Scan + class Scan : protected Track_tls { public: typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; Scan() - : issued_non_pic_error_(false) + : Track_tls(), issued_non_pic_error_(false) { } static inline int @@ -705,33 +793,17 @@ class Target_powerpc : public Sized_target<size, big_endian> unsigned int *dest_shndx); // The class which implements relocation. - class Relocate + class Relocate : protected Track_tls { public: // Use 'at' branch hints when true, 'y' when false. // FIXME maybe: set this with an option. static const bool is_isa_v2 = true; - enum skip_tls - { - CALL_NOT_EXPECTED = 0, - CALL_EXPECTED = 1, - CALL_SKIP = 2 - }; - Relocate() - : call_tls_get_addr_(CALL_NOT_EXPECTED) + : Track_tls() { } - ~Relocate() - { - if (this->call_tls_get_addr_ != CALL_NOT_EXPECTED) - { - // FIXME: This needs to specify the location somehow. - gold_error(_("missing expected __tls_get_addr call")); - } - } - // Do a relocation. Return false if the caller should not issue // any warnings about this relocation. inline bool @@ -743,10 +815,6 @@ class Target_powerpc : public Sized_target<size, big_endian> unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr, section_size_type); - - // This is set if we should skip the next reloc, which should be a - // call to __tls_get_addr. - enum skip_tls call_tls_get_addr_; }; class Relocate_comdat_behavior @@ -4398,6 +4466,25 @@ Target_powerpc<size, big_endian>::Scan::local( const elfcpp::Sym<size, big_endian>& lsym, bool is_discarded) { + this->maybe_skip_tls_get_addr_call(r_type, NULL); + + if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD) + || (size == 32 && r_type == elfcpp::R_PPC_TLSGD)) + { + this->expect_tls_get_addr_call(); + const tls::Tls_optimization tls_type = target->optimize_tls_gd(true); + if (tls_type != tls::TLSOPT_NONE) + this->skip_next_tls_get_addr_call(); + } + else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD) + || (size == 32 && r_type == elfcpp::R_PPC_TLSLD)) + { + this->expect_tls_get_addr_call(); + const tls::Tls_optimization tls_type = target->optimize_tls_ld(); + if (tls_type != tls::TLSOPT_NONE) + this->skip_next_tls_get_addr_call(); + } + Powerpc_relobj<size, big_endian>* ppc_object = static_cast<Powerpc_relobj<size, big_endian>*>(object); @@ -4747,6 +4834,27 @@ Target_powerpc<size, big_endian>::Scan::global( unsigned int r_type, Symbol* gsym) { + if (this->maybe_skip_tls_get_addr_call(r_type, gsym) == Track_tls::SKIP) + return; + + if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD) + || (size == 32 && r_type == elfcpp::R_PPC_TLSGD)) + { + this->expect_tls_get_addr_call(); + const bool final = gsym->final_value_is_known(); + const tls::Tls_optimization tls_type = target->optimize_tls_gd(final); + if (tls_type != tls::TLSOPT_NONE) + this->skip_next_tls_get_addr_call(); + } + else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD) + || (size == 32 && r_type == elfcpp::R_PPC_TLSLD)) + { + this->expect_tls_get_addr_call(); + const tls::Tls_optimization tls_type = target->optimize_tls_ld(); + if (tls_type != tls::TLSOPT_NONE) + this->skip_next_tls_get_addr_call(); + } + Powerpc_relobj<size, big_endian>* ppc_object = static_cast<Powerpc_relobj<size, big_endian>*>(object); @@ -5600,23 +5708,20 @@ Target_powerpc<size, big_endian>::Relocate::relocate( Address address, section_size_type view_size) { - bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24 - || r_type == elfcpp::R_PPC_PLTREL24) - && gsym != NULL - && strcmp(gsym->name(), "__tls_get_addr") == 0); - enum skip_tls last_tls = this->call_tls_get_addr_; - this->call_tls_get_addr_ = CALL_NOT_EXPECTED; - if (is_tls_call) + switch (this->maybe_skip_tls_get_addr_call(r_type, gsym)) { - if (last_tls == CALL_NOT_EXPECTED) - gold_error_at_location(relinfo, relnum, rela.get_r_offset(), - _("__tls_get_addr call lacks marker reloc")); - else if (last_tls == CALL_SKIP) - return false; + case Track_tls::NOT_EXPECTED: + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("__tls_get_addr call lacks marker reloc")); + break; + case Track_tls::EXPECTED: + // We have already complained. + break; + case Track_tls::SKIP: + return true; + case Track_tls::NORMAL: + break; } - else if (last_tls != CALL_NOT_EXPECTED) - gold_error_at_location(relinfo, relnum, rela.get_r_offset(), - _("missing expected __tls_get_addr call")); typedef Powerpc_relocate_functions<size, big_endian> Reloc; typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn; @@ -5913,7 +6018,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate( { // Second instruction of a global dynamic sequence, // the __tls_get_addr call - this->call_tls_get_addr_ = CALL_EXPECTED; + this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset()); const bool final = gsym == NULL || gsym->final_value_is_known(); const tls::Tls_optimization tls_type = target->optimize_tls_gd(final); if (tls_type != tls::TLSOPT_NONE) @@ -5936,7 +6041,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate( view += 2 * big_endian; value = psymval->value(object, rela.get_r_addend()); } - this->call_tls_get_addr_ = CALL_SKIP; + this->skip_next_tls_get_addr_call(); } } else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD) @@ -5944,14 +6049,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate( { // Second instruction of a local dynamic sequence, // the __tls_get_addr call - this->call_tls_get_addr_ = CALL_EXPECTED; + this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset()); const tls::Tls_optimization tls_type = target->optimize_tls_ld(); if (tls_type == tls::TLSOPT_TO_LE) { Insn* iview = reinterpret_cast<Insn*>(view); Insn insn = addi_3_3; elfcpp::Swap<32, big_endian>::writeval(iview, insn); - this->call_tls_get_addr_ = CALL_SKIP; + this->skip_next_tls_get_addr_call(); r_type = elfcpp::R_POWERPC_TPREL16_LO; view += 2 * big_endian; value = dtp_offset; |