aboutsummaryrefslogtreecommitdiff
path: root/gold/i386.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gold/i386.cc')
-rw-r--r--gold/i386.cc312
1 files changed, 276 insertions, 36 deletions
diff --git a/gold/i386.cc b/gold/i386.cc
index 468cb90..53af5ee 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -1,10 +1,14 @@
// i386.cc -- i386 target support for gold.
#include "gold.h"
+
+#include <cstring>
+
#include "elfcpp.h"
#include "reloc.h"
#include "i386.h"
#include "object.h"
+#include "symtab.h"
#include "layout.h"
#include "output.h"
#include "target.h"
@@ -22,13 +26,15 @@ class Target_i386 : public Sized_target<32, false>
{
public:
Target_i386()
- : Sized_target<32, false>(&i386_info)
+ : Sized_target<32, false>(&i386_info),
+ got_(NULL)
{ }
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Sized_object<32, false>* object,
unsigned int sh_type,
const unsigned char* prelocs,
@@ -52,12 +58,16 @@ class Target_i386 : public Sized_target<32, false>
struct Scan
{
inline void
- local(const General_options& options, Sized_object<32, false>* object,
+ local(const General_options& options, Symbol_table* symtab,
+ Layout* layout, Target_i386* target,
+ Sized_object<32, false>* object,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
const elfcpp::Sym<32, false>& lsym);
inline void
- global(const General_options& options, Sized_object<32, false>* object,
+ global(const General_options& options, Symbol_table* symtab,
+ Layout* layout, Target_i386* target,
+ Sized_object<32, false>* object,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
Symbol* gsym);
};
@@ -66,9 +76,25 @@ class Target_i386 : public Sized_target<32, false>
class Relocate
{
public:
- // Do a relocation.
- static inline void
- relocate(const Relocate_info<32, false>*, size_t relnum,
+ Relocate()
+ : skip_call_tls_get_addr_(false)
+ { }
+
+ ~Relocate()
+ {
+ if (this->skip_call_tls_get_addr_)
+ {
+ // FIXME: This needs to specify the location somehow.
+ fprintf(stderr, _("%s: missing expected TLS relocation\n"),
+ program_name);
+ gold_exit(false);
+ }
+ }
+
+ // 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>&,
unsigned int r_type, Sized_symbol<32>*,
elfcpp::Elf_types<32>::Elf_Addr,
@@ -77,7 +103,7 @@ class Target_i386 : public Sized_target<32, false>
private:
// Do a TLS relocation.
- static inline void
+ inline void
relocate_tls(const Relocate_info<32, false>*, size_t relnum,
const elfcpp::Rel<32, false>&,
unsigned int r_type, Sized_symbol<32>*,
@@ -93,6 +119,15 @@ class Target_i386 : public Sized_target<32, false>
unsigned char* view,
off_t view_size);
+ // Do a TLS Global-Dynamic to Local-Exec transition.
+ inline void
+ tls_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,
+ off_t view_size);
+
// Check the range for a TLS relocation.
static inline void
check_range(const Relocate_info<32, false>*, size_t relnum,
@@ -102,6 +137,10 @@ class Target_i386 : public Sized_target<32, false>
static inline void
check_tls(const Relocate_info<32, false>*, size_t relnum,
const elfcpp::Rel<32, false>&, bool);
+
+ // This is set if we should skip the next reloc, which should be a
+ // PLT32 reloc against ___tls_get_addr.
+ bool skip_call_tls_get_addr_;
};
// Adjust TLS relocation type based on the options and whether this
@@ -109,9 +148,16 @@ class Target_i386 : public Sized_target<32, false>
static unsigned int
optimize_tls_reloc(const General_options*, bool is_local, int r_type);
+ // Get the GOT section, creating it if necessary.
+ Output_section_got<32, false>*
+ got_section(Symbol_table*, Layout*);
+
// Information about this specific target which we pass to the
// general Target structure.
static const Target::Target_info i386_info;
+
+ // The GOT section.
+ Output_section_got<32, false>* got_;
};
const Target::Target_info Target_i386::i386_info =
@@ -126,6 +172,34 @@ const Target::Target_info Target_i386::i386_info =
0x1000 // common_pagesize
};
+// Get the GOT section, creating it if necessary.
+
+Output_section_got<32, false>*
+Target_i386::got_section(Symbol_table* symtab, Layout* layout)
+{
+ if (this->got_ == NULL)
+ {
+ this->got_ = new Output_section_got<32, false>();
+
+ assert(symtab != NULL && layout != NULL);
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC, this->got_);
+
+ // The first three entries are reserved.
+ this->got_->add_constant(0);
+ this->got_->add_constant(0);
+ this->got_->add_constant(0);
+
+ // Define _GLOBAL_OFFSET_TABLE_ at the start of the section.
+ symtab->define_in_output_data(this, "_GLOBAL_OFFSET_TABLE_", this->got_,
+ 0, 0, elfcpp::STT_OBJECT,
+ elfcpp::STB_GLOBAL,
+ elfcpp::STV_HIDDEN, 0,
+ false, false);
+ }
+ return this->got_;
+}
+
// Optimize the TLS relocation type based on what we know about the
// symbol. IS_LOCAL is true if this symbol can be resolved entirely
// locally--i.e., does not have to be in the dynamic symbol table.
@@ -188,6 +262,9 @@ Target_i386::optimize_tls_reloc(const General_options* options, bool is_local,
inline void
Target_i386::Scan::local(const General_options& options,
+ Symbol_table* symtab,
+ Layout* layout,
+ Target_i386* target,
Sized_object<32, false>* object,
const elfcpp::Rel<32, false>&, unsigned int r_type,
const elfcpp::Sym<32, false>&)
@@ -211,6 +288,12 @@ Target_i386::Scan::local(const General_options& options,
case elfcpp::R_386_PC8:
break;
+ case elfcpp::R_386_GOTOFF:
+ case elfcpp::R_386_GOTPC:
+ // We need a GOT section.
+ target->got_section(symtab, layout);
+ break;
+
case elfcpp::R_386_COPY:
case elfcpp::R_386_GLOB_DAT:
case elfcpp::R_386_JUMP_SLOT:
@@ -261,8 +344,6 @@ Target_i386::Scan::local(const General_options& options,
case elfcpp::R_386_GOT32:
case elfcpp::R_386_PLT32:
- case elfcpp::R_386_GOTOFF:
- case elfcpp::R_386_GOTPC:
case elfcpp::R_386_32PLT:
case elfcpp::R_386_TLS_GD_32:
case elfcpp::R_386_TLS_GD_PUSH:
@@ -284,6 +365,9 @@ Target_i386::Scan::local(const General_options& options,
inline void
Target_i386::Scan::global(const General_options& options,
+ Symbol_table* symtab,
+ Layout* layout,
+ Target_i386* target,
Sized_object<32, false>* object,
const elfcpp::Rel<32, false>&, unsigned int r_type,
Symbol* gsym)
@@ -307,6 +391,37 @@ Target_i386::Scan::global(const General_options& options,
// relocation in order to avoid a COPY relocation.
break;
+ case elfcpp::R_386_GOT32:
+ // The symbol requires a GOT entry.
+ if (!gsym->has_got_offset())
+ {
+ Output_section_got<32, false>* got = target->got_section(symtab,
+ layout);
+ const unsigned int got_offset = got->add_global(gsym);
+ gsym->set_got_offset(got_offset);
+
+ // If this symbol is not resolved locally, we need to add a
+ // dynamic relocation for it.
+ if (!gsym->is_resolved_locally())
+ abort();
+ }
+ break;
+
+ case elfcpp::R_386_PLT32:
+ // If the symbol is resolved locally, this is just a PC32 reloc.
+ if (gsym->is_resolved_locally())
+ break;
+ fprintf(stderr,
+ _("%s: %s: unsupported reloc %u against global symbol %s\n"),
+ program_name, object->name().c_str(), r_type, gsym->name());
+ break;
+
+ case elfcpp::R_386_GOTOFF:
+ case elfcpp::R_386_GOTPC:
+ // We need a GOT section.
+ target->got_section(symtab, layout);
+ break;
+
case elfcpp::R_386_COPY:
case elfcpp::R_386_GLOB_DAT:
case elfcpp::R_386_JUMP_SLOT:
@@ -332,7 +447,7 @@ Target_i386::Scan::global(const General_options& options,
case elfcpp::R_386_TLS_GOTDESC:
case elfcpp::R_386_TLS_DESC_CALL:
r_type = Target_i386::optimize_tls_reloc(&options,
- !gsym->in_dynsym(),
+ gsym->is_resolved_locally(),
r_type);
switch (r_type)
{
@@ -357,10 +472,6 @@ Target_i386::Scan::global(const General_options& options,
}
break;
- case elfcpp::R_386_GOT32:
- case elfcpp::R_386_PLT32:
- case elfcpp::R_386_GOTOFF:
- case elfcpp::R_386_GOTPC:
case elfcpp::R_386_32PLT:
case elfcpp::R_386_TLS_GD_32:
case elfcpp::R_386_TLS_GD_PUSH:
@@ -384,6 +495,7 @@ Target_i386::Scan::global(const General_options& options,
void
Target_i386::scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Sized_object<32, false>* object,
unsigned int sh_type,
const unsigned char* prelocs,
@@ -399,9 +511,12 @@ Target_i386::scan_relocs(const General_options& options,
gold_exit(false);
}
- gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>(
+ gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL,
+ Target_i386::Scan>(
options,
symtab,
+ layout,
+ this,
object,
prelocs,
reloc_count,
@@ -412,8 +527,9 @@ Target_i386::scan_relocs(const General_options& options,
// Perform a relocation.
-inline void
+inline bool
Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
+ Target_i386* target,
size_t relnum,
const elfcpp::Rel<32, false>& rel,
unsigned int r_type,
@@ -423,6 +539,23 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
elfcpp::Elf_types<32>::Elf_Addr address,
off_t view_size)
{
+ if (this->skip_call_tls_get_addr_)
+ {
+ if (r_type != elfcpp::R_386_PLT32
+ || gsym == NULL
+ || strcmp(gsym->name(), "___tls_get_addr") != 0)
+ {
+ fprintf(stderr, _("%s: %s: missing expected TLS relocation\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str());
+ gold_exit(false);
+ }
+
+ this->skip_call_tls_get_addr_ = false;
+
+ return false;
+ }
+
switch (r_type)
{
case elfcpp::R_386_NONE:
@@ -454,6 +587,34 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
Relocate_functions<32, false>::pcrel8(view, value, address);
break;
+ case elfcpp::R_386_PLT32:
+ if (gsym->is_resolved_locally())
+ Relocate_functions<32, false>::pcrel32(view, value, address);
+ else
+ fprintf(stderr, _("%s: %s: unsupported reloc %u\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
+ break;
+
+ case elfcpp::R_386_GOT32:
+ // Local GOT offsets not yet supported.
+ assert(gsym);
+ assert(gsym->has_got_offset());
+ value = gsym->got_offset();
+ Relocate_functions<32, false>::rel32(view, value);
+ break;
+
+ case elfcpp::R_386_GOTOFF:
+ value -= target->got_section(NULL, NULL)->address();
+ Relocate_functions<32, false>::rel32(view, value);
+ break;
+
+ case elfcpp::R_386_GOTPC:
+ value = target->got_section(NULL, NULL)->address();
+ Relocate_functions<32, false>::pcrel32(view, value, address);
+ break;
+
case elfcpp::R_386_COPY:
case elfcpp::R_386_GLOB_DAT:
case elfcpp::R_386_JUMP_SLOT:
@@ -480,15 +641,10 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
case elfcpp::R_386_TLS_LE_32:
case elfcpp::R_386_TLS_GOTDESC:
case elfcpp::R_386_TLS_DESC_CALL:
- Target_i386::Relocate::relocate_tls(relinfo, relnum, rel, r_type,
- gsym, value, view, address,
- view_size);
+ this->relocate_tls(relinfo, relnum, rel, r_type, gsym, value, view,
+ address, view_size);
break;
- case elfcpp::R_386_GOT32:
- case elfcpp::R_386_PLT32:
- case elfcpp::R_386_GOTOFF:
- case elfcpp::R_386_GOTPC:
case elfcpp::R_386_32PLT:
case elfcpp::R_386_TLS_GD_32:
case elfcpp::R_386_TLS_GD_PUSH:
@@ -507,6 +663,8 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
// gold_exit(false);
break;
}
+
+ return true;
}
// Perform a TLS relocation.
@@ -531,7 +689,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
gold_exit(false);
}
- const bool is_local = gsym == NULL || !gsym->in_dynsym();
+ const bool is_local = gsym == NULL || gsym->is_resolved_locally();
const unsigned int opt_r_type =
Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type);
switch (r_type)
@@ -564,6 +722,20 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
break;
case elfcpp::R_386_TLS_GD:
+ if (opt_r_type == elfcpp::R_386_TLS_LE_32)
+ {
+ this->tls_gd_to_le(relinfo, relnum, tls_segment,
+ rel, r_type, value, view,
+ view_size);
+ break;
+ }
+ fprintf(stderr, _("%s: %s: unsupported reloc %u\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
+ // gold_exit(false);
+ break;
+
case elfcpp::R_386_TLS_LDM:
case elfcpp::R_386_TLS_LDO_32:
case elfcpp::R_386_TLS_GOTDESC:
@@ -667,14 +839,79 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
}
- if (r_type == elfcpp::R_386_TLS_IE_32)
- value = tls_segment->vaddr() + tls_segment->memsz() - value;
- else // elfcpp::R_386_TLS_IE, elfcpp::R_386_TLS_GOTIE
- value = value - (tls_segment->vaddr() + tls_segment->memsz());
+ value = tls_segment->vaddr() + tls_segment->memsz() - value;
+ if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE)
+ value = - value;
Relocate_functions<32, false>::rel32(view, value);
}
+// Do a relocation in which we convert a TLS Global-Dynamic to a
+// Local-Exec.
+
+inline void
+Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ off_t view_size)
+{
+ // leal foo(,%reg,1),%eax; call ___tls_get_addr
+ // ==> movl %gs,0,%eax; subl $foo@tpoff,%eax
+ // leal foo(%reg),%eax; call ___tls_get_addr
+ // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax
+
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2);
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9);
+
+ unsigned char op1 = view[-1];
+ unsigned char op2 = view[-2];
+
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ op2 == 0x8d || op2 == 0x04);
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ view[4] == 0xe8);
+
+ int roff = 5;
+
+ if (op2 == 0x04)
+ {
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -3);
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ view[-3] == 0x8d);
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ ((op1 & 0xc7) == 0x05
+ && op1 != (4 << 3)));
+ memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+ }
+ else
+ {
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ (op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
+ 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->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.
+ // We can skip it.
+ this->skip_call_tls_get_addr_ = true;
+}
+
// Check the range for a TLS relocation.
inline void
@@ -724,8 +961,10 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
{
assert(sh_type == elfcpp::SHT_REL);
- gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>(
+ gold::relocate_section<32, false, Target_i386, elfcpp::SHT_REL,
+ Target_i386::Relocate>(
relinfo,
+ this,
prelocs,
reloc_count,
view,
@@ -733,10 +972,6 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
view_size);
}
-// The i386 target.
-
-Target_i386 target_i386;
-
// The selector for i386 object files.
class Target_selector_i386 : public Target_selector
@@ -747,16 +982,21 @@ public:
{ }
Target*
- recognize(int machine, int osabi, int abiversion) const;
+ recognize(int machine, int osabi, int abiversion);
+
+ private:
+ Target_i386* target_;
};
// Recognize an i386 object file when we already know that the machine
// number is EM_386.
Target*
-Target_selector_i386::recognize(int, int, int) const
+Target_selector_i386::recognize(int, int, int)
{
- return &target_i386;
+ if (this->target_ == NULL)
+ this->target_ = new Target_i386();
+ return this->target_;
}
Target_selector_i386 target_selector_i386;