aboutsummaryrefslogtreecommitdiff
path: root/gold/i386.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gold/i386.cc')
-rw-r--r--gold/i386.cc701
1 files changed, 649 insertions, 52 deletions
diff --git a/gold/i386.cc b/gold/i386.cc
index 32ee881..468cb90 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -2,8 +2,11 @@
#include "gold.h"
#include "elfcpp.h"
+#include "reloc.h"
#include "i386.h"
#include "object.h"
+#include "layout.h"
+#include "output.h"
#include "target.h"
#include "target-reloc.h"
#include "target-select.h"
@@ -22,31 +25,92 @@ class Target_i386 : public Sized_target<32, false>
: Sized_target<32, false>(&i386_info)
{ }
+ // Scan the relocations to look for symbol adjustments.
void
- relocate_section(const Symbol_table* symtab,
- Sized_object<32, false>*,
- unsigned int,
- const unsigned char*,
- size_t,
- unsigned int,
- const elfcpp::Elf_types<32>::Elf_Addr*,
- Symbol**,
- unsigned char*,
- elfcpp::Elf_types<32>::Elf_Addr,
- off_t);
+ scan_relocs(const General_options& options,
+ Symbol_table* symtab,
+ Sized_object<32, false>* object,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ size_t local_symbol_count,
+ const unsigned char* plocal_symbols,
+ Symbol** global_symbols);
- // The class which implements relocation.
- struct Relocate
+ // Relocate a section.
+ void
+ relocate_section(const Relocate_info<32, false>*,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ unsigned char* view,
+ elfcpp::Elf_types<32>::Elf_Addr view_address,
+ off_t view_size);
+
+ private:
+ // The class which scans relocations.
+ struct Scan
{
inline void
- operator()(Sized_object<32, false>*, const elfcpp::Rel<32, false>&,
- unsigned int r_type, Sized_symbol<32>*,
- elfcpp::Elf_types<32>::Elf_Addr,
- unsigned char*, elfcpp::Elf_types<32>::Elf_Addr);
+ local(const General_options& options, 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,
+ const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
+ Symbol* gsym);
};
- private:
+ // The class which implements relocation.
+ class Relocate
+ {
+ public:
+ // Do a relocation.
+ static inline void
+ relocate(const Relocate_info<32, false>*, size_t relnum,
+ const elfcpp::Rel<32, false>&,
+ unsigned int r_type, Sized_symbol<32>*,
+ elfcpp::Elf_types<32>::Elf_Addr,
+ unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
+ off_t);
+
+ private:
+ // Do a TLS relocation.
+ static inline void
+ relocate_tls(const Relocate_info<32, false>*, size_t relnum,
+ const elfcpp::Rel<32, false>&,
+ unsigned int r_type, Sized_symbol<32>*,
+ elfcpp::Elf_types<32>::Elf_Addr,
+ unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t);
+
+ // Do a TLS Initial-Exec to Local-Exec transition.
+ static inline void
+ tls_ie_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,
+ const elfcpp::Rel<32, false>&, off_t, off_t);
+
+ // Check the validity of a TLS relocation. This is like assert.
+ static inline void
+ check_tls(const Relocate_info<32, false>*, size_t relnum,
+ const elfcpp::Rel<32, false>&, bool);
+ };
+
+ // Adjust TLS relocation type based on the options and whether this
+ // is a local symbol.
+ static unsigned int
+ optimize_tls_reloc(const General_options*, bool is_local, int r_type);
+
+ // Information about this specific target which we pass to the
+ // general Target structure.
static const Target::Target_info i386_info;
};
@@ -62,75 +126,608 @@ const Target::Target_info Target_i386::i386_info =
0x1000 // common_pagesize
};
+// 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.
+
+unsigned int
+Target_i386::optimize_tls_reloc(const General_options* options, bool is_local,
+ int r_type)
+{
+ // If we are generating a shared library, then we can't do anything
+ // in the linker.
+ if (options->is_shared())
+ return r_type;
+
+ switch (r_type)
+ {
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_GOTDESC:
+ case elfcpp::R_386_TLS_DESC_CALL:
+ // These are Global-Dynamic which permits fully general TLS
+ // access. Since we know that we are generating an executable,
+ // we can convert this to Initial-Exec. If we also know that
+ // this is a local symbol, we can further switch to Local-Exec.
+ if (is_local)
+ return elfcpp::R_386_TLS_LE_32;
+ return elfcpp::R_386_TLS_IE_32;
+
+ case elfcpp::R_386_TLS_LDM:
+ // This is Local-Dynamic, which refers to a local symbol in the
+ // dynamic TLS block. Since we know that we generating an
+ // executable, we can switch to Local-Exec.
+ return elfcpp::R_386_TLS_LE_32;
+
+ case elfcpp::R_386_TLS_LDO_32:
+ // Another type of Local-Dynamic relocation.
+ return elfcpp::R_386_TLS_LE;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_IE_32:
+ // These are Initial-Exec relocs which get the thread offset
+ // from the GOT. If we know that we are linking against the
+ // local symbol, we can switch to Local-Exec, which links the
+ // thread offset into the instruction.
+ if (is_local)
+ return elfcpp::R_386_TLS_LE_32;
+ return r_type;
+
+ case elfcpp::R_386_TLS_LE:
+ case elfcpp::R_386_TLS_LE_32:
+ // When we already have Local-Exec, there is nothing further we
+ // can do.
+ return r_type;
+
+ default:
+ abort();
+ }
+}
+
+// Scan a relocation for a local symbol.
+
+inline void
+Target_i386::Scan::local(const General_options& options,
+ Sized_object<32, false>* object,
+ const elfcpp::Rel<32, false>&, unsigned int r_type,
+ const elfcpp::Sym<32, false>&)
+{
+ switch (r_type)
+ {
+ case elfcpp::R_386_NONE:
+ case elfcpp::R_386_GNU_VTINHERIT:
+ case elfcpp::R_386_GNU_VTENTRY:
+ break;
+
+ case elfcpp::R_386_32:
+ case elfcpp::R_386_16:
+ case elfcpp::R_386_8:
+ // FIXME: If we are generating a shared object we need to copy
+ // this relocation into the object.
+ break;
+
+ case elfcpp::R_386_PC32:
+ case elfcpp::R_386_PC16:
+ case elfcpp::R_386_PC8:
+ break;
+
+ 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:
+ fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"),
+ program_name, object->name().c_str(), r_type);
+ gold_exit(false);
+ break;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_LE:
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_LDM:
+ case elfcpp::R_386_TLS_LDO_32:
+ case elfcpp::R_386_TLS_IE_32:
+ case elfcpp::R_386_TLS_LE_32:
+ case elfcpp::R_386_TLS_GOTDESC:
+ case elfcpp::R_386_TLS_DESC_CALL:
+ r_type = Target_i386::optimize_tls_reloc(&options, true, r_type);
+ switch (r_type)
+ {
+ case elfcpp::R_386_TLS_LE:
+ case elfcpp::R_386_TLS_LE_32:
+ // FIXME: If generating a shared object, we need to copy
+ // this relocation into the object.
+ break;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_LDM:
+ case elfcpp::R_386_TLS_LDO_32:
+ case elfcpp::R_386_TLS_IE_32:
+ case elfcpp::R_386_TLS_GOTDESC:
+ case elfcpp::R_386_TLS_DESC_CALL:
+ fprintf(stderr,
+ _("%s: %s: unsupported reloc %u against local symbol\n"),
+ program_name, object->name().c_str(), r_type);
+ break;
+ }
+ 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:
+ 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:
+ fprintf(stderr, _("%s: %s: unsupported reloc %u against local symbol\n"),
+ program_name, object->name().c_str(), r_type);
+ break;
+ }
+}
+
+// Scan a relocation for a global symbol.
+
+inline void
+Target_i386::Scan::global(const General_options& options,
+ Sized_object<32, false>* object,
+ const elfcpp::Rel<32, false>&, unsigned int r_type,
+ Symbol* gsym)
+{
+ switch (r_type)
+ {
+ case elfcpp::R_386_NONE:
+ case elfcpp::R_386_GNU_VTINHERIT:
+ case elfcpp::R_386_GNU_VTENTRY:
+ 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:
+ // FIXME: If we are generating a shared object we may need to
+ // copy this relocation into the object. If this symbol is
+ // defined in a shared object, we may need to copy this
+ // relocation in order to avoid a COPY relocation.
+ break;
+
+ 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:
+ fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"),
+ program_name, object->name().c_str(), r_type);
+ gold_exit(false);
+ break;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_LE:
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_LDM:
+ case elfcpp::R_386_TLS_LDO_32:
+ case elfcpp::R_386_TLS_IE_32:
+ case elfcpp::R_386_TLS_LE_32:
+ case elfcpp::R_386_TLS_GOTDESC:
+ case elfcpp::R_386_TLS_DESC_CALL:
+ r_type = Target_i386::optimize_tls_reloc(&options,
+ !gsym->in_dynsym(),
+ r_type);
+ switch (r_type)
+ {
+ case elfcpp::R_386_TLS_LE:
+ case elfcpp::R_386_TLS_LE_32:
+ // FIXME: If generating a shared object, we need to copy
+ // this relocation into the object.
+ break;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_LDM:
+ case elfcpp::R_386_TLS_LDO_32:
+ case elfcpp::R_386_TLS_IE_32:
+ case elfcpp::R_386_TLS_GOTDESC:
+ case elfcpp::R_386_TLS_DESC_CALL:
+ fprintf(stderr,
+ _("%s: %s: unsupported reloc %u against global symbol %s\n"),
+ program_name, object->name().c_str(), r_type, gsym->name());
+ break;
+ }
+ 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:
+ 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:
+ fprintf(stderr,
+ _("%s: %s: unsupported reloc %u against global symbol %s\n"),
+ program_name, object->name().c_str(), r_type, gsym->name());
+ break;
+ }
+}
+
+// Scan relocations for a section.
+
+void
+Target_i386::scan_relocs(const General_options& options,
+ Symbol_table* symtab,
+ Sized_object<32, false>* object,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ size_t local_symbol_count,
+ const unsigned char* plocal_symbols,
+ Symbol** global_symbols)
+{
+ if (sh_type == elfcpp::SHT_RELA)
+ {
+ fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"),
+ program_name, object->name().c_str());
+ gold_exit(false);
+ }
+
+ gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>(
+ options,
+ symtab,
+ object,
+ prelocs,
+ reloc_count,
+ local_symbol_count,
+ plocal_symbols,
+ global_symbols);
+}
+
// Perform a relocation.
inline void
-Target_i386::Relocate::operator()(Sized_object<32, false>* object,
- const elfcpp::Rel<32, false>&,
- unsigned int r_type,
- Sized_symbol<32>*,
- elfcpp::Elf_types<32>::Elf_Addr value,
- unsigned char* view,
- elfcpp::Elf_types<32>::Elf_Addr address)
+Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int r_type,
+ Sized_symbol<32>* gsym,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ elfcpp::Elf_types<32>::Elf_Addr address,
+ off_t view_size)
{
switch (r_type)
{
case elfcpp::R_386_NONE:
+ case elfcpp::R_386_GNU_VTINHERIT:
+ case elfcpp::R_386_GNU_VTENTRY:
break;
case elfcpp::R_386_32:
- {
- elfcpp::Elf_Word* wv = reinterpret_cast<elfcpp::Elf_Word*>(view);
- unsigned int x = elfcpp::read_elf_word<false>(wv);
- elfcpp::write_elf_word<false>(wv, x + value);
- }
+ Relocate_functions<32, false>::rel32(view, value);
break;
case elfcpp::R_386_PC32:
- {
- elfcpp::Elf_Word* wv = reinterpret_cast<elfcpp::Elf_Word*>(view);
- unsigned int x = elfcpp::read_elf_word<false>(wv);
- elfcpp::write_elf_word<false>(wv, x + value - address);
- }
+ Relocate_functions<32, false>::pcrel32(view, value, address);
+ break;
+
+ case elfcpp::R_386_16:
+ Relocate_functions<32, false>::rel16(view, value);
+ break;
+
+ case elfcpp::R_386_PC16:
+ Relocate_functions<32, false>::pcrel16(view, value, address);
break;
+ case elfcpp::R_386_8:
+ Relocate_functions<32, false>::rel8(view, value);
+ break;
+
+ case elfcpp::R_386_PC8:
+ Relocate_functions<32, false>::pcrel8(view, value, address);
+ break;
+
+ 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:
+ fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
+ gold_exit(false);
+ break;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_LE:
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_LDM:
+ case elfcpp::R_386_TLS_LDO_32:
+ case elfcpp::R_386_TLS_IE_32:
+ 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);
+ 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:
+ 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:
fprintf(stderr, _("%s: %s: unsupported reloc %u\n"),
- program_name, object->name().c_str(), r_type);
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
// gold_exit(false);
+ break;
+ }
+}
+
+// Perform a TLS relocation.
+
+inline void
+Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int r_type,
+ Sized_symbol<32>* gsym,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ elfcpp::Elf_types<32>::Elf_Addr,
+ off_t view_size)
+{
+ Output_segment* tls_segment = relinfo->layout->tls_segment();
+ if (tls_segment == NULL)
+ {
+ fprintf(stderr, _("%s: %s: TLS reloc but no TLS segment\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str());
+ gold_exit(false);
+ }
+
+ const bool is_local = gsym == NULL || !gsym->in_dynsym();
+ const unsigned int opt_r_type =
+ Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type);
+ switch (r_type)
+ {
+ case elfcpp::R_386_TLS_LE_32:
+ value = tls_segment->vaddr() + tls_segment->memsz() - value;
+ Relocate_functions<32, false>::rel32(view, value);
+ break;
+
+ case elfcpp::R_386_TLS_LE:
+ value = value - (tls_segment->vaddr() + tls_segment->memsz());
+ Relocate_functions<32, false>::rel32(view, value);
+ break;
+
+ case elfcpp::R_386_TLS_IE:
+ case elfcpp::R_386_TLS_GOTIE:
+ case elfcpp::R_386_TLS_IE_32:
+ if (opt_r_type == elfcpp::R_386_TLS_LE_32)
+ {
+ Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
+ rel, r_type, value, view,
+ view_size);
+ break;
+ }
+ fprintf(stderr, _("%s: %s: unsupported reloc type %u\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
+ // gold_exit(false);
+ break;
+
+ case elfcpp::R_386_TLS_GD:
+ case elfcpp::R_386_TLS_LDM:
+ case elfcpp::R_386_TLS_LDO_32:
+ case elfcpp::R_386_TLS_GOTDESC:
+ case elfcpp::R_386_TLS_DESC_CALL:
+ 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;
+ }
+}
+
+// Do a relocation in which we convert a TLS Initial-Exec to a
+// Local-Exec.
+
+inline void
+Target_i386::Relocate::tls_ie_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,
+ off_t view_size)
+{
+ // We have to actually change the instructions, which means that we
+ // need to examine the opcodes to figure out which instruction we
+ // are looking at.
+ if (r_type == elfcpp::R_386_TLS_IE)
+ {
+ // movl %gs:XX,%eax ==> movl $YY,%eax
+ // movl %gs:XX,%reg ==> movl $YY,%reg
+ // addl %gs:XX,%reg ==> addl $YY,%reg
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -1);
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4);
+
+ unsigned char op1 = view[-1];
+ if (op1 == 0xa1)
+ {
+ // movl XX,%eax ==> movl $YY,%eax
+ view[-1] = 0xb8;
+ }
+ else
+ {
+ Target_i386::Relocate::check_range(relinfo, relnum, rel,
+ view_size, -2);
+
+ unsigned char op2 = view[-2];
+ if (op2 == 0x8b)
+ {
+ // movl XX,%reg ==> movl $YY,%reg
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ (op1 & 0xc7) == 0x05);
+ view[-2] = 0xc7;
+ view[-1] = 0xc0 | ((op1 >> 3) & 7);
+ }
+ else if (op2 == 0x03)
+ {
+ // addl XX,%reg ==> addl $YY,%reg
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ (op1 & 0xc7) == 0x05);
+ view[-2] = 0x81;
+ view[-1] = 0xc0 | ((op1 >> 3) & 7);
+ }
+ else
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
+ }
+ }
+ else
+ {
+ // subl %gs:XX(%reg1),%reg2 ==> subl $YY,%reg2
+ // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2
+ // addl %gs:XX(%reg1),%reg2 ==> addl $YY,$reg2
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2);
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4);
+
+ unsigned char op1 = view[-1];
+ unsigned char op2 = view[-2];
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ (op1 & 0xc0) == 0x80 && (op1 & 7) != 4);
+ if (op2 == 0x8b)
+ {
+ // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2
+ view[-2] = 0xc7;
+ view[-1] = 0xc0 | ((op1 >> 3) & 7);
+ }
+ else if (op2 == 0x2b)
+ {
+ // subl %gs:XX(%reg1),%reg2 ==> subl $YY,%reg2
+ view[-2] = 0x81;
+ view[-1] = 0xe8 | ((op1 >> 3) & 7);
+ }
+ else if (op2 == 0x03)
+ {
+ // addl %gs:XX(%reg1),%reg2 ==> addl $YY,$reg2
+ view[-2] = 0x81;
+ view[-1] = 0xc0 | ((op1 >> 3) & 7);
+ }
+ else
+ 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());
+
+ Relocate_functions<32, false>::rel32(view, value);
+}
+
+// Check the range for a TLS relocation.
+
+inline void
+Target_i386::Relocate::check_range(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ const elfcpp::Rel<32, false>& rel,
+ off_t view_size, off_t off)
+{
+ off_t offset = rel.get_r_offset() + off;
+ if (offset < 0 || offset > view_size)
+ {
+ fprintf(stderr, _("%s: %s: TLS relocation out of range\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str());
+ gold_exit(false);
+ }
+}
+
+// Check the validity of a TLS relocation. This is like assert.
+
+inline void
+Target_i386::Relocate::check_tls(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ const elfcpp::Rel<32, false>& rel,
+ bool valid)
+{
+ if (!valid)
+ {
+ fprintf(stderr,
+ _("%s: %s: TLS relocation against invalid instruction\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str());
+ gold_exit(false);
}
}
// Relocate section data.
void
-Target_i386::relocate_section(const Symbol_table* symtab,
- Sized_object<32, false>* object,
+Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
- unsigned int local_count,
- const elfcpp::Elf_types<32>::Elf_Addr* values,
- Symbol** global_syms,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr address,
off_t view_size)
{
- if (sh_type == elfcpp::SHT_RELA)
- {
- fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"),
- program_name, object->name().c_str());
- gold_exit(false);
- }
+ assert(sh_type == elfcpp::SHT_REL);
gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>(
- symtab,
- object,
+ relinfo,
prelocs,
reloc_count,
- local_count,
- values,
- global_syms,
view,
address,
view_size);