aboutsummaryrefslogtreecommitdiff
path: root/gold/powerpc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gold/powerpc.cc')
-rw-r--r--gold/powerpc.cc191
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;