Can now do a full static link of hello, world in C or C++
[deliverable/binutils-gdb.git] / gold / i386.cc
index 468cb90efe7c095ed291c06340ee3efacfc71529..53af5eeae6f605e20273adad45113d8d0267b0ac 100644 (file)
@@ -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;
This page took 0.041924 seconds and 4 git commands to generate.