// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2020 Free Software Foundation, Inc.
+// Copyright (C) 2008-2021 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>
// and David Edelsohn <edelsohn@gnu.org>
: Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
uniq_(object_id++), special_(0), relatoc_(0), toc_(0),
has_small_toc_reloc_(false), opd_valid_(false),
+ no_tls_marker_(false), tls_marker_(false), tls_opt_error_(false),
e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(),
access_from_map_(), has14_(), stub_table_index_(), st_other_(),
attributes_section_data_(NULL)
return this->no_toc_opt_[off];
}
+ void
+ set_no_tls_marker()
+ {
+ if (!this->no_tls_marker_ && this->tls_marker_)
+ this->tls_opt_error_ = true;
+ this->no_tls_marker_ = true;
+ }
+
+ bool
+ no_tls_marker() const
+ { return this->no_tls_marker_; }
+
+ void
+ set_tls_marker()
+ { this->tls_marker_ = true; }
+
+ bool
+ tls_marker() const
+ { return this->tls_marker_; }
+
+ bool
+ tls_opt_error() const
+ { return this->tls_opt_error_; }
+
// The .got2 section shndx.
unsigned int
got2_shndx() const
// access_from_map_.
bool opd_valid_;
+ // Set when finding a __tls_get_addr call without marker relocs.
+ // Such a call disables GD and LD tls optimisations for the object file.
+ bool no_tls_marker_;
+
+ // Set when finding a __tls_get_addr call with marker relocs, or
+ // when finding a relocation that needs __tls_get_addr calls with
+ // marker relocs.
+ bool tls_marker_;
+
+ // Set when seeing a __tls_get_addr call without marker relocs after
+ // seeing some __tls_get_addr calls with marker relocs.
+ bool tls_opt_error_;
+
// Header e_flags
elfcpp::Elf_Word e_flags_;
glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
tlsld_got_offset_(-1U),
stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
- power10_stubs_(false), plt_thread_safe_(false), plt_localentry0_(false),
+ power10_relocs_(false), plt_thread_safe_(false), plt_localentry0_(false),
plt_localentry0_init_(false), has_localentry0_(false),
- has_tls_get_addr_opt_(false),
- tprel_opt_(parameters->options().tls_optimize()),
+ has_tls_get_addr_opt_(false), no_tprel_opt_(false),
relax_failed_(false), relax_fail_count_(0),
stub_group_size_(0), savres_section_(0),
tls_get_addr_(NULL), tls_get_addr_opt_(NULL),
}
// Accessor
- const Tocsave_loc
+ const Tocsave_loc*
tocsave_loc() const
{
- return this->tocsave_loc_;
+ return &this->tocsave_loc_;
}
void
sym->set_dynsym_index(-1U);
}
+ void
+ set_power10_relocs()
+ {
+ this->power10_relocs_ = true;
+ }
+
bool
power10_stubs() const
- { return this->power10_stubs_; }
-
- void
- set_power10_stubs()
{
- if (parameters->options().power10_stubs_enum()
- != General_options::POWER10_STUBS_NO)
- this->power10_stubs_ = true;
+ return (this->power10_relocs_
+ && (parameters->options().power10_stubs_enum()
+ != General_options::POWER10_STUBS_NO));
}
bool
plt_localentry0() const
{ return this->plt_localentry0_; }
+ bool
+ has_localentry0() const
+ { return this->has_localentry0_; }
+
void
set_has_localentry0()
{
bool
tprel_opt() const
- { return this->tprel_opt_; }
+ { return !this->no_tprel_opt_ && parameters->options().tls_optimize(); }
void
- set_tprel_opt(bool val)
- { this->tprel_opt_ = val; }
+ set_no_tprel_opt()
+ { this->no_tprel_opt_ = true; }
// Remember any symbols seen with non-zero localentry, even those
// not providing a definition
Branches branch_info_;
Tocsave_loc tocsave_loc_;
- bool power10_stubs_;
+ bool power10_relocs_;
bool plt_thread_safe_;
bool plt_localentry0_;
bool plt_localentry0_init_;
bool has_localentry0_;
bool has_tls_get_addr_opt_;
- bool tprel_opt_;
+ bool no_tprel_opt_;
bool relax_failed_;
int relax_fail_count_;
|| (size == 64 && r_type == elfcpp::R_PPC64_PLT16_LO_DS));
}
+// GOT_TYPE_STANDARD (ie. not TLS) GOT relocs
+inline bool
+is_got_reloc(unsigned int r_type)
+{
+ return (r_type == elfcpp::R_POWERPC_GOT16
+ || r_type == elfcpp::R_POWERPC_GOT16_LO
+ || r_type == elfcpp::R_POWERPC_GOT16_HI
+ || r_type == elfcpp::R_POWERPC_GOT16_HA
+ || r_type == elfcpp::R_PPC64_GOT16_DS
+ || r_type == elfcpp::R_PPC64_GOT16_LO_DS
+ || r_type == elfcpp::R_PPC64_GOT_PCREL34);
+}
+
// If INSN is an opcode that may be used with an @tls operand, return
// the transformed insn for TLS optimisation, otherwise return 0. If
// REG is non-zero only match an insn with RB or RA equal to REG.
if (this->local_has_plt_offset(i))
{
Address value = this->local_symbol_value(i, 0);
- if (size == 64)
- value += ppc64_local_entry_offset(i);
size_t off = this->local_plt_offset(i);
elfcpp::Swap<size, big_endian>::writeval(oview + off, value);
modified = true;
from += (this->object_->output_section(this->shndx_)->address()
+ this->offset_);
Address to;
+ unsigned int other = 0;
if (gsym != NULL)
{
switch (gsym->source())
if (status != Symbol_table::CFVS_OK)
return true;
if (size == 64)
- to += this->object_->ppc64_local_entry_offset(gsym);
+ other = gsym->nonvis() >> 3;
}
else
{
return true;
to = symval.value(this->object_, 0);
if (size == 64)
- to += this->object_->ppc64_local_entry_offset(this->r_sym_);
+ other = this->object_->st_other(this->r_sym_) >> 5;
}
if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
to += this->addend_;
&to, &dest_shndx))
return true;
}
- Address delta = to - from;
+ unsigned int local_ent = 0;
+ if (size == 64
+ && this->r_type_ != elfcpp::R_PPC64_REL24_NOTOC)
+ local_ent = elfcpp::ppc64_decode_local_entry(other);
+ Address delta = to + local_ent - from;
if (delta + max_branch_offset >= 2 * max_branch_offset
|| (size == 64
&& this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
&& gsym->output_data() == target->savres_section());
ok = stub_table->add_long_branch_entry(this->object_,
this->r_type_,
- from, to, save_res);
+ from, to, other, save_res);
}
}
if (!ok)
// See Output_data_glink::do_write() for glink contents.
if (len == 0)
{
- gold_assert(parameters->doing_static_link());
// Static linking may need stubs, to support ifunc and long
// branches. We need to create an output section for
// .eh_frame early in the link process, to have a place to
static const uint32_t cror_15_15_15 = 0x4def7b82;
static const uint32_t cror_31_31_31 = 0x4ffffb82;
static const uint32_t ld_0_1 = 0xe8010000;
+static const uint32_t ld_0_11 = 0xe80b0000;
static const uint32_t ld_0_12 = 0xe80c0000;
static const uint32_t ld_2_1 = 0xe8410000;
static const uint32_t ld_2_2 = 0xe8420000;
0, 0, 0, 0, // Replaced with offset to .glink.
0, 0, 0, 0, // Replaced with size of .glink.
0, // Augmentation size.
- elfcpp::DW_CFA_advance_loc + 1,
+ elfcpp::DW_CFA_advance_loc + 2,
elfcpp::DW_CFA_register, 65, 12,
- elfcpp::DW_CFA_advance_loc + 5,
+ elfcpp::DW_CFA_advance_loc + 4,
elfcpp::DW_CFA_restore_extended, 65
};
0, 0, 0, 0, // Replaced with offset to .glink.
0, 0, 0, 0, // Replaced with size of .glink.
0, // Augmentation size.
- elfcpp::DW_CFA_advance_loc + 1,
+ elfcpp::DW_CFA_advance_loc + 2,
+ elfcpp::DW_CFA_register, 65, 0,
+ elfcpp::DW_CFA_advance_loc + 2,
+ elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char glink_eh_frame_fde_64v2_localentry0[] =
+{
+ 0, 0, 0, 0, // Replaced with offset to .glink.
+ 0, 0, 0, 0, // Replaced with size of .glink.
+ 0, // Augmentation size.
+ elfcpp::DW_CFA_advance_loc + 3,
elfcpp::DW_CFA_register, 65, 0,
- elfcpp::DW_CFA_advance_loc + 7,
+ elfcpp::DW_CFA_advance_loc + 2,
elfcpp::DW_CFA_restore_extended, 65
};
{
Branch_stub_ent(unsigned int off, bool notoc, bool save_res)
: off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res),
- tocoff_(0)
+ other_(0), tocoff_(0)
{ }
unsigned int off_;
unsigned int notoc_ : 1;
unsigned int toc_ : 1;
unsigned int save_res_ : 1;
+ unsigned int other_ : 3;
unsigned int tocoff_ : 8;
};
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
// Add a long branch stub.
bool
add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
- unsigned int, Address, Address, bool);
+ unsigned int, Address, Address, unsigned int, bool);
const Branch_stub_ent*
find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
unsigned int r_type,
Address from,
Address to,
+ unsigned int other,
bool save_res)
{
Branch_stub_key key(object, to);
this->need_resize_ = true;
p.first->second.toc_ = true;
}
+ if (size == 64 && p.first->second.other_ == 0)
+ p.first->second.other_ = other;
gold_assert(save_res == p.first->second.save_res_);
if (p.second || (this->resizing_ && !p.first->second.iter_))
{
{
if (size == 64)
return (8
- + (this->targ_->abiversion() < 2 ? 11 * 4 : 14 * 4));
+ + (this->targ_->abiversion() < 2 ? 11 * 4
+ : this->targ_->has_localentry0() ? 14 * 4 : 13 * 4));
return 16 * 4;
}
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v1,
sizeof (glink_eh_frame_fde_64v1));
+ else if (this->targ_->has_localentry0())
+ 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_localentry0,
+ sizeof (glink_eh_frame_fde_64v2));
else
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
}
}
+ off += elfcpp::ppc64_decode_local_entry(p->second.other_);
if (off + (1 << 25) < 2 << 25)
return bytes + 4;
if (!this->targ_->power10_stubs()
}
if (bs->second.toc_)
{
+ delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
if (delta + (1 << 25) >= 2 << 25)
{
Address brlt_addr
}
else
{
+ if (!bs->second.notoc_)
+ delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
{
unsigned char* startp = p;
p = oview + off;
Address loc = this->stub_address() + off;
Address delta = bs->first.dest_ - loc;
+ if (!bs->second.notoc_)
+ delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
if (bs->second.notoc_)
{
unsigned char* startp = p;
}
else
{
+ if (this->targ_->has_localentry0())
+ {
+ write_insn<big_endian>(p, std_2_1 + 24), p += 4;
+ }
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, std_2_1 + 24), p += 4;
- write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_0), p += 4;
+ if (this->targ_->has_localentry0())
+ {
+ write_insn<big_endian>(p, ld_0_11 + l(-20)), p += 4;
+ }
+ else
+ {
+ write_insn<big_endian>(p, ld_0_11 + l(-16)), 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, add_11_0_11), p += 4;
+ write_insn<big_endian>(p, addi_0_12 + l(-44)), 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;
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
- this->maybe_skip_tls_get_addr_call(target, r_type, NULL);
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+
+ switch (this->maybe_skip_tls_get_addr_call(target, r_type, NULL))
+ {
+ case Track_tls::NOT_EXPECTED:
+ ppc_object->set_no_tls_marker();
+ break;
+ default:
+ break;
+ }
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();
+ if (!ppc_object->no_tls_marker())
+ {
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+ if (tls_type != tls::TLSOPT_NONE)
+ {
+ this->skip_next_tls_get_addr_call();
+ ppc_object->set_tls_marker();
+ }
+ }
}
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();
+ if (!ppc_object->no_tls_marker())
+ {
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ if (tls_type != tls::TLSOPT_NONE)
+ {
+ this->skip_next_tls_get_addr_call();
+ ppc_object->set_tls_marker();
+ }
+ }
}
- Powerpc_relobj<size, big_endian>* ppc_object
- = static_cast<Powerpc_relobj<size, big_endian>*>(object);
-
if (is_discarded)
{
if (size == 64
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!ppc_object->no_tls_marker())
+ tls_type = target->optimize_tls_gd(true);
if (tls_type == tls::TLSOPT_NONE)
{
Output_data_got_powerpc<size, big_endian>* got
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
+ ppc_object->set_tls_marker();
}
else
gold_unreachable();
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!ppc_object->no_tls_marker())
+ tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
target->tlsld_got_offset(symtab, layout, object);
else if (tls_type == tls::TLSOPT_TO_LE)
gold_assert(os != NULL);
os->set_needs_symtab_index();
}
+ ppc_object->set_tls_marker();
}
else
gold_unreachable();
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
if (tls_type == tls::TLSOPT_NONE)
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
if ((insn & ((0x3fu << 26) | 0x1f << 16))
!= ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
- target->set_tprel_opt(false);
+ target->set_no_tprel_opt();
}
}
break;
break;
// Fall through.
case elfcpp::R_POWERPC_TPREL16_HI:
- target->set_tprel_opt(false);
+ target->set_no_tprel_opt();
break;
default:
break;
case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
- target->set_power10_stubs();
+ target->set_power10_relocs();
break;
default:
break;
unsigned int r_type,
Symbol* gsym)
{
- if (this->maybe_skip_tls_get_addr_call(target, r_type, gsym)
- == Track_tls::SKIP)
- return;
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+
+ switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
+ {
+ case Track_tls::NOT_EXPECTED:
+ ppc_object->set_no_tls_marker();
+ break;
+ case Track_tls::SKIP:
+ return;
+ default:
+ break;
+ }
if (target->replace_tls_get_addr(gsym))
// Change a __tls_get_addr reference to __tls_get_addr_opt
|| (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();
+ if (!ppc_object->no_tls_marker())
+ {
+ bool final = gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ if (tls_type != tls::TLSOPT_NONE)
+ {
+ this->skip_next_tls_get_addr_call();
+ ppc_object->set_tls_marker();
+ }
+ }
}
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();
+ if (!ppc_object->no_tls_marker())
+ {
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ if (tls_type != tls::TLSOPT_NONE)
+ {
+ this->skip_next_tls_get_addr_call();
+ ppc_object->set_tls_marker();
+ }
+ }
}
- Powerpc_relobj<size, big_endian>* ppc_object
- = static_cast<Powerpc_relobj<size, big_endian>*>(object);
-
// A STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
bool pushed_ifunc = false;
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
{
- const bool final = gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!ppc_object->no_tls_marker())
+ {
+ bool final = gsym->final_value_is_known();
+ tls_type = target->optimize_tls_gd(final);
+ }
if (tls_type == tls::TLSOPT_NONE)
{
Output_data_got_powerpc<size, big_endian>* got
got, off, 0);
}
}
+ ppc_object->set_tls_marker();
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
+ ppc_object->set_tls_marker();
}
else
gold_unreachable();
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!ppc_object->no_tls_marker())
+ tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
target->tlsld_got_offset(symtab, layout, object);
else if (tls_type == tls::TLSOPT_TO_LE)
gold_assert(os != NULL);
os->set_needs_symtab_index();
}
+ ppc_object->set_tls_marker();
}
else
gold_unreachable();
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
{
- const bool final = gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+ bool final = gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_NONE)
{
if (!gsym->has_got_offset(GOT_TYPE_TPREL))
uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
if ((insn & ((0x3fu << 26) | 0x1f << 16))
!= ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
- target->set_tprel_opt(false);
+ target->set_no_tprel_opt();
}
}
break;
break;
// Fall through.
case elfcpp::R_POWERPC_TPREL16_HI:
- target->set_tprel_opt(false);
+ target->set_no_tprel_opt();
break;
default:
break;
case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
- target->set_power10_stubs();
+ target->set_power10_relocs();
break;
default:
break;
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
+
+ if (this->plt_localentry0_ && this->power10_relocs_)
+ {
+ gold_warning(_("--plt-localentry is incompatible with "
+ "power10 pc-relative code"));
+ this->plt_localentry0_ = false;
+ }
}
// Functor class for processing the global symbol table.
const elfcpp::Rela<size, big_endian> rela(preloc);
unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
+ Powerpc_relobj<size, big_endian>* const object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
{
case Track_tls::NOT_EXPECTED:
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("__tls_get_addr call lacks marker reloc"));
+ if (!parameters->options().shared()
+ && parameters->options().tls_optimize())
+ {
+ // It is a hard error to see a __tls_get_addr call without
+ // marker relocs after seeing calls with marker relocs in the
+ // same object file, because dynamic relocation accounting
+ // will be wrong.
+ if (object->tls_opt_error())
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("__tls_get_addr call lacks marker reloc"));
+ else
+ gold_warning_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("__tls_get_addr call lacks marker reloc"));
+ }
break;
case Track_tls::EXPECTED:
// We have already complained.
// Offset from start of insn to d-field reloc.
const int d_offset = big_endian ? 2 : 0;
- Powerpc_relobj<size, big_endian>* const object
- = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
bool has_stub_value = false;
bool localentry0 = false;
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
: object->local_has_plt_offset(r_sym));
if (has_plt_offset
+ && !is_got_reloc(r_type)
&& !is_plt16_reloc<size>(r_type)
&& r_type != elfcpp::R_PPC64_PLT_PCREL34
&& r_type != elfcpp::R_PPC64_PLT_PCREL34_NOTOC
value += ent->tocoff_;
if (size == 64
&& ent->r2save_
- && r_type == elfcpp::R_PPC64_REL24_NOTOC)
- {
- if (!(target->power10_stubs()
- && target->power10_stubs_auto()))
- value += 4;
- }
- else if (size == 64
- && ent->r2save_
- && relnum < reloc_count - 1)
+ && !(gsym != NULL
+ && target->is_tls_get_addr_opt(gsym)))
{
- Reltype next_rela(preloc + reloc_size);
- if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
- == elfcpp::R_PPC64_TOCSAVE
- && next_rela.get_r_offset() == rela.get_r_offset() + 4)
- value += 4;
+ if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ {
+ if (!(target->power10_stubs()
+ && target->power10_stubs_auto()))
+ value += 4;
+ }
+ else if (relnum < reloc_count - 1)
+ {
+ Reltype next_rela(preloc + reloc_size);
+ if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
+ == elfcpp::R_PPC64_TOCSAVE
+ && (next_rela.get_r_offset()
+ == rela.get_r_offset() + 4))
+ value += 4;
+ }
}
localentry0 = ent->localentry0_;
has_stub_value = true;
elfcpp::Swap<32, big_endian>::writeval(iview + 1, pnop & 0xffffffff);
r_type = elfcpp::R_POWERPC_NONE;
}
- else if (r_type == elfcpp::R_POWERPC_GOT16
- || r_type == elfcpp::R_POWERPC_GOT16_LO
- || r_type == elfcpp::R_POWERPC_GOT16_HI
- || r_type == elfcpp::R_POWERPC_GOT16_HA
- || r_type == elfcpp::R_PPC64_GOT16_DS
- || r_type == elfcpp::R_PPC64_GOT16_LO_DS
- || r_type == elfcpp::R_PPC64_GOT_PCREL34)
+ else if (is_got_reloc(r_type))
{
if (gsym != NULL)
{
|| r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
{
// First instruction of a global dynamic sequence, arg setup insn.
- const bool final = gsym == NULL || gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ {
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls_type = target->optimize_tls_gd(final);
+ }
enum Got_type got_type = GOT_TYPE_STANDARD;
if (tls_type == tls::TLSOPT_NONE)
got_type = GOT_TYPE_TLSGD;
|| r_type == elfcpp::R_PPC64_GOT_TLSLD_PCREL34)
{
// First instruction of a local dynamic sequence, arg setup insn.
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
{
value = target->tlsld_got_offset();
|| r_type == elfcpp::R_PPC64_GOT_TPREL_PCREL34)
{
// First instruction of initial exec sequence.
- const bool final = gsym == NULL || gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_NONE)
{
if (gsym != NULL)
// Second instruction of a global dynamic sequence,
// the __tls_get_addr call
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);
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ {
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls_type = target->optimize_tls_gd(final);
+ }
if (tls_type != tls::TLSOPT_NONE)
{
if (tls_type == tls::TLSOPT_TO_IE)
// Second instruction of a local dynamic sequence,
// the __tls_get_addr call
this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_TO_LE)
{
bool is_pcrel = false;
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();
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_TO_LE)
{
Address roff = rela.get_r_offset() & 3;
|| r_type == elfcpp::R_POWERPC_PLT16_HA)))
addend = rela.get_r_addend();
value = psymval->value(object, addend);
+ unsigned int local_ent = 0;
if (size == 64 && is_branch_reloc<size>(r_type))
{
if (target->abiversion() >= 2)
{
if (gsym != NULL)
- value += object->ppc64_local_entry_offset(gsym);
+ local_ent = object->ppc64_local_entry_offset(gsym);
else
- value += object->ppc64_local_entry_offset(r_sym);
+ local_ent = object->ppc64_local_entry_offset(r_sym);
}
else
{
&value, &dest_shndx);
}
}
- Address max_branch_offset = max_branch_delta<size>(r_type);
- if (max_branch_offset != 0
- && (value - address + max_branch_offset >= 2 * max_branch_offset
+ Address max_branch = max_branch_delta<size>(r_type);
+ if (max_branch != 0
+ && (value + local_ent - address + max_branch >= 2 * max_branch
|| (size == 64
&& r_type == elfcpp::R_PPC64_REL24_NOTOC
&& (gsym != NULL
{
if (ent->save_res_)
value = (value - target->savres_section()->address()
+ + stub_table->stub_address()
+ + stub_table->plt_size()
+ stub_table->branch_size());
else
{
}
}
}
+ if (!has_stub_value)
+ value += local_ent;
}
switch (r_type)
loc.object = relinfo->object;
loc.shndx = relinfo->data_shndx;
loc.offset = rela.get_r_offset();
- Tocsave_loc::const_iterator p = target->tocsave_loc().find(loc);
- if (p != target->tocsave_loc().end())
+ const Tocsave_loc *tocsave = target->tocsave_loc();
+ if (tocsave->find(loc) != tocsave->end())
{
// If we've generated plt calls using this tocsave, then
// the nop needs to be changed to save r2.
{
// 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))
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ {
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls_type = this->optimize_tls_gd(final);
+ }
+ switch (tls_type)
{
case tls::TLSOPT_TO_IE:
r_type += (elfcpp::R_POWERPC_GOT_TPREL16
{
// First instruction of a local dynamic sequence,
// arg setup insn.
- if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ tls_type = this->optimize_tls_ld();
+ if (tls_type == 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_GOT_TPREL16_HA)
{
// First instruction of initial exec sequence.
- const bool final = gsym == NULL || gsym->final_value_is_known();
+ 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
{
// 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))
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ {
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls_type = this->optimize_tls_gd(final);
+ }
+ switch (tls_type)
{
case tls::TLSOPT_TO_IE:
r_type = elfcpp::R_POWERPC_NONE;
{
// Second instruction of a local dynamic sequence,
// the __tls_get_addr call
- if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+ tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+ if (!object->no_tls_marker())
+ tls_type = this->optimize_tls_ld();
+ if (tls_type == tls::TLSOPT_TO_LE)
{
const Output_section* os = relinfo->layout->tls_segment()
->first_section();
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();
+ 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;