add_local_tls_pair): New functions.
(Output_data_got::add_local_pair_with_rel): Remove second
reloc param. Expand comment.
(Output_data_got::Got_entry): Rename use_plt_offset_ to
use_plt_or_tls_offset_, similarly for constructor param.
(Output_data_got::Got_entry::write): Add got_index param.
* output.cc (Output_data_got::add_global_tls, add_local_tls,
add_local_tls_pair): New functions.
(Output_data_got::Got_entry::write): Handle tls symbols
with use_plt_or_tls_offset_ set specially.
(Output_data_got::add_local_pair_with_rel): Only one reloc.
(Output_data_got::do_write): Replace iterator with index, pass
index to entry write function.
* target.h (Target::tls_offset_for_local, tls_offset_for_global,
do_tls_offset_for_local, do_tls_offset_for_global): New functions.
* arm.cc (Target_arm::Scan::local): Update add_local_pair_with_rel
call.
* i386.cc (Target_i386::Scan::local): Likewise.
* sparc.cc (Target_sparc::Scan::local): Likewise.
* x86_64.cc (Target_x86_64::Scan::local): Likewise.
* powerpc.cc (Target_powerpc::do_tls_offset_for_local,
do_tls_offset_for_global): New functions.
(Target_powerpc::Scan::local): Correct TLS relocations and got
entry values.
(Target_powerpc::Scan::global): Don't emit unnecessary
dynamic relocations on TLS GOT entries.
+2012-09-11 Alan Modra <amodra@gmail.com>
+
+ * output.h (Output_data_got::add_global_tls, add_local_tls,
+ add_local_tls_pair): New functions.
+ (Output_data_got::add_local_pair_with_rel): Remove second
+ reloc param. Expand comment.
+ (Output_data_got::Got_entry): Rename use_plt_offset_ to
+ use_plt_or_tls_offset_, similarly for constructor param.
+ (Output_data_got::Got_entry::write): Add got_index param.
+ * output.cc (Output_data_got::add_global_tls, add_local_tls,
+ add_local_tls_pair): New functions.
+ (Output_data_got::Got_entry::write): Handle tls symbols
+ with use_plt_or_tls_offset_ set specially.
+ (Output_data_got::add_local_pair_with_rel): Only one reloc.
+ (Output_data_got::do_write): Replace iterator with index, pass
+ index to entry write function.
+ * target.h (Target::tls_offset_for_local, tls_offset_for_global,
+ do_tls_offset_for_local, do_tls_offset_for_global): New functions.
+ * arm.cc (Target_arm::Scan::local): Update add_local_pair_with_rel
+ call.
+ * i386.cc (Target_i386::Scan::local): Likewise.
+ * sparc.cc (Target_sparc::Scan::local): Likewise.
+ * x86_64.cc (Target_x86_64::Scan::local): Likewise.
+ * powerpc.cc (Target_powerpc::do_tls_offset_for_local,
+ do_tls_offset_for_global): New functions.
+ (Target_powerpc::Scan::local): Correct TLS relocations and got
+ entry values.
+ (Target_powerpc::Scan::global): Don't emit unnecessary
+ dynamic relocations on TLS GOT entries.
+
2012-09-10 Matthias Klose <doko@ubuntu.com>
* config.in: Disable sanity check for kfreebsd.
got->add_local_pair_with_rel(object, r_sym, shndx,
GOT_TYPE_TLS_PAIR,
target->rel_dyn_section(layout),
- elfcpp::R_ARM_TLS_DTPMOD32, 0);
+ elfcpp::R_ARM_TLS_DTPMOD32);
else
got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR,
object, r_sym);
got->add_local_pair_with_rel(object, r_sym, shndx,
GOT_TYPE_TLS_PAIR,
target->rel_dyn_section(layout),
- elfcpp::R_386_TLS_DTPMOD32, 0);
+ elfcpp::R_386_TLS_DTPMOD32);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
template<int size, bool big_endian>
void
-Output_data_got<size, big_endian>::Got_entry::write(unsigned char* pov) const
+Output_data_got<size, big_endian>::Got_entry::write(
+ unsigned int got_indx,
+ unsigned char* pov) const
{
Valtype val = 0;
// link-time value, which will be relocated dynamically by a
// RELATIVE relocation.
Symbol* gsym = this->u_.gsym;
- if (this->use_plt_offset_ && gsym->has_plt_offset())
+ if (this->use_plt_or_tls_offset_ && gsym->has_plt_offset())
val = (parameters->target().plt_address_for_global(gsym)
+ gsym->plt_offset());
else
// as small as possible.
sgsym = static_cast<Sized_symbol<size>*>(gsym);
val = sgsym->value();
+ if (this->use_plt_or_tls_offset_ && gsym->type() == elfcpp::STT_TLS)
+ val += parameters->target().tls_offset_for_global(gsym,
+ got_indx);
}
}
break;
default:
{
- const Relobj* object = this->u_.object;
+ const Sized_relobj_file<size, big_endian>* object
+ = static_cast<Sized_relobj_file<size, big_endian>*>(this->u_.object);
const unsigned int lsi = this->local_sym_index_;
- if (!this->use_plt_offset_)
- {
- uint64_t lval = object->local_symbol_value(lsi, 0);
- val = convert_types<Valtype, uint64_t>(lval);
- }
- else
+ bool is_tls = object->local_symbol(lsi)->is_tls_symbol();
+ if (this->use_plt_or_tls_offset_ && !is_tls)
{
uint64_t plt_address =
parameters->target().plt_address_for_local(object, lsi);
val = plt_address + object->local_plt_offset(lsi);
}
+ else
+ {
+ uint64_t lval = object->local_symbol_value(lsi, 0);
+ val = convert_types<Valtype, uint64_t>(lval);
+ if (this->use_plt_or_tls_offset_ && is_tls)
+ val += parameters->target().tls_offset_for_local(object, lsi,
+ got_indx);
+ }
}
break;
}
}
// Add a pair of entries for a local symbol to the GOT, and add
-// dynamic relocations of type R_TYPE_1 and R_TYPE_2, respectively.
-// If R_TYPE_2 == 0, add the second entry with no relocation.
+// a dynamic relocation of type R_TYPE using the section symbol of
+// the output section to which input section SHNDX maps, on the first.
+// The first got entry will have a value of zero, the second the
+// value of the local symbol.
template<int size, bool big_endian>
void
Output_data_got<size, big_endian>::add_local_pair_with_rel(
unsigned int shndx,
unsigned int got_type,
Output_data_reloc_generic* rel_dyn,
- unsigned int r_type_1,
- unsigned int r_type_2)
+ unsigned int r_type)
{
if (object->local_has_got_offset(symndx, got_type))
return;
Got_entry(object, symndx, false));
object->set_local_got_offset(symndx, got_type, got_offset);
Output_section* os = object->output_section(shndx);
- rel_dyn->add_output_section_generic(os, r_type_1, this, got_offset, 0);
+ rel_dyn->add_output_section_generic(os, r_type, this, got_offset, 0);
+}
- if (r_type_2 != 0)
- rel_dyn->add_output_section_generic(os, r_type_2, this,
- got_offset + size / 8, 0);
+// Add a pair of entries for a local symbol to the GOT, and add
+// a dynamic relocation of type R_TYPE using STN_UNDEF on the first.
+// The first got entry will have a value of zero, the second the
+// value of the local symbol offset by Target::tls_offset_for_local.
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_local_tls_pair(
+ Relobj* object,
+ unsigned int symndx,
+ unsigned int got_type,
+ Output_data_reloc_generic* rel_dyn,
+ unsigned int r_type)
+{
+ if (object->local_has_got_offset(symndx, got_type))
+ return;
+
+ unsigned int got_offset
+ = this->add_got_entry_pair(Got_entry(),
+ Got_entry(object, symndx, true));
+ object->set_local_got_offset(symndx, got_type, got_offset);
+ rel_dyn->add_local_generic(object, 0, r_type, this, got_offset, 0);
}
// Reserve a slot in the GOT for a local symbol or the second slot of a pair.
unsigned char* const oview = of->get_output_view(off, oview_size);
unsigned char* pov = oview;
- for (typename Got_entries::const_iterator p = this->entries_.begin();
- p != this->entries_.end();
- ++p)
+ for (unsigned int i = 0; i < this->entries_.size(); ++i)
{
- p->write(pov);
+ this->entries_[i].write(i, pov);
pov += add;
}
bool
add_global_plt(Symbol* gsym, unsigned int got_type);
+ // Like add_global, but for a TLS symbol where the value will be
+ // offset using Target::tls_offset_for_global
+ bool
+ add_global_tls(Symbol* gsym, unsigned int got_type)
+ { return add_global_plt(gsym, got_type); }
+
// Add an entry for a global symbol to the GOT, and add a dynamic
// relocation of type R_TYPE for the GOT entry.
void
bool
add_local_plt(Relobj* object, unsigned int sym_index, unsigned int got_type);
+ // Like add_local, but for a TLS symbol where the value will be
+ // offset using Target::tls_offset_for_local
+ bool
+ add_local_tls(Relobj* object, unsigned int sym_index, unsigned int got_type)
+ { return add_local_plt(object, sym_index, got_type); }
+
// Add an entry for a local symbol to the GOT, and add a dynamic
// relocation of type R_TYPE for the GOT entry.
void
unsigned int r_type);
// Add a pair of entries for a local symbol to the GOT, and add
- // dynamic relocations of type R_TYPE_1 and R_TYPE_2, respectively.
+ // a dynamic relocation of type R_TYPE using the section symbol of
+ // the output section to which input section SHNDX maps, on the first.
+ // The first got entry will have a value of zero, the second the
+ // value of the local symbol.
void
add_local_pair_with_rel(Relobj* object, unsigned int sym_index,
unsigned int shndx, unsigned int got_type,
Output_data_reloc_generic* rel_dyn,
- unsigned int r_type_1, unsigned int r_type_2);
+ unsigned int r_type);
+
+ // Add a pair of entries for a local symbol to the GOT, and add
+ // a dynamic relocation of type R_TYPE using STN_UNDEF on the first.
+ // The first got entry will have a value of zero, the second the
+ // value of the local symbol offset by Target::tls_offset_for_local.
+ void
+ add_local_tls_pair(Relobj* object, unsigned int sym_index,
+ unsigned int got_type,
+ Output_data_reloc_generic* rel_dyn,
+ unsigned int r_type);
// Add a constant to the GOT. This returns the offset of the new
// entry from the start of the GOT.
public:
// Create a zero entry.
Got_entry()
- : local_sym_index_(RESERVED_CODE), use_plt_offset_(false)
+ : local_sym_index_(RESERVED_CODE), use_plt_or_tls_offset_(false)
{ this->u_.constant = 0; }
// Create a global symbol entry.
- Got_entry(Symbol* gsym, bool use_plt_offset)
- : local_sym_index_(GSYM_CODE), use_plt_offset_(use_plt_offset)
+ Got_entry(Symbol* gsym, bool use_plt_or_tls_offset)
+ : local_sym_index_(GSYM_CODE),
+ use_plt_or_tls_offset_(use_plt_or_tls_offset)
{ this->u_.gsym = gsym; }
// Create a local symbol entry.
Got_entry(Relobj* object, unsigned int local_sym_index,
- bool use_plt_offset)
- : local_sym_index_(local_sym_index), use_plt_offset_(use_plt_offset)
+ bool use_plt_or_tls_offset)
+ : local_sym_index_(local_sym_index),
+ use_plt_or_tls_offset_(use_plt_or_tls_offset)
{
gold_assert(local_sym_index != GSYM_CODE
&& local_sym_index != CONSTANT_CODE
// Create a constant entry. The constant is a host value--it will
// be swapped, if necessary, when it is written out.
explicit Got_entry(Valtype constant)
- : local_sym_index_(CONSTANT_CODE), use_plt_offset_(false)
+ : local_sym_index_(CONSTANT_CODE), use_plt_or_tls_offset_(false)
{ this->u_.constant = constant; }
// Write the GOT entry to an output view.
void
- write(unsigned char* pov) const;
+ write(unsigned int got_indx, unsigned char* pov) const;
private:
enum
// for a global symbol, or CONSTANT_CODE for a constant.
unsigned int local_sym_index_ : 31;
// Whether to use the PLT offset of the symbol if it has one.
- bool use_plt_offset_ : 1;
+ // For TLS symbols, whether to offset the symbol value.
+ bool use_plt_or_tls_offset_ : 1;
};
typedef std::vector<Got_entry> Got_entries;
uint64_t
do_dynsym_value(const Symbol*) const;
+ // Return the offset to use for the GOT_INDX'th got entry which is
+ // for a local tls symbol specified by OBJECT, SYMNDX.
+ int64_t
+ do_tls_offset_for_local(const Relobj* object,
+ unsigned int symndx,
+ unsigned int got_indx) const;
+
+ // Return the offset to use for the GOT_INDX'th got entry which is
+ // for global tls symbol GSYM.
+ int64_t
+ do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
+
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& lsym)
+ const elfcpp::Sym<size, big_endian>& /* lsym */)
{
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
- unsigned int shndx = lsym.get_st_shndx();
- bool is_ordinary;
- shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
- gold_assert(is_ordinary);
- got->add_local_pair_with_rel(object, r_sym,
- shndx,
- GOT_TYPE_TLSGD,
- target->rela_dyn_section(layout),
- elfcpp::R_POWERPC_DTPMOD,
- elfcpp::R_POWERPC_DTPREL);
+ Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ got->add_local_tls_pair(object, r_sym, GOT_TYPE_TLSGD,
+ rela_dyn, elfcpp::R_POWERPC_DTPMOD);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
- got->add_local_with_rel(object, r_sym, GOT_TYPE_DTPREL,
- target->rela_dyn_section(layout),
- elfcpp::R_POWERPC_DTPREL);
+ got->add_local_tls(object, r_sym, GOT_TYPE_DTPREL);
}
break;
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
- got->add_local_with_rel(object, r_sym, GOT_TYPE_TPREL,
- target->rela_dyn_section(layout),
- elfcpp::R_POWERPC_TPREL);
+ got->add_local_tls(object, r_sym, GOT_TYPE_TPREL);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
- got->add_global_with_rel(gsym, GOT_TYPE_DTPREL,
- target->rela_dyn_section(layout),
- elfcpp::R_POWERPC_DTPREL);
+ if (!gsym->final_value_is_known()
+ && (gsym->is_from_dynobj()
+ || gsym->is_undefined()
+ || gsym->is_preemptible()))
+ got->add_global_with_rel(gsym, GOT_TYPE_DTPREL,
+ target->rela_dyn_section(layout),
+ elfcpp::R_POWERPC_DTPREL);
+ else
+ got->add_global_tls(gsym, GOT_TYPE_DTPREL);
}
break;
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
- got->add_global_with_rel(gsym, GOT_TYPE_TPREL,
- target->rela_dyn_section(layout),
- elfcpp::R_POWERPC_TPREL);
+ if (!gsym->final_value_is_known()
+ && (gsym->is_from_dynobj()
+ || gsym->is_undefined()
+ || gsym->is_preemptible()))
+ got->add_global_with_rel(gsym, GOT_TYPE_TPREL,
+ target->rela_dyn_section(layout),
+ elfcpp::R_POWERPC_TPREL);
+ else
+ got->add_global_tls(gsym, GOT_TYPE_TPREL);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
gold_unreachable();
}
+// Return the offset to use for the GOT_INDX'th got entry which is
+// for a local tls symbol specified by OBJECT, SYMNDX.
+template<int size, bool big_endian>
+int64_t
+Target_powerpc<size, big_endian>::do_tls_offset_for_local(
+ const Relobj* object,
+ unsigned int symndx,
+ unsigned int got_indx) const
+{
+ const Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<const Powerpc_relobj<size, big_endian>*>(object);
+ if (ppc_object->local_symbol(symndx)->is_tls_symbol())
+ {
+ for (Got_type got_type = GOT_TYPE_TLSGD;
+ got_type <= GOT_TYPE_TPREL;
+ got_type = Got_type(got_type + 1))
+ if (ppc_object->local_has_got_offset(symndx, got_type))
+ {
+ unsigned int off = ppc_object->local_got_offset(symndx, got_type);
+ if (got_type == GOT_TYPE_TLSGD)
+ off += size / 8;
+ if (off == got_indx * (size / 8))
+ {
+ if (got_type == GOT_TYPE_TPREL)
+ return -tp_offset;
+ else
+ return -dtp_offset;
+ }
+ }
+ }
+ gold_unreachable();
+}
+
+// Return the offset to use for the GOT_INDX'th got entry which is
+// for global tls symbol GSYM.
+template<int size, bool big_endian>
+int64_t
+Target_powerpc<size, big_endian>::do_tls_offset_for_global(
+ Symbol* gsym,
+ unsigned int got_indx) const
+{
+ if (gsym->type() == elfcpp::STT_TLS)
+ {
+ for (Got_type got_type = GOT_TYPE_TLSGD;
+ got_type <= GOT_TYPE_TPREL;
+ got_type = Got_type(got_type + 1))
+ if (gsym->has_got_offset(got_type))
+ {
+ unsigned int off = gsym->got_offset(got_type);
+ if (got_type == GOT_TYPE_TLSGD)
+ off += size / 8;
+ if (off == got_indx * (size / 8))
+ {
+ if (got_type == GOT_TYPE_TPREL)
+ return -tp_offset;
+ else
+ return -dtp_offset;
+ }
+ }
+ }
+ gold_unreachable();
+}
+
// The selector for powerpc object files.
template<int size, bool big_endian>
target->rela_dyn_section(layout),
(size == 64
? elfcpp::R_SPARC_TLS_DTPMOD64
- : elfcpp::R_SPARC_TLS_DTPMOD32),
- 0);
+ : elfcpp::R_SPARC_TLS_DTPMOD32));
if (r_type == elfcpp::R_SPARC_TLS_GD_CALL)
generate_tls_call(symtab, layout, target);
}
plt_address_for_local(const Relobj* object, unsigned int symndx) const
{ return this->do_plt_address_for_local(object, symndx); }
+ // Return the offset to use for the GOT_INDX'th got entry which is
+ // for a local tls symbol specified by OBJECT, SYMNDX.
+ int64_t
+ tls_offset_for_local(const Relobj* object,
+ unsigned int symndx,
+ unsigned int got_indx) const
+ { return do_tls_offset_for_local(object, symndx, got_indx); }
+
+ // Return the offset to use for the GOT_INDX'th got entry which is
+ // for global tls symbol GSYM.
+ int64_t
+ tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const
+ { return do_tls_offset_for_global(gsym, got_indx); }
+
// Return whether this target can use relocation types to determine
// if a function's address is taken.
bool
do_plt_address_for_local(const Relobj*, unsigned int) const
{ gold_unreachable(); }
+ virtual int64_t
+ do_tls_offset_for_local(const Relobj*, unsigned int, unsigned int) const
+ { gold_unreachable(); }
+
+ virtual int64_t
+ do_tls_offset_for_global(Symbol*, unsigned int) const
+ { gold_unreachable(); }
+
// Virtual function which may be overriden by the child class.
virtual bool
do_can_check_for_function_pointers() const
shndx,
GOT_TYPE_TLS_PAIR,
target->rela_dyn_section(layout),
- elfcpp::R_X86_64_DTPMOD64, 0);
+ elfcpp::R_X86_64_DTPMOD64);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);