* 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.
This commit is contained in:
parent
d8f5a2749d
commit
e3deeb9c3a
2 changed files with 157 additions and 43 deletions
|
@ -1,3 +1,12 @@
|
|||
2012-12-12 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* 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.
|
||||
|
||||
2012-12-12 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* options.h (General_options): Add --toc-sort/--no-toc-sort.
|
||||
|
|
191
gold/powerpc.cc
191
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;
|
||||
|
|
Loading…
Reference in a new issue