2010-03-22 Doug Kwan <dougkwan@google.com>
[binutils-gdb.git] / gold / arm.cc
index d974b1bd7e7db2e317080ce485bcc9d845f9f70f..890646e7ba25efd04f2538cb435e9b3abcf66b41 100644 (file)
@@ -80,6 +80,12 @@ class Arm_exidx_input_section;
 template<bool big_endian>
 class Arm_relobj;
 
+template<bool big_endian>
+class Arm_relocate_functions;
+
+template<bool big_endian>
+class Arm_output_data_got;
+
 template<bool big_endian>
 class Target_arm;
 
@@ -94,6 +100,9 @@ const int32_t THM_MAX_BWD_BRANCH_OFFSET = (-(1 << 22) + 4);
 const int32_t THM2_MAX_FWD_BRANCH_OFFSET = (((1 << 24) - 2) + 4);
 const int32_t THM2_MAX_BWD_BRANCH_OFFSET = (-(1 << 24) + 4);
 
+// Thread Control Block size.
+const size_t ARM_TCB_SIZE = 8;
+
 // The arm target class.
 //
 // This is a very simple port of gold for ARM-EABI.  It is intended for
@@ -859,8 +868,9 @@ class Stub_table : public Output_data
 {
  public:
   Stub_table(Arm_input_section<big_endian>* owner)
-    : Output_data(), owner_(owner), reloc_stubs_(), cortex_a8_stubs_(),
-      arm_v4bx_stubs_(0xf), prev_data_size_(0), prev_addralign_(1)
+    : Output_data(), owner_(owner), reloc_stubs_(), reloc_stubs_size_(0),
+      reloc_stubs_addralign_(1), cortex_a8_stubs_(), arm_v4bx_stubs_(0xf),
+      prev_data_size_(0), prev_addralign_(1)
   { }
 
   ~Stub_table()
@@ -893,6 +903,15 @@ class Stub_table : public Output_data
     const Stub_template* stub_template = stub->stub_template();
     gold_assert(stub_template->type() == key.stub_type());
     this->reloc_stubs_[key] = stub;
+
+    // Assign stub offset early.  We can do this because we never remove
+    // reloc stubs and they are in the beginning of the stub table.
+    uint64_t align = stub_template->alignment();
+    this->reloc_stubs_size_ = align_address(this->reloc_stubs_size_, align);
+    stub->set_offset(this->reloc_stubs_size_);
+    this->reloc_stubs_size_ += stub_template->size();
+    this->reloc_stubs_addralign_ =
+      std::max(this->reloc_stubs_addralign_, align);
   }
 
   // Add a Cortex-A8 STUB that fixes up a THUMB branch at ADDRESS.
@@ -1001,6 +1020,10 @@ class Stub_table : public Output_data
   Arm_input_section<big_endian>* owner_;
   // The relocation stubs.
   Reloc_stub_map reloc_stubs_;
+  // Size of reloc stubs.
+  off_t reloc_stubs_size_;
+  // Maximum address alignment of reloc stubs.
+  uint64_t reloc_stubs_addralign_;
   // The cortex_a8_stubs.
   Cortex_a8_stub_list cortex_a8_stubs_;
   // The Arm V4BX relocation stubs.
@@ -1315,7 +1338,8 @@ class Arm_output_section : public Output_section
   // is a list of text input sections sorted in ascending order of their
   // output addresses.
   void
-  fix_exidx_coverage(const Text_section_list& sorted_text_section,
+  fix_exidx_coverage(Layout* layout,
+                    const Text_section_list& sorted_text_section,
                     Symbol_table* symtab);
 
  private:
@@ -1775,6 +1799,144 @@ class Cortex_a8_reloc
   Arm_address destination_;
 };
 
+// Arm_output_data_got class.  We derive this from Output_data_got to add
+// extra methods to handle TLS relocations in a static link.
+
+template<bool big_endian>
+class Arm_output_data_got : public Output_data_got<32, big_endian>
+{
+ public:
+  Arm_output_data_got(Symbol_table* symtab, Layout* layout)
+    : Output_data_got<32, big_endian>(), symbol_table_(symtab), layout_(layout)
+  { }
+
+  // Add a static entry for the GOT entry at OFFSET.  GSYM is a global
+  // symbol and R_TYPE is the code of a dynamic relocation that needs to be
+  // applied in a static link.
+  void
+  add_static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
+  { this->static_relocs_.push_back(Static_reloc(got_offset, r_type, gsym)); }
+
+  // Add a static reloc for the GOT entry at OFFSET.  RELOBJ is an object
+  // defining a local symbol with INDEX.  R_TYPE is the code of a dynamic
+  // relocation that needs to be applied in a static link.
+  void
+  add_static_reloc(unsigned int got_offset, unsigned int r_type,
+                  Sized_relobj<32, big_endian>* relobj, unsigned int index)
+  {
+    this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj,
+                                               index));
+  }
+
+  // Add a GOT pair for R_ARM_TLS_GD32.  The creates a pair of GOT entries.
+  // The first one is initialized to be 1, which is the module index for
+  // the main executable and the second one 0.  A reloc of the type
+  // R_ARM_TLS_DTPOFF32 will be created for the second GOT entry and will
+  // be applied by gold.  GSYM is a global symbol.
+  void
+  add_tls_gd32_with_static_reloc(unsigned int got_type, Symbol* gsym);
+
+  // Same as the above but for a local symbol in OBJECT with INDEX.
+  void
+  add_tls_gd32_with_static_reloc(unsigned int got_type,
+                                Sized_relobj<32, big_endian>* object,
+                                unsigned int index);
+
+ protected:
+  // Write out the GOT table.
+  void
+  do_write(Output_file*);
+
+ private:
+  // This class represent dynamic relocations that need to be applied by
+  // gold because we are using TLS relocations in a static link.
+  class Static_reloc
+  {
+   public:
+    Static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
+      : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true)
+    { this->u_.global.symbol = gsym; }
+
+    Static_reloc(unsigned int got_offset, unsigned int r_type,
+         Sized_relobj<32, big_endian>* relobj, unsigned int index)
+      : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false)
+    {
+      this->u_.local.relobj = relobj;
+      this->u_.local.index = index;
+    }
+
+    // Return the GOT offset.
+    unsigned int
+    got_offset() const
+    { return this->got_offset_; }
+
+    // Relocation type.
+    unsigned int
+    r_type() const
+    { return this->r_type_; }
+
+    // Whether the symbol is global or not.
+    bool
+    symbol_is_global() const
+    { return this->symbol_is_global_; }
+
+    // For a relocation against a global symbol, the global symbol.
+    Symbol*
+    symbol() const
+    {
+      gold_assert(this->symbol_is_global_);
+      return this->u_.global.symbol;
+    }
+
+    // For a relocation against a local symbol, the defining object.
+    Sized_relobj<32, big_endian>*
+    relobj() const
+    {
+      gold_assert(!this->symbol_is_global_);
+      return this->u_.local.relobj;
+    }
+
+    // For a relocation against a local symbol, the local symbol index.
+    unsigned int
+    index() const
+    {
+      gold_assert(!this->symbol_is_global_);
+      return this->u_.local.index;
+    }
+
+   private:
+    // GOT offset of the entry to which this relocation is applied.
+    unsigned int got_offset_;
+    // Type of relocation.
+    unsigned int r_type_;
+    // Whether this relocation is against a global symbol.
+    bool symbol_is_global_;
+    // A global or local symbol.
+    union
+    {
+      struct
+      {
+       // For a global symbol, the symbol itself.
+       Symbol* symbol;
+      } global;
+      struct
+      {
+       // For a local symbol, the object defining object.
+       Sized_relobj<32, big_endian>* relobj;
+       // For a local symbol, the symbol index.
+       unsigned int index;
+      } local;
+    } u_;
+  };
+
+  // Symbol table of the output object.
+  Symbol_table* symbol_table_;
+  // Layout of the output object.
+  Layout* layout_;
+  // Static relocs to be applied to the GOT.
+  std::vector<Static_reloc> static_relocs_;
+};
+
 // Utilities for manipulating integers of up to 32-bits
 
 namespace utils
@@ -1847,11 +2009,12 @@ class Target_arm : public Sized_target<32, big_endian>
   Target_arm()
     : Sized_target<32, big_endian>(&arm_info),
       got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
-      copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL), stub_tables_(),
-      stub_factory_(Stub_factory::get_instance()), may_use_blx_(false),
-      should_force_pic_veneer_(false), arm_input_section_map_(),
-      attributes_section_data_(NULL), fix_cortex_a8_(false),
-      cortex_a8_relocs_info_()
+      copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL), 
+      got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
+      stub_tables_(), stub_factory_(Stub_factory::get_instance()),
+      may_use_blx_(false), should_force_pic_veneer_(false),
+      arm_input_section_map_(), attributes_section_data_(NULL),
+      fix_cortex_a8_(false), cortex_a8_relocs_info_()
   { }
 
   // Whether we can use BLX.
@@ -2295,12 +2458,24 @@ class Target_arm : public Sized_target<32, big_endian>
        case elfcpp::R_ARM_THM_JUMP19:
        case elfcpp::R_ARM_PLT32:
        case elfcpp::R_ARM_THM_XPC22:
+       case elfcpp::R_ARM_PREL31:
+       case elfcpp::R_ARM_SBREL31:
          return false;
 
        default:
          return true;
        }
     }
+
+   private:
+    // Do a TLS relocation.
+    inline typename Arm_relocate_functions<big_endian>::Status
+    relocate_tls(const Relocate_info<32, big_endian>*, Target_arm<big_endian>*,
+                 size_t, const elfcpp::Rel<32, big_endian>&, unsigned int,
+                const Sized_symbol<32>*, const Symbol_value<32>*,
+                unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
+                section_size_type);
+
   };
 
   // A class which returns the size required for a relocation type,
@@ -2312,8 +2487,13 @@ class Target_arm : public Sized_target<32, big_endian>
     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
+  optimize_tls_reloc(bool is_final, int r_type);
+
   // Get the GOT section, creating it if necessary.
-  Output_data_got<32, big_endian>*
+  Arm_output_data_got<big_endian>*
   got_section(Symbol_table*, Layout*);
 
   // Get the GOT PLT section.
@@ -2328,6 +2508,15 @@ class Target_arm : public Sized_target<32, big_endian>
   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, big_endian>* object);
+
   // Get the PLT section.
   const Output_data_plt_arm<big_endian>*
   plt_section() const
@@ -2340,6 +2529,10 @@ class Target_arm : public Sized_target<32, big_endian>
   Reloc_section*
   rel_dyn_section(Layout*);
 
+  // Get the section to use for TLS_DESC relocations.
+  Reloc_section*
+  rel_tls_desc_section(Layout*) const;
+
   // Return true if the symbol may need a COPY relocation.
   // References from an executable object to non-function symbols
   // defined in a dynamic object may need a COPY relocation.
@@ -2454,7 +2647,11 @@ class Target_arm : public Sized_target<32, big_endian>
   // The types of GOT entries needed for this platform.
   enum Got_type
   {
-    GOT_TYPE_STANDARD = 0      // GOT entry for a regular symbol
+    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
   };
 
   typedef typename std::vector<Stub_table<big_endian>*> Stub_table_list;
@@ -2470,7 +2667,7 @@ class Target_arm : public Sized_target<32, big_endian>
          Cortex_a8_relocs_info;
 
   // The GOT section.
-  Output_data_got<32, big_endian>* got_;
+  Arm_output_data_got<big_endian>* got_;
   // The PLT section.
   Output_data_plt_arm<big_endian>* plt_;
   // The GOT PLT section.
@@ -2481,6 +2678,10 @@ class Target_arm : public Sized_target<32, big_endian>
   Copy_relocs<elfcpp::SHT_REL, 32, big_endian> 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_;
   // Vector of Stub_tables created.
   Stub_table_list stub_tables_;
   // Stub factory.
@@ -2784,7 +2985,10 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
     Reltype x = psymval->value(object, addend);
     val = utils::bit_select(val, x, 0xffU);
     elfcpp::Swap<8, big_endian>::writeval(wv, val);
-    return (utils::has_signed_unsigned_overflow<8>(x)
+
+    // R_ARM_ABS8 permits signed or unsigned results.
+    int signed_x = static_cast<int32_t>(x);
+    return ((signed_x < -128 || signed_x > 255)
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
   }
@@ -2803,7 +3007,10 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
     Reltype x = psymval->value(object, addend);
     val = utils::bit_select(val, x << 6, 0x7e0U);
     elfcpp::Swap<16, big_endian>::writeval(wv, val);
-    return (utils::has_overflow<5>(x)
+
+    // R_ARM_ABS16 permits signed or unsigned results.
+    int signed_x = static_cast<int32_t>(x);
+    return ((signed_x < -32768 || signed_x > 65535)
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
   }
@@ -3507,12 +3714,14 @@ Arm_relocate_functions<big_endian>::arm_branch_common(
   // to switch mode.
   bool may_use_blx = arm_target->may_use_blx();
   Reloc_stub* stub = NULL;
-  if ((branch_offset > ARM_MAX_FWD_BRANCH_OFFSET)
-      || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
+  if (utils::has_overflow<26>(branch_offset)
       || ((thumb_bit != 0) && !(may_use_blx && r_type == elfcpp::R_ARM_CALL)))
     {
+      Valtype unadjusted_branch_target = psymval->value(object, 0);
+
       Stub_type stub_type =
-       Reloc_stub::stub_type_for_reloc(r_type, address, branch_target,
+       Reloc_stub::stub_type_for_reloc(r_type, address,
+                                       unadjusted_branch_target,
                                        (thumb_bit != 0));
       if (stub_type != arm_stub_none)
        {
@@ -3526,8 +3735,7 @@ Arm_relocate_functions<big_endian>::arm_branch_common(
          thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
          branch_target = stub_table->address() + stub->offset() + addend;
          branch_offset = branch_target - address;
-         gold_assert((branch_offset <= ARM_MAX_FWD_BRANCH_OFFSET)
-                     && (branch_offset >= ARM_MAX_BWD_BRANCH_OFFSET));
+         gold_assert(!utils::has_overflow<26>(branch_offset));
        }
     }
 
@@ -3629,25 +3837,30 @@ Arm_relocate_functions<big_endian>::thumb_branch_common(
  
   int32_t addend = This::thumb32_branch_offset(upper_insn, lower_insn);
   Arm_address branch_target = psymval->value(object, addend);
+
+  // For BLX, bit 1 of target address comes from bit 1 of base address.
+  bool may_use_blx = arm_target->may_use_blx();
+  if (thumb_bit == 0 && may_use_blx)
+    branch_target = utils::bit_select(branch_target, address, 0x2);
+
   int32_t branch_offset = branch_target - address;
 
   // We need a stub if the branch offset is too large or if we need
   // to switch mode.
-  bool may_use_blx = arm_target->may_use_blx();
   bool thumb2 = arm_target->using_thumb2();
-  if ((!thumb2
-       && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
-          || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
-      || (thumb2
-         && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
-             || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
+  if ((!thumb2 && utils::has_overflow<23>(branch_offset))
+      || (thumb2 && utils::has_overflow<25>(branch_offset))
       || ((thumb_bit == 0)
           && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
              || r_type == elfcpp::R_ARM_THM_JUMP24)))
     {
+      Arm_address unadjusted_branch_target = psymval->value(object, 0);
+
       Stub_type stub_type =
-       Reloc_stub::stub_type_for_reloc(r_type, address, branch_target,
+       Reloc_stub::stub_type_for_reloc(r_type, address,
+                                       unadjusted_branch_target,
                                        (thumb_bit != 0));
+
       if (stub_type != arm_stub_none)
        {
          Stub_table<big_endian>* stub_table =
@@ -3659,6 +3872,8 @@ Arm_relocate_functions<big_endian>::thumb_branch_common(
          gold_assert(stub != NULL);
          thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
          branch_target = stub_table->address() + stub->offset() + addend;
+         if (thumb_bit == 0 && may_use_blx) 
+           branch_target = utils::bit_select(branch_target, address, 0x2);
          branch_offset = branch_target - address;
        }
     }
@@ -3679,12 +3894,12 @@ Arm_relocate_functions<big_endian>::thumb_branch_common(
       lower_insn |= 0x1000U;
     }
 
+  // For a BLX instruction, make sure that the relocation is rounded up
+  // to a word boundary.  This follows the semantics of the instruction
+  // which specifies that bit 1 of the target address will come from bit
+  // 1 of the base address.
   if ((lower_insn & 0x5000U) == 0x4000U)
-    // For a BLX instruction, make sure that the relocation is rounded up
-    // to a word boundary.  This follows the semantics of the instruction
-    // which specifies that bit 1 of the target address will come from bit
-    // 1 of the base address.
-    branch_offset = (branch_offset + 2) & ~3;
+    gold_assert((branch_offset & 3) == 0);
 
   // Put BRANCH_OFFSET back into the insn.  Assumes two's complement.
   // We use the Thumb-2 encoding, which is safe even if dealing with
@@ -3695,6 +3910,8 @@ Arm_relocate_functions<big_endian>::thumb_branch_common(
   elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
   elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
 
+  gold_assert(!utils::has_overflow<25>(branch_offset));
+
   return ((thumb2
           ? utils::has_overflow<25>(branch_offset)
           : utils::has_overflow<23>(branch_offset))
@@ -3750,22 +3967,21 @@ Arm_relocate_functions<big_endian>::thm_jump19(
 // Get the GOT section, creating it if necessary.
 
 template<bool big_endian>
-Output_data_got<32, big_endian>*
+Arm_output_data_got<big_endian>*
 Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
 {
   if (this->got_ == NULL)
     {
       gold_assert(symtab != NULL && layout != NULL);
 
-      this->got_ = new Output_data_got<32, big_endian>();
+      this->got_ = new Arm_output_data_got<big_endian>(symtab, layout);
 
       Output_section* os;
       os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
                                           (elfcpp::SHF_ALLOC
                                            | elfcpp::SHF_WRITE),
-                                          this->got_, false, true, true,
-                                          false);
-
+                                          this->got_, false, false, false,
+                                          true);
       // 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
@@ -3775,7 +3991,7 @@ Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
                                           (elfcpp::SHF_ALLOC
                                            | elfcpp::SHF_WRITE),
                                           this->got_plt_, false, false,
-                                          false, true);
+                                          false, false);
 
       // The first three entries are reserved.
       this->got_plt_->set_current_data_size(3 * 4);
@@ -4024,10 +4240,15 @@ Reloc_stub::stub_type_for_reloc(
       thumb_only = little_endian_target->using_thumb_only();
     }
 
-  int64_t branch_offset = (int64_t)destination - location;
-
+  int64_t branch_offset;
   if (r_type == elfcpp::R_ARM_THM_CALL || r_type == elfcpp::R_ARM_THM_JUMP24)
     {
+      // For THUMB BLX instruction, bit 1 of target comes from bit 1 of the
+      // base address (instruction address + 4).
+      if ((r_type == elfcpp::R_ARM_THM_CALL) && may_use_blx && !target_is_thumb)
+       destination = utils::bit_select(destination, location, 0x2);
+      branch_offset = static_cast<int64_t>(destination) - location;
+       
       // Handle cases where:
       // - this call goes too far (different Thumb/Thumb2 max
       //   distance)
@@ -4108,6 +4329,7 @@ Reloc_stub::stub_type_for_reloc(
           || r_type == elfcpp::R_ARM_JUMP24
           || r_type == elfcpp::R_ARM_PLT32)
     {
+      branch_offset = static_cast<int64_t>(destination) - location;
       if (target_is_thumb)
        {
          // Arm to thumb.
@@ -4533,20 +4755,9 @@ template<bool big_endian>
 bool
 Stub_table<big_endian>::update_data_size_and_addralign()
 {
-  off_t size = 0;
-  unsigned addralign = 1;
-
   // Go over all stubs in table to compute data size and address alignment.
-  
-  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
-      p != this->reloc_stubs_.end();
-      ++p)
-    {
-      const Stub_template* stub_template = p->second->stub_template();
-      addralign = std::max(addralign, stub_template->alignment());
-      size = (align_address(size, stub_template->alignment())
-             + stub_template->size());
-    }
+  off_t size = this->reloc_stubs_size_;
+  unsigned addralign = this->reloc_stubs_addralign_;
 
   for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
        p != this->cortex_a8_stubs_.end();
@@ -4591,19 +4802,7 @@ template<bool big_endian>
 void
 Stub_table<big_endian>::finalize_stubs()
 {
-  off_t off = 0;
-  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
-      p != this->reloc_stubs_.end();
-      ++p)
-    {
-      Reloc_stub* stub = p->second;
-      const Stub_template* stub_template = stub->stub_template();
-      uint64_t stub_addralign = stub_template->alignment();
-      off = align_address(off, stub_addralign);
-      stub->set_offset(off);
-      off += stub_template->size();
-    }
-
+  off_t off = this->reloc_stubs_size_;
   for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
        p != this->cortex_a8_stubs_.end();
        ++p)
@@ -5007,9 +5206,10 @@ Arm_exidx_fixup::update_offset_map(
 {
   if (this->section_offset_map_ == NULL)
     this->section_offset_map_ = new Arm_exidx_section_offset_map();
-  section_offset_type output_offset = (delete_entry
-                                      ? -1
-                                      : input_offset - deleted_bytes);
+  section_offset_type output_offset =
+    (delete_entry
+     ? Arm_exidx_input_section::invalid_offset
+     : input_offset - deleted_bytes);
   (*this->section_offset_map_)[input_offset] = output_offset;
 }
 
@@ -5341,6 +5541,7 @@ Arm_output_section<big_endian>::append_text_sections_to_list(
 template<bool big_endian>
 void
 Arm_output_section<big_endian>::fix_exidx_coverage(
+    Layout* layout,
     const Text_section_list& sorted_text_sections,
     Symbol_table* symtab)
 {
@@ -5404,9 +5605,18 @@ Arm_output_section<big_endian>::fix_exidx_coverage(
       if (known_input_sections.find(sid) == known_input_sections.end())
        {
          // This is odd.  We have not seen this EXIDX input section before.
-         // We cannot do fix-up.
-         gold_error(_("EXIDX section %u of %s is not in EXIDX output section"),
-                    exidx_shndx, exidx_relobj->name().c_str());
+         // We cannot do fix-up.  If we saw a SECTIONS clause in a script,
+         // issue a warning instead.  We assume the user knows what he
+         // or she is doing.  Otherwise, this is an error.
+         if (layout->script_options()->saw_sections_clause())
+           gold_warning(_("unwinding may not work because EXIDX input section"
+                          " %u of %s is not in EXIDX output section"),
+                        exidx_shndx, exidx_relobj->name().c_str());
+         else
+           gold_error(_("unwinding may not work because EXIDX input section"
+                        " %u of %s is not in EXIDX output section"),
+                      exidx_shndx, exidx_relobj->name().c_str());
+
          exidx_fixup.add_exidx_cantunwind_as_needed();
          continue;
        }
@@ -6232,6 +6442,11 @@ Arm_relobj<big_endian>::do_gc_process_relocs(Symbol_table* symtab,
   // First, call base class method to process relocations in this object.
   Sized_relobj<32, big_endian>::do_gc_process_relocs(symtab, layout, rd);
 
+  // If --gc-sections is not specified, there is nothing more to do.
+  // This happens when --icf is used but --gc-sections is not.
+  if (!parameters->options().gc_sections())
+    return;
+  
   unsigned int shnum = this->shnum();
   const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
   const unsigned char* pshdrs = this->get_view(this->elf_file()->shoff(),
@@ -6304,7 +6519,7 @@ Arm_relobj<big_endian>::update_output_local_symbol_count()
       Symbol_value<32>& lv((*this->local_values())[i]);
 
       // This local symbol was already discarded by do_count_local_symbols.
-      if (!lv.needs_output_symtab_entry())
+      if (lv.is_output_symtab_index_set() && !lv.has_output_symtab_entry())
        continue;
 
       bool is_ordinary;
@@ -6438,6 +6653,162 @@ Stub_addend_reader<elfcpp::SHT_REL, big_endian>::operator()(
     }
 }
 
+// Arm_output_data_got methods.
+
+// Add a GOT pair for R_ARM_TLS_GD32.  The creates a pair of GOT entries.
+// The first one is initialized to be 1, which is the module index for
+// the main executable and the second one 0.  A reloc of the type
+// R_ARM_TLS_DTPOFF32 will be created for the second GOT entry and will
+// be applied by gold.  GSYM is a global symbol.
+//
+template<bool big_endian>
+void
+Arm_output_data_got<big_endian>::add_tls_gd32_with_static_reloc(
+    unsigned int got_type,
+    Symbol* gsym)
+{
+  if (gsym->has_got_offset(got_type))
+    return;
+
+  // We are doing a static link.  Just mark it as belong to module 1,
+  // the executable.
+  unsigned int got_offset = this->add_constant(1);
+  gsym->set_got_offset(got_type, got_offset); 
+  got_offset = this->add_constant(0);
+  this->static_relocs_.push_back(Static_reloc(got_offset,
+                                             elfcpp::R_ARM_TLS_DTPOFF32,
+                                             gsym));
+}
+
+// Same as the above but for a local symbol.
+
+template<bool big_endian>
+void
+Arm_output_data_got<big_endian>::add_tls_gd32_with_static_reloc(
+  unsigned int got_type,
+  Sized_relobj<32, big_endian>* object,
+  unsigned int index)
+{
+  if (object->local_has_got_offset(index, got_type))
+    return;
+
+  // We are doing a static link.  Just mark it as belong to module 1,
+  // the executable.
+  unsigned int got_offset = this->add_constant(1);
+  object->set_local_got_offset(index, got_type, got_offset);
+  got_offset = this->add_constant(0);
+  this->static_relocs_.push_back(Static_reloc(got_offset, 
+                                             elfcpp::R_ARM_TLS_DTPOFF32, 
+                                             object, index));
+}
+
+template<bool big_endian>
+void
+Arm_output_data_got<big_endian>::do_write(Output_file* of)
+{
+  // Call parent to write out GOT.
+  Output_data_got<32, big_endian>::do_write(of);
+
+  // We are done if there is no fix up.
+  if (this->static_relocs_.empty())
+    return;
+
+  gold_assert(parameters->doing_static_link());
+
+  const off_t offset = this->offset();
+  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);
+
+  Output_segment* tls_segment = this->layout_->tls_segment();
+  gold_assert(tls_segment != NULL);
+  
+  // The thread pointer $tp points to the TCB, which is followed by the
+  // TLS.  So we need to adjust $tp relative addressing by this amount.
+  Arm_address aligned_tcb_size =
+    align_address(ARM_TCB_SIZE, tls_segment->maximum_alignment());
+
+  for (size_t i = 0; i < this->static_relocs_.size(); ++i)
+    {
+      Static_reloc& reloc(this->static_relocs_[i]);
+      
+      Arm_address value;
+      if (!reloc.symbol_is_global())
+       {
+         Sized_relobj<32, big_endian>* object = reloc.relobj();
+         const Symbol_value<32>* psymval =
+           reloc.relobj()->local_symbol(reloc.index());
+
+         // We are doing static linking.  Issue an error and skip this
+         // relocation if the symbol is undefined or in a discarded_section.
+         bool is_ordinary;
+         unsigned int shndx = psymval->input_shndx(&is_ordinary);
+         if ((shndx == elfcpp::SHN_UNDEF)
+             || (is_ordinary
+                 && shndx != elfcpp::SHN_UNDEF
+                 && !object->is_section_included(shndx)
+                 && !this->symbol_table_->is_section_folded(object, shndx)))
+           {
+             gold_error(_("undefined or discarded local symbol %u from "
+                          " object %s in GOT"),
+                        reloc.index(), reloc.relobj()->name().c_str());
+             continue;
+           }
+         
+         value = psymval->value(object, 0);
+       }
+      else
+       {
+         const Symbol* gsym = reloc.symbol();
+         gold_assert(gsym != NULL);
+         if (gsym->is_forwarder())
+           gsym = this->symbol_table_->resolve_forwards(gsym);
+
+         // We are doing static linking.  Issue an error and skip this
+         // relocation if the symbol is undefined or in a discarded_section
+         // unless it is a weakly_undefined symbol.
+         if ((gsym->is_defined_in_discarded_section()
+              || gsym->is_undefined())
+             && !gsym->is_weak_undefined())
+           {
+             gold_error(_("undefined or discarded symbol %s in GOT"),
+                        gsym->name());
+             continue;
+           }
+
+         if (!gsym->is_weak_undefined())
+           {
+             const Sized_symbol<32>* sym =
+               static_cast<const Sized_symbol<32>*>(gsym);
+             value = sym->value();
+           }
+         else
+             value = 0;
+       }
+
+      unsigned got_offset = reloc.got_offset();
+      gold_assert(got_offset < oview_size);
+
+      typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+      Valtype* wv = reinterpret_cast<Valtype*>(oview + got_offset);
+      Valtype x;
+      switch (reloc.r_type())
+       {
+       case elfcpp::R_ARM_TLS_DTPOFF32:
+         x = value;
+         break;
+       case elfcpp::R_ARM_TLS_TPOFF32:
+         x = value + aligned_tcb_size;
+         break;
+       default:
+         gold_unreachable();
+       }
+      elfcpp::Swap<32, big_endian>::writeval(wv, x);
+    }
+
+  of->write_output_view(offset, oview_size, oview);
+}
+
 // A class to handle the PLT data.
 
 template<bool big_endian>
@@ -6673,6 +7044,90 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
   this->plt_->add_entry(gsym);
 }
 
+// Get the section to use for TLS_DESC relocations.
+
+template<bool big_endian>
+typename Target_arm<big_endian>::Reloc_section*
+Target_arm<big_endian>::rel_tls_desc_section(Layout* layout) const
+{
+  return this->plt_section()->rel_tls_desc(layout);
+}
+
+// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::define_tls_base_symbol(
+    Symbol_table* symtab,
+    Layout* layout)
+{
+  if (this->tls_base_symbol_defined_)
+    return;
+
+  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;
+}
+
+// Create a GOT entry for the TLS module index.
+
+template<bool big_endian>
+unsigned int
+Target_arm<big_endian>::got_mod_index_entry(
+    Symbol_table* symtab,
+    Layout* layout,
+    Sized_relobj<32, big_endian>* object)
+{
+  if (this->got_mod_index_offset_ == -1U)
+    {
+      gold_assert(symtab != NULL && layout != NULL && object != NULL);
+      Arm_output_data_got<big_endian>* got = this->got_section(symtab, layout);
+      unsigned int got_offset;
+      if (!parameters->doing_static_link())
+       {
+         got_offset = got->add_constant(0);
+         Reloc_section* rel_dyn = this->rel_dyn_section(layout);
+         rel_dyn->add_local(object, 0, elfcpp::R_ARM_TLS_DTPMOD32, got,
+                            got_offset);
+       }
+      else
+       {
+         // We are doing a static link.  Just mark it as belong to module 1,
+         // the executable.
+         got_offset = got->add_constant(1);
+       }
+
+      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
+// symbol.  IS_FINAL is true if the final address of this symbol is
+// known at link time.
+
+template<bool big_endian>
+tls::Tls_optimization
+Target_arm<big_endian>::optimize_tls_reloc(bool, int)
+{
+  // FIXME: Currently we do not do any TLS optimization.
+  return tls::TLSOPT_NONE;
+}
+
 // Report an unsupported relocation against a local symbol.
 
 template<bool big_endian>
@@ -6896,7 +7351,7 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
     case elfcpp::R_ARM_GOT_PREL:
       {
        // The symbol requires a GOT entry.
-       Output_data_got<32, big_endian>* got =
+       Arm_output_data_got<big_endian>* 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, GOT_TYPE_STANDARD))
@@ -6929,6 +7384,116 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
                 object->name().c_str(), r_type);
       break;
 
+
+      // These are initial TLS relocs, which are expected when
+      // linking.
+    case elfcpp::R_ARM_TLS_GD32:       // Global-dynamic
+    case elfcpp::R_ARM_TLS_LDM32:      // Local-dynamic
+    case elfcpp::R_ARM_TLS_LDO32:      // Alternate local-dynamic
+    case elfcpp::R_ARM_TLS_IE32:       // Initial-exec
+    case elfcpp::R_ARM_TLS_LE32:       // Local-exec
+      {
+       bool output_is_shared = parameters->options().shared();
+       const tls::Tls_optimization optimized_type
+            = Target_arm<big_endian>::optimize_tls_reloc(!output_is_shared,
+                                                        r_type);
+       switch (r_type)
+         {
+         case elfcpp::R_ARM_TLS_GD32:          // Global-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a pair of GOT entries for the module index and
+               // dtv-relative offset.
+                Arm_output_data_got<big_endian>* 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);
+                   break;
+                 }
+
+               if (!parameters->doing_static_link())
+                 got->add_local_pair_with_rel(object, r_sym, shndx,
+                                              GOT_TYPE_TLS_PAIR,
+                                              target->rel_dyn_section(layout),
+                                              elfcpp::R_ARM_TLS_DTPMOD32, 0);
+               else
+                 got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR,
+                                                     object, r_sym);
+             }
+           else
+             // FIXME: TLS optimization not supported yet.
+             gold_unreachable();
+           break;
+
+         case elfcpp::R_ARM_TLS_LDM32:         // Local-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the module index.
+               target->got_mod_index_entry(symtab, layout, object);
+             }
+           else
+             // FIXME: TLS optimization not supported yet.
+             gold_unreachable();
+           break;
+
+         case elfcpp::R_ARM_TLS_LDO32:         // Alternate local-dynamic
+           break;
+
+         case elfcpp::R_ARM_TLS_IE32:          // Initial-exec
+           layout->set_has_static_tls();
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the tp-relative offset.
+               Arm_output_data_got<big_endian>* got
+                 = target->got_section(symtab, layout);
+               unsigned int r_sym =
+                  elfcpp::elf_r_sym<32>(reloc.get_r_info());
+               if (!parameters->doing_static_link())
+                   got->add_local_with_rel(object, r_sym, GOT_TYPE_TLS_OFFSET,
+                                           target->rel_dyn_section(layout),
+                                           elfcpp::R_ARM_TLS_TPOFF32);
+               else if (!object->local_has_got_offset(r_sym,
+                                                      GOT_TYPE_TLS_OFFSET))
+                 {
+                   got->add_local(object, r_sym, GOT_TYPE_TLS_OFFSET);
+                   unsigned int got_offset =
+                     object->local_got_offset(r_sym, GOT_TYPE_TLS_OFFSET);
+                   got->add_static_reloc(got_offset,
+                                         elfcpp::R_ARM_TLS_TPOFF32, object,
+                                         r_sym);
+                 }
+             }
+           else
+             // FIXME: TLS optimization not supported yet.
+             gold_unreachable();
+           break;
+
+         case elfcpp::R_ARM_TLS_LE32:          // Local-exec
+           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());
+               Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+               rel_dyn->add_local(object, r_sym, elfcpp::R_ARM_TLS_TPOFF32,
+                                  output_section, data_shndx,
+                                  reloc.get_r_offset());
+             }
+           break;
+
+         default:
+           gold_unreachable();
+         }
+      }
+      break;
+
     default:
       unsupported_reloc_local(object, r_type);
       break;
@@ -7146,7 +7711,7 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
     case elfcpp::R_ARM_GOT_PREL:
       {
        // The symbol requires a GOT entry.
-       Output_data_got<32, big_endian>* got =
+       Arm_output_data_got<big_endian>* got =
          target->got_section(symtab, layout);
        if (gsym->final_value_is_known())
          got->add_global(gsym, GOT_TYPE_STANDARD);
@@ -7185,6 +7750,96 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
                 object->name().c_str(), r_type);
       break;
 
+      // These are initial tls relocs, which are expected when
+      // linking.
+    case elfcpp::R_ARM_TLS_GD32:       // Global-dynamic
+    case elfcpp::R_ARM_TLS_LDM32:      // Local-dynamic
+    case elfcpp::R_ARM_TLS_LDO32:      // Alternate local-dynamic
+    case elfcpp::R_ARM_TLS_IE32:       // Initial-exec
+    case elfcpp::R_ARM_TLS_LE32:       // Local-exec
+      {
+       const bool is_final = gsym->final_value_is_known();
+       const tls::Tls_optimization optimized_type
+            = Target_arm<big_endian>::optimize_tls_reloc(is_final, r_type);
+       switch (r_type)
+         {
+         case elfcpp::R_ARM_TLS_GD32:          // Global-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a pair of GOT entries for the module index and
+               // dtv-relative offset.
+                Arm_output_data_got<big_endian>* got
+                    = target->got_section(symtab, layout);
+               if (!parameters->doing_static_link())
+                 got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR,
+                                               target->rel_dyn_section(layout),
+                                               elfcpp::R_ARM_TLS_DTPMOD32,
+                                               elfcpp::R_ARM_TLS_DTPOFF32);
+               else
+                 got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR, gsym);
+             }
+           else
+             // FIXME: TLS optimization not supported yet.
+             gold_unreachable();
+           break;
+
+         case elfcpp::R_ARM_TLS_LDM32:         // Local-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the module index.
+               target->got_mod_index_entry(symtab, layout, object);
+             }
+           else
+             // FIXME: TLS optimization not supported yet.
+             gold_unreachable();
+           break;
+
+         case elfcpp::R_ARM_TLS_LDO32:         // Alternate local-dynamic
+           break;
+
+         case elfcpp::R_ARM_TLS_IE32:          // Initial-exec
+           layout->set_has_static_tls();
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the tp-relative offset.
+               Arm_output_data_got<big_endian>* got
+                 = target->got_section(symtab, layout);
+               if (!parameters->doing_static_link())
+                 got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET,
+                                          target->rel_dyn_section(layout),
+                                          elfcpp::R_ARM_TLS_TPOFF32);
+               else if (!gsym->has_got_offset(GOT_TYPE_TLS_OFFSET))
+                 {
+                   got->add_global(gsym, GOT_TYPE_TLS_OFFSET);
+                   unsigned int got_offset =
+                      gsym->got_offset(GOT_TYPE_TLS_OFFSET);
+                   got->add_static_reloc(got_offset,
+                                         elfcpp::R_ARM_TLS_TPOFF32, gsym);
+                 }
+             }
+           else
+             // FIXME: TLS optimization not supported yet.
+             gold_unreachable();
+           break;
+
+         case elfcpp::R_ARM_TLS_LE32:  // Local-exec
+           layout->set_has_static_tls();
+           if (parameters->options().shared())
+             {
+               // We need to create a dynamic relocation.
+                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                rel_dyn->add_global(gsym, elfcpp::R_ARM_TLS_TPOFF32,
+                                   output_section, object,
+                                    data_shndx, reloc.get_r_offset());
+             }
+           break;
+
+         default:
+           gold_unreachable();
+         }
+      }
+      break;
+
     default:
       unsupported_reloc_global(object, r_type, gsym);
       break;
@@ -7271,11 +7926,25 @@ Target_arm<big_endian>::do_finalize_sections(
     const Input_objects* input_objects,
     Symbol_table* symtab)
 {
+  // Create an empty uninitialized attribute section if we still don't have it
+  // at this moment.
+  if (this->attributes_section_data_ == NULL)
+    this->attributes_section_data_ = new Attributes_section_data(NULL, 0);
+
   // Merge processor-specific flags.
   for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
        p != input_objects->relobj_end();
        ++p)
     {
+      // If this input file is a binary file, it has no processor
+      // specific flags and attributes section.
+      Input_file::Format format = (*p)->input_file()->format();
+      if (format != Input_file::FORMAT_ELF)
+       {
+         gold_assert(format == Input_file::FORMAT_BINARY);
+         continue;
+       }
+
       Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(*p);
       this->merge_processor_specific_flags(
@@ -7436,7 +8105,7 @@ Target_arm<big_endian>::Relocate::relocate(
     const Symbol_value<32>* psymval,
     unsigned char* view,
     Arm_address address,
-    section_size_type /* view_size */ )
+    section_size_type view_size)
 {
   typedef Arm_relocate_functions<big_endian> Arm_relocate_functions;
 
@@ -7584,6 +8253,10 @@ Target_arm<big_endian>::Relocate::relocate(
   switch(reloc_property->relative_address_base())
     {
     case Arm_reloc_property::RAB_NONE:
+    // Relocations with relative address bases RAB_TLS and RAB_tp are
+    // handled by relocate_tls.  So we do not need to do anything here.
+    case Arm_reloc_property::RAB_TLS:
+    case Arm_reloc_property::RAB_tp:
       break;
     case Arm_reloc_property::RAB_B_S:
       relative_address_base = sym_origin;
@@ -7887,6 +8560,18 @@ Target_arm<big_endian>::Relocate::relocate(
                                              relative_address_base);
       break;
 
+      // These are initial tls relocs, which are expected when
+      // linking.
+    case elfcpp::R_ARM_TLS_GD32:       // Global-dynamic
+    case elfcpp::R_ARM_TLS_LDM32:      // Local-dynamic
+    case elfcpp::R_ARM_TLS_LDO32:      // Alternate local-dynamic
+    case elfcpp::R_ARM_TLS_IE32:       // Initial-exec
+    case elfcpp::R_ARM_TLS_LE32:       // Local-exec
+      reloc_status =
+       this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval,
+                          view, address, view_size);
+      break;
+
     default:
       gold_unreachable();
     }
@@ -7898,16 +8583,16 @@ Target_arm<big_endian>::Relocate::relocate(
       break;
     case Arm_relocate_functions::STATUS_OVERFLOW:
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
-                            _("relocation overflow in relocation %u"),
-                            r_type);
+                            _("relocation overflow in %s"),
+                            reloc_property->name().c_str());
       break;
     case Arm_relocate_functions::STATUS_BAD_RELOC:
       gold_error_at_location(
        relinfo,
        relnum,
        rel.get_r_offset(),
-       _("unexpected opcode while processing relocation %u"),
-       r_type);
+       _("unexpected opcode while processing relocation %s"),
+       reloc_property->name().c_str());
       break;
     default:
       gold_unreachable();
@@ -7916,6 +8601,145 @@ Target_arm<big_endian>::Relocate::relocate(
   return true;
 }
 
+// Perform a TLS relocation.
+
+template<bool big_endian>
+inline typename Arm_relocate_functions<big_endian>::Status
+Target_arm<big_endian>::Relocate::relocate_tls(
+    const Relocate_info<32, big_endian>* relinfo,
+    Target_arm<big_endian>* target,
+    size_t relnum,
+    const elfcpp::Rel<32, big_endian>& rel,
+    unsigned int r_type,
+    const Sized_symbol<32>* gsym,
+    const Symbol_value<32>* psymval,
+    unsigned char* view,
+    elfcpp::Elf_types<32>::Elf_Addr address,
+    section_size_type /*view_size*/ )
+{
+  typedef Arm_relocate_functions<big_endian> ArmRelocFuncs;
+  typedef Relocate_functions<32, big_endian> RelocFuncs;
+  Output_segment* tls_segment = relinfo->layout->tls_segment();
+
+  const Sized_relobj<32, big_endian>* object = relinfo->object;
+
+  elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(object, 0);
+
+  const bool is_final = (gsym == NULL
+                        ? !parameters->options().shared()
+                        : gsym->final_value_is_known());
+  const tls::Tls_optimization optimized_type
+      = Target_arm<big_endian>::optimize_tls_reloc(is_final, r_type);
+  switch (r_type)
+    {
+    case elfcpp::R_ARM_TLS_GD32:       // Global-dynamic
+        {
+          unsigned int got_type = 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_NONE)
+            {
+             Arm_address got_entry =
+               target->got_plt_section()->address() + got_offset;
+             
+              // Relocate the field with the PC relative offset of the pair of
+              // GOT entries.
+             RelocFuncs::pcrel32(view, got_entry, address);
+              return ArmRelocFuncs::STATUS_OKAY;
+            }
+        }
+      break;
+
+    case elfcpp::R_ARM_TLS_LDM32:      // Local-dynamic
+      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());
+         Arm_address got_entry =
+           target->got_plt_section()->address() + got_offset;
+
+          // Relocate the field with the PC relative offset of the pair of
+          // GOT entries.
+          RelocFuncs::pcrel32(view, got_entry, address);
+         return ArmRelocFuncs::STATUS_OKAY;
+        }
+      break;
+
+    case elfcpp::R_ARM_TLS_LDO32:      // Alternate local-dynamic
+      RelocFuncs::rel32(view, value);
+      return ArmRelocFuncs::STATUS_OKAY;
+
+    case elfcpp::R_ARM_TLS_IE32:       // Initial-exec
+      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 = GOT_TYPE_TLS_OFFSET;
+          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);
+            }
+
+          // All GOT offsets are relative to the end of the GOT.
+          got_offset -= target->got_size();
+
+         Arm_address got_entry =
+           target->got_plt_section()->address() + got_offset;
+
+          // Relocate the field with the PC relative offset of the GOT entry.
+         RelocFuncs::pcrel32(view, got_entry, address);
+         return ArmRelocFuncs::STATUS_OKAY;
+        }
+      break;
+
+    case elfcpp::R_ARM_TLS_LE32:       // Local-exec
+      // 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);
+
+         // $tp points to the TCB, which is followed by the TLS, so we
+         // need to add TCB size to the offset.
+         Arm_address aligned_tcb_size =
+           align_address(ARM_TCB_SIZE, tls_segment->maximum_alignment());
+          RelocFuncs::rel32(view, value + aligned_tcb_size);
+
+        }
+      return ArmRelocFuncs::STATUS_OKAY;
+    
+    default:
+      gold_unreachable();
+    }
+
+  gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                        _("unsupported reloc %u"),
+                        r_type);
+  return ArmRelocFuncs::STATUS_BAD_RELOC;
+}
+
 // Relocate section data.
 
 template<bool big_endian>
@@ -9071,30 +9895,6 @@ Target_arm<big_endian>::scan_reloc_for_stub(
   const Arm_relobj<big_endian>* arm_relobj =
     Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
 
-  if (r_type == elfcpp::R_ARM_V4BX)
-    {
-      const uint32_t reg = (addend & 0xf);
-      if (this->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING
-         && reg < 0xf)
-       {
-         // Try looking up an existing stub from a stub table.
-         Stub_table<big_endian>* stub_table =
-           arm_relobj->stub_table(relinfo->data_shndx);
-         gold_assert(stub_table != NULL);
-
-         if (stub_table->find_arm_v4bx_stub(reg) == NULL)
-           {
-             // create a new stub and add it to stub table.
-             Arm_v4bx_stub* stub =
-               this->stub_factory().make_arm_v4bx_stub(reg);
-             gold_assert(stub != NULL);
-             stub_table->add_arm_v4bx_stub(stub);
-           }
-       }
-
-      return;
-    }
-
   bool target_is_thumb;
   Symbol_value<32> symval;
   if (gsym != NULL)
@@ -9285,15 +10085,36 @@ Target_arm<big_endian>::scan_reloc_section_for_stubs(
            continue;
        }
 
+      // Create a v4bx stub if --fix-v4bx-interworking is used.
       if (r_type == elfcpp::R_ARM_V4BX)
        {
-         // Get the BX instruction.
-         typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
-         const Valtype* wv = reinterpret_cast<const Valtype*>(view + offset);
-         elfcpp::Elf_types<32>::Elf_Swxword insn =
-             elfcpp::Swap<32, big_endian>::readval(wv);
-         this->scan_reloc_for_stub(relinfo, r_type, NULL, 0, NULL,
-                                   insn, NULL);
+         if (this->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING)
+           {
+             // Get the BX instruction.
+             typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+             const Valtype* wv =
+               reinterpret_cast<const Valtype*>(view + offset);
+             elfcpp::Elf_types<32>::Elf_Swxword insn =
+               elfcpp::Swap<32, big_endian>::readval(wv);
+             const uint32_t reg = (insn & 0xf);
+
+             if (reg < 0xf)
+               {
+                 // Try looking up an existing stub from a stub table.
+                 Stub_table<big_endian>* stub_table =
+                   arm_object->stub_table(relinfo->data_shndx);
+                 gold_assert(stub_table != NULL);
+
+                 if (stub_table->find_arm_v4bx_stub(reg) == NULL)
+                   {
+                     // create a new stub and add it to stub table.
+                     Arm_v4bx_stub* stub =
+                       this->stub_factory().make_arm_v4bx_stub(reg);
+                     gold_assert(stub != NULL);
+                     stub_table->add_arm_v4bx_stub(stub);
+                   }
+               }
+           }
          continue;
        }
 
@@ -9497,13 +10318,18 @@ Target_arm<big_endian>::do_relax(
          // Default value.
          // Thumb branch range is +-4MB has to be used as the default
          // maximum size (a given section can contain both ARM and Thumb
-         // code, so the worst case has to be taken into account).
+         // code, so the worst case has to be taken into account).  If we are
+         // fixing cortex-a8 errata, the branch range has to be even smaller,
+         // since wide conditional branch has a range of +-1MB only.
          //
          // This value is 24K less than that, which allows for 2025
          // 12-byte stubs.  If we exceed that, then we will fail to link.
          // The user will have to relink with an explicit group size
          // option.
-         stub_group_size = 4170000;
+         if (this->fix_cortex_a8_)
+           stub_group_size = 1024276;
+         else
+           stub_group_size = 4170000;
        }
 
       group_sections(layout, stub_group_size, stubs_always_after_branch);
@@ -9990,7 +10816,7 @@ Target_arm<big_endian>::fix_exidx_coverage(
       arm_output_section->append_text_sections_to_list(&sorted_text_sections);
     } 
 
-  exidx_section->fix_exidx_coverage(sorted_text_sections, symtab);
+  exidx_section->fix_exidx_coverage(layout, sorted_text_sections, symtab);
 }
 
 Target_selector_arm<false> target_selector_arm;