// i386.cc -- i386 target support for gold.
-// Copyright 2006, 2007 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
// This file is part of gold.
#include "symtab.h"
#include "layout.h"
#include "output.h"
+#include "copy-relocs.h"
#include "target.h"
#include "target-reloc.h"
#include "target-select.h"
#include "tls.h"
+#include "freebsd.h"
+#include "gc.h"
namespace
{
// http://people.redhat.com/drepper/tls.pdf
// http://www.lsd.ic.unicamp.br/~oliva/writeups/TLS/RFC-TLSDESC-x86.txt
-class Target_i386 : public Sized_target<32, false>
+class Target_i386 : public Target_freebsd<32, false>
{
public:
typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section;
Target_i386()
- : Sized_target<32, false>(&i386_info),
- got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
- copy_relocs_(NULL), dynbss_(NULL)
+ : Target_freebsd<32, false>(&i386_info),
+ got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL),
+ global_offset_table_(NULL), rel_dyn_(NULL),
+ copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL),
+ got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
{ }
+ inline bool
+ can_check_for_function_pointers() const
+ { return true; }
+
+ // Process the relocations to determine unreferenced sections for
+ // garbage collection.
+ void
+ gc_process_relocs(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj<32, false>* object,
+ unsigned int data_shndx,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ size_t local_symbol_count,
+ const unsigned char* plocal_symbols);
+
// Scan the relocations to look for symbol adjustments.
void
- scan_relocs(const General_options& options,
- Symbol_table* symtab,
+ scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
// Finalize the sections.
void
- do_finalize_sections(Layout*);
+ do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
// Return the value to use for a dynamic which requires special
// treatment.
bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr view_address,
- off_t view_size);
+ section_size_type view_size,
+ const Reloc_symbol_changes*);
+
+ // Scan the relocs during a relocatable link.
+ void
+ scan_relocatable_relocs(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj<32, false>* object,
+ unsigned int data_shndx,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ size_t local_symbol_count,
+ const unsigned char* plocal_symbols,
+ Relocatable_relocs*);
+
+ // Relocate a section during a relocatable link.
+ void
+ relocate_for_relocatable(const Relocate_info<32, false>*,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ off_t offset_in_output_section,
+ const Relocatable_relocs*,
+ unsigned char* view,
+ elfcpp::Elf_types<32>::Elf_Addr view_address,
+ section_size_type view_size,
+ unsigned char* reloc_view,
+ section_size_type reloc_view_size);
// Return a string used to fill a code section with nops.
std::string
- do_code_fill(off_t length);
+ do_code_fill(section_size_type length) const;
+
+ // Return whether SYM is defined by the ABI.
+ bool
+ do_is_defined_by_abi(const Symbol* sym) const
+ { return strcmp(sym->name(), "___tls_get_addr") == 0; }
+
+ // Return whether a symbol name implies a local label. The UnixWare
+ // 2.1 cc generates temporary symbols that start with .X, so we
+ // recognize them here. FIXME: do other SVR4 compilers also use .X?.
+ // If so, we should move the .X recognition into
+ // Target::do_is_local_label_name.
+ bool
+ do_is_local_label_name(const char* name) const
+ {
+ if (name[0] == '.' && name[1] == 'X')
+ return true;
+ return Target::do_is_local_label_name(name);
+ }
+
+ // Return whether SYM is call to a non-split function.
+ bool
+ do_is_call_to_non_split(const Symbol* sym, unsigned int) const;
+
+ // Adjust -fstack-split code which calls non-stack-split code.
+ void
+ do_calls_non_split(Relobj* object, unsigned int shndx,
+ section_offset_type fnoffset, section_size_type fnsize,
+ unsigned char* view, section_size_type view_size,
+ std::string* from, std::string* to) const;
// Return the size of the GOT section.
- off_t
- got_size()
+ section_size_type
+ got_size() const
{
gold_assert(this->got_ != NULL);
return this->got_->data_size();
}
+ // Return the number of entries in the GOT.
+ unsigned int
+ got_entry_count() const
+ {
+ if (this->got_ == NULL)
+ return 0;
+ return this->got_size() / 4;
+ }
+
+ // Return the number of entries in the PLT.
+ unsigned int
+ plt_entry_count() const;
+
+ // Return the offset of the first non-reserved PLT entry.
+ unsigned int
+ first_plt_entry_offset() const;
+
+ // Return the size of each PLT entry.
+ unsigned int
+ plt_entry_size() const;
+
private:
// The class which scans relocations.
struct Scan
{
inline void
- local(const General_options& options, Symbol_table* symtab,
- Layout* layout, Target_i386* target,
+ local(Symbol_table* symtab, Layout* layout, Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
+ Output_section* output_section,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
const elfcpp::Sym<32, false>& lsym);
inline void
- global(const General_options& options, Symbol_table* symtab,
- Layout* layout, Target_i386* target,
+ global(Symbol_table* symtab, Layout* layout, Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
+ Output_section* output_section,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
Symbol* gsym);
+ inline bool
+ local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout,
+ Target_i386* target,
+ Sized_relobj<32, false>* object,
+ unsigned int data_shndx,
+ Output_section* output_section,
+ const elfcpp::Rel<32, false>& reloc,
+ unsigned int r_type,
+ const elfcpp::Sym<32, false>& lsym);
+
+ inline bool
+ global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout,
+ Target_i386* target,
+ Sized_relobj<32, false>* object,
+ unsigned int data_shndx,
+ Output_section* output_section,
+ const elfcpp::Rel<32, false>& reloc,
+ unsigned int r_type,
+ Symbol* gsym);
+
+ inline bool
+ possible_function_pointer_reloc(unsigned int r_type);
+
static void
unsupported_reloc_local(Sized_relobj<32, false>*, unsigned int r_type);
// Return whether the static relocation needs to be applied.
inline bool
should_apply_static_reloc(const Sized_symbol<32>* gsym,
- bool is_pcrel,
- bool is_32bit);
+ int ref_flags,
+ bool is_32bit,
+ Output_section* output_section);
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
- relocate(const Relocate_info<32, false>*, Target_i386*, size_t relnum,
- const elfcpp::Rel<32, false>&,
+ relocate(const Relocate_info<32, false>*, Target_i386*, Output_section*,
+ size_t relnum, const elfcpp::Rel<32, false>&,
unsigned int r_type, const Sized_symbol<32>*,
const Symbol_value<32>*,
unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
- off_t);
+ section_size_type);
private:
// Do a TLS relocation.
inline void
- relocate_tls(const Relocate_info<32, false>*, size_t relnum,
- const elfcpp::Rel<32, false>&,
+ relocate_tls(const Relocate_info<32, false>*, Target_i386* target,
+ size_t relnum, const elfcpp::Rel<32, false>&,
unsigned int r_type, const Sized_symbol<32>*,
const Symbol_value<32>*,
- unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t);
+ unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
+ section_size_type);
+
+ // Do a TLS General-Dynamic to Initial-Exec transition.
+ inline void
+ tls_gd_to_ie(const Relocate_info<32, false>*, size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>&, unsigned int r_type,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ section_size_type view_size);
// Do a TLS General-Dynamic to Local-Exec transition.
inline void
const elfcpp::Rel<32, false>&, unsigned int r_type,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
- off_t view_size);
+ section_size_type view_size);
+
+ // Do a TLS_GOTDESC or TLS_DESC_CALL General-Dynamic to Initial-Exec
+ // transition.
+ inline void
+ tls_desc_gd_to_ie(const Relocate_info<32, false>*, size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>&, unsigned int r_type,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ section_size_type view_size);
+
+ // Do a TLS_GOTDESC or TLS_DESC_CALL General-Dynamic to Local-Exec
+ // transition.
+ inline void
+ tls_desc_gd_to_le(const Relocate_info<32, false>*, size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>&, unsigned int r_type,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ section_size_type view_size);
// Do a TLS Local-Dynamic to Local-Exec transition.
inline void
const elfcpp::Rel<32, false>&, unsigned int r_type,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
- off_t view_size);
+ section_size_type view_size);
// Do a TLS Initial-Exec to Local-Exec transition.
static inline void
const elfcpp::Rel<32, false>&, unsigned int r_type,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
- off_t view_size);
+ section_size_type view_size);
// We need to keep track of which type of local dynamic relocation
// we have seen, so that we can optimize R_386_TLS_LDO_32 correctly.
Local_dynamic_type local_dynamic_type_;
};
+ // A class which returns the size required for a relocation type,
+ // used while scanning relocs during a relocatable link.
+ class Relocatable_size_for_reloc
+ {
+ public:
+ unsigned int
+ get_size_for_reloc(unsigned int, Relobj*);
+ };
+
// Adjust TLS relocation type based on the options and whether this
// is a local symbol.
static tls::Tls_optimization
return this->got_plt_;
}
+ // Get the GOT section for TLSDESC entries.
+ Output_data_got<32, false>*
+ got_tlsdesc_section() const
+ {
+ gold_assert(this->got_tlsdesc_ != NULL);
+ return this->got_tlsdesc_;
+ }
+
// Create a PLT entry for a global symbol.
void
make_plt_entry(Symbol_table*, Layout*, Symbol*);
+ // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
+ void
+ define_tls_base_symbol(Symbol_table*, Layout*);
+
+ // Create a GOT entry for the TLS module index.
+ unsigned int
+ got_mod_index_entry(Symbol_table* symtab, Layout* layout,
+ Sized_relobj<32, false>* object);
+
// Get the PLT section.
- const Output_data_plt_i386*
+ Output_data_plt_i386*
plt_section() const
{
gold_assert(this->plt_ != NULL);
Reloc_section*
rel_dyn_section(Layout*);
- // Copy a relocation against a global symbol.
+ // Get the section to use for TLS_DESC relocations.
+ Reloc_section*
+ rel_tls_desc_section(Layout*) const;
+
+ // Add a potential copy relocation.
void
- copy_reloc(const General_options*, Symbol_table*, Layout*,
- Sized_relobj<32, false>*, unsigned int,
- Symbol*, const elfcpp::Rel<32, false>&);
+ copy_reloc(Symbol_table* symtab, Layout* layout,
+ Sized_relobj<32, false>* object,
+ unsigned int shndx, Output_section* output_section,
+ Symbol* sym, const elfcpp::Rel<32, false>& reloc)
+ {
+ this->copy_relocs_.copy_reloc(symtab, layout,
+ symtab->get_sized_symbol<32>(sym),
+ object, shndx, output_section, reloc,
+ this->rel_dyn_section(layout));
+ }
// Information about this specific target which we pass to the
// general Target structure.
static const Target::Target_info i386_info;
+ // The types of GOT entries needed for this platform.
+ // These values are exposed to the ABI in an incremental link.
+ // Do not renumber existing values without changing the version
+ // number of the .gnu_incremental_inputs section.
+ enum Got_type
+ {
+ GOT_TYPE_STANDARD = 0, // GOT entry for a regular symbol
+ GOT_TYPE_TLS_NOFFSET = 1, // GOT entry for negative TLS offset
+ GOT_TYPE_TLS_OFFSET = 2, // GOT entry for positive TLS offset
+ GOT_TYPE_TLS_PAIR = 3, // GOT entry for TLS module/offset pair
+ GOT_TYPE_TLS_DESC = 4 // GOT entry for TLS_DESC pair
+ };
+
// The GOT section.
Output_data_got<32, false>* got_;
// The PLT section.
Output_data_plt_i386* plt_;
// The GOT PLT section.
Output_data_space* got_plt_;
+ // The GOT section for TLSDESC relocations.
+ Output_data_got<32, false>* got_tlsdesc_;
+ // The _GLOBAL_OFFSET_TABLE_ symbol.
+ Symbol* global_offset_table_;
// The dynamic reloc section.
Reloc_section* rel_dyn_;
// Relocs saved to avoid a COPY reloc.
- Copy_relocs<32, false>* copy_relocs_;
+ Copy_relocs<elfcpp::SHT_REL, 32, false> copy_relocs_;
// Space for variables copied with a COPY reloc.
Output_data_space* dynbss_;
+ // Offset of the GOT entry for the TLS module index.
+ unsigned int got_mod_index_offset_;
+ // True if the _TLS_MODULE_BASE_ symbol has been defined.
+ bool tls_base_symbol_defined_;
};
const Target::Target_info Target_i386::i386_info =
false, // has_resolve
true, // has_code_fill
true, // is_default_stack_executable
+ '\0', // wrap_char
"/usr/lib/libc.so.1", // dynamic_linker
0x08048000, // default_text_segment_address
- 0x1000, // abi_pagesize
- 0x1000 // common_pagesize
+ 0x1000, // abi_pagesize (overridable by -z max-page-size)
+ 0x1000, // common_pagesize (overridable by -z common-page-size)
+ elfcpp::SHN_UNDEF, // small_common_shndx
+ elfcpp::SHN_UNDEF, // large_common_shndx
+ 0, // small_common_section_flags
+ 0, // large_common_section_flags
+ NULL, // attributes_section
+ NULL // attributes_vendor
};
// Get the GOT section, creating it if necessary.
this->got_ = new Output_data_got<32, false>();
layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
- elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
- this->got_);
-
- // The old GNU linker creates a .got.plt section. We just
- // create another set of data in the .got section. Note that we
- // always create a PLT if we create a GOT, although the PLT
- // might be empty.
- this->got_plt_ = new Output_data_space(4);
- layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
- elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
- this->got_plt_);
+ (elfcpp::SHF_ALLOC
+ | elfcpp::SHF_WRITE),
+ this->got_, ORDER_RELRO_LAST, true);
+
+ this->got_plt_ = new Output_data_space(4, "** GOT PLT");
+ layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC
+ | elfcpp::SHF_WRITE),
+ this->got_plt_, ORDER_NON_RELRO_FIRST,
+ false);
// The first three entries are reserved.
- this->got_plt_->set_space_size(3 * 4);
+ this->got_plt_->set_current_data_size(3 * 4);
+
+ // Those bytes can go into the relro segment.
+ layout->increase_relro(3 * 4);
// Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT.
- symtab->define_in_output_data(this, "_GLOBAL_OFFSET_TABLE_", NULL,
- this->got_plt_,
- 0, 0, elfcpp::STT_OBJECT,
- elfcpp::STB_LOCAL,
- elfcpp::STV_HIDDEN, 0,
- false, false);
+ this->global_offset_table_ =
+ symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
+ Symbol_table::PREDEFINED,
+ this->got_plt_,
+ 0, 0, elfcpp::STT_OBJECT,
+ elfcpp::STB_LOCAL,
+ elfcpp::STV_HIDDEN, 0,
+ false, false);
+
+ // If there are any TLSDESC relocations, they get GOT entries in
+ // .got.plt after the jump slot entries.
+ this->got_tlsdesc_ = new Output_data_got<32, false>();
+ layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC
+ | elfcpp::SHF_WRITE),
+ this->got_tlsdesc_,
+ ORDER_NON_RELRO_FIRST, false);
}
return this->got_;
if (this->rel_dyn_ == NULL)
{
gold_assert(layout != NULL);
- this->rel_dyn_ = new Reloc_section();
+ this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
- elfcpp::SHF_ALLOC, this->rel_dyn_);
+ elfcpp::SHF_ALLOC, this->rel_dyn_,
+ ORDER_DYNAMIC_RELOCS, false);
}
return this->rel_dyn_;
}
rel_plt() const
{ return this->rel_; }
+ // Return where the TLS_DESC relocations should go.
+ Reloc_section*
+ rel_tls_desc(Layout*);
+
+ // Return the number of PLT entries.
+ unsigned int
+ entry_count() const
+ { return this->count_; }
+
+ // Return the offset of the first non-reserved PLT entry.
+ static unsigned int
+ first_plt_entry_offset()
+ { return plt_entry_size; }
+
+ // Return the size of a PLT entry.
+ static unsigned int
+ get_plt_entry_size()
+ { return plt_entry_size; }
+
protected:
void
do_adjust_output_section(Output_section* os);
+ // Write to a map file.
+ void
+ do_print_to_mapfile(Mapfile* mapfile) const
+ { mapfile->print_output_data(this, _("** PLT")); }
+
private:
// The size of an entry in the PLT.
static const int plt_entry_size = 16;
// Set the final size.
void
- do_set_address(uint64_t, off_t)
+ set_final_data_size()
{ this->set_data_size((this->count_ + 1) * plt_entry_size); }
// Write out the PLT data.
// The reloc section.
Reloc_section* rel_;
+ // The TLS_DESC relocations, if necessary. These must follow the
+ // regular PLT relocs.
+ Reloc_section* tls_desc_rel_;
// The .got.plt section.
Output_data_space* got_plt_;
// The number of PLT entries.
Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
Output_data_space* got_plt)
- : Output_section_data(4), got_plt_(got_plt), count_(0)
+ : Output_section_data(4), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0)
{
- this->rel_ = new Reloc_section();
+ this->rel_ = new Reloc_section(false);
layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
- elfcpp::SHF_ALLOC, this->rel_);
+ elfcpp::SHF_ALLOC, this->rel_,
+ ORDER_DYNAMIC_PLT_RELOCS, false);
}
void
++this->count_;
- off_t got_offset = this->got_plt_->data_size();
+ section_offset_type got_offset = this->got_plt_->current_data_size();
// Every PLT entry needs a GOT entry which points back to the PLT
// entry (this will be changed by the dynamic linker, normally
// lazily when the function is called).
- this->got_plt_->set_space_size(got_offset + 4);
+ this->got_plt_->set_current_data_size(got_offset + 4);
// Every PLT entry needs a reloc.
gsym->set_needs_dynsym_entry();
// appear in the relocations.
}
+// Return where the TLS_DESC relocations should go, creating it if
+// necessary. These follow the JUMP_SLOT relocations.
+
+Output_data_plt_i386::Reloc_section*
+Output_data_plt_i386::rel_tls_desc(Layout* layout)
+{
+ if (this->tls_desc_rel_ == NULL)
+ {
+ this->tls_desc_rel_ = new Reloc_section(false);
+ layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
+ elfcpp::SHF_ALLOC, this->tls_desc_rel_,
+ ORDER_DYNAMIC_PLT_RELOCS, false);
+ gold_assert(this->tls_desc_rel_->output_section() ==
+ this->rel_->output_section());
+ }
+ return this->tls_desc_rel_;
+}
+
// The first entry in the PLT for an executable.
unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
Output_data_plt_i386::do_write(Output_file* of)
{
const off_t offset = this->offset();
- const off_t oview_size = this->data_size();
+ const section_size_type oview_size =
+ convert_to_section_size_type(this->data_size());
unsigned char* const oview = of->get_output_view(offset, oview_size);
const off_t got_file_offset = this->got_plt_->offset();
- const off_t got_size = this->got_plt_->data_size();
+ const section_size_type got_size =
+ convert_to_section_size_type(this->got_plt_->data_size());
unsigned char* const got_view = of->get_output_view(got_file_offset,
got_size);
elfcpp::Elf_types<32>::Elf_Addr plt_address = this->address();
elfcpp::Elf_types<32>::Elf_Addr got_address = this->got_plt_->address();
- if (parameters->output_is_shared())
+ if (parameters->options().output_is_position_independent())
memcpy(pov, dyn_first_plt_entry, plt_entry_size);
else
{
{
// Set and adjust the PLT entry itself.
- if (parameters->output_is_shared())
+ if (parameters->options().output_is_position_independent())
{
memcpy(pov, dyn_plt_entry, plt_entry_size);
elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset);
elfcpp::Swap<32, false>::writeval(got_pov, plt_address + plt_offset + 6);
}
- gold_assert(pov - oview == oview_size);
- gold_assert(got_pov - got_view == got_size);
+ gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
+ gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size);
of->write_output_view(offset, oview_size, oview);
of->write_output_view(got_file_offset, got_size, got_view);
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_EXECINSTR),
- this->plt_);
+ this->plt_, ORDER_PLT, false);
}
this->plt_->add_entry(gsym);
}
-// Handle a relocation against a non-function symbol defined in a
-// dynamic object. The traditional way to handle this is to generate
-// a COPY relocation to copy the variable at runtime from the shared
-// object into the executable's data segment. However, this is
-// undesirable in general, as if the size of the object changes in the
-// dynamic object, the executable will no longer work correctly. If
-// this relocation is in a writable section, then we can create a
-// dynamic reloc and the dynamic linker will resolve it to the correct
-// address at runtime. However, we do not want do that if the
-// relocation is in a read-only section, as it would prevent the
-// readonly segment from being shared. And if we have to eventually
-// generate a COPY reloc, then any dynamic relocations will be
-// useless. So this means that if this is a writable section, we need
-// to save the relocation until we see whether we have to create a
-// COPY relocation for this symbol for any other relocation.
+// Return the number of entries in the PLT.
-void
-Target_i386::copy_reloc(const General_options* options,
- Symbol_table* symtab,
- Layout* layout,
- Sized_relobj<32, false>* object,
- unsigned int data_shndx, Symbol* gsym,
- const elfcpp::Rel<32, false>& rel)
+unsigned int
+Target_i386::plt_entry_count() const
{
- Sized_symbol<32>* ssym;
- ssym = symtab->get_sized_symbol SELECT_SIZE_NAME(32) (gsym
- SELECT_SIZE(32));
+ if (this->plt_ == NULL)
+ return 0;
+ return this->plt_->entry_count();
+}
- if (!Copy_relocs<32, false>::need_copy_reloc(options, object,
- data_shndx, ssym))
- {
- // So far we do not need a COPY reloc. Save this relocation.
- // If it turns out that we never need a COPY reloc for this
- // symbol, then we will emit the relocation.
- if (this->copy_relocs_ == NULL)
- this->copy_relocs_ = new Copy_relocs<32, false>();
- this->copy_relocs_->save(ssym, object, data_shndx, rel);
- }
- else
- {
- // Allocate space for this symbol in the .bss section.
+// Return the offset of the first non-reserved PLT entry.
+
+unsigned int
+Target_i386::first_plt_entry_offset() const
+{
+ return Output_data_plt_i386::first_plt_entry_offset();
+}
- elfcpp::Elf_types<32>::Elf_WXword symsize = ssym->symsize();
+// Return the size of each PLT entry.
- // There is no defined way to determine the required alignment
- // of the symbol. We pick the alignment based on the size. We
- // set an arbitrary maximum of 256.
- unsigned int align;
- for (align = 1; align < 512; align <<= 1)
- if ((symsize & align) != 0)
- break;
+unsigned int
+Target_i386::plt_entry_size() const
+{
+ return Output_data_plt_i386::get_plt_entry_size();
+}
- if (this->dynbss_ == NULL)
- {
- this->dynbss_ = new Output_data_space(align);
- layout->add_output_section_data(".bss",
- elfcpp::SHT_NOBITS,
- (elfcpp::SHF_ALLOC
- | elfcpp::SHF_WRITE),
- this->dynbss_);
- }
+// Get the section to use for TLS_DESC relocations.
+
+Target_i386::Reloc_section*
+Target_i386::rel_tls_desc_section(Layout* layout) const
+{
+ return this->plt_section()->rel_tls_desc(layout);
+}
- Output_data_space* dynbss = this->dynbss_;
+// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
- if (align > dynbss->addralign())
- dynbss->set_space_alignment(align);
+void
+Target_i386::define_tls_base_symbol(Symbol_table* symtab, Layout* layout)
+{
+ if (this->tls_base_symbol_defined_)
+ return;
- off_t dynbss_size = dynbss->data_size();
- dynbss_size = align_address(dynbss_size, align);
- off_t offset = dynbss_size;
- dynbss->set_space_size(dynbss_size + symsize);
+ Output_segment* tls_segment = layout->tls_segment();
+ if (tls_segment != NULL)
+ {
+ bool is_exec = parameters->options().output_is_executable();
+ symtab->define_in_output_segment("_TLS_MODULE_BASE_", NULL,
+ Symbol_table::PREDEFINED,
+ tls_segment, 0, 0,
+ elfcpp::STT_TLS,
+ elfcpp::STB_LOCAL,
+ elfcpp::STV_HIDDEN, 0,
+ (is_exec
+ ? Symbol::SEGMENT_END
+ : Symbol::SEGMENT_START),
+ true);
+ }
+ this->tls_base_symbol_defined_ = true;
+}
- symtab->define_with_copy_reloc(this, ssym, dynbss, offset);
+// Create a GOT entry for the TLS module index.
- // Add the COPY reloc.
+unsigned int
+Target_i386::got_mod_index_entry(Symbol_table* symtab, Layout* layout,
+ Sized_relobj<32, false>* object)
+{
+ if (this->got_mod_index_offset_ == -1U)
+ {
+ gold_assert(symtab != NULL && layout != NULL && object != NULL);
Reloc_section* rel_dyn = this->rel_dyn_section(layout);
- rel_dyn->add_global(ssym, elfcpp::R_386_COPY, dynbss, offset);
+ Output_data_got<32, false>* got = this->got_section(symtab, layout);
+ unsigned int got_offset = got->add_constant(0);
+ rel_dyn->add_local(object, 0, elfcpp::R_386_TLS_DTPMOD32, got,
+ got_offset);
+ got->add_constant(0);
+ this->got_mod_index_offset_ = got_offset;
}
+ return this->got_mod_index_offset_;
}
// Optimize the TLS relocation type based on what we know about the
{
// If we are generating a shared library, then we can't do anything
// in the linker.
- if (parameters->output_is_shared())
+ if (parameters->options().shared())
return tls::TLSOPT_NONE;
switch (r_type)
// Scan a relocation for a local symbol.
inline void
-Target_i386::Scan::local(const General_options&,
- Symbol_table* symtab,
+Target_i386::Scan::local(Symbol_table* symtab,
Layout* layout,
Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
+ Output_section* output_section,
const elfcpp::Rel<32, false>& reloc,
unsigned int r_type,
- const elfcpp::Sym<32, false>&)
+ const elfcpp::Sym<32, false>& lsym)
{
switch (r_type)
{
break;
case elfcpp::R_386_32:
- case elfcpp::R_386_16:
- case elfcpp::R_386_8:
// If building a shared library (or a position-independent
// executable), we need to create a dynamic relocation for
// this location. The relocation applied at link time will
// apply the link-time value, so we flag the location with
// an R_386_RELATIVE relocation so the dynamic loader can
// relocate it easily.
- if (parameters->output_is_position_independent())
+ if (parameters->options().output_is_position_independent())
+ {
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ rel_dyn->add_local_relative(object, r_sym, elfcpp::R_386_RELATIVE,
+ output_section, data_shndx,
+ reloc.get_r_offset());
+ }
+ break;
+
+ case elfcpp::R_386_16:
+ case elfcpp::R_386_8:
+ // If building a shared library (or a position-independent
+ // executable), we need to create a dynamic relocation for
+ // this location. Because the addend needs to remain in the
+ // data section, we need to be careful not to apply this
+ // relocation statically.
+ if (parameters->options().output_is_position_independent())
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
- if (r_type == elfcpp::R_386_32)
- rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
- reloc.get_r_offset());
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ if (lsym.get_st_type() != elfcpp::STT_SECTION)
+ rel_dyn->add_local(object, r_sym, r_type, output_section,
+ data_shndx, reloc.get_r_offset());
else
{
- unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
- rel_dyn->add_local(object, r_sym, r_type, data_shndx,
- reloc.get_r_offset());
+ gold_assert(lsym.get_st_value() == 0);
+ unsigned int shndx = lsym.get_st_shndx();
+ bool is_ordinary;
+ shndx = object->adjust_sym_shndx(r_sym, shndx,
+ &is_ordinary);
+ if (!is_ordinary)
+ object->error(_("section symbol %u has bad shndx %u"),
+ r_sym, shndx);
+ else
+ rel_dyn->add_local_section(object, shndx,
+ r_type, output_section,
+ data_shndx, reloc.get_r_offset());
}
}
break;
// The symbol requires a GOT entry.
Output_data_got<32, false>* got = target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
- if (got->add_local(object, r_sym))
+ if (got->add_local(object, r_sym, GOT_TYPE_STANDARD))
{
// If we are generating a shared object, we need to add a
- // dynamic RELATIVE relocation for this symbol.
- if (parameters->output_is_position_independent())
+ // dynamic RELATIVE relocation for this symbol's GOT entry.
+ if (parameters->options().output_is_position_independent())
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
- rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
- data_shndx, reloc.get_r_offset());
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ rel_dyn->add_local_relative(
+ object, r_sym, elfcpp::R_386_RELATIVE, got,
+ object->local_got_offset(r_sym, GOT_TYPE_STANDARD));
}
}
}
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
{
- bool output_is_shared = parameters->output_is_shared();
+ bool output_is_shared = parameters->options().shared();
const tls::Tls_optimization optimized_type
= Target_i386::optimize_tls_reloc(!output_is_shared, r_type);
switch (r_type)
{
case elfcpp::R_386_TLS_GD: // Global-dynamic
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Create a pair of GOT entries for the module index and
+ // dtv-relative offset.
+ Output_data_got<32, false>* got
+ = target->got_section(symtab, layout);
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ unsigned int shndx = lsym.get_st_shndx();
+ bool is_ordinary;
+ shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+ if (!is_ordinary)
+ object->error(_("local symbol %u has bad shndx %u"),
+ r_sym, shndx);
+ else
+ 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);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
+ unsupported_reloc_local(object, r_type);
+ break;
+
case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (from ~oliva)
+ target->define_tls_base_symbol(symtab, layout);
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Create a double GOT entry with an R_386_TLS_DESC
+ // reloc. The R_386_TLS_DESC reloc is resolved
+ // lazily, so the GOT entry needs to be in an area in
+ // .got.plt, not .got. Call got_section to make sure
+ // the section has been created.
+ target->got_section(symtab, layout);
+ Output_data_got<32, false>* got = target->got_tlsdesc_section();
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC))
+ {
+ unsigned int got_offset = got->add_constant(0);
+ // The local symbol value is stored in the second
+ // GOT entry.
+ got->add_local(object, r_sym, GOT_TYPE_TLS_DESC);
+ // That set the GOT offset of the local symbol to
+ // point to the second entry, but we want it to
+ // point to the first.
+ object->set_local_got_offset(r_sym, GOT_TYPE_TLS_DESC,
+ got_offset);
+ Reloc_section* rt = target->rel_tls_desc_section(layout);
+ rt->add_absolute(elfcpp::R_386_TLS_DESC, got, got_offset);
+ }
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
+ unsupported_reloc_local(object, r_type);
+ break;
+
case elfcpp::R_386_TLS_DESC_CALL:
- // FIXME: If not relaxing to LE, we need to generate
- // DTPMOD32 and DTPOFF32 relocs.
- if (optimized_type != tls::TLSOPT_TO_LE)
- unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_386_TLS_LDM: // Local-dynamic
- // FIXME: If not relaxing to LE, we need to generate a
- // DTPMOD32 reloc.
- if (optimized_type != tls::TLSOPT_TO_LE)
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Create a GOT entry for the module index.
+ target->got_mod_index_entry(symtab, layout, object);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_386_TLS_IE: // Initial-exec
case elfcpp::R_386_TLS_IE_32:
case elfcpp::R_386_TLS_GOTIE:
- // FIXME: If not relaxing to LE, we need to generate a
- // TPOFF or TPOFF32 reloc.
- if (optimized_type != tls::TLSOPT_TO_LE)
+ layout->set_has_static_tls();
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // For the R_386_TLS_IE relocation, we need to create a
+ // dynamic relocation when building a shared library.
+ if (r_type == elfcpp::R_386_TLS_IE
+ && parameters->options().shared())
+ {
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ unsigned int r_sym
+ = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ rel_dyn->add_local_relative(object, r_sym,
+ elfcpp::R_386_RELATIVE,
+ output_section, data_shndx,
+ reloc.get_r_offset());
+ }
+ // Create a GOT entry for the tp-relative offset.
+ Output_data_got<32, false>* got
+ = target->got_section(symtab, layout);
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32
+ ? elfcpp::R_386_TLS_TPOFF32
+ : elfcpp::R_386_TLS_TPOFF);
+ unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32
+ ? GOT_TYPE_TLS_OFFSET
+ : GOT_TYPE_TLS_NOFFSET);
+ got->add_local_with_rel(object, r_sym, got_type,
+ target->rel_dyn_section(layout),
+ dyn_r_type);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
- // FIXME: If generating a shared object, we need to copy
- // this relocation into the object.
- gold_assert(!output_is_shared);
+ layout->set_has_static_tls();
+ if (output_is_shared)
+ {
+ // We need to create a dynamic relocation.
+ gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION);
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32
+ ? elfcpp::R_386_TLS_TPOFF32
+ : elfcpp::R_386_TLS_TPOFF);
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ rel_dyn->add_local(object, r_sym, dyn_r_type, output_section,
+ data_shndx, reloc.get_r_offset());
+ }
break;
default:
Symbol* gsym)
{
gold_error(_("%s: unsupported reloc %u against global symbol %s"),
- object->name().c_str(), r_type, gsym->name());
+ object->name().c_str(), r_type, gsym->demangled_name().c_str());
+}
+
+inline bool
+Target_i386::Scan::possible_function_pointer_reloc(unsigned int r_type)
+{
+ switch (r_type)
+ {
+ case elfcpp::R_386_32:
+ case elfcpp::R_386_16:
+ case elfcpp::R_386_8:
+ case elfcpp::R_386_GOTOFF:
+ case elfcpp::R_386_GOT32:
+ {
+ return true;
+ }
+ default:
+ return false;
+ }
+ return false;
+}
+
+inline bool
+Target_i386::Scan::local_reloc_may_be_function_pointer(
+ Symbol_table* ,
+ Layout* ,
+ Target_i386* ,
+ Sized_relobj<32, false>* ,
+ unsigned int ,
+ Output_section* ,
+ const elfcpp::Rel<32, false>& ,
+ unsigned int r_type,
+ const elfcpp::Sym<32, false>&)
+{
+ return possible_function_pointer_reloc(r_type);
+}
+
+inline bool
+Target_i386::Scan::global_reloc_may_be_function_pointer(
+ Symbol_table* ,
+ Layout* ,
+ Target_i386* ,
+ Sized_relobj<32, false>* ,
+ unsigned int ,
+ Output_section* ,
+ const elfcpp::Rel<32, false>& ,
+ unsigned int r_type,
+ Symbol*)
+{
+ return possible_function_pointer_reloc(r_type);
}
// Scan a relocation for a global symbol.
inline void
-Target_i386::Scan::global(const General_options& options,
- Symbol_table* symtab,
+Target_i386::Scan::global(Symbol_table* symtab,
Layout* layout,
Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
+ Output_section* output_section,
const elfcpp::Rel<32, false>& reloc,
unsigned int r_type,
Symbol* gsym)
break;
case elfcpp::R_386_32:
- case elfcpp::R_386_PC32:
case elfcpp::R_386_16:
- case elfcpp::R_386_PC16:
case elfcpp::R_386_8:
- case elfcpp::R_386_PC8:
{
- bool is_pcrel = (r_type == elfcpp::R_386_PC32
- || r_type == elfcpp::R_386_PC16
- || r_type == elfcpp::R_386_PC8);
-
- if (gsym->is_from_dynobj()
- || (parameters->output_is_shared()
- && gsym->is_preemptible()))
- {
- // (a) This symbol is defined in a dynamic object. If it is a
- // function, we make a PLT entry. Otherwise we need to
- // either generate a COPY reloc or copy this reloc.
- // (b) We are building a shared object and this symbol is
- // preemptible. If it is a function, we make a PLT entry.
- // Otherwise, we copy the reloc.
- if (gsym->type() == elfcpp::STT_FUNC)
- {
- target->make_plt_entry(symtab, layout, gsym);
-
- // If this is not a PC relative reference, then we may
- // be taking the address of the function. In that case
- // we need to set the entry in the dynamic symbol table
- // to the address of the PLT entry. We will also need to
- // create a dynamic relocation.
- if (!is_pcrel)
- {
- if (gsym->is_from_dynobj())
- gsym->set_needs_dynsym_value();
- if (parameters->output_is_position_independent())
- {
- Reloc_section* rel_dyn =
- target->rel_dyn_section(layout);
- rel_dyn->add_global(gsym, r_type, object, data_shndx,
- reloc.get_r_offset());
- }
- }
- }
- else if (parameters->output_is_shared())
- {
- // We do not make COPY relocs in shared objects.
+ // Make a PLT entry if necessary.
+ if (gsym->needs_plt_entry())
+ {
+ target->make_plt_entry(symtab, layout, gsym);
+ // Since this is not a PC-relative relocation, we may be
+ // taking the address of a function. In that case we need to
+ // set the entry in the dynamic symbol table to the address of
+ // the PLT entry.
+ if (gsym->is_from_dynobj() && !parameters->options().shared())
+ gsym->set_needs_dynsym_value();
+ }
+ // Make a dynamic relocation if necessary.
+ if (gsym->needs_dynamic_reloc(Symbol::ABSOLUTE_REF))
+ {
+ if (gsym->may_need_copy_reloc())
+ {
+ target->copy_reloc(symtab, layout, object,
+ data_shndx, output_section, gsym, reloc);
+ }
+ else if (r_type == elfcpp::R_386_32
+ && gsym->can_use_relative_reloc(false))
+ {
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
- rel_dyn->add_global(gsym, r_type, object, data_shndx,
- reloc.get_r_offset());
- }
- else
- target->copy_reloc(&options, symtab, layout, object, data_shndx,
- gsym, reloc);
- }
- else if (!is_pcrel && parameters->output_is_position_independent())
+ rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE,
+ output_section, object,
+ data_shndx, reloc.get_r_offset());
+ }
+ else
+ {
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ rel_dyn->add_global(gsym, r_type, output_section, object,
+ data_shndx, reloc.get_r_offset());
+ }
+ }
+ }
+ break;
+
+ case elfcpp::R_386_PC32:
+ case elfcpp::R_386_PC16:
+ case elfcpp::R_386_PC8:
+ {
+ // Make a PLT entry if necessary.
+ if (gsym->needs_plt_entry())
{
- // This is not a PC-relative reference, so we need to generate
- // a dynamic relocation. At this point, we know the symbol
- // is not preemptible, so we can use the RELATIVE relocation.
- Reloc_section* rel_dyn = target->rel_dyn_section(layout);
- if (r_type == elfcpp::R_386_32)
- rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
- reloc.get_r_offset());
+ // These relocations are used for function calls only in
+ // non-PIC code. For a 32-bit relocation in a shared library,
+ // we'll need a text relocation anyway, so we can skip the
+ // PLT entry and let the dynamic linker bind the call directly
+ // to the target. For smaller relocations, we should use a
+ // PLT entry to ensure that the call can reach.
+ if (!parameters->options().shared()
+ || r_type != elfcpp::R_386_PC32)
+ target->make_plt_entry(symtab, layout, gsym);
+ }
+ // Make a dynamic relocation if necessary.
+ int flags = Symbol::NON_PIC_REF;
+ if (gsym->is_func())
+ flags |= Symbol::FUNCTION_CALL;
+ if (gsym->needs_dynamic_reloc(flags))
+ {
+ if (gsym->may_need_copy_reloc())
+ {
+ target->copy_reloc(symtab, layout, object,
+ data_shndx, output_section, gsym, reloc);
+ }
else
- rel_dyn->add_global(gsym, r_type, object, data_shndx,
- reloc.get_r_offset());
+ {
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ rel_dyn->add_global(gsym, r_type, output_section, object,
+ data_shndx, reloc.get_r_offset());
+ }
}
}
break;
{
// The symbol requires a GOT entry.
Output_data_got<32, false>* got = target->got_section(symtab, layout);
- if (got->add_global(gsym))
- {
+ if (gsym->final_value_is_known())
+ got->add_global(gsym, GOT_TYPE_STANDARD);
+ else
+ {
// If this symbol is not fully resolved, we need to add a
- // dynamic relocation for it.
- if (!gsym->final_value_is_known())
+ // GOT entry with a dynamic relocation.
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ if (gsym->is_from_dynobj()
+ || gsym->is_undefined()
+ || gsym->is_preemptible())
+ got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
+ rel_dyn, elfcpp::R_386_GLOB_DAT);
+ else
{
- Reloc_section* rel_dyn = target->rel_dyn_section(layout);
- rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got,
- gsym->got_offset());
+ if (got->add_global(gsym, GOT_TYPE_STANDARD))
+ rel_dyn->add_global_relative(
+ gsym, elfcpp::R_386_RELATIVE, got,
+ gsym->got_offset(GOT_TYPE_STANDARD));
}
}
}
switch (r_type)
{
case elfcpp::R_386_TLS_GD: // Global-dynamic
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Create a pair of GOT entries for the module index and
+ // dtv-relative offset.
+ Output_data_got<32, false>* got
+ = target->got_section(symtab, layout);
+ got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR,
+ target->rel_dyn_section(layout),
+ elfcpp::R_386_TLS_DTPMOD32,
+ elfcpp::R_386_TLS_DTPOFF32);
+ }
+ else if (optimized_type == tls::TLSOPT_TO_IE)
+ {
+ // Create a GOT entry for the tp-relative offset.
+ Output_data_got<32, false>* got
+ = target->got_section(symtab, layout);
+ got->add_global_with_rel(gsym, GOT_TYPE_TLS_NOFFSET,
+ target->rel_dyn_section(layout),
+ elfcpp::R_386_TLS_TPOFF);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
+ unsupported_reloc_global(object, r_type, gsym);
+ break;
+
case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (~oliva url)
+ target->define_tls_base_symbol(symtab, layout);
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Create a double GOT entry with an R_386_TLS_DESC
+ // reloc. The R_386_TLS_DESC reloc is resolved
+ // lazily, so the GOT entry needs to be in an area in
+ // .got.plt, not .got. Call got_section to make sure
+ // the section has been created.
+ target->got_section(symtab, layout);
+ Output_data_got<32, false>* got = target->got_tlsdesc_section();
+ Reloc_section* rt = target->rel_tls_desc_section(layout);
+ got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_DESC, rt,
+ elfcpp::R_386_TLS_DESC, 0);
+ }
+ else if (optimized_type == tls::TLSOPT_TO_IE)
+ {
+ // Create a GOT entry for the tp-relative offset.
+ Output_data_got<32, false>* got
+ = target->got_section(symtab, layout);
+ got->add_global_with_rel(gsym, GOT_TYPE_TLS_NOFFSET,
+ target->rel_dyn_section(layout),
+ elfcpp::R_386_TLS_TPOFF);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
+ unsupported_reloc_global(object, r_type, gsym);
+ break;
+
case elfcpp::R_386_TLS_DESC_CALL:
- // FIXME: If not relaxing to LE, we need to generate
- // DTPMOD32 and DTPOFF32 relocs.
- if (optimized_type != tls::TLSOPT_TO_LE)
- unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_386_TLS_LDM: // Local-dynamic
- // FIXME: If not relaxing to LE, we need to generate a
- // DTPMOD32 reloc.
- if (optimized_type != tls::TLSOPT_TO_LE)
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Create a GOT entry for the module index.
+ target->got_mod_index_entry(symtab, layout, object);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_386_TLS_IE: // Initial-exec
case elfcpp::R_386_TLS_IE_32:
case elfcpp::R_386_TLS_GOTIE:
- // FIXME: If not relaxing to LE, we need to generate a
- // TPOFF or TPOFF32 reloc.
- if (optimized_type != tls::TLSOPT_TO_LE)
+ layout->set_has_static_tls();
+ if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // For the R_386_TLS_IE relocation, we need to create a
+ // dynamic relocation when building a shared library.
+ if (r_type == elfcpp::R_386_TLS_IE
+ && parameters->options().shared())
+ {
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE,
+ output_section, object,
+ data_shndx,
+ reloc.get_r_offset());
+ }
+ // Create a GOT entry for the tp-relative offset.
+ Output_data_got<32, false>* got
+ = target->got_section(symtab, layout);
+ unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32
+ ? elfcpp::R_386_TLS_TPOFF32
+ : elfcpp::R_386_TLS_TPOFF);
+ unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32
+ ? GOT_TYPE_TLS_OFFSET
+ : GOT_TYPE_TLS_NOFFSET);
+ got->add_global_with_rel(gsym, got_type,
+ target->rel_dyn_section(layout),
+ dyn_r_type);
+ }
+ else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
- // FIXME: If generating a shared object, we need to copy
- // this relocation into the object.
- gold_assert(!parameters->output_is_shared());
+ layout->set_has_static_tls();
+ if (parameters->options().shared())
+ {
+ // We need to create a dynamic relocation.
+ unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32
+ ? elfcpp::R_386_TLS_TPOFF32
+ : elfcpp::R_386_TLS_TPOFF);
+ Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+ rel_dyn->add_global(gsym, dyn_r_type, output_section, object,
+ data_shndx, reloc.get_r_offset());
+ }
break;
default:
}
}
+// Process relocations for gc.
+
+void
+Target_i386::gc_process_relocs(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj<32, false>* object,
+ unsigned int data_shndx,
+ unsigned int,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ size_t local_symbol_count,
+ const unsigned char* plocal_symbols)
+{
+ gold::gc_process_relocs<32, false, Target_i386, elfcpp::SHT_REL,
+ Target_i386::Scan,
+ Target_i386::Relocatable_size_for_reloc>(
+ symtab,
+ layout,
+ this,
+ object,
+ data_shndx,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ local_symbol_count,
+ plocal_symbols);
+}
+
// Scan relocations for a section.
void
-Target_i386::scan_relocs(const General_options& options,
- Symbol_table* symtab,
+Target_i386::scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL,
Target_i386::Scan>(
- options,
symtab,
layout,
this,
// Finalize the sections.
void
-Target_i386::do_finalize_sections(Layout* layout)
+Target_i386::do_finalize_sections(
+ Layout* layout,
+ const Input_objects*,
+ Symbol_table* symtab)
{
- // Fill in some more dynamic tags.
- Output_data_dynamic* const odyn = layout->dynamic_data();
- if (odyn != NULL)
- {
- if (this->got_plt_ != NULL)
- odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_);
-
- if (this->plt_ != NULL)
- {
- const Output_data* od = this->plt_->rel_plt();
- odyn->add_section_size(elfcpp::DT_PLTRELSZ, od);
- odyn->add_section_address(elfcpp::DT_JMPREL, od);
- odyn->add_constant(elfcpp::DT_PLTREL, elfcpp::DT_REL);
- }
-
- if (this->rel_dyn_ != NULL)
- {
- const Output_data* od = this->rel_dyn_;
- odyn->add_section_address(elfcpp::DT_REL, od);
- odyn->add_section_size(elfcpp::DT_RELSZ, od);
- odyn->add_constant(elfcpp::DT_RELENT,
- elfcpp::Elf_sizes<32>::rel_size);
- }
-
- if (!parameters->output_is_shared())
- {
- // The value of the DT_DEBUG tag is filled in by the dynamic
- // linker at run time, and used by the debugger.
- odyn->add_constant(elfcpp::DT_DEBUG, 0);
- }
- }
+ const Reloc_section* rel_plt = (this->plt_ == NULL
+ ? NULL
+ : this->plt_->rel_plt());
+ layout->add_target_dynamic_tags(true, this->got_plt_, rel_plt,
+ this->rel_dyn_, true, false);
// Emit any relocs we saved in an attempt to avoid generating COPY
// relocs.
- if (this->copy_relocs_ == NULL)
- return;
- if (this->copy_relocs_->any_to_emit())
+ if (this->copy_relocs_.any_saved_relocs())
+ this->copy_relocs_.emit(this->rel_dyn_section(layout));
+
+ // Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of
+ // the .got.plt section.
+ Symbol* sym = this->global_offset_table_;
+ if (sym != NULL)
{
- Reloc_section* rel_dyn = this->rel_dyn_section(layout);
- this->copy_relocs_->emit(rel_dyn);
+ uint32_t data_size = this->got_plt_->current_data_size();
+ symtab->get_sized_symbol<32>(sym)->set_symsize(data_size);
}
- delete this->copy_relocs_;
- this->copy_relocs_ = NULL;
}
// Return whether a direct absolute static relocation needs to be applied.
inline bool
Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym,
- bool is_pcrel,
- bool is_32bit)
+ int ref_flags,
+ bool is_32bit,
+ Output_section* output_section)
{
- // For local symbols, return FALSE if a non-RELATIVE dynamic
- // relocation was created; return TRUE otherwise.
+ // If the output section is not allocated, then we didn't call
+ // scan_relocs, we didn't create a dynamic reloc, and we must apply
+ // the reloc here.
+ if ((output_section->flags() & elfcpp::SHF_ALLOC) == 0)
+ return true;
+
+ // For local symbols, we will have created a non-RELATIVE dynamic
+ // relocation only if (a) the output is position independent,
+ // (b) the relocation is absolute (not pc- or segment-relative), and
+ // (c) the relocation is not 32 bits wide.
if (gsym == NULL)
- return (!parameters->output_is_position_independent() || is_32bit);
-
- // For global symbols, mimic the logic in Scan::global()
- // to decide whether a non-RELATIVE dynamic relocation was
- // created.
- // FIXME: This is ugly. Try to refactor this logic so it can be
- // shared by Scan::global() and Relocate::relocate().
- if (gsym->is_from_dynobj()
- || (parameters->output_is_shared()
- && gsym->is_preemptible()))
- {
- if (gsym->type() == elfcpp::STT_FUNC)
- {
- if (!is_pcrel && parameters->output_is_position_independent())
- return false;
- }
- else
- return false;
- }
- else if (!is_pcrel && parameters->output_is_position_independent())
- return is_32bit;
-
- // For all other cases, return TRUE
- return true;
-}
+ return !(parameters->options().output_is_position_independent()
+ && (ref_flags & Symbol::ABSOLUTE_REF)
+ && !is_32bit);
+
+ // For global symbols, we use the same helper routines used in the
+ // scan pass. If we did not create a dynamic relocation, or if we
+ // created a RELATIVE dynamic relocation, we should apply the static
+ // relocation.
+ bool has_dyn = gsym->needs_dynamic_reloc(ref_flags);
+ bool is_rel = (ref_flags & Symbol::ABSOLUTE_REF)
+ && gsym->can_use_relative_reloc(ref_flags
+ & Symbol::FUNCTION_CALL);
+ return !has_dyn || is_rel;
+}
// Perform a relocation.
inline bool
Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
Target_i386* target,
+ Output_section *output_section,
size_t relnum,
const elfcpp::Rel<32, false>& rel,
unsigned int r_type,
const Symbol_value<32>* psymval,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr address,
- off_t view_size)
+ section_size_type view_size)
{
if (this->skip_call_tls_get_addr_)
{
- if (r_type != elfcpp::R_386_PLT32
+ if ((r_type != elfcpp::R_386_PLT32
+ && r_type != elfcpp::R_386_PC32)
|| gsym == NULL
|| strcmp(gsym->name(), "___tls_get_addr") != 0)
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
// Pick the value to use for symbols defined in shared objects.
Symbol_value<32> symval;
if (gsym != NULL
- && (gsym->is_from_dynobj()
- || (parameters->output_is_shared()
- && gsym->is_preemptible()))
- && gsym->has_plt_offset())
+ && gsym->use_plt_offset(r_type == elfcpp::R_386_PC8
+ || r_type == elfcpp::R_386_PC16
+ || r_type == elfcpp::R_386_PC32))
{
symval.set_output_value(target->plt_section()->address()
+ gsym->plt_offset());
case elfcpp::R_386_GOT32:
if (gsym != NULL)
{
- gold_assert(gsym->has_got_offset());
- got_offset = gsym->got_offset() - target->got_size();
+ gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
+ got_offset = (gsym->got_offset(GOT_TYPE_STANDARD)
+ - target->got_size());
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
- got_offset = object->local_got_offset(r_sym) - target->got_size();
+ gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
+ got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD)
+ - target->got_size());
}
have_got_offset = true;
break;
break;
case elfcpp::R_386_32:
- if (should_apply_static_reloc(gsym, false, true))
+ if (should_apply_static_reloc(gsym, Symbol::ABSOLUTE_REF, true,
+ output_section))
Relocate_functions<32, false>::rel32(view, object, psymval);
break;
case elfcpp::R_386_PC32:
- if (should_apply_static_reloc(gsym, true, true))
- Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+ {
+ int ref_flags = Symbol::NON_PIC_REF;
+ if (gsym != NULL && gsym->is_func())
+ ref_flags |= Symbol::FUNCTION_CALL;
+ if (should_apply_static_reloc(gsym, ref_flags, true, output_section))
+ Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+ }
break;
case elfcpp::R_386_16:
- if (should_apply_static_reloc(gsym, false, false))
+ if (should_apply_static_reloc(gsym, Symbol::ABSOLUTE_REF, false,
+ output_section))
Relocate_functions<32, false>::rel16(view, object, psymval);
break;
case elfcpp::R_386_PC16:
- if (should_apply_static_reloc(gsym, true, false))
- Relocate_functions<32, false>::pcrel16(view, object, psymval, address);
+ {
+ int ref_flags = Symbol::NON_PIC_REF;
+ if (gsym != NULL && gsym->is_func())
+ ref_flags |= Symbol::FUNCTION_CALL;
+ if (should_apply_static_reloc(gsym, ref_flags, false, output_section))
+ Relocate_functions<32, false>::pcrel16(view, object, psymval, address);
+ }
break;
case elfcpp::R_386_8:
- if (should_apply_static_reloc(gsym, false, false))
+ if (should_apply_static_reloc(gsym, Symbol::ABSOLUTE_REF, false,
+ output_section))
Relocate_functions<32, false>::rel8(view, object, psymval);
break;
case elfcpp::R_386_PC8:
- if (should_apply_static_reloc(gsym, true, false))
- Relocate_functions<32, false>::pcrel8(view, object, psymval, address);
+ {
+ int ref_flags = Symbol::NON_PIC_REF;
+ if (gsym != NULL && gsym->is_func())
+ ref_flags |= Symbol::FUNCTION_CALL;
+ if (should_apply_static_reloc(gsym, ref_flags, false,
+ output_section))
+ Relocate_functions<32, false>::pcrel8(view, object, psymval, address);
+ }
break;
case elfcpp::R_386_PLT32:
gold_assert(gsym == NULL
|| gsym->has_plt_offset()
- || gsym->final_value_is_known());
+ || gsym->final_value_is_known()
+ || (gsym->is_defined()
+ && !gsym->is_from_dynobj()
+ && !gsym->is_preemptible()));
Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
break;
case elfcpp::R_386_TLS_GOTIE:
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
- this->relocate_tls(relinfo, relnum, rel, r_type, gsym, psymval, view,
- address, view_size);
+ this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval,
+ view, address, view_size);
break;
case elfcpp::R_386_32PLT:
inline void
Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
+ Target_i386* target,
size_t relnum,
const elfcpp::Rel<32, false>& rel,
unsigned int r_type,
const Symbol_value<32>* psymval,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr,
- off_t view_size)
+ section_size_type view_size)
{
Output_segment* tls_segment = relinfo->layout->tls_segment();
- if (tls_segment == NULL)
- {
- gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
- _("TLS reloc but no TLS segment"));
- return;
- }
- elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(relinfo->object, 0);
+ const Sized_relobj<32, false>* object = relinfo->object;
+
+ elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(object, 0);
const bool is_final = (gsym == NULL
- ? !parameters->output_is_position_independent()
+ ? !parameters->options().shared()
: gsym->final_value_is_known());
const tls::Tls_optimization optimized_type
= Target_i386::optimize_tls_reloc(is_final, r_type);
case elfcpp::R_386_TLS_GD: // Global-dynamic
if (optimized_type == tls::TLSOPT_TO_LE)
{
+ gold_assert(tls_segment != NULL);
this->tls_gd_to_le(relinfo, relnum, tls_segment,
rel, r_type, value, view,
view_size);
break;
}
+ else
+ {
+ unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE
+ ? GOT_TYPE_TLS_NOFFSET
+ : GOT_TYPE_TLS_PAIR);
+ unsigned int got_offset;
+ if (gsym != NULL)
+ {
+ gold_assert(gsym->has_got_offset(got_type));
+ got_offset = gsym->got_offset(got_type) - target->got_size();
+ }
+ else
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+ gold_assert(object->local_has_got_offset(r_sym, got_type));
+ got_offset = (object->local_got_offset(r_sym, got_type)
+ - target->got_size());
+ }
+ if (optimized_type == tls::TLSOPT_TO_IE)
+ {
+ gold_assert(tls_segment != NULL);
+ this->tls_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type,
+ got_offset, view, view_size);
+ break;
+ }
+ else if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Relocate the field with the offset of the pair of GOT
+ // entries.
+ Relocate_functions<32, false>::rel32(view, got_offset);
+ break;
+ }
+ }
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (from ~oliva url)
case elfcpp::R_386_TLS_DESC_CALL:
+ this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
+ if (optimized_type == tls::TLSOPT_TO_LE)
+ {
+ gold_assert(tls_segment != NULL);
+ this->tls_desc_gd_to_le(relinfo, relnum, tls_segment,
+ rel, r_type, value, view,
+ view_size);
+ break;
+ }
+ else
+ {
+ unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE
+ ? GOT_TYPE_TLS_NOFFSET
+ : GOT_TYPE_TLS_DESC);
+ unsigned int got_offset = 0;
+ if (r_type == elfcpp::R_386_TLS_GOTDESC
+ && optimized_type == tls::TLSOPT_NONE)
+ {
+ // We created GOT entries in the .got.tlsdesc portion of
+ // the .got.plt section, but the offset stored in the
+ // symbol is the offset within .got.tlsdesc.
+ got_offset = (target->got_size()
+ + target->got_plt_section()->data_size());
+ }
+ if (gsym != NULL)
+ {
+ gold_assert(gsym->has_got_offset(got_type));
+ got_offset += gsym->got_offset(got_type) - target->got_size();
+ }
+ else
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+ gold_assert(object->local_has_got_offset(r_sym, got_type));
+ got_offset += (object->local_got_offset(r_sym, got_type)
+ - target->got_size());
+ }
+ if (optimized_type == tls::TLSOPT_TO_IE)
+ {
+ gold_assert(tls_segment != NULL);
+ this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type,
+ got_offset, view, view_size);
+ break;
+ }
+ else if (optimized_type == tls::TLSOPT_NONE)
+ {
+ if (r_type == elfcpp::R_386_TLS_GOTDESC)
+ {
+ // Relocate the field with the offset of the pair of GOT
+ // entries.
+ Relocate_functions<32, false>::rel32(view, got_offset);
+ }
+ break;
+ }
+ }
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
if (optimized_type == tls::TLSOPT_TO_LE)
{
+ gold_assert(tls_segment != NULL);
this->tls_ld_to_le(relinfo, relnum, tls_segment, rel, r_type,
value, view, view_size);
break;
}
+ else if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Relocate the field with the offset of the GOT entry for
+ // the module index.
+ unsigned int got_offset;
+ got_offset = (target->got_mod_index_entry(NULL, NULL, NULL)
+ - target->got_size());
+ Relocate_functions<32, false>::rel32(view, got_offset);
+ break;
+ }
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
break;
case elfcpp::R_386_TLS_LDO_32: // Alternate local-dynamic
- // This reloc can appear in debugging sections, in which case we
- // won't see the TLS_LDM reloc. The local_dynamic_type field
- // tells us this.
- if (optimized_type != tls::TLSOPT_TO_LE
- || this->local_dynamic_type_ == LOCAL_DYNAMIC_NONE)
- value = value - tls_segment->vaddr();
- else if (this->local_dynamic_type_ == LOCAL_DYNAMIC_GNU)
- value = value - (tls_segment->vaddr() + tls_segment->memsz());
- else
- value = tls_segment->vaddr() + tls_segment->memsz() - value;
+ if (optimized_type == tls::TLSOPT_TO_LE)
+ {
+ // This reloc can appear in debugging sections, in which
+ // case we must not convert to local-exec. We decide what
+ // to do based on whether the section is marked as
+ // containing executable code. That is what the GNU linker
+ // does as well.
+ elfcpp::Shdr<32, false> shdr(relinfo->data_shdr);
+ if ((shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0)
+ {
+ gold_assert(tls_segment != NULL);
+ value -= tls_segment->memsz();
+ }
+ }
Relocate_functions<32, false>::rel32(view, value);
break;
case elfcpp::R_386_TLS_IE_32:
if (optimized_type == tls::TLSOPT_TO_LE)
{
+ gold_assert(tls_segment != NULL);
Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
rel, r_type, value, view,
view_size);
break;
}
+ else if (optimized_type == tls::TLSOPT_NONE)
+ {
+ // Relocate the field with the offset of the GOT entry for
+ // the tp-relative offset of the symbol.
+ unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32
+ ? GOT_TYPE_TLS_OFFSET
+ : GOT_TYPE_TLS_NOFFSET);
+ unsigned int got_offset;
+ if (gsym != NULL)
+ {
+ gold_assert(gsym->has_got_offset(got_type));
+ got_offset = gsym->got_offset(got_type);
+ }
+ else
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+ gold_assert(object->local_has_got_offset(r_sym, got_type));
+ got_offset = object->local_got_offset(r_sym, got_type);
+ }
+ // For the R_386_TLS_IE relocation, we need to apply the
+ // absolute address of the GOT entry.
+ if (r_type == elfcpp::R_386_TLS_IE)
+ got_offset += target->got_plt_section()->address();
+ // All GOT offsets are relative to the end of the GOT.
+ got_offset -= target->got_size();
+ Relocate_functions<32, false>::rel32(view, got_offset);
+ break;
+ }
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
break;
case elfcpp::R_386_TLS_LE: // Local-exec
- value = value - (tls_segment->vaddr() + tls_segment->memsz());
- Relocate_functions<32, false>::rel32(view, value);
+ // If we're creating a shared library, a dynamic relocation will
+ // have been created for this location, so do not apply it now.
+ if (!parameters->options().shared())
+ {
+ gold_assert(tls_segment != NULL);
+ value -= tls_segment->memsz();
+ Relocate_functions<32, false>::rel32(view, value);
+ }
break;
case elfcpp::R_386_TLS_LE_32:
- value = tls_segment->vaddr() + tls_segment->memsz() - value;
- Relocate_functions<32, false>::rel32(view, value);
+ // If we're creating a shared library, a dynamic relocation will
+ // have been created for this location, so do not apply it now.
+ if (!parameters->options().shared())
+ {
+ gold_assert(tls_segment != NULL);
+ value = tls_segment->memsz() - value;
+ Relocate_functions<32, false>::rel32(view, value);
+ }
break;
}
}
unsigned int,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
- off_t view_size)
+ section_size_type view_size)
{
// leal foo(,%reg,1),%eax; call ___tls_get_addr
// ==> movl %gs:0,%eax; subl $foo@tpoff,%eax
{
tls::check_tls(relinfo, relnum, rel.get_r_offset(),
(op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
- if (static_cast<off_t>(rel.get_r_offset() + 9) < view_size
+ if (rel.get_r_offset() + 9 < view_size
+ && view[9] == 0x90)
+ {
+ // There is a trailing nop. Use the size byte subl.
+ memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+ roff = 6;
+ }
+ else
+ {
+ // Use the five byte subl.
+ memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
+ }
+ }
+
+ value = tls_segment->memsz() - value;
+ Relocate_functions<32, false>::rel32(view + roff, value);
+
+ // The next reloc should be a PLT32 reloc against __tls_get_addr.
+ // We can skip it.
+ this->skip_call_tls_get_addr_ = true;
+}
+
+// Do a relocation in which we convert a TLS General-Dynamic to an
+// Initial-Exec.
+
+inline void
+Target_i386::Relocate::tls_gd_to_ie(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ Output_segment*,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ section_size_type view_size)
+{
+ // leal foo(,%ebx,1),%eax; call ___tls_get_addr
+ // ==> movl %gs:0,%eax; addl foo@gotntpoff(%ebx),%eax
+
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9);
+
+ unsigned char op1 = view[-1];
+ unsigned char op2 = view[-2];
+
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ op2 == 0x8d || op2 == 0x04);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8);
+
+ int roff = 5;
+
+ // FIXME: For now, support only the first (SIB) form.
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(), op2 == 0x04);
+
+ if (op2 == 0x04)
+ {
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -3);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[-3] == 0x8d);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ ((op1 & 0xc7) == 0x05 && op1 != (4 << 3)));
+ memcpy(view - 3, "\x65\xa1\0\0\0\0\x03\x83\0\0\0", 12);
+ }
+ else
+ {
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ (op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
+ if (rel.get_r_offset() + 9 < view_size
&& view[9] == 0x90)
{
+ // FIXME: This is not the right instruction sequence.
// There is a trailing nop. Use the size byte subl.
memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
roff = 6;
}
else
{
+ // FIXME: This is not the right instruction sequence.
// Use the five byte subl.
memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
}
}
- value = tls_segment->vaddr() + tls_segment->memsz() - value;
Relocate_functions<32, false>::rel32(view + roff, value);
// The next reloc should be a PLT32 reloc against __tls_get_addr.
this->skip_call_tls_get_addr_ = true;
}
+// Do a relocation in which we convert a TLS_GOTDESC or TLS_DESC_CALL
+// General-Dynamic to a Local-Exec.
+
+inline void
+Target_i386::Relocate::tls_desc_gd_to_le(
+ const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int r_type,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ section_size_type view_size)
+{
+ if (r_type == elfcpp::R_386_TLS_GOTDESC)
+ {
+ // leal foo@TLSDESC(%ebx), %eax
+ // ==> leal foo@NTPOFF, %eax
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ view[-2] == 0x8d && view[-1] == 0x83);
+ view[-1] = 0x05;
+ value -= tls_segment->memsz();
+ Relocate_functions<32, false>::rel32(view, value);
+ }
+ else
+ {
+ // call *foo@TLSCALL(%eax)
+ // ==> nop; nop
+ gold_assert(r_type == elfcpp::R_386_TLS_DESC_CALL);
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 2);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ view[0] == 0xff && view[1] == 0x10);
+ view[0] = 0x66;
+ view[1] = 0x90;
+ }
+}
+
+// Do a relocation in which we convert a TLS_GOTDESC or TLS_DESC_CALL
+// General-Dynamic to an Initial-Exec.
+
+inline void
+Target_i386::Relocate::tls_desc_gd_to_ie(
+ const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ Output_segment*,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int r_type,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ section_size_type view_size)
+{
+ if (r_type == elfcpp::R_386_TLS_GOTDESC)
+ {
+ // leal foo@TLSDESC(%ebx), %eax
+ // ==> movl foo@GOTNTPOFF(%ebx), %eax
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ view[-2] == 0x8d && view[-1] == 0x83);
+ view[-2] = 0x8b;
+ Relocate_functions<32, false>::rel32(view, value);
+ }
+ else
+ {
+ // call *foo@TLSCALL(%eax)
+ // ==> nop; nop
+ gold_assert(r_type == elfcpp::R_386_TLS_DESC_CALL);
+ tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 2);
+ tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+ view[0] == 0xff && view[1] == 0x10);
+ view[0] = 0x66;
+ view[1] = 0x90;
+ }
+}
+
// Do a relocation in which we convert a TLS Local-Dynamic to a
// Local-Exec.
unsigned int,
elfcpp::Elf_types<32>::Elf_Addr,
unsigned char* view,
- off_t view_size)
+ section_size_type view_size)
{
// leal foo(%reg), %eax; call ___tls_get_addr
// ==> movl %gs:0,%eax; nop; leal 0(%esi,1),%esi
unsigned int r_type,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
- off_t view_size)
+ section_size_type view_size)
{
// We have to actually change the instructions, which means that we
// need to examine the opcodes to figure out which instruction we
tls::check_tls(relinfo, relnum, rel.get_r_offset(), 0);
}
- value = tls_segment->vaddr() + tls_segment->memsz() - value;
+ value = tls_segment->memsz() - value;
if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE)
value = - value;
bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr address,
- off_t view_size)
+ section_size_type view_size,
+ const Reloc_symbol_changes* reloc_symbol_changes)
{
gold_assert(sh_type == elfcpp::SHT_REL);
needs_special_offset_handling,
view,
address,
- view_size);
+ view_size,
+ reloc_symbol_changes);
+}
+
+// Return the size of a relocation while scanning during a relocatable
+// link.
+
+unsigned int
+Target_i386::Relocatable_size_for_reloc::get_size_for_reloc(
+ unsigned int r_type,
+ Relobj* object)
+{
+ switch (r_type)
+ {
+ case elfcpp::R_386_NONE:
+ case elfcpp::R_386_GNU_VTINHERIT:
+ case elfcpp::R_386_GNU_VTENTRY:
+ case elfcpp::R_386_TLS_GD: // Global-dynamic
+ case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (from ~oliva url)
+ case elfcpp::R_386_TLS_DESC_CALL:
+ case elfcpp::R_386_TLS_LDM: // Local-dynamic
+ case elfcpp::R_386_TLS_LDO_32: // Alternate local-dynamic
+ case elfcpp::R_386_TLS_IE: // Initial-exec
+ case elfcpp::R_386_TLS_IE_32:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_LE: // Local-exec
+ case elfcpp::R_386_TLS_LE_32:
+ return 0;
+
+ case elfcpp::R_386_32:
+ case elfcpp::R_386_PC32:
+ case elfcpp::R_386_GOT32:
+ case elfcpp::R_386_PLT32:
+ case elfcpp::R_386_GOTOFF:
+ case elfcpp::R_386_GOTPC:
+ return 4;
+
+ case elfcpp::R_386_16:
+ case elfcpp::R_386_PC16:
+ return 2;
+
+ case elfcpp::R_386_8:
+ case elfcpp::R_386_PC8:
+ return 1;
+
+ // These are relocations which should only be seen by the
+ // dynamic linker, and should never be seen here.
+ case elfcpp::R_386_COPY:
+ case elfcpp::R_386_GLOB_DAT:
+ case elfcpp::R_386_JUMP_SLOT:
+ case elfcpp::R_386_RELATIVE:
+ case elfcpp::R_386_TLS_TPOFF:
+ case elfcpp::R_386_TLS_DTPMOD32:
+ case elfcpp::R_386_TLS_DTPOFF32:
+ case elfcpp::R_386_TLS_TPOFF32:
+ case elfcpp::R_386_TLS_DESC:
+ object->error(_("unexpected reloc %u in object file"), r_type);
+ return 0;
+
+ case elfcpp::R_386_32PLT:
+ case elfcpp::R_386_TLS_GD_32:
+ case elfcpp::R_386_TLS_GD_PUSH:
+ case elfcpp::R_386_TLS_GD_CALL:
+ case elfcpp::R_386_TLS_GD_POP:
+ case elfcpp::R_386_TLS_LDM_32:
+ case elfcpp::R_386_TLS_LDM_PUSH:
+ case elfcpp::R_386_TLS_LDM_CALL:
+ case elfcpp::R_386_TLS_LDM_POP:
+ case elfcpp::R_386_USED_BY_INTEL_200:
+ default:
+ object->error(_("unsupported reloc %u in object file"), r_type);
+ return 0;
+ }
+}
+
+// Scan the relocs during a relocatable link.
+
+void
+Target_i386::scan_relocatable_relocs(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj<32, false>* object,
+ unsigned int data_shndx,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ size_t local_symbol_count,
+ const unsigned char* plocal_symbols,
+ Relocatable_relocs* rr)
+{
+ gold_assert(sh_type == elfcpp::SHT_REL);
+
+ typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_REL,
+ Relocatable_size_for_reloc> Scan_relocatable_relocs;
+
+ gold::scan_relocatable_relocs<32, false, elfcpp::SHT_REL,
+ Scan_relocatable_relocs>(
+ symtab,
+ layout,
+ object,
+ data_shndx,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ local_symbol_count,
+ plocal_symbols,
+ rr);
+}
+
+// Relocate a section during a relocatable link.
+
+void
+Target_i386::relocate_for_relocatable(
+ const Relocate_info<32, false>* relinfo,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ off_t offset_in_output_section,
+ const Relocatable_relocs* rr,
+ unsigned char* view,
+ elfcpp::Elf_types<32>::Elf_Addr view_address,
+ section_size_type view_size,
+ unsigned char* reloc_view,
+ section_size_type reloc_view_size)
+{
+ gold_assert(sh_type == elfcpp::SHT_REL);
+
+ gold::relocate_for_relocatable<32, false, elfcpp::SHT_REL>(
+ relinfo,
+ prelocs,
+ reloc_count,
+ output_section,
+ offset_in_output_section,
+ rr,
+ view,
+ view_address,
+ view_size,
+ reloc_view,
+ reloc_view_size);
}
// Return the value to use for a dynamic which requires special
// the specified length.
std::string
-Target_i386::do_code_fill(off_t length)
+Target_i386::do_code_fill(section_size_type length) const
{
if (length >= 16)
{
return std::string(nops[length], length);
}
+// Return whether SYM should be treated as a call to a non-split
+// function. We don't want that to be true of a call to a
+// get_pc_thunk function.
+
+bool
+Target_i386::do_is_call_to_non_split(const Symbol* sym, unsigned int) const
+{
+ return (sym->type() == elfcpp::STT_FUNC
+ && !is_prefix_of("__i686.get_pc_thunk.", sym->name()));
+}
+
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fstack-split. The function calls non-stack-split
+// code. We have to change the function so that it always ensures
+// that it has enough stack space to run some random function.
+
+void
+Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx,
+ section_offset_type fnoffset,
+ section_size_type fnsize,
+ unsigned char* view,
+ section_size_type view_size,
+ std::string* from,
+ std::string* to) const
+{
+ // The function starts with a comparison of the stack pointer and a
+ // field in the TCB. This is followed by a jump.
+
+ // cmp %gs:NN,%esp
+ if (this->match_view(view, view_size, fnoffset, "\x65\x3b\x25", 3)
+ && fnsize > 7)
+ {
+ // We will call __morestack if the carry flag is set after this
+ // comparison. We turn the comparison into an stc instruction
+ // and some nops.
+ view[fnoffset] = '\xf9';
+ this->set_view_to_nop(view, view_size, fnoffset + 1, 6);
+ }
+ // lea NN(%esp),%ecx
+ // lea NN(%esp),%edx
+ else if ((this->match_view(view, view_size, fnoffset, "\x8d\x8c\x24", 3)
+ || this->match_view(view, view_size, fnoffset, "\x8d\x94\x24", 3))
+ && fnsize > 7)
+ {
+ // This is loading an offset from the stack pointer for a
+ // comparison. The offset is negative, so we decrease the
+ // offset by the amount of space we need for the stack. This
+ // means we will avoid calling __morestack if there happens to
+ // be plenty of space on the stack already.
+ unsigned char* pval = view + fnoffset + 3;
+ uint32_t val = elfcpp::Swap_unaligned<32, false>::readval(pval);
+ val -= parameters->options().split_stack_adjust_size();
+ elfcpp::Swap_unaligned<32, false>::writeval(pval, val);
+ }
+ else
+ {
+ if (!object->has_no_split_stack())
+ object->error(_("failed to match split-stack sequence at "
+ "section %u offset %0zx"),
+ shndx, static_cast<size_t>(fnoffset));
+ return;
+ }
+
+ // We have to change the function so that it calls
+ // __morestack_non_split instead of __morestack. The former will
+ // allocate additional stack space.
+ *from = "__morestack";
+ *to = "__morestack_non_split";
+}
+
// The selector for i386 object files.
-class Target_selector_i386 : public Target_selector
+class Target_selector_i386 : public Target_selector_freebsd
{
public:
Target_selector_i386()
- : Target_selector(elfcpp::EM_386, 32, false)
+ : Target_selector_freebsd(elfcpp::EM_386, 32, false,
+ "elf32-i386", "elf32-i386-freebsd")
{ }
Target*
- recognize(int machine, int osabi, int abiversion);
-
- private:
- Target_i386* target_;
+ do_instantiate_target()
+ { return new Target_i386(); }
};
-// Recognize an i386 object file when we already know that the machine
-// number is EM_386.
-
-Target*
-Target_selector_i386::recognize(int, int, int)
-{
- if (this->target_ == NULL)
- this->target_ = new Target_i386();
- return this->target_;
-}
-
Target_selector_i386 target_selector_i386;
} // End anonymous namespace.