OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

[/] [open8_urisc/] [trunk/] [gnu/] [binutils/] [gold/] [arm.cc] - Diff between revs 163 and 166

Only display areas with differences | Details | Blame | View Log

Rev 163 Rev 166
// arm.cc -- arm target support for gold.
// arm.cc -- arm target support for gold.
 
 
// Copyright 2009, 2010, 2011 Free Software Foundation, Inc.
// Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
// Written by Doug Kwan <dougkwan@google.com> based on the i386 code
// Written by Doug Kwan <dougkwan@google.com> based on the i386 code
// by Ian Lance Taylor <iant@google.com>.
// by Ian Lance Taylor <iant@google.com>.
// This file also contains borrowed and adapted code from
// This file also contains borrowed and adapted code from
// bfd/elf32-arm.c.
// bfd/elf32-arm.c.
 
 
// This file is part of gold.
// This file is part of gold.
 
 
// This program is free software; you can redistribute it and/or modify
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// (at your option) any later version.
 
 
// This program is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// GNU General Public License for more details.
 
 
// You should have received a copy of the GNU General Public License
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.
// MA 02110-1301, USA.
 
 
#include "gold.h"
#include "gold.h"
 
 
#include <cstring>
#include <cstring>
#include <limits>
#include <limits>
#include <cstdio>
#include <cstdio>
#include <string>
#include <string>
#include <algorithm>
#include <algorithm>
#include <map>
#include <map>
#include <utility>
#include <utility>
#include <set>
#include <set>
 
 
#include "elfcpp.h"
#include "elfcpp.h"
#include "parameters.h"
#include "parameters.h"
#include "reloc.h"
#include "reloc.h"
#include "arm.h"
#include "arm.h"
#include "object.h"
#include "object.h"
#include "symtab.h"
#include "symtab.h"
#include "layout.h"
#include "layout.h"
#include "output.h"
#include "output.h"
#include "copy-relocs.h"
#include "copy-relocs.h"
#include "target.h"
#include "target.h"
#include "target-reloc.h"
#include "target-reloc.h"
#include "target-select.h"
#include "target-select.h"
#include "tls.h"
#include "tls.h"
#include "defstd.h"
#include "defstd.h"
#include "gc.h"
#include "gc.h"
#include "attributes.h"
#include "attributes.h"
#include "arm-reloc-property.h"
#include "arm-reloc-property.h"
 
 
namespace
namespace
{
{
 
 
using namespace gold;
using namespace gold;
 
 
template<bool big_endian>
template<bool big_endian>
class Output_data_plt_arm;
class Output_data_plt_arm;
 
 
template<bool big_endian>
template<bool big_endian>
class Stub_table;
class Stub_table;
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_input_section;
class Arm_input_section;
 
 
class Arm_exidx_cantunwind;
class Arm_exidx_cantunwind;
 
 
class Arm_exidx_merged_section;
class Arm_exidx_merged_section;
 
 
class Arm_exidx_fixup;
class Arm_exidx_fixup;
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_output_section;
class Arm_output_section;
 
 
class Arm_exidx_input_section;
class Arm_exidx_input_section;
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_relobj;
class Arm_relobj;
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_relocate_functions;
class Arm_relocate_functions;
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_output_data_got;
class Arm_output_data_got;
 
 
template<bool big_endian>
template<bool big_endian>
class Target_arm;
class Target_arm;
 
 
// For convenience.
// For convenience.
typedef elfcpp::Elf_types<32>::Elf_Addr Arm_address;
typedef elfcpp::Elf_types<32>::Elf_Addr Arm_address;
 
 
// Maximum branch offsets for ARM, THUMB and THUMB2.
// Maximum branch offsets for ARM, THUMB and THUMB2.
const int32_t ARM_MAX_FWD_BRANCH_OFFSET = ((((1 << 23) - 1) << 2) + 8);
const int32_t ARM_MAX_FWD_BRANCH_OFFSET = ((((1 << 23) - 1) << 2) + 8);
const int32_t ARM_MAX_BWD_BRANCH_OFFSET = ((-((1 << 23) << 2)) + 8);
const int32_t ARM_MAX_BWD_BRANCH_OFFSET = ((-((1 << 23) << 2)) + 8);
const int32_t THM_MAX_FWD_BRANCH_OFFSET = ((1 << 22) -2 + 4);
const int32_t THM_MAX_FWD_BRANCH_OFFSET = ((1 << 22) -2 + 4);
const int32_t THM_MAX_BWD_BRANCH_OFFSET = (-(1 << 22) + 4);
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_FWD_BRANCH_OFFSET = (((1 << 24) - 2) + 4);
const int32_t THM2_MAX_BWD_BRANCH_OFFSET = (-(1 << 24) + 4);
const int32_t THM2_MAX_BWD_BRANCH_OFFSET = (-(1 << 24) + 4);
 
 
// Thread Control Block size.
// Thread Control Block size.
const size_t ARM_TCB_SIZE = 8;
const size_t ARM_TCB_SIZE = 8;
 
 
// The arm target class.
// The arm target class.
//
//
// This is a very simple port of gold for ARM-EABI.  It is intended for
// This is a very simple port of gold for ARM-EABI.  It is intended for
// supporting Android only for the time being.
// supporting Android only for the time being.
// 
// 
// TODOs:
// TODOs:
// - Implement all static relocation types documented in arm-reloc.def.
// - Implement all static relocation types documented in arm-reloc.def.
// - Make PLTs more flexible for different architecture features like
// - Make PLTs more flexible for different architecture features like
//   Thumb-2 and BE8.
//   Thumb-2 and BE8.
// There are probably a lot more.
// There are probably a lot more.
 
 
// Ideally we would like to avoid using global variables but this is used
// Ideally we would like to avoid using global variables but this is used
// very in many places and sometimes in loops.  If we use a function
// very in many places and sometimes in loops.  If we use a function
// returning a static instance of Arm_reloc_property_table, it will be very
// returning a static instance of Arm_reloc_property_table, it will be very
// slow in an threaded environment since the static instance needs to be
// slow in an threaded environment since the static instance needs to be
// locked.  The pointer is below initialized in the
// locked.  The pointer is below initialized in the
// Target::do_select_as_default_target() hook so that we do not spend time
// Target::do_select_as_default_target() hook so that we do not spend time
// building the table if we are not linking ARM objects.
// building the table if we are not linking ARM objects.
//
//
// An alternative is to to process the information in arm-reloc.def in
// An alternative is to to process the information in arm-reloc.def in
// compilation time and generate a representation of it in PODs only.  That
// compilation time and generate a representation of it in PODs only.  That
// way we can avoid initialization when the linker starts.
// way we can avoid initialization when the linker starts.
 
 
Arm_reloc_property_table* arm_reloc_property_table = NULL;
Arm_reloc_property_table* arm_reloc_property_table = NULL;
 
 
// Instruction template class.  This class is similar to the insn_sequence
// Instruction template class.  This class is similar to the insn_sequence
// struct in bfd/elf32-arm.c.
// struct in bfd/elf32-arm.c.
 
 
class Insn_template
class Insn_template
{
{
 public:
 public:
  // Types of instruction templates.
  // Types of instruction templates.
  enum Type
  enum Type
    {
    {
      THUMB16_TYPE = 1,
      THUMB16_TYPE = 1,
      // THUMB16_SPECIAL_TYPE is used by sub-classes of Stub for instruction 
      // THUMB16_SPECIAL_TYPE is used by sub-classes of Stub for instruction 
      // templates with class-specific semantics.  Currently this is used
      // templates with class-specific semantics.  Currently this is used
      // only by the Cortex_a8_stub class for handling condition codes in
      // only by the Cortex_a8_stub class for handling condition codes in
      // conditional branches.
      // conditional branches.
      THUMB16_SPECIAL_TYPE,
      THUMB16_SPECIAL_TYPE,
      THUMB32_TYPE,
      THUMB32_TYPE,
      ARM_TYPE,
      ARM_TYPE,
      DATA_TYPE
      DATA_TYPE
    };
    };
 
 
  // Factory methods to create instruction templates in different formats.
  // Factory methods to create instruction templates in different formats.
 
 
  static const Insn_template
  static const Insn_template
  thumb16_insn(uint32_t data)
  thumb16_insn(uint32_t data)
  { return Insn_template(data, THUMB16_TYPE, elfcpp::R_ARM_NONE, 0); }
  { return Insn_template(data, THUMB16_TYPE, elfcpp::R_ARM_NONE, 0); }
 
 
  // A Thumb conditional branch, in which the proper condition is inserted
  // A Thumb conditional branch, in which the proper condition is inserted
  // when we build the stub.
  // when we build the stub.
  static const Insn_template
  static const Insn_template
  thumb16_bcond_insn(uint32_t data)
  thumb16_bcond_insn(uint32_t data)
  { return Insn_template(data, THUMB16_SPECIAL_TYPE, elfcpp::R_ARM_NONE, 1); }
  { return Insn_template(data, THUMB16_SPECIAL_TYPE, elfcpp::R_ARM_NONE, 1); }
 
 
  static const Insn_template
  static const Insn_template
  thumb32_insn(uint32_t data)
  thumb32_insn(uint32_t data)
  { return Insn_template(data, THUMB32_TYPE, elfcpp::R_ARM_NONE, 0); }
  { return Insn_template(data, THUMB32_TYPE, elfcpp::R_ARM_NONE, 0); }
 
 
  static const Insn_template
  static const Insn_template
  thumb32_b_insn(uint32_t data, int reloc_addend)
  thumb32_b_insn(uint32_t data, int reloc_addend)
  {
  {
    return Insn_template(data, THUMB32_TYPE, elfcpp::R_ARM_THM_JUMP24,
    return Insn_template(data, THUMB32_TYPE, elfcpp::R_ARM_THM_JUMP24,
                         reloc_addend);
                         reloc_addend);
  }
  }
 
 
  static const Insn_template
  static const Insn_template
  arm_insn(uint32_t data)
  arm_insn(uint32_t data)
  { return Insn_template(data, ARM_TYPE, elfcpp::R_ARM_NONE, 0); }
  { return Insn_template(data, ARM_TYPE, elfcpp::R_ARM_NONE, 0); }
 
 
  static const Insn_template
  static const Insn_template
  arm_rel_insn(unsigned data, int reloc_addend)
  arm_rel_insn(unsigned data, int reloc_addend)
  { return Insn_template(data, ARM_TYPE, elfcpp::R_ARM_JUMP24, reloc_addend); }
  { return Insn_template(data, ARM_TYPE, elfcpp::R_ARM_JUMP24, reloc_addend); }
 
 
  static const Insn_template
  static const Insn_template
  data_word(unsigned data, unsigned int r_type, int reloc_addend)
  data_word(unsigned data, unsigned int r_type, int reloc_addend)
  { return Insn_template(data, DATA_TYPE, r_type, reloc_addend); }
  { return Insn_template(data, DATA_TYPE, r_type, reloc_addend); }
 
 
  // Accessors.  This class is used for read-only objects so no modifiers
  // Accessors.  This class is used for read-only objects so no modifiers
  // are provided.
  // are provided.
 
 
  uint32_t
  uint32_t
  data() const
  data() const
  { return this->data_; }
  { return this->data_; }
 
 
  // Return the instruction sequence type of this.
  // Return the instruction sequence type of this.
  Type
  Type
  type() const
  type() const
  { return this->type_; }
  { return this->type_; }
 
 
  // Return the ARM relocation type of this.
  // Return the ARM relocation type of this.
  unsigned int
  unsigned int
  r_type() const
  r_type() const
  { return this->r_type_; }
  { return this->r_type_; }
 
 
  int32_t
  int32_t
  reloc_addend() const
  reloc_addend() const
  { return this->reloc_addend_; }
  { return this->reloc_addend_; }
 
 
  // Return size of instruction template in bytes.
  // Return size of instruction template in bytes.
  size_t
  size_t
  size() const;
  size() const;
 
 
  // Return byte-alignment of instruction template.
  // Return byte-alignment of instruction template.
  unsigned
  unsigned
  alignment() const;
  alignment() const;
 
 
 private:
 private:
  // We make the constructor private to ensure that only the factory
  // We make the constructor private to ensure that only the factory
  // methods are used.
  // methods are used.
  inline
  inline
  Insn_template(unsigned data, Type type, unsigned int r_type, int reloc_addend)
  Insn_template(unsigned data, Type type, unsigned int r_type, int reloc_addend)
    : data_(data), type_(type), r_type_(r_type), reloc_addend_(reloc_addend)
    : data_(data), type_(type), r_type_(r_type), reloc_addend_(reloc_addend)
  { }
  { }
 
 
  // Instruction specific data.  This is used to store information like
  // Instruction specific data.  This is used to store information like
  // some of the instruction bits.
  // some of the instruction bits.
  uint32_t data_;
  uint32_t data_;
  // Instruction template type.
  // Instruction template type.
  Type type_;
  Type type_;
  // Relocation type if there is a relocation or R_ARM_NONE otherwise.
  // Relocation type if there is a relocation or R_ARM_NONE otherwise.
  unsigned int r_type_;
  unsigned int r_type_;
  // Relocation addend.
  // Relocation addend.
  int32_t reloc_addend_;
  int32_t reloc_addend_;
};
};
 
 
// Macro for generating code to stub types. One entry per long/short
// Macro for generating code to stub types. One entry per long/short
// branch stub
// branch stub
 
 
#define DEF_STUBS \
#define DEF_STUBS \
  DEF_STUB(long_branch_any_any) \
  DEF_STUB(long_branch_any_any) \
  DEF_STUB(long_branch_v4t_arm_thumb) \
  DEF_STUB(long_branch_v4t_arm_thumb) \
  DEF_STUB(long_branch_thumb_only) \
  DEF_STUB(long_branch_thumb_only) \
  DEF_STUB(long_branch_v4t_thumb_thumb) \
  DEF_STUB(long_branch_v4t_thumb_thumb) \
  DEF_STUB(long_branch_v4t_thumb_arm) \
  DEF_STUB(long_branch_v4t_thumb_arm) \
  DEF_STUB(short_branch_v4t_thumb_arm) \
  DEF_STUB(short_branch_v4t_thumb_arm) \
  DEF_STUB(long_branch_any_arm_pic) \
  DEF_STUB(long_branch_any_arm_pic) \
  DEF_STUB(long_branch_any_thumb_pic) \
  DEF_STUB(long_branch_any_thumb_pic) \
  DEF_STUB(long_branch_v4t_thumb_thumb_pic) \
  DEF_STUB(long_branch_v4t_thumb_thumb_pic) \
  DEF_STUB(long_branch_v4t_arm_thumb_pic) \
  DEF_STUB(long_branch_v4t_arm_thumb_pic) \
  DEF_STUB(long_branch_v4t_thumb_arm_pic) \
  DEF_STUB(long_branch_v4t_thumb_arm_pic) \
  DEF_STUB(long_branch_thumb_only_pic) \
  DEF_STUB(long_branch_thumb_only_pic) \
  DEF_STUB(a8_veneer_b_cond) \
  DEF_STUB(a8_veneer_b_cond) \
  DEF_STUB(a8_veneer_b) \
  DEF_STUB(a8_veneer_b) \
  DEF_STUB(a8_veneer_bl) \
  DEF_STUB(a8_veneer_bl) \
  DEF_STUB(a8_veneer_blx) \
  DEF_STUB(a8_veneer_blx) \
  DEF_STUB(v4_veneer_bx)
  DEF_STUB(v4_veneer_bx)
 
 
// Stub types.
// Stub types.
 
 
#define DEF_STUB(x) arm_stub_##x,
#define DEF_STUB(x) arm_stub_##x,
typedef enum
typedef enum
  {
  {
    arm_stub_none,
    arm_stub_none,
    DEF_STUBS
    DEF_STUBS
 
 
    // First reloc stub type.
    // First reloc stub type.
    arm_stub_reloc_first = arm_stub_long_branch_any_any,
    arm_stub_reloc_first = arm_stub_long_branch_any_any,
    // Last  reloc stub type.
    // Last  reloc stub type.
    arm_stub_reloc_last = arm_stub_long_branch_thumb_only_pic,
    arm_stub_reloc_last = arm_stub_long_branch_thumb_only_pic,
 
 
    // First Cortex-A8 stub type.
    // First Cortex-A8 stub type.
    arm_stub_cortex_a8_first = arm_stub_a8_veneer_b_cond,
    arm_stub_cortex_a8_first = arm_stub_a8_veneer_b_cond,
    // Last Cortex-A8 stub type.
    // Last Cortex-A8 stub type.
    arm_stub_cortex_a8_last = arm_stub_a8_veneer_blx,
    arm_stub_cortex_a8_last = arm_stub_a8_veneer_blx,
 
 
    // Last stub type.
    // Last stub type.
    arm_stub_type_last = arm_stub_v4_veneer_bx
    arm_stub_type_last = arm_stub_v4_veneer_bx
  } Stub_type;
  } Stub_type;
#undef DEF_STUB
#undef DEF_STUB
 
 
// Stub template class.  Templates are meant to be read-only objects.
// Stub template class.  Templates are meant to be read-only objects.
// A stub template for a stub type contains all read-only attributes
// A stub template for a stub type contains all read-only attributes
// common to all stubs of the same type.
// common to all stubs of the same type.
 
 
class Stub_template
class Stub_template
{
{
 public:
 public:
  Stub_template(Stub_type, const Insn_template*, size_t);
  Stub_template(Stub_type, const Insn_template*, size_t);
 
 
  ~Stub_template()
  ~Stub_template()
  { }
  { }
 
 
  // Return stub type.
  // Return stub type.
  Stub_type
  Stub_type
  type() const
  type() const
  { return this->type_; }
  { return this->type_; }
 
 
  // Return an array of instruction templates.
  // Return an array of instruction templates.
  const Insn_template*
  const Insn_template*
  insns() const
  insns() const
  { return this->insns_; }
  { return this->insns_; }
 
 
  // Return size of template in number of instructions.
  // Return size of template in number of instructions.
  size_t
  size_t
  insn_count() const
  insn_count() const
  { return this->insn_count_; }
  { return this->insn_count_; }
 
 
  // Return size of template in bytes.
  // Return size of template in bytes.
  size_t
  size_t
  size() const
  size() const
  { return this->size_; }
  { return this->size_; }
 
 
  // Return alignment of the stub template.
  // Return alignment of the stub template.
  unsigned
  unsigned
  alignment() const
  alignment() const
  { return this->alignment_; }
  { return this->alignment_; }
 
 
  // Return whether entry point is in thumb mode.
  // Return whether entry point is in thumb mode.
  bool
  bool
  entry_in_thumb_mode() const
  entry_in_thumb_mode() const
  { return this->entry_in_thumb_mode_; }
  { return this->entry_in_thumb_mode_; }
 
 
  // Return number of relocations in this template.
  // Return number of relocations in this template.
  size_t
  size_t
  reloc_count() const
  reloc_count() const
  { return this->relocs_.size(); }
  { return this->relocs_.size(); }
 
 
  // Return index of the I-th instruction with relocation.
  // Return index of the I-th instruction with relocation.
  size_t
  size_t
  reloc_insn_index(size_t i) const
  reloc_insn_index(size_t i) const
  {
  {
    gold_assert(i < this->relocs_.size());
    gold_assert(i < this->relocs_.size());
    return this->relocs_[i].first;
    return this->relocs_[i].first;
  }
  }
 
 
  // Return the offset of the I-th instruction with relocation from the
  // Return the offset of the I-th instruction with relocation from the
  // beginning of the stub.
  // beginning of the stub.
  section_size_type
  section_size_type
  reloc_offset(size_t i) const
  reloc_offset(size_t i) const
  {
  {
    gold_assert(i < this->relocs_.size());
    gold_assert(i < this->relocs_.size());
    return this->relocs_[i].second;
    return this->relocs_[i].second;
  }
  }
 
 
 private:
 private:
  // This contains information about an instruction template with a relocation
  // This contains information about an instruction template with a relocation
  // and its offset from start of stub.
  // and its offset from start of stub.
  typedef std::pair<size_t, section_size_type> Reloc;
  typedef std::pair<size_t, section_size_type> Reloc;
 
 
  // A Stub_template may not be copied.  We want to share templates as much
  // A Stub_template may not be copied.  We want to share templates as much
  // as possible.
  // as possible.
  Stub_template(const Stub_template&);
  Stub_template(const Stub_template&);
  Stub_template& operator=(const Stub_template&);
  Stub_template& operator=(const Stub_template&);
 
 
  // Stub type.
  // Stub type.
  Stub_type type_;
  Stub_type type_;
  // Points to an array of Insn_templates.
  // Points to an array of Insn_templates.
  const Insn_template* insns_;
  const Insn_template* insns_;
  // Number of Insn_templates in insns_[].
  // Number of Insn_templates in insns_[].
  size_t insn_count_;
  size_t insn_count_;
  // Size of templated instructions in bytes.
  // Size of templated instructions in bytes.
  size_t size_;
  size_t size_;
  // Alignment of templated instructions.
  // Alignment of templated instructions.
  unsigned alignment_;
  unsigned alignment_;
  // Flag to indicate if entry is in thumb mode.
  // Flag to indicate if entry is in thumb mode.
  bool entry_in_thumb_mode_;
  bool entry_in_thumb_mode_;
  // A table of reloc instruction indices and offsets.  We can find these by
  // A table of reloc instruction indices and offsets.  We can find these by
  // looking at the instruction templates but we pre-compute and then stash
  // looking at the instruction templates but we pre-compute and then stash
  // them here for speed. 
  // them here for speed. 
  std::vector<Reloc> relocs_;
  std::vector<Reloc> relocs_;
};
};
 
 
//
//
// A class for code stubs.  This is a base class for different type of
// A class for code stubs.  This is a base class for different type of
// stubs used in the ARM target.
// stubs used in the ARM target.
//
//
 
 
class Stub
class Stub
{
{
 private:
 private:
  static const section_offset_type invalid_offset =
  static const section_offset_type invalid_offset =
    static_cast<section_offset_type>(-1);
    static_cast<section_offset_type>(-1);
 
 
 public:
 public:
  Stub(const Stub_template* stub_template)
  Stub(const Stub_template* stub_template)
    : stub_template_(stub_template), offset_(invalid_offset)
    : stub_template_(stub_template), offset_(invalid_offset)
  { }
  { }
 
 
  virtual
  virtual
   ~Stub()
   ~Stub()
  { }
  { }
 
 
  // Return the stub template.
  // Return the stub template.
  const Stub_template*
  const Stub_template*
  stub_template() const
  stub_template() const
  { return this->stub_template_; }
  { return this->stub_template_; }
 
 
  // Return offset of code stub from beginning of its containing stub table.
  // Return offset of code stub from beginning of its containing stub table.
  section_offset_type
  section_offset_type
  offset() const
  offset() const
  {
  {
    gold_assert(this->offset_ != invalid_offset);
    gold_assert(this->offset_ != invalid_offset);
    return this->offset_;
    return this->offset_;
  }
  }
 
 
  // Set offset of code stub from beginning of its containing stub table.
  // Set offset of code stub from beginning of its containing stub table.
  void
  void
  set_offset(section_offset_type offset)
  set_offset(section_offset_type offset)
  { this->offset_ = offset; }
  { this->offset_ = offset; }
 
 
  // Return the relocation target address of the i-th relocation in the
  // Return the relocation target address of the i-th relocation in the
  // stub.  This must be defined in a child class.
  // stub.  This must be defined in a child class.
  Arm_address
  Arm_address
  reloc_target(size_t i)
  reloc_target(size_t i)
  { return this->do_reloc_target(i); }
  { return this->do_reloc_target(i); }
 
 
  // Write a stub at output VIEW.  BIG_ENDIAN select how a stub is written.
  // Write a stub at output VIEW.  BIG_ENDIAN select how a stub is written.
  void
  void
  write(unsigned char* view, section_size_type view_size, bool big_endian)
  write(unsigned char* view, section_size_type view_size, bool big_endian)
  { this->do_write(view, view_size, big_endian); }
  { this->do_write(view, view_size, big_endian); }
 
 
  // Return the instruction for THUMB16_SPECIAL_TYPE instruction template
  // Return the instruction for THUMB16_SPECIAL_TYPE instruction template
  // for the i-th instruction.
  // for the i-th instruction.
  uint16_t
  uint16_t
  thumb16_special(size_t i)
  thumb16_special(size_t i)
  { return this->do_thumb16_special(i); }
  { return this->do_thumb16_special(i); }
 
 
 protected:
 protected:
  // This must be defined in the child class.
  // This must be defined in the child class.
  virtual Arm_address
  virtual Arm_address
  do_reloc_target(size_t) = 0;
  do_reloc_target(size_t) = 0;
 
 
  // This may be overridden in the child class.
  // This may be overridden in the child class.
  virtual void
  virtual void
  do_write(unsigned char* view, section_size_type view_size, bool big_endian)
  do_write(unsigned char* view, section_size_type view_size, bool big_endian)
  {
  {
    if (big_endian)
    if (big_endian)
      this->do_fixed_endian_write<true>(view, view_size);
      this->do_fixed_endian_write<true>(view, view_size);
    else
    else
      this->do_fixed_endian_write<false>(view, view_size);
      this->do_fixed_endian_write<false>(view, view_size);
  }
  }
 
 
  // This must be overridden if a child class uses the THUMB16_SPECIAL_TYPE
  // This must be overridden if a child class uses the THUMB16_SPECIAL_TYPE
  // instruction template.
  // instruction template.
  virtual uint16_t
  virtual uint16_t
  do_thumb16_special(size_t)
  do_thumb16_special(size_t)
  { gold_unreachable(); }
  { gold_unreachable(); }
 
 
 private:
 private:
  // A template to implement do_write.
  // A template to implement do_write.
  template<bool big_endian>
  template<bool big_endian>
  void inline
  void inline
  do_fixed_endian_write(unsigned char*, section_size_type);
  do_fixed_endian_write(unsigned char*, section_size_type);
 
 
  // Its template.
  // Its template.
  const Stub_template* stub_template_;
  const Stub_template* stub_template_;
  // Offset within the section of containing this stub.
  // Offset within the section of containing this stub.
  section_offset_type offset_;
  section_offset_type offset_;
};
};
 
 
// Reloc stub class.  These are stubs we use to fix up relocation because
// Reloc stub class.  These are stubs we use to fix up relocation because
// of limited branch ranges.
// of limited branch ranges.
 
 
class Reloc_stub : public Stub
class Reloc_stub : public Stub
{
{
 public:
 public:
  static const unsigned int invalid_index = static_cast<unsigned int>(-1);
  static const unsigned int invalid_index = static_cast<unsigned int>(-1);
  // We assume we never jump to this address.
  // We assume we never jump to this address.
  static const Arm_address invalid_address = static_cast<Arm_address>(-1);
  static const Arm_address invalid_address = static_cast<Arm_address>(-1);
 
 
  // Return destination address.
  // Return destination address.
  Arm_address
  Arm_address
  destination_address() const
  destination_address() const
  {
  {
    gold_assert(this->destination_address_ != this->invalid_address);
    gold_assert(this->destination_address_ != this->invalid_address);
    return this->destination_address_;
    return this->destination_address_;
  }
  }
 
 
  // Set destination address.
  // Set destination address.
  void
  void
  set_destination_address(Arm_address address)
  set_destination_address(Arm_address address)
  {
  {
    gold_assert(address != this->invalid_address);
    gold_assert(address != this->invalid_address);
    this->destination_address_ = address;
    this->destination_address_ = address;
  }
  }
 
 
  // Reset destination address.
  // Reset destination address.
  void
  void
  reset_destination_address()
  reset_destination_address()
  { this->destination_address_ = this->invalid_address; }
  { this->destination_address_ = this->invalid_address; }
 
 
  // Determine stub type for a branch of a relocation of R_TYPE going
  // Determine stub type for a branch of a relocation of R_TYPE going
  // from BRANCH_ADDRESS to BRANCH_TARGET.  If TARGET_IS_THUMB is set,
  // from BRANCH_ADDRESS to BRANCH_TARGET.  If TARGET_IS_THUMB is set,
  // the branch target is a thumb instruction.  TARGET is used for look
  // the branch target is a thumb instruction.  TARGET is used for look
  // up ARM-specific linker settings.
  // up ARM-specific linker settings.
  static Stub_type
  static Stub_type
  stub_type_for_reloc(unsigned int r_type, Arm_address branch_address,
  stub_type_for_reloc(unsigned int r_type, Arm_address branch_address,
                      Arm_address branch_target, bool target_is_thumb);
                      Arm_address branch_target, bool target_is_thumb);
 
 
  // Reloc_stub key.  A key is logically a triplet of a stub type, a symbol
  // Reloc_stub key.  A key is logically a triplet of a stub type, a symbol
  // and an addend.  Since we treat global and local symbol differently, we
  // and an addend.  Since we treat global and local symbol differently, we
  // use a Symbol object for a global symbol and a object-index pair for
  // use a Symbol object for a global symbol and a object-index pair for
  // a local symbol.
  // a local symbol.
  class Key
  class Key
  {
  {
   public:
   public:
    // If SYMBOL is not null, this is a global symbol, we ignore RELOBJ and
    // If SYMBOL is not null, this is a global symbol, we ignore RELOBJ and
    // R_SYM.  Otherwise, this is a local symbol and RELOBJ must non-NULL
    // R_SYM.  Otherwise, this is a local symbol and RELOBJ must non-NULL
    // and R_SYM must not be invalid_index.
    // and R_SYM must not be invalid_index.
    Key(Stub_type stub_type, const Symbol* symbol, const Relobj* relobj,
    Key(Stub_type stub_type, const Symbol* symbol, const Relobj* relobj,
        unsigned int r_sym, int32_t addend)
        unsigned int r_sym, int32_t addend)
      : stub_type_(stub_type), addend_(addend)
      : stub_type_(stub_type), addend_(addend)
    {
    {
      if (symbol != NULL)
      if (symbol != NULL)
        {
        {
          this->r_sym_ = Reloc_stub::invalid_index;
          this->r_sym_ = Reloc_stub::invalid_index;
          this->u_.symbol = symbol;
          this->u_.symbol = symbol;
        }
        }
      else
      else
        {
        {
          gold_assert(relobj != NULL && r_sym != invalid_index);
          gold_assert(relobj != NULL && r_sym != invalid_index);
          this->r_sym_ = r_sym;
          this->r_sym_ = r_sym;
          this->u_.relobj = relobj;
          this->u_.relobj = relobj;
        }
        }
    }
    }
 
 
    ~Key()
    ~Key()
    { }
    { }
 
 
    // Accessors: Keys are meant to be read-only object so no modifiers are
    // Accessors: Keys are meant to be read-only object so no modifiers are
    // provided.
    // provided.
 
 
    // Return stub type.
    // Return stub type.
    Stub_type
    Stub_type
    stub_type() const
    stub_type() const
    { return this->stub_type_; }
    { return this->stub_type_; }
 
 
    // Return the local symbol index or invalid_index.
    // Return the local symbol index or invalid_index.
    unsigned int
    unsigned int
    r_sym() const
    r_sym() const
    { return this->r_sym_; }
    { return this->r_sym_; }
 
 
    // Return the symbol if there is one.
    // Return the symbol if there is one.
    const Symbol*
    const Symbol*
    symbol() const
    symbol() const
    { return this->r_sym_ == invalid_index ? this->u_.symbol : NULL; }
    { return this->r_sym_ == invalid_index ? this->u_.symbol : NULL; }
 
 
    // Return the relobj if there is one.
    // Return the relobj if there is one.
    const Relobj*
    const Relobj*
    relobj() const
    relobj() const
    { return this->r_sym_ != invalid_index ? this->u_.relobj : NULL; }
    { return this->r_sym_ != invalid_index ? this->u_.relobj : NULL; }
 
 
    // Whether this equals to another key k.
    // Whether this equals to another key k.
    bool
    bool
    eq(const Key& k) const
    eq(const Key& k) const
    {
    {
      return ((this->stub_type_ == k.stub_type_)
      return ((this->stub_type_ == k.stub_type_)
              && (this->r_sym_ == k.r_sym_)
              && (this->r_sym_ == k.r_sym_)
              && ((this->r_sym_ != Reloc_stub::invalid_index)
              && ((this->r_sym_ != Reloc_stub::invalid_index)
                  ? (this->u_.relobj == k.u_.relobj)
                  ? (this->u_.relobj == k.u_.relobj)
                  : (this->u_.symbol == k.u_.symbol))
                  : (this->u_.symbol == k.u_.symbol))
              && (this->addend_ == k.addend_));
              && (this->addend_ == k.addend_));
    }
    }
 
 
    // Return a hash value.
    // Return a hash value.
    size_t
    size_t
    hash_value() const
    hash_value() const
    {
    {
      return (this->stub_type_
      return (this->stub_type_
              ^ this->r_sym_
              ^ this->r_sym_
              ^ gold::string_hash<char>(
              ^ gold::string_hash<char>(
                    (this->r_sym_ != Reloc_stub::invalid_index)
                    (this->r_sym_ != Reloc_stub::invalid_index)
                    ? this->u_.relobj->name().c_str()
                    ? this->u_.relobj->name().c_str()
                    : this->u_.symbol->name())
                    : this->u_.symbol->name())
              ^ this->addend_);
              ^ this->addend_);
    }
    }
 
 
    // Functors for STL associative containers.
    // Functors for STL associative containers.
    struct hash
    struct hash
    {
    {
      size_t
      size_t
      operator()(const Key& k) const
      operator()(const Key& k) const
      { return k.hash_value(); }
      { return k.hash_value(); }
    };
    };
 
 
    struct equal_to
    struct equal_to
    {
    {
      bool
      bool
      operator()(const Key& k1, const Key& k2) const
      operator()(const Key& k1, const Key& k2) const
      { return k1.eq(k2); }
      { return k1.eq(k2); }
    };
    };
 
 
    // Name of key.  This is mainly for debugging.
    // Name of key.  This is mainly for debugging.
    std::string
    std::string
    name() const;
    name() const;
 
 
   private:
   private:
    // Stub type.
    // Stub type.
    Stub_type stub_type_;
    Stub_type stub_type_;
    // If this is a local symbol, this is the index in the defining object.
    // If this is a local symbol, this is the index in the defining object.
    // Otherwise, it is invalid_index for a global symbol.
    // Otherwise, it is invalid_index for a global symbol.
    unsigned int r_sym_;
    unsigned int r_sym_;
    // If r_sym_ is an invalid index, this points to a global symbol.
    // If r_sym_ is an invalid index, this points to a global symbol.
    // Otherwise, it points to a relobj.  We used the unsized and target
    // Otherwise, it points to a relobj.  We used the unsized and target
    // independent Symbol and Relobj classes instead of Sized_symbol<32> and  
    // independent Symbol and Relobj classes instead of Sized_symbol<32> and  
    // Arm_relobj, in order to avoid making the stub class a template
    // Arm_relobj, in order to avoid making the stub class a template
    // as most of the stub machinery is endianness-neutral.  However, it
    // as most of the stub machinery is endianness-neutral.  However, it
    // may require a bit of casting done by users of this class.
    // may require a bit of casting done by users of this class.
    union
    union
    {
    {
      const Symbol* symbol;
      const Symbol* symbol;
      const Relobj* relobj;
      const Relobj* relobj;
    } u_;
    } u_;
    // Addend associated with a reloc.
    // Addend associated with a reloc.
    int32_t addend_;
    int32_t addend_;
  };
  };
 
 
 protected:
 protected:
  // Reloc_stubs are created via a stub factory.  So these are protected.
  // Reloc_stubs are created via a stub factory.  So these are protected.
  Reloc_stub(const Stub_template* stub_template)
  Reloc_stub(const Stub_template* stub_template)
    : Stub(stub_template), destination_address_(invalid_address)
    : Stub(stub_template), destination_address_(invalid_address)
  { }
  { }
 
 
  ~Reloc_stub()
  ~Reloc_stub()
  { }
  { }
 
 
  friend class Stub_factory;
  friend class Stub_factory;
 
 
  // Return the relocation target address of the i-th relocation in the
  // Return the relocation target address of the i-th relocation in the
  // stub.
  // stub.
  Arm_address
  Arm_address
  do_reloc_target(size_t i)
  do_reloc_target(size_t i)
  {
  {
    // All reloc stub have only one relocation.
    // All reloc stub have only one relocation.
    gold_assert(i == 0);
    gold_assert(i == 0);
    return this->destination_address_;
    return this->destination_address_;
  }
  }
 
 
 private:
 private:
  // Address of destination.
  // Address of destination.
  Arm_address destination_address_;
  Arm_address destination_address_;
};
};
 
 
// Cortex-A8 stub class.  We need a Cortex-A8 stub to redirect any 32-bit
// Cortex-A8 stub class.  We need a Cortex-A8 stub to redirect any 32-bit
// THUMB branch that meets the following conditions:
// THUMB branch that meets the following conditions:
// 
// 
// 1. The branch straddles across a page boundary. i.e. lower 12-bit of
// 1. The branch straddles across a page boundary. i.e. lower 12-bit of
//    branch address is 0xffe.
//    branch address is 0xffe.
// 2. The branch target address is in the same page as the first word of the
// 2. The branch target address is in the same page as the first word of the
//    branch.
//    branch.
// 3. The branch follows a 32-bit instruction which is not a branch.
// 3. The branch follows a 32-bit instruction which is not a branch.
//
//
// To do the fix up, we need to store the address of the branch instruction
// To do the fix up, we need to store the address of the branch instruction
// and its target at least.  We also need to store the original branch
// and its target at least.  We also need to store the original branch
// instruction bits for the condition code in a conditional branch.  The
// instruction bits for the condition code in a conditional branch.  The
// condition code is used in a special instruction template.  We also want
// condition code is used in a special instruction template.  We also want
// to identify input sections needing Cortex-A8 workaround quickly.  We store
// to identify input sections needing Cortex-A8 workaround quickly.  We store
// extra information about object and section index of the code section
// extra information about object and section index of the code section
// containing a branch being fixed up.  The information is used to mark
// containing a branch being fixed up.  The information is used to mark
// the code section when we finalize the Cortex-A8 stubs.
// the code section when we finalize the Cortex-A8 stubs.
//
//
 
 
class Cortex_a8_stub : public Stub
class Cortex_a8_stub : public Stub
{
{
 public:
 public:
  ~Cortex_a8_stub()
  ~Cortex_a8_stub()
  { }
  { }
 
 
  // Return the object of the code section containing the branch being fixed
  // Return the object of the code section containing the branch being fixed
  // up.
  // up.
  Relobj*
  Relobj*
  relobj() const
  relobj() const
  { return this->relobj_; }
  { return this->relobj_; }
 
 
  // Return the section index of the code section containing the branch being
  // Return the section index of the code section containing the branch being
  // fixed up.
  // fixed up.
  unsigned int
  unsigned int
  shndx() const
  shndx() const
  { return this->shndx_; }
  { return this->shndx_; }
 
 
  // Return the source address of stub.  This is the address of the original
  // Return the source address of stub.  This is the address of the original
  // branch instruction.  LSB is 1 always set to indicate that it is a THUMB
  // branch instruction.  LSB is 1 always set to indicate that it is a THUMB
  // instruction.
  // instruction.
  Arm_address
  Arm_address
  source_address() const
  source_address() const
  { return this->source_address_; }
  { return this->source_address_; }
 
 
  // Return the destination address of the stub.  This is the branch taken
  // Return the destination address of the stub.  This is the branch taken
  // address of the original branch instruction.  LSB is 1 if it is a THUMB
  // address of the original branch instruction.  LSB is 1 if it is a THUMB
  // instruction address.
  // instruction address.
  Arm_address
  Arm_address
  destination_address() const
  destination_address() const
  { return this->destination_address_; }
  { return this->destination_address_; }
 
 
  // Return the instruction being fixed up.
  // Return the instruction being fixed up.
  uint32_t
  uint32_t
  original_insn() const
  original_insn() const
  { return this->original_insn_; }
  { return this->original_insn_; }
 
 
 protected:
 protected:
  // Cortex_a8_stubs are created via a stub factory.  So these are protected.
  // Cortex_a8_stubs are created via a stub factory.  So these are protected.
  Cortex_a8_stub(const Stub_template* stub_template, Relobj* relobj,
  Cortex_a8_stub(const Stub_template* stub_template, Relobj* relobj,
                 unsigned int shndx, Arm_address source_address,
                 unsigned int shndx, Arm_address source_address,
                 Arm_address destination_address, uint32_t original_insn)
                 Arm_address destination_address, uint32_t original_insn)
    : Stub(stub_template), relobj_(relobj), shndx_(shndx),
    : Stub(stub_template), relobj_(relobj), shndx_(shndx),
      source_address_(source_address | 1U),
      source_address_(source_address | 1U),
      destination_address_(destination_address),
      destination_address_(destination_address),
      original_insn_(original_insn)
      original_insn_(original_insn)
  { }
  { }
 
 
  friend class Stub_factory;
  friend class Stub_factory;
 
 
  // Return the relocation target address of the i-th relocation in the
  // Return the relocation target address of the i-th relocation in the
  // stub.
  // stub.
  Arm_address
  Arm_address
  do_reloc_target(size_t i)
  do_reloc_target(size_t i)
  {
  {
    if (this->stub_template()->type() == arm_stub_a8_veneer_b_cond)
    if (this->stub_template()->type() == arm_stub_a8_veneer_b_cond)
      {
      {
        // The conditional branch veneer has two relocations.
        // The conditional branch veneer has two relocations.
        gold_assert(i < 2);
        gold_assert(i < 2);
        return i == 0 ? this->source_address_ + 4 : this->destination_address_;
        return i == 0 ? this->source_address_ + 4 : this->destination_address_;
      }
      }
    else
    else
      {
      {
        // All other Cortex-A8 stubs have only one relocation.
        // All other Cortex-A8 stubs have only one relocation.
        gold_assert(i == 0);
        gold_assert(i == 0);
        return this->destination_address_;
        return this->destination_address_;
      }
      }
  }
  }
 
 
  // Return an instruction for the THUMB16_SPECIAL_TYPE instruction template.
  // Return an instruction for the THUMB16_SPECIAL_TYPE instruction template.
  uint16_t
  uint16_t
  do_thumb16_special(size_t);
  do_thumb16_special(size_t);
 
 
 private:
 private:
  // Object of the code section containing the branch being fixed up.
  // Object of the code section containing the branch being fixed up.
  Relobj* relobj_;
  Relobj* relobj_;
  // Section index of the code section containing the branch begin fixed up.
  // Section index of the code section containing the branch begin fixed up.
  unsigned int shndx_;
  unsigned int shndx_;
  // Source address of original branch.
  // Source address of original branch.
  Arm_address source_address_;
  Arm_address source_address_;
  // Destination address of the original branch.
  // Destination address of the original branch.
  Arm_address destination_address_;
  Arm_address destination_address_;
  // Original branch instruction.  This is needed for copying the condition
  // Original branch instruction.  This is needed for copying the condition
  // code from a condition branch to its stub.
  // code from a condition branch to its stub.
  uint32_t original_insn_;
  uint32_t original_insn_;
};
};
 
 
// ARMv4 BX Rx branch relocation stub class.
// ARMv4 BX Rx branch relocation stub class.
class Arm_v4bx_stub : public Stub
class Arm_v4bx_stub : public Stub
{
{
 public:
 public:
  ~Arm_v4bx_stub()
  ~Arm_v4bx_stub()
  { }
  { }
 
 
  // Return the associated register.
  // Return the associated register.
  uint32_t
  uint32_t
  reg() const
  reg() const
  { return this->reg_; }
  { return this->reg_; }
 
 
 protected:
 protected:
  // Arm V4BX stubs are created via a stub factory.  So these are protected.
  // Arm V4BX stubs are created via a stub factory.  So these are protected.
  Arm_v4bx_stub(const Stub_template* stub_template, const uint32_t reg)
  Arm_v4bx_stub(const Stub_template* stub_template, const uint32_t reg)
    : Stub(stub_template), reg_(reg)
    : Stub(stub_template), reg_(reg)
  { }
  { }
 
 
  friend class Stub_factory;
  friend class Stub_factory;
 
 
  // Return the relocation target address of the i-th relocation in the
  // Return the relocation target address of the i-th relocation in the
  // stub.
  // stub.
  Arm_address
  Arm_address
  do_reloc_target(size_t)
  do_reloc_target(size_t)
  { gold_unreachable(); }
  { gold_unreachable(); }
 
 
  // This may be overridden in the child class.
  // This may be overridden in the child class.
  virtual void
  virtual void
  do_write(unsigned char* view, section_size_type view_size, bool big_endian)
  do_write(unsigned char* view, section_size_type view_size, bool big_endian)
  {
  {
    if (big_endian)
    if (big_endian)
      this->do_fixed_endian_v4bx_write<true>(view, view_size);
      this->do_fixed_endian_v4bx_write<true>(view, view_size);
    else
    else
      this->do_fixed_endian_v4bx_write<false>(view, view_size);
      this->do_fixed_endian_v4bx_write<false>(view, view_size);
  }
  }
 
 
 private:
 private:
  // A template to implement do_write.
  // A template to implement do_write.
  template<bool big_endian>
  template<bool big_endian>
  void inline
  void inline
  do_fixed_endian_v4bx_write(unsigned char* view, section_size_type)
  do_fixed_endian_v4bx_write(unsigned char* view, section_size_type)
  {
  {
    const Insn_template* insns = this->stub_template()->insns();
    const Insn_template* insns = this->stub_template()->insns();
    elfcpp::Swap<32, big_endian>::writeval(view,
    elfcpp::Swap<32, big_endian>::writeval(view,
                                           (insns[0].data()
                                           (insns[0].data()
                                           + (this->reg_ << 16)));
                                           + (this->reg_ << 16)));
    view += insns[0].size();
    view += insns[0].size();
    elfcpp::Swap<32, big_endian>::writeval(view,
    elfcpp::Swap<32, big_endian>::writeval(view,
                                           (insns[1].data() + this->reg_));
                                           (insns[1].data() + this->reg_));
    view += insns[1].size();
    view += insns[1].size();
    elfcpp::Swap<32, big_endian>::writeval(view,
    elfcpp::Swap<32, big_endian>::writeval(view,
                                           (insns[2].data() + this->reg_));
                                           (insns[2].data() + this->reg_));
  }
  }
 
 
  // A register index (r0-r14), which is associated with the stub.
  // A register index (r0-r14), which is associated with the stub.
  uint32_t reg_;
  uint32_t reg_;
};
};
 
 
// Stub factory class.
// Stub factory class.
 
 
class Stub_factory
class Stub_factory
{
{
 public:
 public:
  // Return the unique instance of this class.
  // Return the unique instance of this class.
  static const Stub_factory&
  static const Stub_factory&
  get_instance()
  get_instance()
  {
  {
    static Stub_factory singleton;
    static Stub_factory singleton;
    return singleton;
    return singleton;
  }
  }
 
 
  // Make a relocation stub.
  // Make a relocation stub.
  Reloc_stub*
  Reloc_stub*
  make_reloc_stub(Stub_type stub_type) const
  make_reloc_stub(Stub_type stub_type) const
  {
  {
    gold_assert(stub_type >= arm_stub_reloc_first
    gold_assert(stub_type >= arm_stub_reloc_first
                && stub_type <= arm_stub_reloc_last);
                && stub_type <= arm_stub_reloc_last);
    return new Reloc_stub(this->stub_templates_[stub_type]);
    return new Reloc_stub(this->stub_templates_[stub_type]);
  }
  }
 
 
  // Make a Cortex-A8 stub.
  // Make a Cortex-A8 stub.
  Cortex_a8_stub*
  Cortex_a8_stub*
  make_cortex_a8_stub(Stub_type stub_type, Relobj* relobj, unsigned int shndx,
  make_cortex_a8_stub(Stub_type stub_type, Relobj* relobj, unsigned int shndx,
                      Arm_address source, Arm_address destination,
                      Arm_address source, Arm_address destination,
                      uint32_t original_insn) const
                      uint32_t original_insn) const
  {
  {
    gold_assert(stub_type >= arm_stub_cortex_a8_first
    gold_assert(stub_type >= arm_stub_cortex_a8_first
                && stub_type <= arm_stub_cortex_a8_last);
                && stub_type <= arm_stub_cortex_a8_last);
    return new Cortex_a8_stub(this->stub_templates_[stub_type], relobj, shndx,
    return new Cortex_a8_stub(this->stub_templates_[stub_type], relobj, shndx,
                              source, destination, original_insn);
                              source, destination, original_insn);
  }
  }
 
 
  // Make an ARM V4BX relocation stub.
  // Make an ARM V4BX relocation stub.
  // This method creates a stub from the arm_stub_v4_veneer_bx template only.
  // This method creates a stub from the arm_stub_v4_veneer_bx template only.
  Arm_v4bx_stub*
  Arm_v4bx_stub*
  make_arm_v4bx_stub(uint32_t reg) const
  make_arm_v4bx_stub(uint32_t reg) const
  {
  {
    gold_assert(reg < 0xf);
    gold_assert(reg < 0xf);
    return new Arm_v4bx_stub(this->stub_templates_[arm_stub_v4_veneer_bx],
    return new Arm_v4bx_stub(this->stub_templates_[arm_stub_v4_veneer_bx],
                             reg);
                             reg);
  }
  }
 
 
 private:
 private:
  // Constructor and destructor are protected since we only return a single
  // Constructor and destructor are protected since we only return a single
  // instance created in Stub_factory::get_instance().
  // instance created in Stub_factory::get_instance().
 
 
  Stub_factory();
  Stub_factory();
 
 
  // A Stub_factory may not be copied since it is a singleton.
  // A Stub_factory may not be copied since it is a singleton.
  Stub_factory(const Stub_factory&);
  Stub_factory(const Stub_factory&);
  Stub_factory& operator=(Stub_factory&);
  Stub_factory& operator=(Stub_factory&);
 
 
  // Stub templates.  These are initialized in the constructor.
  // Stub templates.  These are initialized in the constructor.
  const Stub_template* stub_templates_[arm_stub_type_last+1];
  const Stub_template* stub_templates_[arm_stub_type_last+1];
};
};
 
 
// A class to hold stubs for the ARM target.
// A class to hold stubs for the ARM target.
 
 
template<bool big_endian>
template<bool big_endian>
class Stub_table : public Output_data
class Stub_table : public Output_data
{
{
 public:
 public:
  Stub_table(Arm_input_section<big_endian>* owner)
  Stub_table(Arm_input_section<big_endian>* owner)
    : Output_data(), owner_(owner), reloc_stubs_(), reloc_stubs_size_(0),
    : Output_data(), owner_(owner), reloc_stubs_(), reloc_stubs_size_(0),
      reloc_stubs_addralign_(1), cortex_a8_stubs_(), arm_v4bx_stubs_(0xf),
      reloc_stubs_addralign_(1), cortex_a8_stubs_(), arm_v4bx_stubs_(0xf),
      prev_data_size_(0), prev_addralign_(1)
      prev_data_size_(0), prev_addralign_(1)
  { }
  { }
 
 
  ~Stub_table()
  ~Stub_table()
  { }
  { }
 
 
  // Owner of this stub table.
  // Owner of this stub table.
  Arm_input_section<big_endian>*
  Arm_input_section<big_endian>*
  owner() const
  owner() const
  { return this->owner_; }
  { return this->owner_; }
 
 
  // Whether this stub table is empty.
  // Whether this stub table is empty.
  bool
  bool
  empty() const
  empty() const
  {
  {
    return (this->reloc_stubs_.empty()
    return (this->reloc_stubs_.empty()
            && this->cortex_a8_stubs_.empty()
            && this->cortex_a8_stubs_.empty()
            && this->arm_v4bx_stubs_.empty());
            && this->arm_v4bx_stubs_.empty());
  }
  }
 
 
  // Return the current data size.
  // Return the current data size.
  off_t
  off_t
  current_data_size() const
  current_data_size() const
  { return this->current_data_size_for_child(); }
  { return this->current_data_size_for_child(); }
 
 
  // Add a STUB using KEY.  The caller is responsible for avoiding addition
  // Add a STUB using KEY.  The caller is responsible for avoiding addition
  // if a STUB with the same key has already been added.
  // if a STUB with the same key has already been added.
  void
  void
  add_reloc_stub(Reloc_stub* stub, const Reloc_stub::Key& key)
  add_reloc_stub(Reloc_stub* stub, const Reloc_stub::Key& key)
  {
  {
    const Stub_template* stub_template = stub->stub_template();
    const Stub_template* stub_template = stub->stub_template();
    gold_assert(stub_template->type() == key.stub_type());
    gold_assert(stub_template->type() == key.stub_type());
    this->reloc_stubs_[key] = stub;
    this->reloc_stubs_[key] = stub;
 
 
    // Assign stub offset early.  We can do this because we never remove
    // Assign stub offset early.  We can do this because we never remove
    // reloc stubs and they are in the beginning of the stub table.
    // reloc stubs and they are in the beginning of the stub table.
    uint64_t align = stub_template->alignment();
    uint64_t align = stub_template->alignment();
    this->reloc_stubs_size_ = align_address(this->reloc_stubs_size_, align);
    this->reloc_stubs_size_ = align_address(this->reloc_stubs_size_, align);
    stub->set_offset(this->reloc_stubs_size_);
    stub->set_offset(this->reloc_stubs_size_);
    this->reloc_stubs_size_ += stub_template->size();
    this->reloc_stubs_size_ += stub_template->size();
    this->reloc_stubs_addralign_ =
    this->reloc_stubs_addralign_ =
      std::max(this->reloc_stubs_addralign_, align);
      std::max(this->reloc_stubs_addralign_, align);
  }
  }
 
 
  // Add a Cortex-A8 STUB that fixes up a THUMB branch at ADDRESS.
  // Add a Cortex-A8 STUB that fixes up a THUMB branch at ADDRESS.
  // The caller is responsible for avoiding addition if a STUB with the same
  // The caller is responsible for avoiding addition if a STUB with the same
  // address has already been added.
  // address has already been added.
  void
  void
  add_cortex_a8_stub(Arm_address address, Cortex_a8_stub* stub)
  add_cortex_a8_stub(Arm_address address, Cortex_a8_stub* stub)
  {
  {
    std::pair<Arm_address, Cortex_a8_stub*> value(address, stub);
    std::pair<Arm_address, Cortex_a8_stub*> value(address, stub);
    this->cortex_a8_stubs_.insert(value);
    this->cortex_a8_stubs_.insert(value);
  }
  }
 
 
  // Add an ARM V4BX relocation stub. A register index will be retrieved
  // Add an ARM V4BX relocation stub. A register index will be retrieved
  // from the stub.
  // from the stub.
  void
  void
  add_arm_v4bx_stub(Arm_v4bx_stub* stub)
  add_arm_v4bx_stub(Arm_v4bx_stub* stub)
  {
  {
    gold_assert(stub != NULL && this->arm_v4bx_stubs_[stub->reg()] == NULL);
    gold_assert(stub != NULL && this->arm_v4bx_stubs_[stub->reg()] == NULL);
    this->arm_v4bx_stubs_[stub->reg()] = stub;
    this->arm_v4bx_stubs_[stub->reg()] = stub;
  }
  }
 
 
  // Remove all Cortex-A8 stubs.
  // Remove all Cortex-A8 stubs.
  void
  void
  remove_all_cortex_a8_stubs();
  remove_all_cortex_a8_stubs();
 
 
  // Look up a relocation stub using KEY.  Return NULL if there is none.
  // Look up a relocation stub using KEY.  Return NULL if there is none.
  Reloc_stub*
  Reloc_stub*
  find_reloc_stub(const Reloc_stub::Key& key) const
  find_reloc_stub(const Reloc_stub::Key& key) const
  {
  {
    typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.find(key);
    typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.find(key);
    return (p != this->reloc_stubs_.end()) ? p->second : NULL;
    return (p != this->reloc_stubs_.end()) ? p->second : NULL;
  }
  }
 
 
  // Look up an arm v4bx relocation stub using the register index.
  // Look up an arm v4bx relocation stub using the register index.
  // Return NULL if there is none.
  // Return NULL if there is none.
  Arm_v4bx_stub*
  Arm_v4bx_stub*
  find_arm_v4bx_stub(const uint32_t reg) const
  find_arm_v4bx_stub(const uint32_t reg) const
  {
  {
    gold_assert(reg < 0xf);
    gold_assert(reg < 0xf);
    return this->arm_v4bx_stubs_[reg];
    return this->arm_v4bx_stubs_[reg];
  }
  }
 
 
  // Relocate stubs in this stub table.
  // Relocate stubs in this stub table.
  void
  void
  relocate_stubs(const Relocate_info<32, big_endian>*,
  relocate_stubs(const Relocate_info<32, big_endian>*,
                 Target_arm<big_endian>*, Output_section*,
                 Target_arm<big_endian>*, Output_section*,
                 unsigned char*, Arm_address, section_size_type);
                 unsigned char*, Arm_address, section_size_type);
 
 
  // Update data size and alignment at the end of a relaxation pass.  Return
  // Update data size and alignment at the end of a relaxation pass.  Return
  // true if either data size or alignment is different from that of the
  // true if either data size or alignment is different from that of the
  // previous relaxation pass.
  // previous relaxation pass.
  bool
  bool
  update_data_size_and_addralign();
  update_data_size_and_addralign();
 
 
  // Finalize stubs.  Set the offsets of all stubs and mark input sections
  // Finalize stubs.  Set the offsets of all stubs and mark input sections
  // needing the Cortex-A8 workaround.
  // needing the Cortex-A8 workaround.
  void
  void
  finalize_stubs();
  finalize_stubs();
 
 
  // Apply Cortex-A8 workaround to an address range.
  // Apply Cortex-A8 workaround to an address range.
  void
  void
  apply_cortex_a8_workaround_to_address_range(Target_arm<big_endian>*,
  apply_cortex_a8_workaround_to_address_range(Target_arm<big_endian>*,
                                              unsigned char*, Arm_address,
                                              unsigned char*, Arm_address,
                                              section_size_type);
                                              section_size_type);
 
 
 protected:
 protected:
  // Write out section contents.
  // Write out section contents.
  void
  void
  do_write(Output_file*);
  do_write(Output_file*);
 
 
  // Return the required alignment.
  // Return the required alignment.
  uint64_t
  uint64_t
  do_addralign() const
  do_addralign() const
  { return this->prev_addralign_; }
  { return this->prev_addralign_; }
 
 
  // Reset address and file offset.
  // Reset address and file offset.
  void
  void
  do_reset_address_and_file_offset()
  do_reset_address_and_file_offset()
  { this->set_current_data_size_for_child(this->prev_data_size_); }
  { this->set_current_data_size_for_child(this->prev_data_size_); }
 
 
  // Set final data size.
  // Set final data size.
  void
  void
  set_final_data_size()
  set_final_data_size()
  { this->set_data_size(this->current_data_size()); }
  { this->set_data_size(this->current_data_size()); }
 
 
 private:
 private:
  // Relocate one stub.
  // Relocate one stub.
  void
  void
  relocate_stub(Stub*, const Relocate_info<32, big_endian>*,
  relocate_stub(Stub*, const Relocate_info<32, big_endian>*,
                Target_arm<big_endian>*, Output_section*,
                Target_arm<big_endian>*, Output_section*,
                unsigned char*, Arm_address, section_size_type);
                unsigned char*, Arm_address, section_size_type);
 
 
  // Unordered map of relocation stubs.
  // Unordered map of relocation stubs.
  typedef
  typedef
    Unordered_map<Reloc_stub::Key, Reloc_stub*, Reloc_stub::Key::hash,
    Unordered_map<Reloc_stub::Key, Reloc_stub*, Reloc_stub::Key::hash,
                  Reloc_stub::Key::equal_to>
                  Reloc_stub::Key::equal_to>
    Reloc_stub_map;
    Reloc_stub_map;
 
 
  // List of Cortex-A8 stubs ordered by addresses of branches being
  // List of Cortex-A8 stubs ordered by addresses of branches being
  // fixed up in output.
  // fixed up in output.
  typedef std::map<Arm_address, Cortex_a8_stub*> Cortex_a8_stub_list;
  typedef std::map<Arm_address, Cortex_a8_stub*> Cortex_a8_stub_list;
  // List of Arm V4BX relocation stubs ordered by associated registers.
  // List of Arm V4BX relocation stubs ordered by associated registers.
  typedef std::vector<Arm_v4bx_stub*> Arm_v4bx_stub_list;
  typedef std::vector<Arm_v4bx_stub*> Arm_v4bx_stub_list;
 
 
  // Owner of this stub table.
  // Owner of this stub table.
  Arm_input_section<big_endian>* owner_;
  Arm_input_section<big_endian>* owner_;
  // The relocation stubs.
  // The relocation stubs.
  Reloc_stub_map reloc_stubs_;
  Reloc_stub_map reloc_stubs_;
  // Size of reloc stubs.
  // Size of reloc stubs.
  off_t reloc_stubs_size_;
  off_t reloc_stubs_size_;
  // Maximum address alignment of reloc stubs.
  // Maximum address alignment of reloc stubs.
  uint64_t reloc_stubs_addralign_;
  uint64_t reloc_stubs_addralign_;
  // The cortex_a8_stubs.
  // The cortex_a8_stubs.
  Cortex_a8_stub_list cortex_a8_stubs_;
  Cortex_a8_stub_list cortex_a8_stubs_;
  // The Arm V4BX relocation stubs.
  // The Arm V4BX relocation stubs.
  Arm_v4bx_stub_list arm_v4bx_stubs_;
  Arm_v4bx_stub_list arm_v4bx_stubs_;
  // data size of this in the previous pass.
  // data size of this in the previous pass.
  off_t prev_data_size_;
  off_t prev_data_size_;
  // address alignment of this in the previous pass.
  // address alignment of this in the previous pass.
  uint64_t prev_addralign_;
  uint64_t prev_addralign_;
};
};
 
 
// Arm_exidx_cantunwind class.  This represents an EXIDX_CANTUNWIND entry
// Arm_exidx_cantunwind class.  This represents an EXIDX_CANTUNWIND entry
// we add to the end of an EXIDX input section that goes into the output.
// we add to the end of an EXIDX input section that goes into the output.
 
 
class Arm_exidx_cantunwind : public Output_section_data
class Arm_exidx_cantunwind : public Output_section_data
{
{
 public:
 public:
  Arm_exidx_cantunwind(Relobj* relobj, unsigned int shndx)
  Arm_exidx_cantunwind(Relobj* relobj, unsigned int shndx)
    : Output_section_data(8, 4, true), relobj_(relobj), shndx_(shndx)
    : Output_section_data(8, 4, true), relobj_(relobj), shndx_(shndx)
  { }
  { }
 
 
  // Return the object containing the section pointed by this.
  // Return the object containing the section pointed by this.
  Relobj*
  Relobj*
  relobj() const
  relobj() const
  { return this->relobj_; }
  { return this->relobj_; }
 
 
  // Return the section index of the section pointed by this.
  // Return the section index of the section pointed by this.
  unsigned int
  unsigned int
  shndx() const
  shndx() const
  { return this->shndx_; }
  { return this->shndx_; }
 
 
 protected:
 protected:
  void
  void
  do_write(Output_file* of)
  do_write(Output_file* of)
  {
  {
    if (parameters->target().is_big_endian())
    if (parameters->target().is_big_endian())
      this->do_fixed_endian_write<true>(of);
      this->do_fixed_endian_write<true>(of);
    else
    else
      this->do_fixed_endian_write<false>(of);
      this->do_fixed_endian_write<false>(of);
  }
  }
 
 
  // Write to a map file.
  // Write to a map file.
  void
  void
  do_print_to_mapfile(Mapfile* mapfile) const
  do_print_to_mapfile(Mapfile* mapfile) const
  { mapfile->print_output_data(this, _("** ARM cantunwind")); }
  { mapfile->print_output_data(this, _("** ARM cantunwind")); }
 
 
 private:
 private:
  // Implement do_write for a given endianness.
  // Implement do_write for a given endianness.
  template<bool big_endian>
  template<bool big_endian>
  void inline
  void inline
  do_fixed_endian_write(Output_file*);
  do_fixed_endian_write(Output_file*);
 
 
  // The object containing the section pointed by this.
  // The object containing the section pointed by this.
  Relobj* relobj_;
  Relobj* relobj_;
  // The section index of the section pointed by this.
  // The section index of the section pointed by this.
  unsigned int shndx_;
  unsigned int shndx_;
};
};
 
 
// During EXIDX coverage fix-up, we compact an EXIDX section.  The
// During EXIDX coverage fix-up, we compact an EXIDX section.  The
// Offset map is used to map input section offset within the EXIDX section
// Offset map is used to map input section offset within the EXIDX section
// to the output offset from the start of this EXIDX section. 
// to the output offset from the start of this EXIDX section. 
 
 
typedef std::map<section_offset_type, section_offset_type>
typedef std::map<section_offset_type, section_offset_type>
        Arm_exidx_section_offset_map;
        Arm_exidx_section_offset_map;
 
 
// Arm_exidx_merged_section class.  This represents an EXIDX input section
// Arm_exidx_merged_section class.  This represents an EXIDX input section
// with some of its entries merged.
// with some of its entries merged.
 
 
class Arm_exidx_merged_section : public Output_relaxed_input_section
class Arm_exidx_merged_section : public Output_relaxed_input_section
{
{
 public:
 public:
  // Constructor for Arm_exidx_merged_section.
  // Constructor for Arm_exidx_merged_section.
  // EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
  // EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
  // SECTION_OFFSET_MAP points to a section offset map describing how
  // SECTION_OFFSET_MAP points to a section offset map describing how
  // parts of the input section are mapped to output.  DELETED_BYTES is
  // parts of the input section are mapped to output.  DELETED_BYTES is
  // the number of bytes deleted from the EXIDX input section.
  // the number of bytes deleted from the EXIDX input section.
  Arm_exidx_merged_section(
  Arm_exidx_merged_section(
      const Arm_exidx_input_section& exidx_input_section,
      const Arm_exidx_input_section& exidx_input_section,
      const Arm_exidx_section_offset_map& section_offset_map,
      const Arm_exidx_section_offset_map& section_offset_map,
      uint32_t deleted_bytes);
      uint32_t deleted_bytes);
 
 
  // Build output contents.
  // Build output contents.
  void
  void
  build_contents(const unsigned char*, section_size_type);
  build_contents(const unsigned char*, section_size_type);
 
 
  // Return the original EXIDX input section.
  // Return the original EXIDX input section.
  const Arm_exidx_input_section&
  const Arm_exidx_input_section&
  exidx_input_section() const
  exidx_input_section() const
  { return this->exidx_input_section_; }
  { return this->exidx_input_section_; }
 
 
  // Return the section offset map.
  // Return the section offset map.
  const Arm_exidx_section_offset_map&
  const Arm_exidx_section_offset_map&
  section_offset_map() const
  section_offset_map() const
  { return this->section_offset_map_; }
  { return this->section_offset_map_; }
 
 
 protected:
 protected:
  // Write merged section into file OF.
  // Write merged section into file OF.
  void
  void
  do_write(Output_file* of);
  do_write(Output_file* of);
 
 
  bool
  bool
  do_output_offset(const Relobj*, unsigned int, section_offset_type,
  do_output_offset(const Relobj*, unsigned int, section_offset_type,
                  section_offset_type*) const;
                  section_offset_type*) const;
 
 
 private:
 private:
  // Original EXIDX input section.
  // Original EXIDX input section.
  const Arm_exidx_input_section& exidx_input_section_;
  const Arm_exidx_input_section& exidx_input_section_;
  // Section offset map.
  // Section offset map.
  const Arm_exidx_section_offset_map& section_offset_map_;
  const Arm_exidx_section_offset_map& section_offset_map_;
  // Merged section contents.  We need to keep build the merged section 
  // Merged section contents.  We need to keep build the merged section 
  // and save it here to avoid accessing the original EXIDX section when
  // and save it here to avoid accessing the original EXIDX section when
  // we cannot lock the sections' object.
  // we cannot lock the sections' object.
  unsigned char* section_contents_;
  unsigned char* section_contents_;
};
};
 
 
// A class to wrap an ordinary input section containing executable code.
// A class to wrap an ordinary input section containing executable code.
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_input_section : public Output_relaxed_input_section
class Arm_input_section : public Output_relaxed_input_section
{
{
 public:
 public:
  Arm_input_section(Relobj* relobj, unsigned int shndx)
  Arm_input_section(Relobj* relobj, unsigned int shndx)
    : Output_relaxed_input_section(relobj, shndx, 1),
    : Output_relaxed_input_section(relobj, shndx, 1),
      original_addralign_(1), original_size_(0), stub_table_(NULL),
      original_addralign_(1), original_size_(0), stub_table_(NULL),
      original_contents_(NULL)
      original_contents_(NULL)
  { }
  { }
 
 
  ~Arm_input_section()
  ~Arm_input_section()
  { delete[] this->original_contents_; }
  { delete[] this->original_contents_; }
 
 
  // Initialize.
  // Initialize.
  void
  void
  init();
  init();
 
 
  // Whether this is a stub table owner.
  // Whether this is a stub table owner.
  bool
  bool
  is_stub_table_owner() const
  is_stub_table_owner() const
  { return this->stub_table_ != NULL && this->stub_table_->owner() == this; }
  { return this->stub_table_ != NULL && this->stub_table_->owner() == this; }
 
 
  // Return the stub table.
  // Return the stub table.
  Stub_table<big_endian>*
  Stub_table<big_endian>*
  stub_table() const
  stub_table() const
  { return this->stub_table_; }
  { return this->stub_table_; }
 
 
  // Set the stub_table.
  // Set the stub_table.
  void
  void
  set_stub_table(Stub_table<big_endian>* stub_table)
  set_stub_table(Stub_table<big_endian>* stub_table)
  { this->stub_table_ = stub_table; }
  { this->stub_table_ = stub_table; }
 
 
  // Downcast a base pointer to an Arm_input_section pointer.  This is
  // Downcast a base pointer to an Arm_input_section pointer.  This is
  // not type-safe but we only use Arm_input_section not the base class.
  // not type-safe but we only use Arm_input_section not the base class.
  static Arm_input_section<big_endian>*
  static Arm_input_section<big_endian>*
  as_arm_input_section(Output_relaxed_input_section* poris)
  as_arm_input_section(Output_relaxed_input_section* poris)
  { return static_cast<Arm_input_section<big_endian>*>(poris); }
  { return static_cast<Arm_input_section<big_endian>*>(poris); }
 
 
  // Return the original size of the section.
  // Return the original size of the section.
  uint32_t
  uint32_t
  original_size() const
  original_size() const
  { return this->original_size_; }
  { return this->original_size_; }
 
 
 protected:
 protected:
  // Write data to output file.
  // Write data to output file.
  void
  void
  do_write(Output_file*);
  do_write(Output_file*);
 
 
  // Return required alignment of this.
  // Return required alignment of this.
  uint64_t
  uint64_t
  do_addralign() const
  do_addralign() const
  {
  {
    if (this->is_stub_table_owner())
    if (this->is_stub_table_owner())
      return std::max(this->stub_table_->addralign(),
      return std::max(this->stub_table_->addralign(),
                      static_cast<uint64_t>(this->original_addralign_));
                      static_cast<uint64_t>(this->original_addralign_));
    else
    else
      return this->original_addralign_;
      return this->original_addralign_;
  }
  }
 
 
  // Finalize data size.
  // Finalize data size.
  void
  void
  set_final_data_size();
  set_final_data_size();
 
 
  // Reset address and file offset.
  // Reset address and file offset.
  void
  void
  do_reset_address_and_file_offset();
  do_reset_address_and_file_offset();
 
 
  // Output offset.
  // Output offset.
  bool
  bool
  do_output_offset(const Relobj* object, unsigned int shndx,
  do_output_offset(const Relobj* object, unsigned int shndx,
                   section_offset_type offset,
                   section_offset_type offset,
                   section_offset_type* poutput) const
                   section_offset_type* poutput) const
  {
  {
    if ((object == this->relobj())
    if ((object == this->relobj())
        && (shndx == this->shndx())
        && (shndx == this->shndx())
        && (offset >= 0)
        && (offset >= 0)
        && (offset <=
        && (offset <=
            convert_types<section_offset_type, uint32_t>(this->original_size_)))
            convert_types<section_offset_type, uint32_t>(this->original_size_)))
      {
      {
        *poutput = offset;
        *poutput = offset;
        return true;
        return true;
      }
      }
    else
    else
      return false;
      return false;
  }
  }
 
 
 private:
 private:
  // Copying is not allowed.
  // Copying is not allowed.
  Arm_input_section(const Arm_input_section&);
  Arm_input_section(const Arm_input_section&);
  Arm_input_section& operator=(const Arm_input_section&);
  Arm_input_section& operator=(const Arm_input_section&);
 
 
  // Address alignment of the original input section.
  // Address alignment of the original input section.
  uint32_t original_addralign_;
  uint32_t original_addralign_;
  // Section size of the original input section.
  // Section size of the original input section.
  uint32_t original_size_;
  uint32_t original_size_;
  // Stub table.
  // Stub table.
  Stub_table<big_endian>* stub_table_;
  Stub_table<big_endian>* stub_table_;
  // Original section contents.  We have to make a copy here since the file
  // Original section contents.  We have to make a copy here since the file
  // containing the original section may not be locked when we need to access
  // containing the original section may not be locked when we need to access
  // the contents.
  // the contents.
  unsigned char* original_contents_;
  unsigned char* original_contents_;
};
};
 
 
// Arm_exidx_fixup class.  This is used to define a number of methods
// Arm_exidx_fixup class.  This is used to define a number of methods
// and keep states for fixing up EXIDX coverage.
// and keep states for fixing up EXIDX coverage.
 
 
class Arm_exidx_fixup
class Arm_exidx_fixup
{
{
 public:
 public:
  Arm_exidx_fixup(Output_section* exidx_output_section,
  Arm_exidx_fixup(Output_section* exidx_output_section,
                  bool merge_exidx_entries = true)
                  bool merge_exidx_entries = true)
    : exidx_output_section_(exidx_output_section), last_unwind_type_(UT_NONE),
    : exidx_output_section_(exidx_output_section), last_unwind_type_(UT_NONE),
      last_inlined_entry_(0), last_input_section_(NULL),
      last_inlined_entry_(0), last_input_section_(NULL),
      section_offset_map_(NULL), first_output_text_section_(NULL),
      section_offset_map_(NULL), first_output_text_section_(NULL),
      merge_exidx_entries_(merge_exidx_entries)
      merge_exidx_entries_(merge_exidx_entries)
  { }
  { }
 
 
  ~Arm_exidx_fixup()
  ~Arm_exidx_fixup()
  { delete this->section_offset_map_; }
  { delete this->section_offset_map_; }
 
 
  // Process an EXIDX section for entry merging.  SECTION_CONTENTS points
  // Process an EXIDX section for entry merging.  SECTION_CONTENTS points
  // to the EXIDX contents and SECTION_SIZE is the size of the contents. Return
  // to the EXIDX contents and SECTION_SIZE is the size of the contents. Return
  // number of bytes to be deleted in output.  If parts of the input EXIDX
  // number of bytes to be deleted in output.  If parts of the input EXIDX
  // section are merged a heap allocated Arm_exidx_section_offset_map is store
  // section are merged a heap allocated Arm_exidx_section_offset_map is store
  // in the located PSECTION_OFFSET_MAP.   The caller owns the map and is
  // in the located PSECTION_OFFSET_MAP.   The caller owns the map and is
  // responsible for releasing it.
  // responsible for releasing it.
  template<bool big_endian>
  template<bool big_endian>
  uint32_t
  uint32_t
  process_exidx_section(const Arm_exidx_input_section* exidx_input_section,
  process_exidx_section(const Arm_exidx_input_section* exidx_input_section,
                        const unsigned char* section_contents,
                        const unsigned char* section_contents,
                        section_size_type section_size,
                        section_size_type section_size,
                        Arm_exidx_section_offset_map** psection_offset_map);
                        Arm_exidx_section_offset_map** psection_offset_map);
 
 
  // Append an EXIDX_CANTUNWIND entry pointing at the end of the last
  // Append an EXIDX_CANTUNWIND entry pointing at the end of the last
  // input section, if there is not one already.
  // input section, if there is not one already.
  void
  void
  add_exidx_cantunwind_as_needed();
  add_exidx_cantunwind_as_needed();
 
 
  // Return the output section for the text section which is linked to the
  // Return the output section for the text section which is linked to the
  // first exidx input in output.
  // first exidx input in output.
  Output_section*
  Output_section*
  first_output_text_section() const
  first_output_text_section() const
  { return this->first_output_text_section_; }
  { return this->first_output_text_section_; }
 
 
 private:
 private:
  // Copying is not allowed.
  // Copying is not allowed.
  Arm_exidx_fixup(const Arm_exidx_fixup&);
  Arm_exidx_fixup(const Arm_exidx_fixup&);
  Arm_exidx_fixup& operator=(const Arm_exidx_fixup&);
  Arm_exidx_fixup& operator=(const Arm_exidx_fixup&);
 
 
  // Type of EXIDX unwind entry.
  // Type of EXIDX unwind entry.
  enum Unwind_type
  enum Unwind_type
  {
  {
    // No type.
    // No type.
    UT_NONE,
    UT_NONE,
    // EXIDX_CANTUNWIND.
    // EXIDX_CANTUNWIND.
    UT_EXIDX_CANTUNWIND,
    UT_EXIDX_CANTUNWIND,
    // Inlined entry.
    // Inlined entry.
    UT_INLINED_ENTRY,
    UT_INLINED_ENTRY,
    // Normal entry.
    // Normal entry.
    UT_NORMAL_ENTRY,
    UT_NORMAL_ENTRY,
  };
  };
 
 
  // Process an EXIDX entry.  We only care about the second word of the
  // Process an EXIDX entry.  We only care about the second word of the
  // entry.  Return true if the entry can be deleted.
  // entry.  Return true if the entry can be deleted.
  bool
  bool
  process_exidx_entry(uint32_t second_word);
  process_exidx_entry(uint32_t second_word);
 
 
  // Update the current section offset map during EXIDX section fix-up.
  // Update the current section offset map during EXIDX section fix-up.
  // If there is no map, create one.  INPUT_OFFSET is the offset of a
  // If there is no map, create one.  INPUT_OFFSET is the offset of a
  // reference point, DELETED_BYTES is the number of deleted by in the
  // reference point, DELETED_BYTES is the number of deleted by in the
  // section so far.  If DELETE_ENTRY is true, the reference point and
  // section so far.  If DELETE_ENTRY is true, the reference point and
  // all offsets after the previous reference point are discarded.
  // all offsets after the previous reference point are discarded.
  void
  void
  update_offset_map(section_offset_type input_offset,
  update_offset_map(section_offset_type input_offset,
                    section_size_type deleted_bytes, bool delete_entry);
                    section_size_type deleted_bytes, bool delete_entry);
 
 
  // EXIDX output section.
  // EXIDX output section.
  Output_section* exidx_output_section_;
  Output_section* exidx_output_section_;
  // Unwind type of the last EXIDX entry processed.
  // Unwind type of the last EXIDX entry processed.
  Unwind_type last_unwind_type_;
  Unwind_type last_unwind_type_;
  // Last seen inlined EXIDX entry.
  // Last seen inlined EXIDX entry.
  uint32_t last_inlined_entry_;
  uint32_t last_inlined_entry_;
  // Last processed EXIDX input section.
  // Last processed EXIDX input section.
  const Arm_exidx_input_section* last_input_section_;
  const Arm_exidx_input_section* last_input_section_;
  // Section offset map created in process_exidx_section.
  // Section offset map created in process_exidx_section.
  Arm_exidx_section_offset_map* section_offset_map_;
  Arm_exidx_section_offset_map* section_offset_map_;
  // Output section for the text section which is linked to the first exidx
  // Output section for the text section which is linked to the first exidx
  // input in output.
  // input in output.
  Output_section* first_output_text_section_;
  Output_section* first_output_text_section_;
 
 
  bool merge_exidx_entries_;
  bool merge_exidx_entries_;
};
};
 
 
// Arm output section class.  This is defined mainly to add a number of
// Arm output section class.  This is defined mainly to add a number of
// stub generation methods.
// stub generation methods.
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_output_section : public Output_section
class Arm_output_section : public Output_section
{
{
 public:
 public:
  typedef std::vector<std::pair<Relobj*, unsigned int> > Text_section_list;
  typedef std::vector<std::pair<Relobj*, unsigned int> > Text_section_list;
 
 
  // We need to force SHF_LINK_ORDER in a SHT_ARM_EXIDX section.
  // We need to force SHF_LINK_ORDER in a SHT_ARM_EXIDX section.
  Arm_output_section(const char* name, elfcpp::Elf_Word type,
  Arm_output_section(const char* name, elfcpp::Elf_Word type,
                     elfcpp::Elf_Xword flags)
                     elfcpp::Elf_Xword flags)
    : Output_section(name, type,
    : Output_section(name, type,
                     (type == elfcpp::SHT_ARM_EXIDX
                     (type == elfcpp::SHT_ARM_EXIDX
                      ? flags | elfcpp::SHF_LINK_ORDER
                      ? flags | elfcpp::SHF_LINK_ORDER
                      : flags))
                      : flags))
  {
  {
    if (type == elfcpp::SHT_ARM_EXIDX)
    if (type == elfcpp::SHT_ARM_EXIDX)
      this->set_always_keeps_input_sections();
      this->set_always_keeps_input_sections();
  }
  }
 
 
  ~Arm_output_section()
  ~Arm_output_section()
  { }
  { }
 
 
  // Group input sections for stub generation.
  // Group input sections for stub generation.
  void
  void
  group_sections(section_size_type, bool, Target_arm<big_endian>*, const Task*);
  group_sections(section_size_type, bool, Target_arm<big_endian>*, const Task*);
 
 
  // Downcast a base pointer to an Arm_output_section pointer.  This is
  // Downcast a base pointer to an Arm_output_section pointer.  This is
  // not type-safe but we only use Arm_output_section not the base class.
  // not type-safe but we only use Arm_output_section not the base class.
  static Arm_output_section<big_endian>*
  static Arm_output_section<big_endian>*
  as_arm_output_section(Output_section* os)
  as_arm_output_section(Output_section* os)
  { return static_cast<Arm_output_section<big_endian>*>(os); }
  { return static_cast<Arm_output_section<big_endian>*>(os); }
 
 
  // Append all input text sections in this into LIST.
  // Append all input text sections in this into LIST.
  void
  void
  append_text_sections_to_list(Text_section_list* list);
  append_text_sections_to_list(Text_section_list* list);
 
 
  // Fix EXIDX coverage of this EXIDX output section.  SORTED_TEXT_SECTION
  // Fix EXIDX coverage of this EXIDX output section.  SORTED_TEXT_SECTION
  // is a list of text input sections sorted in ascending order of their
  // is a list of text input sections sorted in ascending order of their
  // output addresses.
  // output addresses.
  void
  void
  fix_exidx_coverage(Layout* layout,
  fix_exidx_coverage(Layout* layout,
                     const Text_section_list& sorted_text_section,
                     const Text_section_list& sorted_text_section,
                     Symbol_table* symtab,
                     Symbol_table* symtab,
                     bool merge_exidx_entries,
                     bool merge_exidx_entries,
                     const Task* task);
                     const Task* task);
 
 
  // Link an EXIDX section into its corresponding text section.
  // Link an EXIDX section into its corresponding text section.
  void
  void
  set_exidx_section_link();
  set_exidx_section_link();
 
 
 private:
 private:
  // For convenience.
  // For convenience.
  typedef Output_section::Input_section Input_section;
  typedef Output_section::Input_section Input_section;
  typedef Output_section::Input_section_list Input_section_list;
  typedef Output_section::Input_section_list Input_section_list;
 
 
  // Create a stub group.
  // Create a stub group.
  void create_stub_group(Input_section_list::const_iterator,
  void create_stub_group(Input_section_list::const_iterator,
                         Input_section_list::const_iterator,
                         Input_section_list::const_iterator,
                         Input_section_list::const_iterator,
                         Input_section_list::const_iterator,
                         Target_arm<big_endian>*,
                         Target_arm<big_endian>*,
                         std::vector<Output_relaxed_input_section*>*,
                         std::vector<Output_relaxed_input_section*>*,
                         const Task* task);
                         const Task* task);
};
};
 
 
// Arm_exidx_input_section class.  This represents an EXIDX input section.
// Arm_exidx_input_section class.  This represents an EXIDX input section.
 
 
class Arm_exidx_input_section
class Arm_exidx_input_section
{
{
 public:
 public:
  static const section_offset_type invalid_offset =
  static const section_offset_type invalid_offset =
    static_cast<section_offset_type>(-1);
    static_cast<section_offset_type>(-1);
 
 
  Arm_exidx_input_section(Relobj* relobj, unsigned int shndx,
  Arm_exidx_input_section(Relobj* relobj, unsigned int shndx,
                          unsigned int link, uint32_t size,
                          unsigned int link, uint32_t size,
                          uint32_t addralign, uint32_t text_size)
                          uint32_t addralign, uint32_t text_size)
    : relobj_(relobj), shndx_(shndx), link_(link), size_(size),
    : relobj_(relobj), shndx_(shndx), link_(link), size_(size),
      addralign_(addralign), text_size_(text_size), has_errors_(false)
      addralign_(addralign), text_size_(text_size), has_errors_(false)
  { }
  { }
 
 
  ~Arm_exidx_input_section()
  ~Arm_exidx_input_section()
  { }
  { }
 
 
  // Accessors:  This is a read-only class.
  // Accessors:  This is a read-only class.
 
 
  // Return the object containing this EXIDX input section.
  // Return the object containing this EXIDX input section.
  Relobj*
  Relobj*
  relobj() const
  relobj() const
  { return this->relobj_; }
  { return this->relobj_; }
 
 
  // Return the section index of this EXIDX input section.
  // Return the section index of this EXIDX input section.
  unsigned int
  unsigned int
  shndx() const
  shndx() const
  { return this->shndx_; }
  { return this->shndx_; }
 
 
  // Return the section index of linked text section in the same object.
  // Return the section index of linked text section in the same object.
  unsigned int
  unsigned int
  link() const
  link() const
  { return this->link_; }
  { return this->link_; }
 
 
  // Return size of the EXIDX input section.
  // Return size of the EXIDX input section.
  uint32_t
  uint32_t
  size() const
  size() const
  { return this->size_; }
  { return this->size_; }
 
 
  // Return address alignment of EXIDX input section.
  // Return address alignment of EXIDX input section.
  uint32_t
  uint32_t
  addralign() const
  addralign() const
  { return this->addralign_; }
  { return this->addralign_; }
 
 
  // Return size of the associated text input section.
  // Return size of the associated text input section.
  uint32_t
  uint32_t
  text_size() const
  text_size() const
  { return this->text_size_; }
  { return this->text_size_; }
 
 
  // Whether there are any errors in the EXIDX input section.
  // Whether there are any errors in the EXIDX input section.
  bool
  bool
  has_errors() const
  has_errors() const
  { return this->has_errors_; }
  { return this->has_errors_; }
 
 
  // Set has-errors flag.
  // Set has-errors flag.
  void
  void
  set_has_errors()
  set_has_errors()
  { this->has_errors_ = true; }
  { this->has_errors_ = true; }
 
 
 private:
 private:
  // Object containing this.
  // Object containing this.
  Relobj* relobj_;
  Relobj* relobj_;
  // Section index of this.
  // Section index of this.
  unsigned int shndx_;
  unsigned int shndx_;
  // text section linked to this in the same object.
  // text section linked to this in the same object.
  unsigned int link_;
  unsigned int link_;
  // Size of this.  For ARM 32-bit is sufficient.
  // Size of this.  For ARM 32-bit is sufficient.
  uint32_t size_;
  uint32_t size_;
  // Address alignment of this.  For ARM 32-bit is sufficient.
  // Address alignment of this.  For ARM 32-bit is sufficient.
  uint32_t addralign_;
  uint32_t addralign_;
  // Size of associated text section.
  // Size of associated text section.
  uint32_t text_size_;
  uint32_t text_size_;
  // Whether this has any errors.
  // Whether this has any errors.
  bool has_errors_;
  bool has_errors_;
};
};
 
 
// Arm_relobj class.
// Arm_relobj class.
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_relobj : public Sized_relobj_file<32, big_endian>
class Arm_relobj : public Sized_relobj_file<32, big_endian>
{
{
 public:
 public:
  static const Arm_address invalid_address = static_cast<Arm_address>(-1);
  static const Arm_address invalid_address = static_cast<Arm_address>(-1);
 
 
  Arm_relobj(const std::string& name, Input_file* input_file, off_t offset,
  Arm_relobj(const std::string& name, Input_file* input_file, off_t offset,
             const typename elfcpp::Ehdr<32, big_endian>& ehdr)
             const typename elfcpp::Ehdr<32, big_endian>& ehdr)
    : Sized_relobj_file<32, big_endian>(name, input_file, offset, ehdr),
    : Sized_relobj_file<32, big_endian>(name, input_file, offset, ehdr),
      stub_tables_(), local_symbol_is_thumb_function_(),
      stub_tables_(), local_symbol_is_thumb_function_(),
      attributes_section_data_(NULL), mapping_symbols_info_(),
      attributes_section_data_(NULL), mapping_symbols_info_(),
      section_has_cortex_a8_workaround_(NULL), exidx_section_map_(),
      section_has_cortex_a8_workaround_(NULL), exidx_section_map_(),
      output_local_symbol_count_needs_update_(false),
      output_local_symbol_count_needs_update_(false),
      merge_flags_and_attributes_(true)
      merge_flags_and_attributes_(true)
  { }
  { }
 
 
  ~Arm_relobj()
  ~Arm_relobj()
  { delete this->attributes_section_data_; }
  { delete this->attributes_section_data_; }
 
 
  // Return the stub table of the SHNDX-th section if there is one.
  // Return the stub table of the SHNDX-th section if there is one.
  Stub_table<big_endian>*
  Stub_table<big_endian>*
  stub_table(unsigned int shndx) const
  stub_table(unsigned int shndx) const
  {
  {
    gold_assert(shndx < this->stub_tables_.size());
    gold_assert(shndx < this->stub_tables_.size());
    return this->stub_tables_[shndx];
    return this->stub_tables_[shndx];
  }
  }
 
 
  // Set STUB_TABLE to be the stub_table of the SHNDX-th section.
  // Set STUB_TABLE to be the stub_table of the SHNDX-th section.
  void
  void
  set_stub_table(unsigned int shndx, Stub_table<big_endian>* stub_table)
  set_stub_table(unsigned int shndx, Stub_table<big_endian>* stub_table)
  {
  {
    gold_assert(shndx < this->stub_tables_.size());
    gold_assert(shndx < this->stub_tables_.size());
    this->stub_tables_[shndx] = stub_table;
    this->stub_tables_[shndx] = stub_table;
  }
  }
 
 
  // Whether a local symbol is a THUMB function.  R_SYM is the symbol table
  // Whether a local symbol is a THUMB function.  R_SYM is the symbol table
  // index.  This is only valid after do_count_local_symbol is called.
  // index.  This is only valid after do_count_local_symbol is called.
  bool
  bool
  local_symbol_is_thumb_function(unsigned int r_sym) const
  local_symbol_is_thumb_function(unsigned int r_sym) const
  {
  {
    gold_assert(r_sym < this->local_symbol_is_thumb_function_.size());
    gold_assert(r_sym < this->local_symbol_is_thumb_function_.size());
    return this->local_symbol_is_thumb_function_[r_sym];
    return this->local_symbol_is_thumb_function_[r_sym];
  }
  }
 
 
  // Scan all relocation sections for stub generation.
  // Scan all relocation sections for stub generation.
  void
  void
  scan_sections_for_stubs(Target_arm<big_endian>*, const Symbol_table*,
  scan_sections_for_stubs(Target_arm<big_endian>*, const Symbol_table*,
                          const Layout*);
                          const Layout*);
 
 
  // Convert regular input section with index SHNDX to a relaxed section.
  // Convert regular input section with index SHNDX to a relaxed section.
  void
  void
  convert_input_section_to_relaxed_section(unsigned shndx)
  convert_input_section_to_relaxed_section(unsigned shndx)
  {
  {
    // The stubs have relocations and we need to process them after writing
    // The stubs have relocations and we need to process them after writing
    // out the stubs.  So relocation now must follow section write.
    // out the stubs.  So relocation now must follow section write.
    this->set_section_offset(shndx, -1ULL);
    this->set_section_offset(shndx, -1ULL);
    this->set_relocs_must_follow_section_writes();
    this->set_relocs_must_follow_section_writes();
  }
  }
 
 
  // Downcast a base pointer to an Arm_relobj pointer.  This is
  // Downcast a base pointer to an Arm_relobj pointer.  This is
  // not type-safe but we only use Arm_relobj not the base class.
  // not type-safe but we only use Arm_relobj not the base class.
  static Arm_relobj<big_endian>*
  static Arm_relobj<big_endian>*
  as_arm_relobj(Relobj* relobj)
  as_arm_relobj(Relobj* relobj)
  { return static_cast<Arm_relobj<big_endian>*>(relobj); }
  { return static_cast<Arm_relobj<big_endian>*>(relobj); }
 
 
  // Processor-specific flags in ELF file header.  This is valid only after
  // Processor-specific flags in ELF file header.  This is valid only after
  // reading symbols.
  // reading symbols.
  elfcpp::Elf_Word
  elfcpp::Elf_Word
  processor_specific_flags() const
  processor_specific_flags() const
  { return this->processor_specific_flags_; }
  { return this->processor_specific_flags_; }
 
 
  // Attribute section data  This is the contents of the .ARM.attribute section
  // Attribute section data  This is the contents of the .ARM.attribute section
  // if there is one.
  // if there is one.
  const Attributes_section_data*
  const Attributes_section_data*
  attributes_section_data() const
  attributes_section_data() const
  { return this->attributes_section_data_; }
  { return this->attributes_section_data_; }
 
 
  // Mapping symbol location.
  // Mapping symbol location.
  typedef std::pair<unsigned int, Arm_address> Mapping_symbol_position;
  typedef std::pair<unsigned int, Arm_address> Mapping_symbol_position;
 
 
  // Functor for STL container.
  // Functor for STL container.
  struct Mapping_symbol_position_less
  struct Mapping_symbol_position_less
  {
  {
    bool
    bool
    operator()(const Mapping_symbol_position& p1,
    operator()(const Mapping_symbol_position& p1,
               const Mapping_symbol_position& p2) const
               const Mapping_symbol_position& p2) const
    {
    {
      return (p1.first < p2.first
      return (p1.first < p2.first
              || (p1.first == p2.first && p1.second < p2.second));
              || (p1.first == p2.first && p1.second < p2.second));
    }
    }
  };
  };
 
 
  // We only care about the first character of a mapping symbol, so
  // We only care about the first character of a mapping symbol, so
  // we only store that instead of the whole symbol name.
  // we only store that instead of the whole symbol name.
  typedef std::map<Mapping_symbol_position, char,
  typedef std::map<Mapping_symbol_position, char,
                   Mapping_symbol_position_less> Mapping_symbols_info;
                   Mapping_symbol_position_less> Mapping_symbols_info;
 
 
  // Whether a section contains any Cortex-A8 workaround.
  // Whether a section contains any Cortex-A8 workaround.
  bool
  bool
  section_has_cortex_a8_workaround(unsigned int shndx) const
  section_has_cortex_a8_workaround(unsigned int shndx) const
  {
  {
    return (this->section_has_cortex_a8_workaround_ != NULL
    return (this->section_has_cortex_a8_workaround_ != NULL
            && (*this->section_has_cortex_a8_workaround_)[shndx]);
            && (*this->section_has_cortex_a8_workaround_)[shndx]);
  }
  }
 
 
  // Mark a section that has Cortex-A8 workaround.
  // Mark a section that has Cortex-A8 workaround.
  void
  void
  mark_section_for_cortex_a8_workaround(unsigned int shndx)
  mark_section_for_cortex_a8_workaround(unsigned int shndx)
  {
  {
    if (this->section_has_cortex_a8_workaround_ == NULL)
    if (this->section_has_cortex_a8_workaround_ == NULL)
      this->section_has_cortex_a8_workaround_ =
      this->section_has_cortex_a8_workaround_ =
        new std::vector<bool>(this->shnum(), false);
        new std::vector<bool>(this->shnum(), false);
    (*this->section_has_cortex_a8_workaround_)[shndx] = true;
    (*this->section_has_cortex_a8_workaround_)[shndx] = true;
  }
  }
 
 
  // Return the EXIDX section of an text section with index SHNDX or NULL
  // Return the EXIDX section of an text section with index SHNDX or NULL
  // if the text section has no associated EXIDX section.
  // if the text section has no associated EXIDX section.
  const Arm_exidx_input_section*
  const Arm_exidx_input_section*
  exidx_input_section_by_link(unsigned int shndx) const
  exidx_input_section_by_link(unsigned int shndx) const
  {
  {
    Exidx_section_map::const_iterator p = this->exidx_section_map_.find(shndx);
    Exidx_section_map::const_iterator p = this->exidx_section_map_.find(shndx);
    return ((p != this->exidx_section_map_.end()
    return ((p != this->exidx_section_map_.end()
             && p->second->link() == shndx)
             && p->second->link() == shndx)
            ? p->second
            ? p->second
            : NULL);
            : NULL);
  }
  }
 
 
  // Return the EXIDX section with index SHNDX or NULL if there is none.
  // Return the EXIDX section with index SHNDX or NULL if there is none.
  const Arm_exidx_input_section*
  const Arm_exidx_input_section*
  exidx_input_section_by_shndx(unsigned shndx) const
  exidx_input_section_by_shndx(unsigned shndx) const
  {
  {
    Exidx_section_map::const_iterator p = this->exidx_section_map_.find(shndx);
    Exidx_section_map::const_iterator p = this->exidx_section_map_.find(shndx);
    return ((p != this->exidx_section_map_.end()
    return ((p != this->exidx_section_map_.end()
             && p->second->shndx() == shndx)
             && p->second->shndx() == shndx)
            ? p->second
            ? p->second
            : NULL);
            : NULL);
  }
  }
 
 
  // Whether output local symbol count needs updating.
  // Whether output local symbol count needs updating.
  bool
  bool
  output_local_symbol_count_needs_update() const
  output_local_symbol_count_needs_update() const
  { return this->output_local_symbol_count_needs_update_; }
  { return this->output_local_symbol_count_needs_update_; }
 
 
  // Set output_local_symbol_count_needs_update flag to be true.
  // Set output_local_symbol_count_needs_update flag to be true.
  void
  void
  set_output_local_symbol_count_needs_update()
  set_output_local_symbol_count_needs_update()
  { this->output_local_symbol_count_needs_update_ = true; }
  { this->output_local_symbol_count_needs_update_ = true; }
 
 
  // Update output local symbol count at the end of relaxation.
  // Update output local symbol count at the end of relaxation.
  void
  void
  update_output_local_symbol_count();
  update_output_local_symbol_count();
 
 
  // Whether we want to merge processor-specific flags and attributes.
  // Whether we want to merge processor-specific flags and attributes.
  bool
  bool
  merge_flags_and_attributes() const
  merge_flags_and_attributes() const
  { return this->merge_flags_and_attributes_; }
  { return this->merge_flags_and_attributes_; }
 
 
  // Export list of EXIDX section indices.
  // Export list of EXIDX section indices.
  void
  void
  get_exidx_shndx_list(std::vector<unsigned int>* list) const
  get_exidx_shndx_list(std::vector<unsigned int>* list) const
  {
  {
    list->clear();
    list->clear();
    for (Exidx_section_map::const_iterator p = this->exidx_section_map_.begin();
    for (Exidx_section_map::const_iterator p = this->exidx_section_map_.begin();
         p != this->exidx_section_map_.end();
         p != this->exidx_section_map_.end();
         ++p)
         ++p)
      {
      {
        if (p->second->shndx() == p->first)
        if (p->second->shndx() == p->first)
          list->push_back(p->first);
          list->push_back(p->first);
      }
      }
    // Sort list to make result independent of implementation of map. 
    // Sort list to make result independent of implementation of map. 
    std::sort(list->begin(), list->end());
    std::sort(list->begin(), list->end());
  }
  }
 
 
 protected:
 protected:
  // Post constructor setup.
  // Post constructor setup.
  void
  void
  do_setup()
  do_setup()
  {
  {
    // Call parent's setup method.
    // Call parent's setup method.
    Sized_relobj_file<32, big_endian>::do_setup();
    Sized_relobj_file<32, big_endian>::do_setup();
 
 
    // Initialize look-up tables.
    // Initialize look-up tables.
    Stub_table_list empty_stub_table_list(this->shnum(), NULL);
    Stub_table_list empty_stub_table_list(this->shnum(), NULL);
    this->stub_tables_.swap(empty_stub_table_list);
    this->stub_tables_.swap(empty_stub_table_list);
  }
  }
 
 
  // Count the local symbols.
  // Count the local symbols.
  void
  void
  do_count_local_symbols(Stringpool_template<char>*,
  do_count_local_symbols(Stringpool_template<char>*,
                         Stringpool_template<char>*);
                         Stringpool_template<char>*);
 
 
  void
  void
  do_relocate_sections(
  do_relocate_sections(
      const Symbol_table* symtab, const Layout* layout,
      const Symbol_table* symtab, const Layout* layout,
      const unsigned char* pshdrs, Output_file* of,
      const unsigned char* pshdrs, Output_file* of,
      typename Sized_relobj_file<32, big_endian>::Views* pivews);
      typename Sized_relobj_file<32, big_endian>::Views* pivews);
 
 
  // Read the symbol information.
  // Read the symbol information.
  void
  void
  do_read_symbols(Read_symbols_data* sd);
  do_read_symbols(Read_symbols_data* sd);
 
 
  // Process relocs for garbage collection.
  // Process relocs for garbage collection.
  void
  void
  do_gc_process_relocs(Symbol_table*, Layout*, Read_relocs_data*);
  do_gc_process_relocs(Symbol_table*, Layout*, Read_relocs_data*);
 
 
 private:
 private:
 
 
  // Whether a section needs to be scanned for relocation stubs.
  // Whether a section needs to be scanned for relocation stubs.
  bool
  bool
  section_needs_reloc_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
  section_needs_reloc_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
                                    const Relobj::Output_sections&,
                                    const Relobj::Output_sections&,
                                    const Symbol_table*, const unsigned char*);
                                    const Symbol_table*, const unsigned char*);
 
 
  // Whether a section is a scannable text section.
  // Whether a section is a scannable text section.
  bool
  bool
  section_is_scannable(const elfcpp::Shdr<32, big_endian>&, unsigned int,
  section_is_scannable(const elfcpp::Shdr<32, big_endian>&, unsigned int,
                       const Output_section*, const Symbol_table*);
                       const Output_section*, const Symbol_table*);
 
 
  // Whether a section needs to be scanned for the Cortex-A8 erratum.
  // Whether a section needs to be scanned for the Cortex-A8 erratum.
  bool
  bool
  section_needs_cortex_a8_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
  section_needs_cortex_a8_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
                                        unsigned int, Output_section*,
                                        unsigned int, Output_section*,
                                        const Symbol_table*);
                                        const Symbol_table*);
 
 
  // Scan a section for the Cortex-A8 erratum.
  // Scan a section for the Cortex-A8 erratum.
  void
  void
  scan_section_for_cortex_a8_erratum(const elfcpp::Shdr<32, big_endian>&,
  scan_section_for_cortex_a8_erratum(const elfcpp::Shdr<32, big_endian>&,
                                     unsigned int, Output_section*,
                                     unsigned int, Output_section*,
                                     Target_arm<big_endian>*);
                                     Target_arm<big_endian>*);
 
 
  // Find the linked text section of an EXIDX section by looking at the
  // Find the linked text section of an EXIDX section by looking at the
  // first relocation of the EXIDX section.  PSHDR points to the section
  // first relocation of the EXIDX section.  PSHDR points to the section
  // headers of a relocation section and PSYMS points to the local symbols.
  // headers of a relocation section and PSYMS points to the local symbols.
  // PSHNDX points to a location storing the text section index if found.
  // PSHNDX points to a location storing the text section index if found.
  // Return whether we can find the linked section.
  // Return whether we can find the linked section.
  bool
  bool
  find_linked_text_section(const unsigned char* pshdr,
  find_linked_text_section(const unsigned char* pshdr,
                           const unsigned char* psyms, unsigned int* pshndx);
                           const unsigned char* psyms, unsigned int* pshndx);
 
 
  //
  //
  // Make a new Arm_exidx_input_section object for EXIDX section with
  // Make a new Arm_exidx_input_section object for EXIDX section with
  // index SHNDX and section header SHDR.  TEXT_SHNDX is the section
  // index SHNDX and section header SHDR.  TEXT_SHNDX is the section
  // index of the linked text section.
  // index of the linked text section.
  void
  void
  make_exidx_input_section(unsigned int shndx,
  make_exidx_input_section(unsigned int shndx,
                           const elfcpp::Shdr<32, big_endian>& shdr,
                           const elfcpp::Shdr<32, big_endian>& shdr,
                           unsigned int text_shndx,
                           unsigned int text_shndx,
                           const elfcpp::Shdr<32, big_endian>& text_shdr);
                           const elfcpp::Shdr<32, big_endian>& text_shdr);
 
 
  // Return the output address of either a plain input section or a
  // Return the output address of either a plain input section or a
  // relaxed input section.  SHNDX is the section index.
  // relaxed input section.  SHNDX is the section index.
  Arm_address
  Arm_address
  simple_input_section_output_address(unsigned int, Output_section*);
  simple_input_section_output_address(unsigned int, Output_section*);
 
 
  typedef std::vector<Stub_table<big_endian>*> Stub_table_list;
  typedef std::vector<Stub_table<big_endian>*> Stub_table_list;
  typedef Unordered_map<unsigned int, const Arm_exidx_input_section*>
  typedef Unordered_map<unsigned int, const Arm_exidx_input_section*>
    Exidx_section_map;
    Exidx_section_map;
 
 
  // List of stub tables.
  // List of stub tables.
  Stub_table_list stub_tables_;
  Stub_table_list stub_tables_;
  // Bit vector to tell if a local symbol is a thumb function or not.
  // Bit vector to tell if a local symbol is a thumb function or not.
  // This is only valid after do_count_local_symbol is called.
  // This is only valid after do_count_local_symbol is called.
  std::vector<bool> local_symbol_is_thumb_function_;
  std::vector<bool> local_symbol_is_thumb_function_;
  // processor-specific flags in ELF file header.
  // processor-specific flags in ELF file header.
  elfcpp::Elf_Word processor_specific_flags_;
  elfcpp::Elf_Word processor_specific_flags_;
  // Object attributes if there is an .ARM.attributes section or NULL.
  // Object attributes if there is an .ARM.attributes section or NULL.
  Attributes_section_data* attributes_section_data_;
  Attributes_section_data* attributes_section_data_;
  // Mapping symbols information.
  // Mapping symbols information.
  Mapping_symbols_info mapping_symbols_info_;
  Mapping_symbols_info mapping_symbols_info_;
  // Bitmap to indicate sections with Cortex-A8 workaround or NULL.
  // Bitmap to indicate sections with Cortex-A8 workaround or NULL.
  std::vector<bool>* section_has_cortex_a8_workaround_;
  std::vector<bool>* section_has_cortex_a8_workaround_;
  // Map a text section to its associated .ARM.exidx section, if there is one.
  // Map a text section to its associated .ARM.exidx section, if there is one.
  Exidx_section_map exidx_section_map_;
  Exidx_section_map exidx_section_map_;
  // Whether output local symbol count needs updating.
  // Whether output local symbol count needs updating.
  bool output_local_symbol_count_needs_update_;
  bool output_local_symbol_count_needs_update_;
  // Whether we merge processor flags and attributes of this object to
  // Whether we merge processor flags and attributes of this object to
  // output.
  // output.
  bool merge_flags_and_attributes_;
  bool merge_flags_and_attributes_;
};
};
 
 
// Arm_dynobj class.
// Arm_dynobj class.
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_dynobj : public Sized_dynobj<32, big_endian>
class Arm_dynobj : public Sized_dynobj<32, big_endian>
{
{
 public:
 public:
  Arm_dynobj(const std::string& name, Input_file* input_file, off_t offset,
  Arm_dynobj(const std::string& name, Input_file* input_file, off_t offset,
             const elfcpp::Ehdr<32, big_endian>& ehdr)
             const elfcpp::Ehdr<32, big_endian>& ehdr)
    : Sized_dynobj<32, big_endian>(name, input_file, offset, ehdr),
    : Sized_dynobj<32, big_endian>(name, input_file, offset, ehdr),
      processor_specific_flags_(0), attributes_section_data_(NULL)
      processor_specific_flags_(0), attributes_section_data_(NULL)
  { }
  { }
 
 
  ~Arm_dynobj()
  ~Arm_dynobj()
  { delete this->attributes_section_data_; }
  { delete this->attributes_section_data_; }
 
 
  // Downcast a base pointer to an Arm_relobj pointer.  This is
  // Downcast a base pointer to an Arm_relobj pointer.  This is
  // not type-safe but we only use Arm_relobj not the base class.
  // not type-safe but we only use Arm_relobj not the base class.
  static Arm_dynobj<big_endian>*
  static Arm_dynobj<big_endian>*
  as_arm_dynobj(Dynobj* dynobj)
  as_arm_dynobj(Dynobj* dynobj)
  { return static_cast<Arm_dynobj<big_endian>*>(dynobj); }
  { return static_cast<Arm_dynobj<big_endian>*>(dynobj); }
 
 
  // Processor-specific flags in ELF file header.  This is valid only after
  // Processor-specific flags in ELF file header.  This is valid only after
  // reading symbols.
  // reading symbols.
  elfcpp::Elf_Word
  elfcpp::Elf_Word
  processor_specific_flags() const
  processor_specific_flags() const
  { return this->processor_specific_flags_; }
  { return this->processor_specific_flags_; }
 
 
  // Attributes section data.
  // Attributes section data.
  const Attributes_section_data*
  const Attributes_section_data*
  attributes_section_data() const
  attributes_section_data() const
  { return this->attributes_section_data_; }
  { return this->attributes_section_data_; }
 
 
 protected:
 protected:
  // Read the symbol information.
  // Read the symbol information.
  void
  void
  do_read_symbols(Read_symbols_data* sd);
  do_read_symbols(Read_symbols_data* sd);
 
 
 private:
 private:
  // processor-specific flags in ELF file header.
  // processor-specific flags in ELF file header.
  elfcpp::Elf_Word processor_specific_flags_;
  elfcpp::Elf_Word processor_specific_flags_;
  // Object attributes if there is an .ARM.attributes section or NULL.
  // Object attributes if there is an .ARM.attributes section or NULL.
  Attributes_section_data* attributes_section_data_;
  Attributes_section_data* attributes_section_data_;
};
};
 
 
// Functor to read reloc addends during stub generation.
// Functor to read reloc addends during stub generation.
 
 
template<int sh_type, bool big_endian>
template<int sh_type, bool big_endian>
struct Stub_addend_reader
struct Stub_addend_reader
{
{
  // Return the addend for a relocation of a particular type.  Depending
  // Return the addend for a relocation of a particular type.  Depending
  // on whether this is a REL or RELA relocation, read the addend from a
  // on whether this is a REL or RELA relocation, read the addend from a
  // view or from a Reloc object.
  // view or from a Reloc object.
  elfcpp::Elf_types<32>::Elf_Swxword
  elfcpp::Elf_types<32>::Elf_Swxword
  operator()(
  operator()(
    unsigned int /* r_type */,
    unsigned int /* r_type */,
    const unsigned char* /* view */,
    const unsigned char* /* view */,
    const typename Reloc_types<sh_type,
    const typename Reloc_types<sh_type,
                               32, big_endian>::Reloc& /* reloc */) const;
                               32, big_endian>::Reloc& /* reloc */) const;
};
};
 
 
// Specialized Stub_addend_reader for SHT_REL type relocation sections.
// Specialized Stub_addend_reader for SHT_REL type relocation sections.
 
 
template<bool big_endian>
template<bool big_endian>
struct Stub_addend_reader<elfcpp::SHT_REL, big_endian>
struct Stub_addend_reader<elfcpp::SHT_REL, big_endian>
{
{
  elfcpp::Elf_types<32>::Elf_Swxword
  elfcpp::Elf_types<32>::Elf_Swxword
  operator()(
  operator()(
    unsigned int,
    unsigned int,
    const unsigned char*,
    const unsigned char*,
    const typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc&) const;
    const typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc&) const;
};
};
 
 
// Specialized Stub_addend_reader for RELA type relocation sections.
// Specialized Stub_addend_reader for RELA type relocation sections.
// We currently do not handle RELA type relocation sections but it is trivial
// We currently do not handle RELA type relocation sections but it is trivial
// to implement the addend reader.  This is provided for completeness and to
// to implement the addend reader.  This is provided for completeness and to
// make it easier to add support for RELA relocation sections in the future.
// make it easier to add support for RELA relocation sections in the future.
 
 
template<bool big_endian>
template<bool big_endian>
struct Stub_addend_reader<elfcpp::SHT_RELA, big_endian>
struct Stub_addend_reader<elfcpp::SHT_RELA, big_endian>
{
{
  elfcpp::Elf_types<32>::Elf_Swxword
  elfcpp::Elf_types<32>::Elf_Swxword
  operator()(
  operator()(
    unsigned int,
    unsigned int,
    const unsigned char*,
    const unsigned char*,
    const typename Reloc_types<elfcpp::SHT_RELA, 32,
    const typename Reloc_types<elfcpp::SHT_RELA, 32,
                               big_endian>::Reloc& reloc) const
                               big_endian>::Reloc& reloc) const
  { return reloc.get_r_addend(); }
  { return reloc.get_r_addend(); }
};
};
 
 
// Cortex_a8_reloc class.  We keep record of relocation that may need
// Cortex_a8_reloc class.  We keep record of relocation that may need
// the Cortex-A8 erratum workaround.
// the Cortex-A8 erratum workaround.
 
 
class Cortex_a8_reloc
class Cortex_a8_reloc
{
{
 public:
 public:
  Cortex_a8_reloc(Reloc_stub* reloc_stub, unsigned r_type,
  Cortex_a8_reloc(Reloc_stub* reloc_stub, unsigned r_type,
                  Arm_address destination)
                  Arm_address destination)
    : reloc_stub_(reloc_stub), r_type_(r_type), destination_(destination)
    : reloc_stub_(reloc_stub), r_type_(r_type), destination_(destination)
  { }
  { }
 
 
  ~Cortex_a8_reloc()
  ~Cortex_a8_reloc()
  { }
  { }
 
 
  // Accessors:  This is a read-only class.
  // Accessors:  This is a read-only class.
 
 
  // Return the relocation stub associated with this relocation if there is
  // Return the relocation stub associated with this relocation if there is
  // one.
  // one.
  const Reloc_stub*
  const Reloc_stub*
  reloc_stub() const
  reloc_stub() const
  { return this->reloc_stub_; }
  { return this->reloc_stub_; }
 
 
  // Return the relocation type.
  // Return the relocation type.
  unsigned int
  unsigned int
  r_type() const
  r_type() const
  { return this->r_type_; }
  { return this->r_type_; }
 
 
  // Return the destination address of the relocation.  LSB stores the THUMB
  // Return the destination address of the relocation.  LSB stores the THUMB
  // bit.
  // bit.
  Arm_address
  Arm_address
  destination() const
  destination() const
  { return this->destination_; }
  { return this->destination_; }
 
 
 private:
 private:
  // Associated relocation stub if there is one, or NULL.
  // Associated relocation stub if there is one, or NULL.
  const Reloc_stub* reloc_stub_;
  const Reloc_stub* reloc_stub_;
  // Relocation type.
  // Relocation type.
  unsigned int r_type_;
  unsigned int r_type_;
  // Destination address of this relocation.  LSB is used to distinguish
  // Destination address of this relocation.  LSB is used to distinguish
  // ARM/THUMB mode.
  // ARM/THUMB mode.
  Arm_address destination_;
  Arm_address destination_;
};
};
 
 
// Arm_output_data_got class.  We derive this from Output_data_got to add
// Arm_output_data_got class.  We derive this from Output_data_got to add
// extra methods to handle TLS relocations in a static link.
// extra methods to handle TLS relocations in a static link.
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_output_data_got : public Output_data_got<32, big_endian>
class Arm_output_data_got : public Output_data_got<32, big_endian>
{
{
 public:
 public:
  Arm_output_data_got(Symbol_table* symtab, Layout* layout)
  Arm_output_data_got(Symbol_table* symtab, Layout* layout)
    : Output_data_got<32, big_endian>(), 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
  // 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
  // symbol and R_TYPE is the code of a dynamic relocation that needs to be
  // applied in a static link.
  // applied in a static link.
  void
  void
  add_static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
  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)); }
  { 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
  // 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
  // defining a local symbol with INDEX.  R_TYPE is the code of a dynamic
  // relocation that needs to be applied in a static link.
  // relocation that needs to be applied in a static link.
  void
  void
  add_static_reloc(unsigned int got_offset, unsigned int r_type,
  add_static_reloc(unsigned int got_offset, unsigned int r_type,
                   Sized_relobj_file<32, big_endian>* relobj,
                   Sized_relobj_file<32, big_endian>* relobj,
                   unsigned int index)
                   unsigned int index)
  {
  {
    this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj,
    this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj,
                                                index));
                                                index));
  }
  }
 
 
  // Add a GOT pair for R_ARM_TLS_GD32.  The creates a pair of GOT entries.
  // 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 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
  // 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
  // R_ARM_TLS_DTPOFF32 will be created for the second GOT entry and will
  // be applied by gold.  GSYM is a global symbol.
  // be applied by gold.  GSYM is a global symbol.
  void
  void
  add_tls_gd32_with_static_reloc(unsigned int got_type, Symbol* gsym);
  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.
  // Same as the above but for a local symbol in OBJECT with INDEX.
  void
  void
  add_tls_gd32_with_static_reloc(unsigned int got_type,
  add_tls_gd32_with_static_reloc(unsigned int got_type,
                                 Sized_relobj_file<32, big_endian>* object,
                                 Sized_relobj_file<32, big_endian>* object,
                                 unsigned int index);
                                 unsigned int index);
 
 
 protected:
 protected:
  // Write out the GOT table.
  // Write out the GOT table.
  void
  void
  do_write(Output_file*);
  do_write(Output_file*);
 
 
 private:
 private:
  // This class represent dynamic relocations that need to be applied by
  // This class represent dynamic relocations that need to be applied by
  // gold because we are using TLS relocations in a static link.
  // gold because we are using TLS relocations in a static link.
  class Static_reloc
  class Static_reloc
  {
  {
   public:
   public:
    Static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
    Static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
      : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true)
      : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true)
    { this->u_.global.symbol = gsym; }
    { this->u_.global.symbol = gsym; }
 
 
    Static_reloc(unsigned int got_offset, unsigned int r_type,
    Static_reloc(unsigned int got_offset, unsigned int r_type,
          Sized_relobj_file<32, big_endian>* relobj, unsigned int index)
          Sized_relobj_file<32, big_endian>* relobj, unsigned int index)
      : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false)
      : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false)
    {
    {
      this->u_.local.relobj = relobj;
      this->u_.local.relobj = relobj;
      this->u_.local.index = index;
      this->u_.local.index = index;
    }
    }
 
 
    // Return the GOT offset.
    // Return the GOT offset.
    unsigned int
    unsigned int
    got_offset() const
    got_offset() const
    { return this->got_offset_; }
    { return this->got_offset_; }
 
 
    // Relocation type.
    // Relocation type.
    unsigned int
    unsigned int
    r_type() const
    r_type() const
    { return this->r_type_; }
    { return this->r_type_; }
 
 
    // Whether the symbol is global or not.
    // Whether the symbol is global or not.
    bool
    bool
    symbol_is_global() const
    symbol_is_global() const
    { return this->symbol_is_global_; }
    { return this->symbol_is_global_; }
 
 
    // For a relocation against a global symbol, the global symbol.
    // For a relocation against a global symbol, the global symbol.
    Symbol*
    Symbol*
    symbol() const
    symbol() const
    {
    {
      gold_assert(this->symbol_is_global_);
      gold_assert(this->symbol_is_global_);
      return this->u_.global.symbol;
      return this->u_.global.symbol;
    }
    }
 
 
    // For a relocation against a local symbol, the defining object.
    // For a relocation against a local symbol, the defining object.
    Sized_relobj_file<32, big_endian>*
    Sized_relobj_file<32, big_endian>*
    relobj() const
    relobj() const
    {
    {
      gold_assert(!this->symbol_is_global_);
      gold_assert(!this->symbol_is_global_);
      return this->u_.local.relobj;
      return this->u_.local.relobj;
    }
    }
 
 
    // For a relocation against a local symbol, the local symbol index.
    // For a relocation against a local symbol, the local symbol index.
    unsigned int
    unsigned int
    index() const
    index() const
    {
    {
      gold_assert(!this->symbol_is_global_);
      gold_assert(!this->symbol_is_global_);
      return this->u_.local.index;
      return this->u_.local.index;
    }
    }
 
 
   private:
   private:
    // GOT offset of the entry to which this relocation is applied.
    // GOT offset of the entry to which this relocation is applied.
    unsigned int got_offset_;
    unsigned int got_offset_;
    // Type of relocation.
    // Type of relocation.
    unsigned int r_type_;
    unsigned int r_type_;
    // Whether this relocation is against a global symbol.
    // Whether this relocation is against a global symbol.
    bool symbol_is_global_;
    bool symbol_is_global_;
    // A global or local symbol.
    // A global or local symbol.
    union
    union
    {
    {
      struct
      struct
      {
      {
        // For a global symbol, the symbol itself.
        // For a global symbol, the symbol itself.
        Symbol* symbol;
        Symbol* symbol;
      } global;
      } global;
      struct
      struct
      {
      {
        // For a local symbol, the object defining object.
        // For a local symbol, the object defining object.
        Sized_relobj_file<32, big_endian>* relobj;
        Sized_relobj_file<32, big_endian>* relobj;
        // For a local symbol, the symbol index.
        // For a local symbol, the symbol index.
        unsigned int index;
        unsigned int index;
      } local;
      } local;
    } u_;
    } u_;
  };
  };
 
 
  // Symbol table of the output object.
  // Symbol table of the output object.
  Symbol_table* symbol_table_;
  Symbol_table* symbol_table_;
  // Layout of the output object.
  // Layout of the output object.
  Layout* layout_;
  Layout* layout_;
  // Static relocs to be applied to the GOT.
  // Static relocs to be applied to the GOT.
  std::vector<Static_reloc> static_relocs_;
  std::vector<Static_reloc> static_relocs_;
};
};
 
 
// The ARM target has many relocation types with odd-sizes or noncontiguous
// The ARM target has many relocation types with odd-sizes or noncontiguous
// bits.  The default handling of relocatable relocation cannot process these
// bits.  The default handling of relocatable relocation cannot process these
// relocations.  So we have to extend the default code.
// relocations.  So we have to extend the default code.
 
 
template<bool big_endian, int sh_type, typename Classify_reloc>
template<bool big_endian, int sh_type, typename Classify_reloc>
class Arm_scan_relocatable_relocs :
class Arm_scan_relocatable_relocs :
  public Default_scan_relocatable_relocs<sh_type, Classify_reloc>
  public Default_scan_relocatable_relocs<sh_type, Classify_reloc>
{
{
 public:
 public:
  // Return the strategy to use for a local symbol which is a section
  // Return the strategy to use for a local symbol which is a section
  // symbol, given the relocation type.
  // symbol, given the relocation type.
  inline Relocatable_relocs::Reloc_strategy
  inline Relocatable_relocs::Reloc_strategy
  local_section_strategy(unsigned int r_type, Relobj*)
  local_section_strategy(unsigned int r_type, Relobj*)
  {
  {
    if (sh_type == elfcpp::SHT_RELA)
    if (sh_type == elfcpp::SHT_RELA)
      return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA;
      return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA;
    else
    else
      {
      {
        if (r_type == elfcpp::R_ARM_TARGET1
        if (r_type == elfcpp::R_ARM_TARGET1
            || r_type == elfcpp::R_ARM_TARGET2)
            || r_type == elfcpp::R_ARM_TARGET2)
          {
          {
            const Target_arm<big_endian>* arm_target =
            const Target_arm<big_endian>* arm_target =
              Target_arm<big_endian>::default_target();
              Target_arm<big_endian>::default_target();
            r_type = arm_target->get_real_reloc_type(r_type);
            r_type = arm_target->get_real_reloc_type(r_type);
          }
          }
 
 
        switch(r_type)
        switch(r_type)
          {
          {
          // Relocations that write nothing.  These exclude R_ARM_TARGET1
          // Relocations that write nothing.  These exclude R_ARM_TARGET1
          // and R_ARM_TARGET2.
          // and R_ARM_TARGET2.
          case elfcpp::R_ARM_NONE:
          case elfcpp::R_ARM_NONE:
          case elfcpp::R_ARM_V4BX:
          case elfcpp::R_ARM_V4BX:
          case elfcpp::R_ARM_TLS_GOTDESC:
          case elfcpp::R_ARM_TLS_GOTDESC:
          case elfcpp::R_ARM_TLS_CALL:
          case elfcpp::R_ARM_TLS_CALL:
          case elfcpp::R_ARM_TLS_DESCSEQ:
          case elfcpp::R_ARM_TLS_DESCSEQ:
          case elfcpp::R_ARM_THM_TLS_CALL:
          case elfcpp::R_ARM_THM_TLS_CALL:
          case elfcpp::R_ARM_GOTRELAX:
          case elfcpp::R_ARM_GOTRELAX:
          case elfcpp::R_ARM_GNU_VTENTRY:
          case elfcpp::R_ARM_GNU_VTENTRY:
          case elfcpp::R_ARM_GNU_VTINHERIT:
          case elfcpp::R_ARM_GNU_VTINHERIT:
          case elfcpp::R_ARM_THM_TLS_DESCSEQ16:
          case elfcpp::R_ARM_THM_TLS_DESCSEQ16:
          case elfcpp::R_ARM_THM_TLS_DESCSEQ32:
          case elfcpp::R_ARM_THM_TLS_DESCSEQ32:
            return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0;
            return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0;
          // These should have been converted to something else above.
          // These should have been converted to something else above.
          case elfcpp::R_ARM_TARGET1:
          case elfcpp::R_ARM_TARGET1:
          case elfcpp::R_ARM_TARGET2:
          case elfcpp::R_ARM_TARGET2:
            gold_unreachable();
            gold_unreachable();
          // Relocations that write full 32 bits and
          // Relocations that write full 32 bits and
          // have alignment of 1.
          // have alignment of 1.
          case elfcpp::R_ARM_ABS32:
          case elfcpp::R_ARM_ABS32:
          case elfcpp::R_ARM_REL32:
          case elfcpp::R_ARM_REL32:
          case elfcpp::R_ARM_SBREL32:
          case elfcpp::R_ARM_SBREL32:
          case elfcpp::R_ARM_GOTOFF32:
          case elfcpp::R_ARM_GOTOFF32:
          case elfcpp::R_ARM_BASE_PREL:
          case elfcpp::R_ARM_BASE_PREL:
          case elfcpp::R_ARM_GOT_BREL:
          case elfcpp::R_ARM_GOT_BREL:
          case elfcpp::R_ARM_BASE_ABS:
          case elfcpp::R_ARM_BASE_ABS:
          case elfcpp::R_ARM_ABS32_NOI:
          case elfcpp::R_ARM_ABS32_NOI:
          case elfcpp::R_ARM_REL32_NOI:
          case elfcpp::R_ARM_REL32_NOI:
          case elfcpp::R_ARM_PLT32_ABS:
          case elfcpp::R_ARM_PLT32_ABS:
          case elfcpp::R_ARM_GOT_ABS:
          case elfcpp::R_ARM_GOT_ABS:
          case elfcpp::R_ARM_GOT_PREL:
          case elfcpp::R_ARM_GOT_PREL:
          case elfcpp::R_ARM_TLS_GD32:
          case elfcpp::R_ARM_TLS_GD32:
          case elfcpp::R_ARM_TLS_LDM32:
          case elfcpp::R_ARM_TLS_LDM32:
          case elfcpp::R_ARM_TLS_LDO32:
          case elfcpp::R_ARM_TLS_LDO32:
          case elfcpp::R_ARM_TLS_IE32:
          case elfcpp::R_ARM_TLS_IE32:
          case elfcpp::R_ARM_TLS_LE32:
          case elfcpp::R_ARM_TLS_LE32:
            return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4_UNALIGNED;
            return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4_UNALIGNED;
          default:
          default:
            // For all other static relocations, return RELOC_SPECIAL.
            // For all other static relocations, return RELOC_SPECIAL.
            return Relocatable_relocs::RELOC_SPECIAL;
            return Relocatable_relocs::RELOC_SPECIAL;
          }
          }
      }
      }
  }
  }
};
};
 
 
// Utilities for manipulating integers of up to 32-bits
 
 
 
namespace utils
 
{
 
  // Sign extend an n-bit unsigned integer stored in an uint32_t into
 
  // an int32_t.  NO_BITS must be between 1 to 32.
 
  template<int no_bits>
 
  static inline int32_t
 
  sign_extend(uint32_t bits)
 
  {
 
    gold_assert(no_bits >= 0 && no_bits <= 32);
 
    if (no_bits == 32)
 
      return static_cast<int32_t>(bits);
 
    uint32_t mask = (~((uint32_t) 0)) >> (32 - no_bits);
 
    bits &= mask;
 
    uint32_t top_bit = 1U << (no_bits - 1);
 
    int32_t as_signed = static_cast<int32_t>(bits);
 
    return (bits & top_bit) ? as_signed + (-top_bit * 2) : as_signed;
 
  }
 
 
 
  // Detects overflow of an NO_BITS integer stored in a uint32_t.
 
  template<int no_bits>
 
  static inline bool
 
  has_overflow(uint32_t bits)
 
  {
 
    gold_assert(no_bits >= 0 && no_bits <= 32);
 
    if (no_bits == 32)
 
      return false;
 
    int32_t max = (1 << (no_bits - 1)) - 1;
 
    int32_t min = -(1 << (no_bits - 1));
 
    int32_t as_signed = static_cast<int32_t>(bits);
 
    return as_signed > max || as_signed < min;
 
  }
 
 
 
  // Detects overflow of an NO_BITS integer stored in a uint32_t when it
 
  // fits in the given number of bits as either a signed or unsigned value.
 
  // For example, has_signed_unsigned_overflow<8> would check
 
  // -128 <= bits <= 255
 
  template<int no_bits>
 
  static inline bool
 
  has_signed_unsigned_overflow(uint32_t bits)
 
  {
 
    gold_assert(no_bits >= 2 && no_bits <= 32);
 
    if (no_bits == 32)
 
      return false;
 
    int32_t max = static_cast<int32_t>((1U << no_bits) - 1);
 
    int32_t min = -(1 << (no_bits - 1));
 
    int32_t as_signed = static_cast<int32_t>(bits);
 
    return as_signed > max || as_signed < min;
 
  }
 
 
 
  // Select bits from A and B using bits in MASK.  For each n in [0..31],
 
  // the n-th bit in the result is chosen from the n-th bits of A and B.
 
  // A zero selects A and a one selects B.
 
  static inline uint32_t
 
  bit_select(uint32_t a, uint32_t b, uint32_t mask)
 
  { return (a & ~mask) | (b & mask); }
 
};
 
 
 
template<bool big_endian>
template<bool big_endian>
class Target_arm : public Sized_target<32, big_endian>
class Target_arm : public Sized_target<32, big_endian>
{
{
 public:
 public:
  typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
  typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
    Reloc_section;
    Reloc_section;
 
 
  // When were are relocating a stub, we pass this as the relocation number.
  // When were are relocating a stub, we pass this as the relocation number.
  static const size_t fake_relnum_for_stubs = static_cast<size_t>(-1);
  static const size_t fake_relnum_for_stubs = static_cast<size_t>(-1);
 
 
  Target_arm()
  Target_arm()
    : Sized_target<32, big_endian>(&arm_info),
    : Sized_target<32, big_endian>(&arm_info),
      got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
      got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
      copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL),
      copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL),
      got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
      got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
      stub_tables_(), stub_factory_(Stub_factory::get_instance()),
      stub_tables_(), stub_factory_(Stub_factory::get_instance()),
      should_force_pic_veneer_(false),
      should_force_pic_veneer_(false),
      arm_input_section_map_(), attributes_section_data_(NULL),
      arm_input_section_map_(), attributes_section_data_(NULL),
      fix_cortex_a8_(false), cortex_a8_relocs_info_()
      fix_cortex_a8_(false), cortex_a8_relocs_info_()
  { }
  { }
 
 
  // Whether we force PCI branch veneers.
  // Whether we force PCI branch veneers.
  bool
  bool
  should_force_pic_veneer() const
  should_force_pic_veneer() const
  { return this->should_force_pic_veneer_; }
  { return this->should_force_pic_veneer_; }
 
 
  // Set PIC veneer flag.
  // Set PIC veneer flag.
  void
  void
  set_should_force_pic_veneer(bool value)
  set_should_force_pic_veneer(bool value)
  { this->should_force_pic_veneer_ = value; }
  { this->should_force_pic_veneer_ = value; }
 
 
  // Whether we use THUMB-2 instructions.
  // Whether we use THUMB-2 instructions.
  bool
  bool
  using_thumb2() const
  using_thumb2() const
  {
  {
    Object_attribute* attr =
    Object_attribute* attr =
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
    int arch = attr->int_value();
    int arch = attr->int_value();
    return arch == elfcpp::TAG_CPU_ARCH_V6T2 || arch >= elfcpp::TAG_CPU_ARCH_V7;
    return arch == elfcpp::TAG_CPU_ARCH_V6T2 || arch >= elfcpp::TAG_CPU_ARCH_V7;
  }
  }
 
 
  // Whether we use THUMB/THUMB-2 instructions only.
  // Whether we use THUMB/THUMB-2 instructions only.
  bool
  bool
  using_thumb_only() const
  using_thumb_only() const
  {
  {
    Object_attribute* attr =
    Object_attribute* attr =
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
 
 
    if (attr->int_value() == elfcpp::TAG_CPU_ARCH_V6_M
    if (attr->int_value() == elfcpp::TAG_CPU_ARCH_V6_M
        || attr->int_value() == elfcpp::TAG_CPU_ARCH_V6S_M)
        || attr->int_value() == elfcpp::TAG_CPU_ARCH_V6S_M)
      return true;
      return true;
    if (attr->int_value() != elfcpp::TAG_CPU_ARCH_V7
    if (attr->int_value() != elfcpp::TAG_CPU_ARCH_V7
        && attr->int_value() != elfcpp::TAG_CPU_ARCH_V7E_M)
        && attr->int_value() != elfcpp::TAG_CPU_ARCH_V7E_M)
      return false;
      return false;
    attr = this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
    attr = this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
    return attr->int_value() == 'M';
    return attr->int_value() == 'M';
  }
  }
 
 
  // Whether we have an NOP instruction.  If not, use mov r0, r0 instead.
  // Whether we have an NOP instruction.  If not, use mov r0, r0 instead.
  bool
  bool
  may_use_arm_nop() const
  may_use_arm_nop() const
  {
  {
    Object_attribute* attr =
    Object_attribute* attr =
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
    int arch = attr->int_value();
    int arch = attr->int_value();
    return (arch == elfcpp::TAG_CPU_ARCH_V6T2
    return (arch == elfcpp::TAG_CPU_ARCH_V6T2
            || arch == elfcpp::TAG_CPU_ARCH_V6K
            || arch == elfcpp::TAG_CPU_ARCH_V6K
            || arch == elfcpp::TAG_CPU_ARCH_V7
            || arch == elfcpp::TAG_CPU_ARCH_V7
            || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
            || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
  }
  }
 
 
  // Whether we have THUMB-2 NOP.W instruction.
  // Whether we have THUMB-2 NOP.W instruction.
  bool
  bool
  may_use_thumb2_nop() const
  may_use_thumb2_nop() const
  {
  {
    Object_attribute* attr =
    Object_attribute* attr =
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
    int arch = attr->int_value();
    int arch = attr->int_value();
    return (arch == elfcpp::TAG_CPU_ARCH_V6T2
    return (arch == elfcpp::TAG_CPU_ARCH_V6T2
            || arch == elfcpp::TAG_CPU_ARCH_V7
            || arch == elfcpp::TAG_CPU_ARCH_V7
            || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
            || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
  }
  }
 
 
  // Whether we have v4T interworking instructions available.
  // Whether we have v4T interworking instructions available.
  bool
  bool
  may_use_v4t_interworking() const
  may_use_v4t_interworking() const
  {
  {
    Object_attribute* attr =
    Object_attribute* attr =
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
    int arch = attr->int_value();
    int arch = attr->int_value();
    return (arch != elfcpp::TAG_CPU_ARCH_PRE_V4
    return (arch != elfcpp::TAG_CPU_ARCH_PRE_V4
            && arch != elfcpp::TAG_CPU_ARCH_V4);
            && arch != elfcpp::TAG_CPU_ARCH_V4);
  }
  }
 
 
  // Whether we have v5T interworking instructions available.
  // Whether we have v5T interworking instructions available.
  bool
  bool
  may_use_v5t_interworking() const
  may_use_v5t_interworking() const
  {
  {
    Object_attribute* attr =
    Object_attribute* attr =
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
    int arch = attr->int_value();
    int arch = attr->int_value();
    if (parameters->options().fix_arm1176())
    if (parameters->options().fix_arm1176())
      return (arch == elfcpp::TAG_CPU_ARCH_V6T2
      return (arch == elfcpp::TAG_CPU_ARCH_V6T2
              || arch == elfcpp::TAG_CPU_ARCH_V7
              || arch == elfcpp::TAG_CPU_ARCH_V7
              || arch == elfcpp::TAG_CPU_ARCH_V6_M
              || arch == elfcpp::TAG_CPU_ARCH_V6_M
              || arch == elfcpp::TAG_CPU_ARCH_V6S_M
              || arch == elfcpp::TAG_CPU_ARCH_V6S_M
              || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
              || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
    else
    else
      return (arch != elfcpp::TAG_CPU_ARCH_PRE_V4
      return (arch != elfcpp::TAG_CPU_ARCH_PRE_V4
              && arch != elfcpp::TAG_CPU_ARCH_V4
              && arch != elfcpp::TAG_CPU_ARCH_V4
              && arch != elfcpp::TAG_CPU_ARCH_V4T);
              && arch != elfcpp::TAG_CPU_ARCH_V4T);
  }
  }
 
 
  // Process the relocations to determine unreferenced sections for 
  // Process the relocations to determine unreferenced sections for 
  // garbage collection.
  // garbage collection.
  void
  void
  gc_process_relocs(Symbol_table* symtab,
  gc_process_relocs(Symbol_table* symtab,
                    Layout* layout,
                    Layout* layout,
                    Sized_relobj_file<32, big_endian>* object,
                    Sized_relobj_file<32, big_endian>* object,
                    unsigned int data_shndx,
                    unsigned int data_shndx,
                    unsigned int sh_type,
                    unsigned int sh_type,
                    const unsigned char* prelocs,
                    const unsigned char* prelocs,
                    size_t reloc_count,
                    size_t reloc_count,
                    Output_section* output_section,
                    Output_section* output_section,
                    bool needs_special_offset_handling,
                    bool needs_special_offset_handling,
                    size_t local_symbol_count,
                    size_t local_symbol_count,
                    const unsigned char* plocal_symbols);
                    const unsigned char* plocal_symbols);
 
 
  // Scan the relocations to look for symbol adjustments.
  // Scan the relocations to look for symbol adjustments.
  void
  void
  scan_relocs(Symbol_table* symtab,
  scan_relocs(Symbol_table* symtab,
              Layout* layout,
              Layout* layout,
              Sized_relobj_file<32, big_endian>* object,
              Sized_relobj_file<32, big_endian>* object,
              unsigned int data_shndx,
              unsigned int data_shndx,
              unsigned int sh_type,
              unsigned int sh_type,
              const unsigned char* prelocs,
              const unsigned char* prelocs,
              size_t reloc_count,
              size_t reloc_count,
              Output_section* output_section,
              Output_section* output_section,
              bool needs_special_offset_handling,
              bool needs_special_offset_handling,
              size_t local_symbol_count,
              size_t local_symbol_count,
              const unsigned char* plocal_symbols);
              const unsigned char* plocal_symbols);
 
 
  // Finalize the sections.
  // Finalize the sections.
  void
  void
  do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
  do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
 
 
  // Return the value to use for a dynamic symbol which requires special
  // Return the value to use for a dynamic symbol which requires special
  // treatment.
  // treatment.
  uint64_t
  uint64_t
  do_dynsym_value(const Symbol*) const;
  do_dynsym_value(const Symbol*) const;
 
 
  // Relocate a section.
  // Relocate a section.
  void
  void
  relocate_section(const Relocate_info<32, big_endian>*,
  relocate_section(const Relocate_info<32, big_endian>*,
                   unsigned int sh_type,
                   unsigned int sh_type,
                   const unsigned char* prelocs,
                   const unsigned char* prelocs,
                   size_t reloc_count,
                   size_t reloc_count,
                   Output_section* output_section,
                   Output_section* output_section,
                   bool needs_special_offset_handling,
                   bool needs_special_offset_handling,
                   unsigned char* view,
                   unsigned char* view,
                   Arm_address view_address,
                   Arm_address view_address,
                   section_size_type view_size,
                   section_size_type view_size,
                   const Reloc_symbol_changes*);
                   const Reloc_symbol_changes*);
 
 
  // Scan the relocs during a relocatable link.
  // Scan the relocs during a relocatable link.
  void
  void
  scan_relocatable_relocs(Symbol_table* symtab,
  scan_relocatable_relocs(Symbol_table* symtab,
                          Layout* layout,
                          Layout* layout,
                          Sized_relobj_file<32, big_endian>* object,
                          Sized_relobj_file<32, big_endian>* object,
                          unsigned int data_shndx,
                          unsigned int data_shndx,
                          unsigned int sh_type,
                          unsigned int sh_type,
                          const unsigned char* prelocs,
                          const unsigned char* prelocs,
                          size_t reloc_count,
                          size_t reloc_count,
                          Output_section* output_section,
                          Output_section* output_section,
                          bool needs_special_offset_handling,
                          bool needs_special_offset_handling,
                          size_t local_symbol_count,
                          size_t local_symbol_count,
                          const unsigned char* plocal_symbols,
                          const unsigned char* plocal_symbols,
                          Relocatable_relocs*);
                          Relocatable_relocs*);
 
 
  // Relocate a section during a relocatable link.
  // Relocate a section during a relocatable link.
  void
  void
  relocate_for_relocatable(const Relocate_info<32, big_endian>*,
  relocate_for_relocatable(const Relocate_info<32, big_endian>*,
                           unsigned int sh_type,
                           unsigned int sh_type,
                           const unsigned char* prelocs,
                           const unsigned char* prelocs,
                           size_t reloc_count,
                           size_t reloc_count,
                           Output_section* output_section,
                           Output_section* output_section,
                           off_t offset_in_output_section,
                           off_t offset_in_output_section,
                           const Relocatable_relocs*,
                           const Relocatable_relocs*,
                           unsigned char* view,
                           unsigned char* view,
                           Arm_address view_address,
                           Arm_address view_address,
                           section_size_type view_size,
                           section_size_type view_size,
                           unsigned char* reloc_view,
                           unsigned char* reloc_view,
                           section_size_type reloc_view_size);
                           section_size_type reloc_view_size);
 
 
  // Perform target-specific processing in a relocatable link.  This is
  // Perform target-specific processing in a relocatable link.  This is
  // only used if we use the relocation strategy RELOC_SPECIAL.
  // only used if we use the relocation strategy RELOC_SPECIAL.
  void
  void
  relocate_special_relocatable(const Relocate_info<32, big_endian>* relinfo,
  relocate_special_relocatable(const Relocate_info<32, big_endian>* relinfo,
                               unsigned int sh_type,
                               unsigned int sh_type,
                               const unsigned char* preloc_in,
                               const unsigned char* preloc_in,
                               size_t relnum,
                               size_t relnum,
                               Output_section* output_section,
                               Output_section* output_section,
                               off_t offset_in_output_section,
                               off_t offset_in_output_section,
                               unsigned char* view,
                               unsigned char* view,
                               typename elfcpp::Elf_types<32>::Elf_Addr
                               typename elfcpp::Elf_types<32>::Elf_Addr
                                 view_address,
                                 view_address,
                               section_size_type view_size,
                               section_size_type view_size,
                               unsigned char* preloc_out);
                               unsigned char* preloc_out);
 
 
  // Return whether SYM is defined by the ABI.
  // Return whether SYM is defined by the ABI.
  bool
  bool
  do_is_defined_by_abi(Symbol* sym) const
  do_is_defined_by_abi(Symbol* sym) const
  { return strcmp(sym->name(), "__tls_get_addr") == 0; }
  { return strcmp(sym->name(), "__tls_get_addr") == 0; }
 
 
  // Return whether there is a GOT section.
  // Return whether there is a GOT section.
  bool
  bool
  has_got_section() const
  has_got_section() const
  { return this->got_ != NULL; }
  { return this->got_ != NULL; }
 
 
  // Return the size of the GOT section.
  // Return the size of the GOT section.
  section_size_type
  section_size_type
  got_size() const
  got_size() const
  {
  {
    gold_assert(this->got_ != NULL);
    gold_assert(this->got_ != NULL);
    return this->got_->data_size();
    return this->got_->data_size();
  }
  }
 
 
  // Return the number of entries in the GOT.
  // Return the number of entries in the GOT.
  unsigned int
  unsigned int
  got_entry_count() const
  got_entry_count() const
  {
  {
    if (!this->has_got_section())
    if (!this->has_got_section())
      return 0;
      return 0;
    return this->got_size() / 4;
    return this->got_size() / 4;
  }
  }
 
 
  // Return the number of entries in the PLT.
  // Return the number of entries in the PLT.
  unsigned int
  unsigned int
  plt_entry_count() const;
  plt_entry_count() const;
 
 
  // Return the offset of the first non-reserved PLT entry.
  // Return the offset of the first non-reserved PLT entry.
  unsigned int
  unsigned int
  first_plt_entry_offset() const;
  first_plt_entry_offset() const;
 
 
  // Return the size of each PLT entry.
  // Return the size of each PLT entry.
  unsigned int
  unsigned int
  plt_entry_size() const;
  plt_entry_size() const;
 
 
  // Map platform-specific reloc types
  // Map platform-specific reloc types
  static unsigned int
  static unsigned int
  get_real_reloc_type(unsigned int r_type);
  get_real_reloc_type(unsigned int r_type);
 
 
  //
  //
  // Methods to support stub-generations.
  // Methods to support stub-generations.
  //
  //
 
 
  // Return the stub factory
  // Return the stub factory
  const Stub_factory&
  const Stub_factory&
  stub_factory() const
  stub_factory() const
  { return this->stub_factory_; }
  { return this->stub_factory_; }
 
 
  // Make a new Arm_input_section object.
  // Make a new Arm_input_section object.
  Arm_input_section<big_endian>*
  Arm_input_section<big_endian>*
  new_arm_input_section(Relobj*, unsigned int);
  new_arm_input_section(Relobj*, unsigned int);
 
 
  // Find the Arm_input_section object corresponding to the SHNDX-th input
  // Find the Arm_input_section object corresponding to the SHNDX-th input
  // section of RELOBJ.
  // section of RELOBJ.
  Arm_input_section<big_endian>*
  Arm_input_section<big_endian>*
  find_arm_input_section(Relobj* relobj, unsigned int shndx) const;
  find_arm_input_section(Relobj* relobj, unsigned int shndx) const;
 
 
  // Make a new Stub_table
  // Make a new Stub_table
  Stub_table<big_endian>*
  Stub_table<big_endian>*
  new_stub_table(Arm_input_section<big_endian>*);
  new_stub_table(Arm_input_section<big_endian>*);
 
 
  // Scan a section for stub generation.
  // Scan a section for stub generation.
  void
  void
  scan_section_for_stubs(const Relocate_info<32, big_endian>*, unsigned int,
  scan_section_for_stubs(const Relocate_info<32, big_endian>*, unsigned int,
                         const unsigned char*, size_t, Output_section*,
                         const unsigned char*, size_t, Output_section*,
                         bool, const unsigned char*, Arm_address,
                         bool, const unsigned char*, Arm_address,
                         section_size_type);
                         section_size_type);
 
 
  // Relocate a stub. 
  // Relocate a stub. 
  void
  void
  relocate_stub(Stub*, const Relocate_info<32, big_endian>*,
  relocate_stub(Stub*, const Relocate_info<32, big_endian>*,
                Output_section*, unsigned char*, Arm_address,
                Output_section*, unsigned char*, Arm_address,
                section_size_type);
                section_size_type);
 
 
  // Get the default ARM target.
  // Get the default ARM target.
  static Target_arm<big_endian>*
  static Target_arm<big_endian>*
  default_target()
  default_target()
  {
  {
    gold_assert(parameters->target().machine_code() == elfcpp::EM_ARM
    gold_assert(parameters->target().machine_code() == elfcpp::EM_ARM
                && parameters->target().is_big_endian() == big_endian);
                && parameters->target().is_big_endian() == big_endian);
    return static_cast<Target_arm<big_endian>*>(
    return static_cast<Target_arm<big_endian>*>(
             parameters->sized_target<32, big_endian>());
             parameters->sized_target<32, big_endian>());
  }
  }
 
 
  // Whether NAME belongs to a mapping symbol.
  // Whether NAME belongs to a mapping symbol.
  static bool
  static bool
  is_mapping_symbol_name(const char* name)
  is_mapping_symbol_name(const char* name)
  {
  {
    return (name
    return (name
            && name[0] == '$'
            && name[0] == '$'
            && (name[1] == 'a' || name[1] == 't' || name[1] == 'd')
            && (name[1] == 'a' || name[1] == 't' || name[1] == 'd')
            && (name[2] == '\0' || name[2] == '.'));
            && (name[2] == '\0' || name[2] == '.'));
  }
  }
 
 
  // Whether we work around the Cortex-A8 erratum.
  // Whether we work around the Cortex-A8 erratum.
  bool
  bool
  fix_cortex_a8() const
  fix_cortex_a8() const
  { return this->fix_cortex_a8_; }
  { return this->fix_cortex_a8_; }
 
 
  // Whether we merge exidx entries in debuginfo.
  // Whether we merge exidx entries in debuginfo.
  bool
  bool
  merge_exidx_entries() const
  merge_exidx_entries() const
  { return parameters->options().merge_exidx_entries(); }
  { return parameters->options().merge_exidx_entries(); }
 
 
  // Whether we fix R_ARM_V4BX relocation.
  // Whether we fix R_ARM_V4BX relocation.
  // 0 - do not fix
  // 0 - do not fix
  // 1 - replace with MOV instruction (armv4 target)
  // 1 - replace with MOV instruction (armv4 target)
  // 2 - make interworking veneer (>= armv4t targets only)
  // 2 - make interworking veneer (>= armv4t targets only)
  General_options::Fix_v4bx
  General_options::Fix_v4bx
  fix_v4bx() const
  fix_v4bx() const
  { return parameters->options().fix_v4bx(); }
  { return parameters->options().fix_v4bx(); }
 
 
  // Scan a span of THUMB code section for Cortex-A8 erratum.
  // Scan a span of THUMB code section for Cortex-A8 erratum.
  void
  void
  scan_span_for_cortex_a8_erratum(Arm_relobj<big_endian>*, unsigned int,
  scan_span_for_cortex_a8_erratum(Arm_relobj<big_endian>*, unsigned int,
                                  section_size_type, section_size_type,
                                  section_size_type, section_size_type,
                                  const unsigned char*, Arm_address);
                                  const unsigned char*, Arm_address);
 
 
  // Apply Cortex-A8 workaround to a branch.
  // Apply Cortex-A8 workaround to a branch.
  void
  void
  apply_cortex_a8_workaround(const Cortex_a8_stub*, Arm_address,
  apply_cortex_a8_workaround(const Cortex_a8_stub*, Arm_address,
                             unsigned char*, Arm_address);
                             unsigned char*, Arm_address);
 
 
 protected:
 protected:
  // Make an ELF object.
  // Make an ELF object.
  Object*
  Object*
  do_make_elf_object(const std::string&, Input_file*, off_t,
  do_make_elf_object(const std::string&, Input_file*, off_t,
                     const elfcpp::Ehdr<32, big_endian>& ehdr);
                     const elfcpp::Ehdr<32, big_endian>& ehdr);
 
 
  Object*
  Object*
  do_make_elf_object(const std::string&, Input_file*, off_t,
  do_make_elf_object(const std::string&, Input_file*, off_t,
                     const elfcpp::Ehdr<32, !big_endian>&)
                     const elfcpp::Ehdr<32, !big_endian>&)
  { gold_unreachable(); }
  { gold_unreachable(); }
 
 
  Object*
  Object*
  do_make_elf_object(const std::string&, Input_file*, off_t,
  do_make_elf_object(const std::string&, Input_file*, off_t,
                      const elfcpp::Ehdr<64, false>&)
                      const elfcpp::Ehdr<64, false>&)
  { gold_unreachable(); }
  { gold_unreachable(); }
 
 
  Object*
  Object*
  do_make_elf_object(const std::string&, Input_file*, off_t,
  do_make_elf_object(const std::string&, Input_file*, off_t,
                     const elfcpp::Ehdr<64, true>&)
                     const elfcpp::Ehdr<64, true>&)
  { gold_unreachable(); }
  { gold_unreachable(); }
 
 
  // Make an output section.
  // Make an output section.
  Output_section*
  Output_section*
  do_make_output_section(const char* name, elfcpp::Elf_Word type,
  do_make_output_section(const char* name, elfcpp::Elf_Word type,
                         elfcpp::Elf_Xword flags)
                         elfcpp::Elf_Xword flags)
  { return new Arm_output_section<big_endian>(name, type, flags); }
  { return new Arm_output_section<big_endian>(name, type, flags); }
 
 
  void
  void
  do_adjust_elf_header(unsigned char* view, int len) const;
  do_adjust_elf_header(unsigned char* view, int len) const;
 
 
  // We only need to generate stubs, and hence perform relaxation if we are
  // We only need to generate stubs, and hence perform relaxation if we are
  // not doing relocatable linking.
  // not doing relocatable linking.
  bool
  bool
  do_may_relax() const
  do_may_relax() const
  { return !parameters->options().relocatable(); }
  { return !parameters->options().relocatable(); }
 
 
  bool
  bool
  do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
  do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
 
 
  // Determine whether an object attribute tag takes an integer, a
  // Determine whether an object attribute tag takes an integer, a
  // string or both.
  // string or both.
  int
  int
  do_attribute_arg_type(int tag) const;
  do_attribute_arg_type(int tag) const;
 
 
  // Reorder tags during output.
  // Reorder tags during output.
  int
  int
  do_attributes_order(int num) const;
  do_attributes_order(int num) const;
 
 
  // This is called when the target is selected as the default.
  // This is called when the target is selected as the default.
  void
  void
  do_select_as_default_target()
  do_select_as_default_target()
  {
  {
    // No locking is required since there should only be one default target.
    // No locking is required since there should only be one default target.
    // We cannot have both the big-endian and little-endian ARM targets
    // We cannot have both the big-endian and little-endian ARM targets
    // as the default.
    // as the default.
    gold_assert(arm_reloc_property_table == NULL);
    gold_assert(arm_reloc_property_table == NULL);
    arm_reloc_property_table = new Arm_reloc_property_table();
    arm_reloc_property_table = new Arm_reloc_property_table();
  }
  }
 
 
  // Virtual function which is set to return true by a target if
  // Virtual function which is set to return true by a target if
  // it can use relocation types to determine if a function's
  // it can use relocation types to determine if a function's
  // pointer is taken.
  // pointer is taken.
  virtual bool
  virtual bool
  do_can_check_for_function_pointers() const
  do_can_check_for_function_pointers() const
  { return true; }
  { return true; }
 
 
  // Whether a section called SECTION_NAME may have function pointers to
  // Whether a section called SECTION_NAME may have function pointers to
  // sections not eligible for safe ICF folding.
  // sections not eligible for safe ICF folding.
  virtual bool
  virtual bool
  do_section_may_have_icf_unsafe_pointers(const char* section_name) const
  do_section_may_have_icf_unsafe_pointers(const char* section_name) const
  {
  {
    return (!is_prefix_of(".ARM.exidx", section_name)
    return (!is_prefix_of(".ARM.exidx", section_name)
            && !is_prefix_of(".ARM.extab", section_name)
            && !is_prefix_of(".ARM.extab", section_name)
            && Target::do_section_may_have_icf_unsafe_pointers(section_name));
            && Target::do_section_may_have_icf_unsafe_pointers(section_name));
  }
  }
 
 
 private:
 private:
  // The class which scans relocations.
  // The class which scans relocations.
  class Scan
  class Scan
  {
  {
   public:
   public:
    Scan()
    Scan()
      : issued_non_pic_error_(false)
      : issued_non_pic_error_(false)
    { }
    { }
 
 
    static inline int
    static inline int
    get_reference_flags(unsigned int r_type);
    get_reference_flags(unsigned int r_type);
 
 
    inline void
    inline void
    local(Symbol_table* symtab, Layout* layout, Target_arm* target,
    local(Symbol_table* symtab, Layout* layout, Target_arm* target,
          Sized_relobj_file<32, big_endian>* object,
          Sized_relobj_file<32, big_endian>* object,
          unsigned int data_shndx,
          unsigned int data_shndx,
          Output_section* output_section,
          Output_section* output_section,
          const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
          const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
          const elfcpp::Sym<32, big_endian>& lsym);
          const elfcpp::Sym<32, big_endian>& lsym);
 
 
    inline void
    inline void
    global(Symbol_table* symtab, Layout* layout, Target_arm* target,
    global(Symbol_table* symtab, Layout* layout, Target_arm* target,
           Sized_relobj_file<32, big_endian>* object,
           Sized_relobj_file<32, big_endian>* object,
           unsigned int data_shndx,
           unsigned int data_shndx,
           Output_section* output_section,
           Output_section* output_section,
           const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
           const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
           Symbol* gsym);
           Symbol* gsym);
 
 
    inline bool
    inline bool
    local_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
    local_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
                                        Sized_relobj_file<32, big_endian>* ,
                                        Sized_relobj_file<32, big_endian>* ,
                                        unsigned int ,
                                        unsigned int ,
                                        Output_section* ,
                                        Output_section* ,
                                        const elfcpp::Rel<32, big_endian>& ,
                                        const elfcpp::Rel<32, big_endian>& ,
                                        unsigned int ,
                                        unsigned int ,
                                        const elfcpp::Sym<32, big_endian>&);
                                        const elfcpp::Sym<32, big_endian>&);
 
 
    inline bool
    inline bool
    global_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
    global_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
                                         Sized_relobj_file<32, big_endian>* ,
                                         Sized_relobj_file<32, big_endian>* ,
                                         unsigned int ,
                                         unsigned int ,
                                         Output_section* ,
                                         Output_section* ,
                                         const elfcpp::Rel<32, big_endian>& ,
                                         const elfcpp::Rel<32, big_endian>& ,
                                         unsigned int , Symbol*);
                                         unsigned int , Symbol*);
 
 
   private:
   private:
    static void
    static void
    unsupported_reloc_local(Sized_relobj_file<32, big_endian>*,
    unsupported_reloc_local(Sized_relobj_file<32, big_endian>*,
                            unsigned int r_type);
                            unsigned int r_type);
 
 
    static void
    static void
    unsupported_reloc_global(Sized_relobj_file<32, big_endian>*,
    unsupported_reloc_global(Sized_relobj_file<32, big_endian>*,
                             unsigned int r_type, Symbol*);
                             unsigned int r_type, Symbol*);
 
 
    void
    void
    check_non_pic(Relobj*, unsigned int r_type);
    check_non_pic(Relobj*, unsigned int r_type);
 
 
    // Almost identical to Symbol::needs_plt_entry except that it also
    // Almost identical to Symbol::needs_plt_entry except that it also
    // handles STT_ARM_TFUNC.
    // handles STT_ARM_TFUNC.
    static bool
    static bool
    symbol_needs_plt_entry(const Symbol* sym)
    symbol_needs_plt_entry(const Symbol* sym)
    {
    {
      // An undefined symbol from an executable does not need a PLT entry.
      // An undefined symbol from an executable does not need a PLT entry.
      if (sym->is_undefined() && !parameters->options().shared())
      if (sym->is_undefined() && !parameters->options().shared())
        return false;
        return false;
 
 
      return (!parameters->doing_static_link()
      return (!parameters->doing_static_link()
              && (sym->type() == elfcpp::STT_FUNC
              && (sym->type() == elfcpp::STT_FUNC
                  || sym->type() == elfcpp::STT_ARM_TFUNC)
                  || sym->type() == elfcpp::STT_ARM_TFUNC)
              && (sym->is_from_dynobj()
              && (sym->is_from_dynobj()
                  || sym->is_undefined()
                  || sym->is_undefined()
                  || sym->is_preemptible()));
                  || sym->is_preemptible()));
    }
    }
 
 
    inline bool
    inline bool
    possible_function_pointer_reloc(unsigned int r_type);
    possible_function_pointer_reloc(unsigned int r_type);
 
 
    // Whether we have issued an error about a non-PIC compilation.
    // Whether we have issued an error about a non-PIC compilation.
    bool issued_non_pic_error_;
    bool issued_non_pic_error_;
  };
  };
 
 
  // The class which implements relocation.
  // The class which implements relocation.
  class Relocate
  class Relocate
  {
  {
   public:
   public:
    Relocate()
    Relocate()
    { }
    { }
 
 
    ~Relocate()
    ~Relocate()
    { }
    { }
 
 
    // Return whether the static relocation needs to be applied.
    // Return whether the static relocation needs to be applied.
    inline bool
    inline bool
    should_apply_static_reloc(const Sized_symbol<32>* gsym,
    should_apply_static_reloc(const Sized_symbol<32>* gsym,
                              unsigned int r_type,
                              unsigned int r_type,
                              bool is_32bit,
                              bool is_32bit,
                              Output_section* output_section);
                              Output_section* output_section);
 
 
    // Do a relocation.  Return false if the caller should not issue
    // Do a relocation.  Return false if the caller should not issue
    // any warnings about this relocation.
    // any warnings about this relocation.
    inline bool
    inline bool
    relocate(const Relocate_info<32, big_endian>*, Target_arm*,
    relocate(const Relocate_info<32, big_endian>*, Target_arm*,
             Output_section*,  size_t relnum,
             Output_section*,  size_t relnum,
             const elfcpp::Rel<32, big_endian>&,
             const elfcpp::Rel<32, big_endian>&,
             unsigned int r_type, const Sized_symbol<32>*,
             unsigned int r_type, const Sized_symbol<32>*,
             const Symbol_value<32>*,
             const Symbol_value<32>*,
             unsigned char*, Arm_address,
             unsigned char*, Arm_address,
             section_size_type);
             section_size_type);
 
 
    // Return whether we want to pass flag NON_PIC_REF for this
    // Return whether we want to pass flag NON_PIC_REF for this
    // reloc.  This means the relocation type accesses a symbol not via
    // reloc.  This means the relocation type accesses a symbol not via
    // GOT or PLT.
    // GOT or PLT.
    static inline bool
    static inline bool
    reloc_is_non_pic(unsigned int r_type)
    reloc_is_non_pic(unsigned int r_type)
    {
    {
      switch (r_type)
      switch (r_type)
        {
        {
        // These relocation types reference GOT or PLT entries explicitly.
        // These relocation types reference GOT or PLT entries explicitly.
        case elfcpp::R_ARM_GOT_BREL:
        case elfcpp::R_ARM_GOT_BREL:
        case elfcpp::R_ARM_GOT_ABS:
        case elfcpp::R_ARM_GOT_ABS:
        case elfcpp::R_ARM_GOT_PREL:
        case elfcpp::R_ARM_GOT_PREL:
        case elfcpp::R_ARM_GOT_BREL12:
        case elfcpp::R_ARM_GOT_BREL12:
        case elfcpp::R_ARM_PLT32_ABS:
        case elfcpp::R_ARM_PLT32_ABS:
        case elfcpp::R_ARM_TLS_GD32:
        case elfcpp::R_ARM_TLS_GD32:
        case elfcpp::R_ARM_TLS_LDM32:
        case elfcpp::R_ARM_TLS_LDM32:
        case elfcpp::R_ARM_TLS_IE32:
        case elfcpp::R_ARM_TLS_IE32:
        case elfcpp::R_ARM_TLS_IE12GP:
        case elfcpp::R_ARM_TLS_IE12GP:
 
 
        // These relocate types may use PLT entries.
        // These relocate types may use PLT entries.
        case elfcpp::R_ARM_CALL:
        case elfcpp::R_ARM_CALL:
        case elfcpp::R_ARM_THM_CALL:
        case elfcpp::R_ARM_THM_CALL:
        case elfcpp::R_ARM_JUMP24:
        case elfcpp::R_ARM_JUMP24:
        case elfcpp::R_ARM_THM_JUMP24:
        case elfcpp::R_ARM_THM_JUMP24:
        case elfcpp::R_ARM_THM_JUMP19:
        case elfcpp::R_ARM_THM_JUMP19:
        case elfcpp::R_ARM_PLT32:
        case elfcpp::R_ARM_PLT32:
        case elfcpp::R_ARM_THM_XPC22:
        case elfcpp::R_ARM_THM_XPC22:
        case elfcpp::R_ARM_PREL31:
        case elfcpp::R_ARM_PREL31:
        case elfcpp::R_ARM_SBREL31:
        case elfcpp::R_ARM_SBREL31:
          return false;
          return false;
 
 
        default:
        default:
          return true;
          return true;
        }
        }
    }
    }
 
 
   private:
   private:
    // Do a TLS relocation.
    // Do a TLS relocation.
    inline typename Arm_relocate_functions<big_endian>::Status
    inline typename Arm_relocate_functions<big_endian>::Status
    relocate_tls(const Relocate_info<32, big_endian>*, Target_arm<big_endian>*,
    relocate_tls(const Relocate_info<32, big_endian>*, Target_arm<big_endian>*,
                 size_t, const elfcpp::Rel<32, big_endian>&, unsigned int,
                 size_t, const elfcpp::Rel<32, big_endian>&, unsigned int,
                 const Sized_symbol<32>*, const Symbol_value<32>*,
                 const Sized_symbol<32>*, const Symbol_value<32>*,
                 unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
                 unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
                 section_size_type);
                 section_size_type);
 
 
  };
  };
 
 
  // A class which returns the size required for a relocation type,
  // A class which returns the size required for a relocation type,
  // used while scanning relocs during a relocatable link.
  // used while scanning relocs during a relocatable link.
  class Relocatable_size_for_reloc
  class Relocatable_size_for_reloc
  {
  {
   public:
   public:
    unsigned int
    unsigned int
    get_size_for_reloc(unsigned int, Relobj*);
    get_size_for_reloc(unsigned int, Relobj*);
  };
  };
 
 
  // Adjust TLS relocation type based on the options and whether this
  // Adjust TLS relocation type based on the options and whether this
  // is a local symbol.
  // is a local symbol.
  static tls::Tls_optimization
  static tls::Tls_optimization
  optimize_tls_reloc(bool is_final, int r_type);
  optimize_tls_reloc(bool is_final, int r_type);
 
 
  // Get the GOT section, creating it if necessary.
  // Get the GOT section, creating it if necessary.
  Arm_output_data_got<big_endian>*
  Arm_output_data_got<big_endian>*
  got_section(Symbol_table*, Layout*);
  got_section(Symbol_table*, Layout*);
 
 
  // Get the GOT PLT section.
  // Get the GOT PLT section.
  Output_data_space*
  Output_data_space*
  got_plt_section() const
  got_plt_section() const
  {
  {
    gold_assert(this->got_plt_ != NULL);
    gold_assert(this->got_plt_ != NULL);
    return this->got_plt_;
    return this->got_plt_;
  }
  }
 
 
  // Create a PLT entry for a global symbol.
  // Create a PLT entry for a global symbol.
  void
  void
  make_plt_entry(Symbol_table*, Layout*, Symbol*);
  make_plt_entry(Symbol_table*, Layout*, Symbol*);
 
 
  // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
  // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
  void
  void
  define_tls_base_symbol(Symbol_table*, Layout*);
  define_tls_base_symbol(Symbol_table*, Layout*);
 
 
  // Create a GOT entry for the TLS module index.
  // Create a GOT entry for the TLS module index.
  unsigned int
  unsigned int
  got_mod_index_entry(Symbol_table* symtab, Layout* layout,
  got_mod_index_entry(Symbol_table* symtab, Layout* layout,
                      Sized_relobj_file<32, big_endian>* object);
                      Sized_relobj_file<32, big_endian>* object);
 
 
  // Get the PLT section.
  // Get the PLT section.
  const Output_data_plt_arm<big_endian>*
  const Output_data_plt_arm<big_endian>*
  plt_section() const
  plt_section() const
  {
  {
    gold_assert(this->plt_ != NULL);
    gold_assert(this->plt_ != NULL);
    return this->plt_;
    return this->plt_;
  }
  }
 
 
  // Get the dynamic reloc section, creating it if necessary.
  // Get the dynamic reloc section, creating it if necessary.
  Reloc_section*
  Reloc_section*
  rel_dyn_section(Layout*);
  rel_dyn_section(Layout*);
 
 
  // Get the section to use for TLS_DESC relocations.
  // Get the section to use for TLS_DESC relocations.
  Reloc_section*
  Reloc_section*
  rel_tls_desc_section(Layout*) const;
  rel_tls_desc_section(Layout*) const;
 
 
  // Return true if the symbol may need a COPY relocation.
  // Return true if the symbol may need a COPY relocation.
  // References from an executable object to non-function symbols
  // References from an executable object to non-function symbols
  // defined in a dynamic object may need a COPY relocation.
  // defined in a dynamic object may need a COPY relocation.
  bool
  bool
  may_need_copy_reloc(Symbol* gsym)
  may_need_copy_reloc(Symbol* gsym)
  {
  {
    return (gsym->type() != elfcpp::STT_ARM_TFUNC
    return (gsym->type() != elfcpp::STT_ARM_TFUNC
            && gsym->may_need_copy_reloc());
            && gsym->may_need_copy_reloc());
  }
  }
 
 
  // Add a potential copy relocation.
  // Add a potential copy relocation.
  void
  void
  copy_reloc(Symbol_table* symtab, Layout* layout,
  copy_reloc(Symbol_table* symtab, Layout* layout,
             Sized_relobj_file<32, big_endian>* object,
             Sized_relobj_file<32, big_endian>* object,
             unsigned int shndx, Output_section* output_section,
             unsigned int shndx, Output_section* output_section,
             Symbol* sym, const elfcpp::Rel<32, big_endian>& reloc)
             Symbol* sym, const elfcpp::Rel<32, big_endian>& reloc)
  {
  {
    this->copy_relocs_.copy_reloc(symtab, layout,
    this->copy_relocs_.copy_reloc(symtab, layout,
                                  symtab->get_sized_symbol<32>(sym),
                                  symtab->get_sized_symbol<32>(sym),
                                  object, shndx, output_section, reloc,
                                  object, shndx, output_section, reloc,
                                  this->rel_dyn_section(layout));
                                  this->rel_dyn_section(layout));
  }
  }
 
 
  // Whether two EABI versions are compatible.
  // Whether two EABI versions are compatible.
  static bool
  static bool
  are_eabi_versions_compatible(elfcpp::Elf_Word v1, elfcpp::Elf_Word v2);
  are_eabi_versions_compatible(elfcpp::Elf_Word v1, elfcpp::Elf_Word v2);
 
 
  // Merge processor-specific flags from input object and those in the ELF
  // Merge processor-specific flags from input object and those in the ELF
  // header of the output.
  // header of the output.
  void
  void
  merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word);
  merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word);
 
 
  // Get the secondary compatible architecture.
  // Get the secondary compatible architecture.
  static int
  static int
  get_secondary_compatible_arch(const Attributes_section_data*);
  get_secondary_compatible_arch(const Attributes_section_data*);
 
 
  // Set the secondary compatible architecture.
  // Set the secondary compatible architecture.
  static void
  static void
  set_secondary_compatible_arch(Attributes_section_data*, int);
  set_secondary_compatible_arch(Attributes_section_data*, int);
 
 
  static int
  static int
  tag_cpu_arch_combine(const char*, int, int*, int, int);
  tag_cpu_arch_combine(const char*, int, int*, int, int);
 
 
  // Helper to print AEABI enum tag value.
  // Helper to print AEABI enum tag value.
  static std::string
  static std::string
  aeabi_enum_name(unsigned int);
  aeabi_enum_name(unsigned int);
 
 
  // Return string value for TAG_CPU_name.
  // Return string value for TAG_CPU_name.
  static std::string
  static std::string
  tag_cpu_name_value(unsigned int);
  tag_cpu_name_value(unsigned int);
 
 
  // Merge object attributes from input object and those in the output.
  // Merge object attributes from input object and those in the output.
  void
  void
  merge_object_attributes(const char*, const Attributes_section_data*);
  merge_object_attributes(const char*, const Attributes_section_data*);
 
 
  // Helper to get an AEABI object attribute
  // Helper to get an AEABI object attribute
  Object_attribute*
  Object_attribute*
  get_aeabi_object_attribute(int tag) const
  get_aeabi_object_attribute(int tag) const
  {
  {
    Attributes_section_data* pasd = this->attributes_section_data_;
    Attributes_section_data* pasd = this->attributes_section_data_;
    gold_assert(pasd != NULL);
    gold_assert(pasd != NULL);
    Object_attribute* attr =
    Object_attribute* attr =
      pasd->get_attribute(Object_attribute::OBJ_ATTR_PROC, tag);
      pasd->get_attribute(Object_attribute::OBJ_ATTR_PROC, tag);
    gold_assert(attr != NULL);
    gold_assert(attr != NULL);
    return attr;
    return attr;
  }
  }
 
 
  //
  //
  // Methods to support stub-generations.
  // Methods to support stub-generations.
  //
  //
 
 
  // Group input sections for stub generation.
  // Group input sections for stub generation.
  void
  void
  group_sections(Layout*, section_size_type, bool, const Task*);
  group_sections(Layout*, section_size_type, bool, const Task*);
 
 
  // Scan a relocation for stub generation.
  // Scan a relocation for stub generation.
  void
  void
  scan_reloc_for_stub(const Relocate_info<32, big_endian>*, unsigned int,
  scan_reloc_for_stub(const Relocate_info<32, big_endian>*, unsigned int,
                      const Sized_symbol<32>*, unsigned int,
                      const Sized_symbol<32>*, unsigned int,
                      const Symbol_value<32>*,
                      const Symbol_value<32>*,
                      elfcpp::Elf_types<32>::Elf_Swxword, Arm_address);
                      elfcpp::Elf_types<32>::Elf_Swxword, Arm_address);
 
 
  // Scan a relocation section for stub.
  // Scan a relocation section for stub.
  template<int sh_type>
  template<int sh_type>
  void
  void
  scan_reloc_section_for_stubs(
  scan_reloc_section_for_stubs(
      const Relocate_info<32, big_endian>* relinfo,
      const Relocate_info<32, big_endian>* relinfo,
      const unsigned char* prelocs,
      const unsigned char* prelocs,
      size_t reloc_count,
      size_t reloc_count,
      Output_section* output_section,
      Output_section* output_section,
      bool needs_special_offset_handling,
      bool needs_special_offset_handling,
      const unsigned char* view,
      const unsigned char* view,
      elfcpp::Elf_types<32>::Elf_Addr view_address,
      elfcpp::Elf_types<32>::Elf_Addr view_address,
      section_size_type);
      section_size_type);
 
 
  // Fix .ARM.exidx section coverage.
  // Fix .ARM.exidx section coverage.
  void
  void
  fix_exidx_coverage(Layout*, const Input_objects*,
  fix_exidx_coverage(Layout*, const Input_objects*,
                     Arm_output_section<big_endian>*, Symbol_table*,
                     Arm_output_section<big_endian>*, Symbol_table*,
                     const Task*);
                     const Task*);
 
 
  // Functors for STL set.
  // Functors for STL set.
  struct output_section_address_less_than
  struct output_section_address_less_than
  {
  {
    bool
    bool
    operator()(const Output_section* s1, const Output_section* s2) const
    operator()(const Output_section* s1, const Output_section* s2) const
    { return s1->address() < s2->address(); }
    { return s1->address() < s2->address(); }
  };
  };
 
 
  // Information about this specific target which we pass to the
  // Information about this specific target which we pass to the
  // general Target structure.
  // general Target structure.
  static const Target::Target_info arm_info;
  static const Target::Target_info arm_info;
 
 
  // The types of GOT entries needed for this platform.
  // The types of GOT entries needed for this platform.
  // These values are exposed to the ABI in an incremental link.
  // These values are exposed to the ABI in an incremental link.
  // Do not renumber existing values without changing the version
  // Do not renumber existing values without changing the version
  // number of the .gnu_incremental_inputs section.
  // number of the .gnu_incremental_inputs section.
  enum Got_type
  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_NOFFSET = 1,   // GOT entry for negative TLS offset
    GOT_TYPE_TLS_OFFSET = 2,    // GOT entry for positive 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_PAIR = 3,      // GOT entry for TLS module/offset pair
    GOT_TYPE_TLS_DESC = 4       // GOT entry for TLS_DESC pair
    GOT_TYPE_TLS_DESC = 4       // GOT entry for TLS_DESC pair
  };
  };
 
 
  typedef typename std::vector<Stub_table<big_endian>*> Stub_table_list;
  typedef typename std::vector<Stub_table<big_endian>*> Stub_table_list;
 
 
  // Map input section to Arm_input_section.
  // Map input section to Arm_input_section.
  typedef Unordered_map<Section_id,
  typedef Unordered_map<Section_id,
                        Arm_input_section<big_endian>*,
                        Arm_input_section<big_endian>*,
                        Section_id_hash>
                        Section_id_hash>
          Arm_input_section_map;
          Arm_input_section_map;
 
 
  // Map output addresses to relocs for Cortex-A8 erratum.
  // Map output addresses to relocs for Cortex-A8 erratum.
  typedef Unordered_map<Arm_address, const Cortex_a8_reloc*>
  typedef Unordered_map<Arm_address, const Cortex_a8_reloc*>
          Cortex_a8_relocs_info;
          Cortex_a8_relocs_info;
 
 
  // The GOT section.
  // The GOT section.
  Arm_output_data_got<big_endian>* got_;
  Arm_output_data_got<big_endian>* got_;
  // The PLT section.
  // The PLT section.
  Output_data_plt_arm<big_endian>* plt_;
  Output_data_plt_arm<big_endian>* plt_;
  // The GOT PLT section.
  // The GOT PLT section.
  Output_data_space* got_plt_;
  Output_data_space* got_plt_;
  // The dynamic reloc section.
  // The dynamic reloc section.
  Reloc_section* rel_dyn_;
  Reloc_section* rel_dyn_;
  // Relocs saved to avoid a COPY reloc.
  // Relocs saved to avoid a COPY reloc.
  Copy_relocs<elfcpp::SHT_REL, 32, big_endian> copy_relocs_;
  Copy_relocs<elfcpp::SHT_REL, 32, big_endian> copy_relocs_;
  // Space for variables copied with a COPY reloc.
  // Space for variables copied with a COPY reloc.
  Output_data_space* dynbss_;
  Output_data_space* dynbss_;
  // Offset of the GOT entry for the TLS module index.
  // Offset of the GOT entry for the TLS module index.
  unsigned int got_mod_index_offset_;
  unsigned int got_mod_index_offset_;
  // True if the _TLS_MODULE_BASE_ symbol has been defined.
  // True if the _TLS_MODULE_BASE_ symbol has been defined.
  bool tls_base_symbol_defined_;
  bool tls_base_symbol_defined_;
  // Vector of Stub_tables created.
  // Vector of Stub_tables created.
  Stub_table_list stub_tables_;
  Stub_table_list stub_tables_;
  // Stub factory.
  // Stub factory.
  const Stub_factory &stub_factory_;
  const Stub_factory &stub_factory_;
  // Whether we force PIC branch veneers.
  // Whether we force PIC branch veneers.
  bool should_force_pic_veneer_;
  bool should_force_pic_veneer_;
  // Map for locating Arm_input_sections.
  // Map for locating Arm_input_sections.
  Arm_input_section_map arm_input_section_map_;
  Arm_input_section_map arm_input_section_map_;
  // Attributes section data in output.
  // Attributes section data in output.
  Attributes_section_data* attributes_section_data_;
  Attributes_section_data* attributes_section_data_;
  // Whether we want to fix code for Cortex-A8 erratum.
  // Whether we want to fix code for Cortex-A8 erratum.
  bool fix_cortex_a8_;
  bool fix_cortex_a8_;
  // Map addresses to relocs for Cortex-A8 erratum.
  // Map addresses to relocs for Cortex-A8 erratum.
  Cortex_a8_relocs_info cortex_a8_relocs_info_;
  Cortex_a8_relocs_info cortex_a8_relocs_info_;
};
};
 
 
template<bool big_endian>
template<bool big_endian>
const Target::Target_info Target_arm<big_endian>::arm_info =
const Target::Target_info Target_arm<big_endian>::arm_info =
{
{
  32,                   // size
  32,                   // size
  big_endian,           // is_big_endian
  big_endian,           // is_big_endian
  elfcpp::EM_ARM,       // machine_code
  elfcpp::EM_ARM,       // machine_code
  false,                // has_make_symbol
  false,                // has_make_symbol
  false,                // has_resolve
  false,                // has_resolve
  false,                // has_code_fill
  false,                // has_code_fill
  true,                 // is_default_stack_executable
  true,                 // is_default_stack_executable
  false,                // can_icf_inline_merge_sections
  false,                // can_icf_inline_merge_sections
  '\0',                 // wrap_char
  '\0',                 // wrap_char
  "/usr/lib/libc.so.1", // dynamic_linker
  "/usr/lib/libc.so.1", // dynamic_linker
  0x8000,               // default_text_segment_address
  0x8000,               // default_text_segment_address
  0x1000,               // abi_pagesize (overridable by -z max-page-size)
  0x1000,               // abi_pagesize (overridable by -z max-page-size)
  0x1000,               // common_pagesize (overridable by -z common-page-size)
  0x1000,               // common_pagesize (overridable by -z common-page-size)
  elfcpp::SHN_UNDEF,    // small_common_shndx
  elfcpp::SHN_UNDEF,    // small_common_shndx
  elfcpp::SHN_UNDEF,    // large_common_shndx
  elfcpp::SHN_UNDEF,    // large_common_shndx
  0,                     // small_common_section_flags
  0,                     // small_common_section_flags
  0,                     // large_common_section_flags
  0,                     // large_common_section_flags
  ".ARM.attributes",    // attributes_section
  ".ARM.attributes",    // attributes_section
  "aeabi"               // attributes_vendor
  "aeabi"               // attributes_vendor
};
};
 
 
// Arm relocate functions class
// Arm relocate functions class
//
//
 
 
template<bool big_endian>
template<bool big_endian>
class Arm_relocate_functions : public Relocate_functions<32, big_endian>
class Arm_relocate_functions : public Relocate_functions<32, big_endian>
{
{
 public:
 public:
  typedef enum
  typedef enum
  {
  {
    STATUS_OKAY,        // No error during relocation.
    STATUS_OKAY,        // No error during relocation.
    STATUS_OVERFLOW,    // Relocation overflow.
    STATUS_OVERFLOW,    // Relocation overflow.
    STATUS_BAD_RELOC    // Relocation cannot be applied.
    STATUS_BAD_RELOC    // Relocation cannot be applied.
  } Status;
  } Status;
 
 
 private:
 private:
  typedef Relocate_functions<32, big_endian> Base;
  typedef Relocate_functions<32, big_endian> Base;
  typedef Arm_relocate_functions<big_endian> This;
  typedef Arm_relocate_functions<big_endian> This;
 
 
  // Encoding of imm16 argument for movt and movw ARM instructions
  // Encoding of imm16 argument for movt and movw ARM instructions
  // from ARM ARM:
  // from ARM ARM:
  //     
  //     
  //     imm16 := imm4 | imm12
  //     imm16 := imm4 | imm12
  //
  //
  //  f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 
  //  f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 
  // +-------+---------------+-------+-------+-----------------------+
  // +-------+---------------+-------+-------+-----------------------+
  // |       |               |imm4   |       |imm12                  |
  // |       |               |imm4   |       |imm12                  |
  // +-------+---------------+-------+-------+-----------------------+
  // +-------+---------------+-------+-------+-----------------------+
 
 
  // Extract the relocation addend from VAL based on the ARM
  // Extract the relocation addend from VAL based on the ARM
  // instruction encoding described above.
  // instruction encoding described above.
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  extract_arm_movw_movt_addend(
  extract_arm_movw_movt_addend(
      typename elfcpp::Swap<32, big_endian>::Valtype val)
      typename elfcpp::Swap<32, big_endian>::Valtype val)
  {
  {
    // According to the Elf ABI for ARM Architecture the immediate
    // According to the Elf ABI for ARM Architecture the immediate
    // field is sign-extended to form the addend.
    // field is sign-extended to form the addend.
    return utils::sign_extend<16>(((val >> 4) & 0xf000) | (val & 0xfff));
    return Bits<16>::sign_extend32(((val >> 4) & 0xf000) | (val & 0xfff));
  }
  }
 
 
  // Insert X into VAL based on the ARM instruction encoding described
  // Insert X into VAL based on the ARM instruction encoding described
  // above.
  // above.
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  insert_val_arm_movw_movt(
  insert_val_arm_movw_movt(
      typename elfcpp::Swap<32, big_endian>::Valtype val,
      typename elfcpp::Swap<32, big_endian>::Valtype val,
      typename elfcpp::Swap<32, big_endian>::Valtype x)
      typename elfcpp::Swap<32, big_endian>::Valtype x)
  {
  {
    val &= 0xfff0f000;
    val &= 0xfff0f000;
    val |= x & 0x0fff;
    val |= x & 0x0fff;
    val |= (x & 0xf000) << 4;
    val |= (x & 0xf000) << 4;
    return val;
    return val;
  }
  }
 
 
  // Encoding of imm16 argument for movt and movw Thumb2 instructions
  // Encoding of imm16 argument for movt and movw Thumb2 instructions
  // from ARM ARM:
  // from ARM ARM:
  //     
  //     
  //     imm16 := imm4 | i | imm3 | imm8
  //     imm16 := imm4 | i | imm3 | imm8
  //
  //
  //  f e d c b a 9 8 7 6 5 4 3 2 1 0  f e d c b a 9 8 7 6 5 4 3 2 1 0 
  //  f e d c b a 9 8 7 6 5 4 3 2 1 0  f e d c b a 9 8 7 6 5 4 3 2 1 0 
  // +---------+-+-----------+-------++-+-----+-------+---------------+
  // +---------+-+-----------+-------++-+-----+-------+---------------+
  // |         |i|           |imm4   || |imm3 |       |imm8           |
  // |         |i|           |imm4   || |imm3 |       |imm8           |
  // +---------+-+-----------+-------++-+-----+-------+---------------+
  // +---------+-+-----------+-------++-+-----+-------+---------------+
 
 
  // Extract the relocation addend from VAL based on the Thumb2
  // Extract the relocation addend from VAL based on the Thumb2
  // instruction encoding described above.
  // instruction encoding described above.
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  extract_thumb_movw_movt_addend(
  extract_thumb_movw_movt_addend(
      typename elfcpp::Swap<32, big_endian>::Valtype val)
      typename elfcpp::Swap<32, big_endian>::Valtype val)
  {
  {
    // According to the Elf ABI for ARM Architecture the immediate
    // According to the Elf ABI for ARM Architecture the immediate
    // field is sign-extended to form the addend.
    // field is sign-extended to form the addend.
    return utils::sign_extend<16>(((val >> 4) & 0xf000)
    return Bits<16>::sign_extend32(((val >> 4) & 0xf000)
                                  | ((val >> 15) & 0x0800)
                                  | ((val >> 15) & 0x0800)
                                  | ((val >> 4) & 0x0700)
                                   | ((val >> 4) & 0x0700)
                                  | (val & 0x00ff));
                                   | (val & 0x00ff));
  }
  }
 
 
  // Insert X into VAL based on the Thumb2 instruction encoding
  // Insert X into VAL based on the Thumb2 instruction encoding
  // described above.
  // described above.
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  static inline typename elfcpp::Swap<32, big_endian>::Valtype
  insert_val_thumb_movw_movt(
  insert_val_thumb_movw_movt(
      typename elfcpp::Swap<32, big_endian>::Valtype val,
      typename elfcpp::Swap<32, big_endian>::Valtype val,
      typename elfcpp::Swap<32, big_endian>::Valtype x)
      typename elfcpp::Swap<32, big_endian>::Valtype x)
  {
  {
    val &= 0xfbf08f00;
    val &= 0xfbf08f00;
    val |= (x & 0xf000) << 4;
    val |= (x & 0xf000) << 4;
    val |= (x & 0x0800) << 15;
    val |= (x & 0x0800) << 15;
    val |= (x & 0x0700) << 4;
    val |= (x & 0x0700) << 4;
    val |= (x & 0x00ff);
    val |= (x & 0x00ff);
    return val;
    return val;
  }
  }
 
 
  // Calculate the smallest constant Kn for the specified residual.
  // Calculate the smallest constant Kn for the specified residual.
  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
  static uint32_t
  static uint32_t
  calc_grp_kn(typename elfcpp::Swap<32, big_endian>::Valtype residual)
  calc_grp_kn(typename elfcpp::Swap<32, big_endian>::Valtype residual)
  {
  {
    int32_t msb;
    int32_t msb;
 
 
    if (residual == 0)
    if (residual == 0)
      return 0;
      return 0;
    // Determine the most significant bit in the residual and
    // Determine the most significant bit in the residual and
    // align the resulting value to a 2-bit boundary.
    // align the resulting value to a 2-bit boundary.
    for (msb = 30; (msb >= 0) && !(residual & (3 << msb)); msb -= 2)
    for (msb = 30; (msb >= 0) && !(residual & (3 << msb)); msb -= 2)
      ;
      ;
    // The desired shift is now (msb - 6), or zero, whichever
    // The desired shift is now (msb - 6), or zero, whichever
    // is the greater.
    // is the greater.
    return (((msb - 6) < 0) ? 0 : (msb - 6));
    return (((msb - 6) < 0) ? 0 : (msb - 6));
  }
  }
 
 
  // Calculate the final residual for the specified group index.
  // Calculate the final residual for the specified group index.
  // If the passed group index is less than zero, the method will return
  // If the passed group index is less than zero, the method will return
  // the value of the specified residual without any change.
  // the value of the specified residual without any change.
  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
  static typename elfcpp::Swap<32, big_endian>::Valtype
  static typename elfcpp::Swap<32, big_endian>::Valtype
  calc_grp_residual(typename elfcpp::Swap<32, big_endian>::Valtype residual,
  calc_grp_residual(typename elfcpp::Swap<32, big_endian>::Valtype residual,
                    const int group)
                    const int group)
  {
  {
    for (int n = 0; n <= group; n++)
    for (int n = 0; n <= group; n++)
      {
      {
        // Calculate which part of the value to mask.
        // Calculate which part of the value to mask.
        uint32_t shift = calc_grp_kn(residual);
        uint32_t shift = calc_grp_kn(residual);
        // Calculate the residual for the next time around.
        // Calculate the residual for the next time around.
        residual &= ~(residual & (0xff << shift));
        residual &= ~(residual & (0xff << shift));
      }
      }
 
 
    return residual;
    return residual;
  }
  }
 
 
  // Calculate the value of Gn for the specified group index.
  // Calculate the value of Gn for the specified group index.
  // We return it in the form of an encoded constant-and-rotation.
  // We return it in the form of an encoded constant-and-rotation.
  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
  static typename elfcpp::Swap<32, big_endian>::Valtype
  static typename elfcpp::Swap<32, big_endian>::Valtype
  calc_grp_gn(typename elfcpp::Swap<32, big_endian>::Valtype residual,
  calc_grp_gn(typename elfcpp::Swap<32, big_endian>::Valtype residual,
              const int group)
              const int group)
  {
  {
    typename elfcpp::Swap<32, big_endian>::Valtype gn = 0;
    typename elfcpp::Swap<32, big_endian>::Valtype gn = 0;
    uint32_t shift = 0;
    uint32_t shift = 0;
 
 
    for (int n = 0; n <= group; n++)
    for (int n = 0; n <= group; n++)
      {
      {
        // Calculate which part of the value to mask.
        // Calculate which part of the value to mask.
        shift = calc_grp_kn(residual);
        shift = calc_grp_kn(residual);
        // Calculate Gn in 32-bit as well as encoded constant-and-rotation form.
        // Calculate Gn in 32-bit as well as encoded constant-and-rotation form.
        gn = residual & (0xff << shift);
        gn = residual & (0xff << shift);
        // Calculate the residual for the next time around.
        // Calculate the residual for the next time around.
        residual &= ~gn;
        residual &= ~gn;
      }
      }
    // Return Gn in the form of an encoded constant-and-rotation.
    // Return Gn in the form of an encoded constant-and-rotation.
    return ((gn >> shift) | ((gn <= 0xff ? 0 : (32 - shift) / 2) << 8));
    return ((gn >> shift) | ((gn <= 0xff ? 0 : (32 - shift) / 2) << 8));
  }
  }
 
 
 public:
 public:
  // Handle ARM long branches.
  // Handle ARM long branches.
  static typename This::Status
  static typename This::Status
  arm_branch_common(unsigned int, const Relocate_info<32, big_endian>*,
  arm_branch_common(unsigned int, const Relocate_info<32, big_endian>*,
                    unsigned char*, const Sized_symbol<32>*,
                    unsigned char*, const Sized_symbol<32>*,
                    const Arm_relobj<big_endian>*, unsigned int,
                    const Arm_relobj<big_endian>*, unsigned int,
                    const Symbol_value<32>*, Arm_address, Arm_address, bool);
                    const Symbol_value<32>*, Arm_address, Arm_address, bool);
 
 
  // Handle THUMB long branches.
  // Handle THUMB long branches.
  static typename This::Status
  static typename This::Status
  thumb_branch_common(unsigned int, const Relocate_info<32, big_endian>*,
  thumb_branch_common(unsigned int, const Relocate_info<32, big_endian>*,
                      unsigned char*, const Sized_symbol<32>*,
                      unsigned char*, const Sized_symbol<32>*,
                      const Arm_relobj<big_endian>*, unsigned int,
                      const Arm_relobj<big_endian>*, unsigned int,
                      const Symbol_value<32>*, Arm_address, Arm_address, bool);
                      const Symbol_value<32>*, Arm_address, Arm_address, bool);
 
 
 
 
  // Return the branch offset of a 32-bit THUMB branch.
  // Return the branch offset of a 32-bit THUMB branch.
  static inline int32_t
  static inline int32_t
  thumb32_branch_offset(uint16_t upper_insn, uint16_t lower_insn)
  thumb32_branch_offset(uint16_t upper_insn, uint16_t lower_insn)
  {
  {
    // We use the Thumb-2 encoding (backwards compatible with Thumb-1)
    // We use the Thumb-2 encoding (backwards compatible with Thumb-1)
    // involving the J1 and J2 bits.
    // involving the J1 and J2 bits.
    uint32_t s = (upper_insn & (1U << 10)) >> 10;
    uint32_t s = (upper_insn & (1U << 10)) >> 10;
    uint32_t upper = upper_insn & 0x3ffU;
    uint32_t upper = upper_insn & 0x3ffU;
    uint32_t lower = lower_insn & 0x7ffU;
    uint32_t lower = lower_insn & 0x7ffU;
    uint32_t j1 = (lower_insn & (1U << 13)) >> 13;
    uint32_t j1 = (lower_insn & (1U << 13)) >> 13;
    uint32_t j2 = (lower_insn & (1U << 11)) >> 11;
    uint32_t j2 = (lower_insn & (1U << 11)) >> 11;
    uint32_t i1 = j1 ^ s ? 0 : 1;
    uint32_t i1 = j1 ^ s ? 0 : 1;
    uint32_t i2 = j2 ^ s ? 0 : 1;
    uint32_t i2 = j2 ^ s ? 0 : 1;
 
 
    return utils::sign_extend<25>((s << 24) | (i1 << 23) | (i2 << 22)
    return Bits<25>::sign_extend32((s << 24) | (i1 << 23) | (i2 << 22)
                                  | (upper << 12) | (lower << 1));
                                  | (upper << 12) | (lower << 1));
  }
  }
 
 
  // Insert OFFSET to a 32-bit THUMB branch and return the upper instruction.
  // Insert OFFSET to a 32-bit THUMB branch and return the upper instruction.
  // UPPER_INSN is the original upper instruction of the branch.  Caller is
  // UPPER_INSN is the original upper instruction of the branch.  Caller is
  // responsible for overflow checking and BLX offset adjustment.
  // responsible for overflow checking and BLX offset adjustment.
  static inline uint16_t
  static inline uint16_t
  thumb32_branch_upper(uint16_t upper_insn, int32_t offset)
  thumb32_branch_upper(uint16_t upper_insn, int32_t offset)
  {
  {
    uint32_t s = offset < 0 ? 1 : 0;
    uint32_t s = offset < 0 ? 1 : 0;
    uint32_t bits = static_cast<uint32_t>(offset);
    uint32_t bits = static_cast<uint32_t>(offset);
    return (upper_insn & ~0x7ffU) | ((bits >> 12) & 0x3ffU) | (s << 10);
    return (upper_insn & ~0x7ffU) | ((bits >> 12) & 0x3ffU) | (s << 10);
  }
  }
 
 
  // Insert OFFSET to a 32-bit THUMB branch and return the lower instruction.
  // Insert OFFSET to a 32-bit THUMB branch and return the lower instruction.
  // LOWER_INSN is the original lower instruction of the branch.  Caller is
  // LOWER_INSN is the original lower instruction of the branch.  Caller is
  // responsible for overflow checking and BLX offset adjustment.
  // responsible for overflow checking and BLX offset adjustment.
  static inline uint16_t
  static inline uint16_t
  thumb32_branch_lower(uint16_t lower_insn, int32_t offset)
  thumb32_branch_lower(uint16_t lower_insn, int32_t offset)
  {
  {
    uint32_t s = offset < 0 ? 1 : 0;
    uint32_t s = offset < 0 ? 1 : 0;
    uint32_t bits = static_cast<uint32_t>(offset);
    uint32_t bits = static_cast<uint32_t>(offset);
    return ((lower_insn & ~0x2fffU)
    return ((lower_insn & ~0x2fffU)
            | ((((bits >> 23) & 1) ^ !s) << 13)
            | ((((bits >> 23) & 1) ^ !s) << 13)
            | ((((bits >> 22) & 1) ^ !s) << 11)
            | ((((bits >> 22) & 1) ^ !s) << 11)
            | ((bits >> 1) & 0x7ffU));
            | ((bits >> 1) & 0x7ffU));
  }
  }
 
 
  // Return the branch offset of a 32-bit THUMB conditional branch.
  // Return the branch offset of a 32-bit THUMB conditional branch.
  static inline int32_t
  static inline int32_t
  thumb32_cond_branch_offset(uint16_t upper_insn, uint16_t lower_insn)
  thumb32_cond_branch_offset(uint16_t upper_insn, uint16_t lower_insn)
  {
  {
    uint32_t s = (upper_insn & 0x0400U) >> 10;
    uint32_t s = (upper_insn & 0x0400U) >> 10;
    uint32_t j1 = (lower_insn & 0x2000U) >> 13;
    uint32_t j1 = (lower_insn & 0x2000U) >> 13;
    uint32_t j2 = (lower_insn & 0x0800U) >> 11;
    uint32_t j2 = (lower_insn & 0x0800U) >> 11;
    uint32_t lower = (lower_insn & 0x07ffU);
    uint32_t lower = (lower_insn & 0x07ffU);
    uint32_t upper = (s << 8) | (j2 << 7) | (j1 << 6) | (upper_insn & 0x003fU);
    uint32_t upper = (s << 8) | (j2 << 7) | (j1 << 6) | (upper_insn & 0x003fU);
 
 
    return utils::sign_extend<21>((upper << 12) | (lower << 1));
    return Bits<21>::sign_extend32((upper << 12) | (lower << 1));
  }
  }
 
 
  // Insert OFFSET to a 32-bit THUMB conditional branch and return the upper
  // Insert OFFSET to a 32-bit THUMB conditional branch and return the upper
  // instruction.  UPPER_INSN is the original upper instruction of the branch.
  // instruction.  UPPER_INSN is the original upper instruction of the branch.
  // Caller is responsible for overflow checking.
  // Caller is responsible for overflow checking.
  static inline uint16_t
  static inline uint16_t
  thumb32_cond_branch_upper(uint16_t upper_insn, int32_t offset)
  thumb32_cond_branch_upper(uint16_t upper_insn, int32_t offset)
  {
  {
    uint32_t s = offset < 0 ? 1 : 0;
    uint32_t s = offset < 0 ? 1 : 0;
    uint32_t bits = static_cast<uint32_t>(offset);
    uint32_t bits = static_cast<uint32_t>(offset);
    return (upper_insn & 0xfbc0U) | (s << 10) | ((bits & 0x0003f000U) >> 12);
    return (upper_insn & 0xfbc0U) | (s << 10) | ((bits & 0x0003f000U) >> 12);
  }
  }
 
 
  // Insert OFFSET to a 32-bit THUMB conditional branch and return the lower
  // Insert OFFSET to a 32-bit THUMB conditional branch and return the lower
  // instruction.  LOWER_INSN is the original lower instruction of the branch.
  // instruction.  LOWER_INSN is the original lower instruction of the branch.
  // The caller is responsible for overflow checking.
  // The caller is responsible for overflow checking.
  static inline uint16_t
  static inline uint16_t
  thumb32_cond_branch_lower(uint16_t lower_insn, int32_t offset)
  thumb32_cond_branch_lower(uint16_t lower_insn, int32_t offset)
  {
  {
    uint32_t bits = static_cast<uint32_t>(offset);
    uint32_t bits = static_cast<uint32_t>(offset);
    uint32_t j2 = (bits & 0x00080000U) >> 19;
    uint32_t j2 = (bits & 0x00080000U) >> 19;
    uint32_t j1 = (bits & 0x00040000U) >> 18;
    uint32_t j1 = (bits & 0x00040000U) >> 18;
    uint32_t lo = (bits & 0x00000ffeU) >> 1;
    uint32_t lo = (bits & 0x00000ffeU) >> 1;
 
 
    return (lower_insn & 0xd000U) | (j1 << 13) | (j2 << 11) | lo;
    return (lower_insn & 0xd000U) | (j1 << 13) | (j2 << 11) | lo;
  }
  }
 
 
  // R_ARM_ABS8: S + A
  // R_ARM_ABS8: S + A
  static inline typename This::Status
  static inline typename This::Status
  abs8(unsigned char* view,
  abs8(unsigned char* view,
       const Sized_relobj_file<32, big_endian>* object,
       const Sized_relobj_file<32, big_endian>* object,
       const Symbol_value<32>* psymval)
       const Symbol_value<32>* psymval)
  {
  {
    typedef typename elfcpp::Swap<8, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<8, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<8, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<8, big_endian>::readval(wv);
    int32_t addend = utils::sign_extend<8>(val);
    int32_t addend = Bits<8>::sign_extend32(val);
    Arm_address x = psymval->value(object, addend);
    Arm_address x = psymval->value(object, addend);
    val = utils::bit_select(val, x, 0xffU);
    val = Bits<32>::bit_select32(val, x, 0xffU);
    elfcpp::Swap<8, big_endian>::writeval(wv, val);
    elfcpp::Swap<8, big_endian>::writeval(wv, val);
 
 
    // R_ARM_ABS8 permits signed or unsigned results.
    // R_ARM_ABS8 permits signed or unsigned results.
    int signed_x = static_cast<int32_t>(x);
    return (Bits<8>::has_signed_unsigned_overflow32(x)
    return ((signed_x < -128 || signed_x > 255)
 
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_THM_ABS5: S + A
  // R_ARM_THM_ABS5: S + A
  static inline typename This::Status
  static inline typename This::Status
  thm_abs5(unsigned char* view,
  thm_abs5(unsigned char* view,
       const Sized_relobj_file<32, big_endian>* object,
       const Sized_relobj_file<32, big_endian>* object,
       const Symbol_value<32>* psymval)
       const Symbol_value<32>* psymval)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    Reltype addend = (val & 0x7e0U) >> 6;
    Reltype addend = (val & 0x7e0U) >> 6;
    Reltype x = psymval->value(object, addend);
    Reltype x = psymval->value(object, addend);
    val = utils::bit_select(val, x << 6, 0x7e0U);
    val = Bits<32>::bit_select32(val, x << 6, 0x7e0U);
    elfcpp::Swap<16, big_endian>::writeval(wv, val);
    elfcpp::Swap<16, big_endian>::writeval(wv, val);
 
    return (Bits<5>::has_overflow32(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_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_ABS12: S + A
  // R_ARM_ABS12: S + A
  static inline typename This::Status
  static inline typename This::Status
  abs12(unsigned char* view,
  abs12(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval)
        const Symbol_value<32>* psymval)
  {
  {
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Reltype addend = val & 0x0fffU;
    Reltype addend = val & 0x0fffU;
    Reltype x = psymval->value(object, addend);
    Reltype x = psymval->value(object, addend);
    val = utils::bit_select(val, x, 0x0fffU);
    val = Bits<32>::bit_select32(val, x, 0x0fffU);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    return (utils::has_overflow<12>(x)
    return (Bits<12>::has_overflow32(x)
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_ABS16: S + A
  // R_ARM_ABS16: S + A
  static inline typename This::Status
  static inline typename This::Status
  abs16(unsigned char* view,
  abs16(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval)
        const Symbol_value<32>* psymval)
  {
  {
    typedef typename elfcpp::Swap_unaligned<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap_unaligned<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype val = elfcpp::Swap_unaligned<16, big_endian>::readval(view);
    Valtype val = elfcpp::Swap_unaligned<16, big_endian>::readval(view);
    int32_t addend = utils::sign_extend<16>(val);
    int32_t addend = Bits<16>::sign_extend32(val);
    Arm_address x = psymval->value(object, addend);
    Arm_address x = psymval->value(object, addend);
    val = utils::bit_select(val, x, 0xffffU);
    val = Bits<32>::bit_select32(val, x, 0xffffU);
    elfcpp::Swap_unaligned<16, big_endian>::writeval(view, val);
    elfcpp::Swap_unaligned<16, big_endian>::writeval(view, val);
 
 
    // R_ARM_ABS16 permits signed or unsigned results.
    // R_ARM_ABS16 permits signed or unsigned results.
    int signed_x = static_cast<int32_t>(x);
    return (Bits<16>::has_signed_unsigned_overflow32(x)
    return ((signed_x < -32768 || signed_x > 65536)
 
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_ABS32: (S + A) | T
  // R_ARM_ABS32: (S + A) | T
  static inline typename This::Status
  static inline typename This::Status
  abs32(unsigned char* view,
  abs32(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval,
        const Symbol_value<32>* psymval,
        Arm_address thumb_bit)
        Arm_address thumb_bit)
  {
  {
    typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
    Valtype addend = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
    Valtype addend = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
    Valtype x = psymval->value(object, addend) | thumb_bit;
    Valtype x = psymval->value(object, addend) | thumb_bit;
    elfcpp::Swap_unaligned<32, big_endian>::writeval(view, x);
    elfcpp::Swap_unaligned<32, big_endian>::writeval(view, x);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_REL32: (S + A) | T - P
  // R_ARM_REL32: (S + A) | T - P
  static inline typename This::Status
  static inline typename This::Status
  rel32(unsigned char* view,
  rel32(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval,
        const Symbol_value<32>* psymval,
        Arm_address address,
        Arm_address address,
        Arm_address thumb_bit)
        Arm_address thumb_bit)
  {
  {
    typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
    Valtype addend = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
    Valtype addend = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
    Valtype x = (psymval->value(object, addend) | thumb_bit) - address;
    Valtype x = (psymval->value(object, addend) | thumb_bit) - address;
    elfcpp::Swap_unaligned<32, big_endian>::writeval(view, x);
    elfcpp::Swap_unaligned<32, big_endian>::writeval(view, x);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_THM_JUMP24: (S + A) | T - P
  // R_ARM_THM_JUMP24: (S + A) | T - P
  static typename This::Status
  static typename This::Status
  thm_jump19(unsigned char* view, const Arm_relobj<big_endian>* object,
  thm_jump19(unsigned char* view, const Arm_relobj<big_endian>* object,
             const Symbol_value<32>* psymval, Arm_address address,
             const Symbol_value<32>* psymval, Arm_address address,
             Arm_address thumb_bit);
             Arm_address thumb_bit);
 
 
  // R_ARM_THM_JUMP6: S + A – P
  // R_ARM_THM_JUMP6: S + A – P
  static inline typename This::Status
  static inline typename This::Status
  thm_jump6(unsigned char* view,
  thm_jump6(unsigned char* view,
            const Sized_relobj_file<32, big_endian>* object,
            const Sized_relobj_file<32, big_endian>* object,
            const Symbol_value<32>* psymval,
            const Symbol_value<32>* psymval,
            Arm_address address)
            Arm_address address)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    // bit[9]:bit[7:3]:’0’ (mask: 0x02f8)
    // bit[9]:bit[7:3]:’0’ (mask: 0x02f8)
    Reltype addend = (((val & 0x0200) >> 3) | ((val & 0x00f8) >> 2));
    Reltype addend = (((val & 0x0200) >> 3) | ((val & 0x00f8) >> 2));
    Reltype x = (psymval->value(object, addend) - address);
    Reltype x = (psymval->value(object, addend) - address);
    val = (val & 0xfd07) | ((x  & 0x0040) << 3) | ((val & 0x003e) << 2);
    val = (val & 0xfd07) | ((x  & 0x0040) << 3) | ((val & 0x003e) << 2);
    elfcpp::Swap<16, big_endian>::writeval(wv, val);
    elfcpp::Swap<16, big_endian>::writeval(wv, val);
    // CZB does only forward jumps.
    // CZB does only forward jumps.
    return ((x > 0x007e)
    return ((x > 0x007e)
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_THM_JUMP8: S + A – P
  // R_ARM_THM_JUMP8: S + A – P
  static inline typename This::Status
  static inline typename This::Status
  thm_jump8(unsigned char* view,
  thm_jump8(unsigned char* view,
            const Sized_relobj_file<32, big_endian>* object,
            const Sized_relobj_file<32, big_endian>* object,
            const Symbol_value<32>* psymval,
            const Symbol_value<32>* psymval,
            Arm_address address)
            Arm_address address)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    int32_t addend = utils::sign_extend<8>((val & 0x00ff) << 1);
    int32_t addend = Bits<8>::sign_extend32((val & 0x00ff) << 1);
    int32_t x = (psymval->value(object, addend) - address);
    int32_t x = (psymval->value(object, addend) - address);
    elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xff00)
    elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xff00)
                                                | ((x & 0x01fe) >> 1)));
                                                | ((x & 0x01fe) >> 1)));
    // We do a 9-bit overflow check because x is right-shifted by 1 bit.
    // We do a 9-bit overflow check because x is right-shifted by 1 bit.
    return (utils::has_overflow<9>(x)
    return (Bits<9>::has_overflow32(x)
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_THM_JUMP11: S + A – P
  // R_ARM_THM_JUMP11: S + A – P
  static inline typename This::Status
  static inline typename This::Status
  thm_jump11(unsigned char* view,
  thm_jump11(unsigned char* view,
            const Sized_relobj_file<32, big_endian>* object,
            const Sized_relobj_file<32, big_endian>* object,
            const Symbol_value<32>* psymval,
            const Symbol_value<32>* psymval,
            Arm_address address)
            Arm_address address)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
    int32_t addend = utils::sign_extend<11>((val & 0x07ff) << 1);
    int32_t addend = Bits<11>::sign_extend32((val & 0x07ff) << 1);
    int32_t x = (psymval->value(object, addend) - address);
    int32_t x = (psymval->value(object, addend) - address);
    elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xf800)
    elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xf800)
                                                | ((x & 0x0ffe) >> 1)));
                                                | ((x & 0x0ffe) >> 1)));
    // We do a 12-bit overflow check because x is right-shifted by 1 bit.
    // We do a 12-bit overflow check because x is right-shifted by 1 bit.
    return (utils::has_overflow<12>(x)
    return (Bits<12>::has_overflow32(x)
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_BASE_PREL: B(S) + A - P
  // R_ARM_BASE_PREL: B(S) + A - P
  static inline typename This::Status
  static inline typename This::Status
  base_prel(unsigned char* view,
  base_prel(unsigned char* view,
            Arm_address origin,
            Arm_address origin,
            Arm_address address)
            Arm_address address)
  {
  {
    Base::rel32(view, origin - address);
    Base::rel32(view, origin - address);
    return STATUS_OKAY;
    return STATUS_OKAY;
  }
  }
 
 
  // R_ARM_BASE_ABS: B(S) + A
  // R_ARM_BASE_ABS: B(S) + A
  static inline typename This::Status
  static inline typename This::Status
  base_abs(unsigned char* view,
  base_abs(unsigned char* view,
           Arm_address origin)
           Arm_address origin)
  {
  {
    Base::rel32(view, origin);
    Base::rel32(view, origin);
    return STATUS_OKAY;
    return STATUS_OKAY;
  }
  }
 
 
  // R_ARM_GOT_BREL: GOT(S) + A - GOT_ORG
  // R_ARM_GOT_BREL: GOT(S) + A - GOT_ORG
  static inline typename This::Status
  static inline typename This::Status
  got_brel(unsigned char* view,
  got_brel(unsigned char* view,
           typename elfcpp::Swap<32, big_endian>::Valtype got_offset)
           typename elfcpp::Swap<32, big_endian>::Valtype got_offset)
  {
  {
    Base::rel32(view, got_offset);
    Base::rel32(view, got_offset);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_GOT_PREL: GOT(S) + A - P
  // R_ARM_GOT_PREL: GOT(S) + A - P
  static inline typename This::Status
  static inline typename This::Status
  got_prel(unsigned char* view,
  got_prel(unsigned char* view,
           Arm_address got_entry,
           Arm_address got_entry,
           Arm_address address)
           Arm_address address)
  {
  {
    Base::rel32(view, got_entry - address);
    Base::rel32(view, got_entry - address);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_PREL: (S + A) | T - P
  // R_ARM_PREL: (S + A) | T - P
  static inline typename This::Status
  static inline typename This::Status
  prel31(unsigned char* view,
  prel31(unsigned char* view,
         const Sized_relobj_file<32, big_endian>* object,
         const Sized_relobj_file<32, big_endian>* object,
         const Symbol_value<32>* psymval,
         const Symbol_value<32>* psymval,
         Arm_address address,
         Arm_address address,
         Arm_address thumb_bit)
         Arm_address thumb_bit)
  {
  {
    typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
    Valtype val = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
    Valtype val = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
    Valtype addend = utils::sign_extend<31>(val);
    Valtype addend = Bits<31>::sign_extend32(val);
    Valtype x = (psymval->value(object, addend) | thumb_bit) - address;
    Valtype x = (psymval->value(object, addend) | thumb_bit) - address;
    val = utils::bit_select(val, x, 0x7fffffffU);
    val = Bits<32>::bit_select32(val, x, 0x7fffffffU);
    elfcpp::Swap_unaligned<32, big_endian>::writeval(view, val);
    elfcpp::Swap_unaligned<32, big_endian>::writeval(view, val);
    return (utils::has_overflow<31>(x) ?
    return (Bits<31>::has_overflow32(x)
            This::STATUS_OVERFLOW : This::STATUS_OKAY);
            ? This::STATUS_OVERFLOW
 
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_MOVW_ABS_NC: (S + A) | T     (relative address base is )
  // R_ARM_MOVW_ABS_NC: (S + A) | T     (relative address base is )
  // R_ARM_MOVW_PREL_NC: (S + A) | T - P
  // R_ARM_MOVW_PREL_NC: (S + A) | T - P
  // R_ARM_MOVW_BREL_NC: ((S + A) | T) - B(S)
  // R_ARM_MOVW_BREL_NC: ((S + A) | T) - B(S)
  // R_ARM_MOVW_BREL: ((S + A) | T) - B(S)
  // R_ARM_MOVW_BREL: ((S + A) | T) - B(S)
  static inline typename This::Status
  static inline typename This::Status
  movw(unsigned char* view,
  movw(unsigned char* view,
       const Sized_relobj_file<32, big_endian>* object,
       const Sized_relobj_file<32, big_endian>* object,
       const Symbol_value<32>* psymval,
       const Symbol_value<32>* psymval,
       Arm_address relative_address_base,
       Arm_address relative_address_base,
       Arm_address thumb_bit,
       Arm_address thumb_bit,
       bool check_overflow)
       bool check_overflow)
  {
  {
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype addend = This::extract_arm_movw_movt_addend(val);
    Valtype addend = This::extract_arm_movw_movt_addend(val);
    Valtype x = ((psymval->value(object, addend) | thumb_bit)
    Valtype x = ((psymval->value(object, addend) | thumb_bit)
                 - relative_address_base);
                 - relative_address_base);
    val = This::insert_val_arm_movw_movt(val, x);
    val = This::insert_val_arm_movw_movt(val, x);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    return ((check_overflow && utils::has_overflow<16>(x))
    return ((check_overflow && Bits<16>::has_overflow32(x))
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_MOVT_ABS: S + A      (relative address base is 0)
  // R_ARM_MOVT_ABS: S + A      (relative address base is 0)
  // R_ARM_MOVT_PREL: S + A - P
  // R_ARM_MOVT_PREL: S + A - P
  // R_ARM_MOVT_BREL: S + A - B(S)
  // R_ARM_MOVT_BREL: S + A - B(S)
  static inline typename This::Status
  static inline typename This::Status
  movt(unsigned char* view,
  movt(unsigned char* view,
       const Sized_relobj_file<32, big_endian>* object,
       const Sized_relobj_file<32, big_endian>* object,
       const Symbol_value<32>* psymval,
       const Symbol_value<32>* psymval,
       Arm_address relative_address_base)
       Arm_address relative_address_base)
  {
  {
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype addend = This::extract_arm_movw_movt_addend(val);
    Valtype addend = This::extract_arm_movw_movt_addend(val);
    Valtype x = (psymval->value(object, addend) - relative_address_base) >> 16;
    Valtype x = (psymval->value(object, addend) - relative_address_base) >> 16;
    val = This::insert_val_arm_movw_movt(val, x);
    val = This::insert_val_arm_movw_movt(val, x);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    // FIXME: IHI0044D says that we should check for overflow.
    // FIXME: IHI0044D says that we should check for overflow.
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_THM_MOVW_ABS_NC: S + A | T           (relative_address_base is 0)
  // R_ARM_THM_MOVW_ABS_NC: S + A | T           (relative_address_base is 0)
  // R_ARM_THM_MOVW_PREL_NC: (S + A) | T - P
  // R_ARM_THM_MOVW_PREL_NC: (S + A) | T - P
  // R_ARM_THM_MOVW_BREL_NC: ((S + A) | T) - B(S)
  // R_ARM_THM_MOVW_BREL_NC: ((S + A) | T) - B(S)
  // R_ARM_THM_MOVW_BREL: ((S + A) | T) - B(S)
  // R_ARM_THM_MOVW_BREL: ((S + A) | T) - B(S)
  static inline typename This::Status
  static inline typename This::Status
  thm_movw(unsigned char* view,
  thm_movw(unsigned char* view,
           const Sized_relobj_file<32, big_endian>* object,
           const Sized_relobj_file<32, big_endian>* object,
           const Symbol_value<32>* psymval,
           const Symbol_value<32>* psymval,
           Arm_address relative_address_base,
           Arm_address relative_address_base,
           Arm_address thumb_bit,
           Arm_address thumb_bit,
           bool check_overflow)
           bool check_overflow)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Reltype val = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
    Reltype val = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
                  | elfcpp::Swap<16, big_endian>::readval(wv + 1);
                  | elfcpp::Swap<16, big_endian>::readval(wv + 1);
    Reltype addend = This::extract_thumb_movw_movt_addend(val);
    Reltype addend = This::extract_thumb_movw_movt_addend(val);
    Reltype x =
    Reltype x =
      (psymval->value(object, addend) | thumb_bit) - relative_address_base;
      (psymval->value(object, addend) | thumb_bit) - relative_address_base;
    val = This::insert_val_thumb_movw_movt(val, x);
    val = This::insert_val_thumb_movw_movt(val, x);
    elfcpp::Swap<16, big_endian>::writeval(wv, val >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv, val >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
    return ((check_overflow && utils::has_overflow<16>(x))
    return ((check_overflow && Bits<16>::has_overflow32(x))
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_THM_MOVT_ABS: S + A          (relative address base is 0)
  // R_ARM_THM_MOVT_ABS: S + A          (relative address base is 0)
  // R_ARM_THM_MOVT_PREL: S + A - P
  // R_ARM_THM_MOVT_PREL: S + A - P
  // R_ARM_THM_MOVT_BREL: S + A - B(S)
  // R_ARM_THM_MOVT_BREL: S + A - B(S)
  static inline typename This::Status
  static inline typename This::Status
  thm_movt(unsigned char* view,
  thm_movt(unsigned char* view,
           const Sized_relobj_file<32, big_endian>* object,
           const Sized_relobj_file<32, big_endian>* object,
           const Symbol_value<32>* psymval,
           const Symbol_value<32>* psymval,
           Arm_address relative_address_base)
           Arm_address relative_address_base)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Reltype val = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
    Reltype val = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
                  | elfcpp::Swap<16, big_endian>::readval(wv + 1);
                  | elfcpp::Swap<16, big_endian>::readval(wv + 1);
    Reltype addend = This::extract_thumb_movw_movt_addend(val);
    Reltype addend = This::extract_thumb_movw_movt_addend(val);
    Reltype x = (psymval->value(object, addend) - relative_address_base) >> 16;
    Reltype x = (psymval->value(object, addend) - relative_address_base) >> 16;
    val = This::insert_val_thumb_movw_movt(val, x);
    val = This::insert_val_thumb_movw_movt(val, x);
    elfcpp::Swap<16, big_endian>::writeval(wv, val >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv, val >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_THM_ALU_PREL_11_0: ((S + A) | T) - Pa (Thumb32)
  // R_ARM_THM_ALU_PREL_11_0: ((S + A) | T) - Pa (Thumb32)
  static inline typename This::Status
  static inline typename This::Status
  thm_alu11(unsigned char* view,
  thm_alu11(unsigned char* view,
            const Sized_relobj_file<32, big_endian>* object,
            const Sized_relobj_file<32, big_endian>* object,
            const Symbol_value<32>* psymval,
            const Symbol_value<32>* psymval,
            Arm_address address,
            Arm_address address,
            Arm_address thumb_bit)
            Arm_address thumb_bit)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Reltype insn = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
    Reltype insn = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
                   | elfcpp::Swap<16, big_endian>::readval(wv + 1);
                   | elfcpp::Swap<16, big_endian>::readval(wv + 1);
 
 
    //        f e d c b|a|9|8 7 6 5|4|3 2 1 0||f|e d c|b a 9 8|7 6 5 4 3 2 1 0
    //        f e d c b|a|9|8 7 6 5|4|3 2 1 0||f|e d c|b a 9 8|7 6 5 4 3 2 1 0
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
    // ADD{S} 1 1 1 1 0|i|0|1 0 0 0|S|1 1 0 1||0|imm3 |Rd     |imm8
    // ADD{S} 1 1 1 1 0|i|0|1 0 0 0|S|1 1 0 1||0|imm3 |Rd     |imm8
    // ADDW   1 1 1 1 0|i|1|0 0 0 0|0|1 1 0 1||0|imm3 |Rd     |imm8
    // ADDW   1 1 1 1 0|i|1|0 0 0 0|0|1 1 0 1||0|imm3 |Rd     |imm8
    // ADR[+] 1 1 1 1 0|i|1|0 0 0 0|0|1 1 1 1||0|imm3 |Rd     |imm8
    // ADR[+] 1 1 1 1 0|i|1|0 0 0 0|0|1 1 1 1||0|imm3 |Rd     |imm8
    // SUB{S} 1 1 1 1 0|i|0|1 1 0 1|S|1 1 0 1||0|imm3 |Rd     |imm8
    // SUB{S} 1 1 1 1 0|i|0|1 1 0 1|S|1 1 0 1||0|imm3 |Rd     |imm8
    // SUBW   1 1 1 1 0|i|1|0 1 0 1|0|1 1 0 1||0|imm3 |Rd     |imm8
    // SUBW   1 1 1 1 0|i|1|0 1 0 1|0|1 1 0 1||0|imm3 |Rd     |imm8
    // ADR[-] 1 1 1 1 0|i|1|0 1 0 1|0|1 1 1 1||0|imm3 |Rd     |imm8
    // ADR[-] 1 1 1 1 0|i|1|0 1 0 1|0|1 1 1 1||0|imm3 |Rd     |imm8
 
 
    // Determine a sign for the addend.
    // Determine a sign for the addend.
    const int sign = ((insn & 0xf8ef0000) == 0xf0ad0000
    const int sign = ((insn & 0xf8ef0000) == 0xf0ad0000
                      || (insn & 0xf8ef0000) == 0xf0af0000) ? -1 : 1;
                      || (insn & 0xf8ef0000) == 0xf0af0000) ? -1 : 1;
    // Thumb2 addend encoding:
    // Thumb2 addend encoding:
    // imm12 := i | imm3 | imm8
    // imm12 := i | imm3 | imm8
    int32_t addend = (insn & 0xff)
    int32_t addend = (insn & 0xff)
                     | ((insn & 0x00007000) >> 4)
                     | ((insn & 0x00007000) >> 4)
                     | ((insn & 0x04000000) >> 15);
                     | ((insn & 0x04000000) >> 15);
    // Apply a sign to the added.
    // Apply a sign to the added.
    addend *= sign;
    addend *= sign;
 
 
    int32_t x = (psymval->value(object, addend) | thumb_bit)
    int32_t x = (psymval->value(object, addend) | thumb_bit)
                - (address & 0xfffffffc);
                - (address & 0xfffffffc);
    Reltype val = abs(x);
    Reltype val = abs(x);
    // Mask out the value and a distinct part of the ADD/SUB opcode
    // Mask out the value and a distinct part of the ADD/SUB opcode
    // (bits 7:5 of opword).
    // (bits 7:5 of opword).
    insn = (insn & 0xfb0f8f00)
    insn = (insn & 0xfb0f8f00)
           | (val & 0xff)
           | (val & 0xff)
           | ((val & 0x700) << 4)
           | ((val & 0x700) << 4)
           | ((val & 0x800) << 15);
           | ((val & 0x800) << 15);
    // Set the opcode according to whether the value to go in the
    // Set the opcode according to whether the value to go in the
    // place is negative.
    // place is negative.
    if (x < 0)
    if (x < 0)
      insn |= 0x00a00000;
      insn |= 0x00a00000;
 
 
    elfcpp::Swap<16, big_endian>::writeval(wv, insn >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv, insn >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, insn & 0xffff);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, insn & 0xffff);
    return ((val > 0xfff) ?
    return ((val > 0xfff) ?
            This::STATUS_OVERFLOW : This::STATUS_OKAY);
            This::STATUS_OVERFLOW : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_THM_PC8: S + A - Pa (Thumb)
  // R_ARM_THM_PC8: S + A - Pa (Thumb)
  static inline typename This::Status
  static inline typename This::Status
  thm_pc8(unsigned char* view,
  thm_pc8(unsigned char* view,
          const Sized_relobj_file<32, big_endian>* object,
          const Sized_relobj_file<32, big_endian>* object,
          const Symbol_value<32>* psymval,
          const Symbol_value<32>* psymval,
          Arm_address address)
          Arm_address address)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype insn = elfcpp::Swap<16, big_endian>::readval(wv);
    Valtype insn = elfcpp::Swap<16, big_endian>::readval(wv);
    Reltype addend = ((insn & 0x00ff) << 2);
    Reltype addend = ((insn & 0x00ff) << 2);
    int32_t x = (psymval->value(object, addend) - (address & 0xfffffffc));
    int32_t x = (psymval->value(object, addend) - (address & 0xfffffffc));
    Reltype val = abs(x);
    Reltype val = abs(x);
    insn = (insn & 0xff00) | ((val & 0x03fc) >> 2);
    insn = (insn & 0xff00) | ((val & 0x03fc) >> 2);
 
 
    elfcpp::Swap<16, big_endian>::writeval(wv, insn);
    elfcpp::Swap<16, big_endian>::writeval(wv, insn);
    return ((val > 0x03fc)
    return ((val > 0x03fc)
            ? This::STATUS_OVERFLOW
            ? This::STATUS_OVERFLOW
            : This::STATUS_OKAY);
            : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_THM_PC12: S + A - Pa (Thumb32)
  // R_ARM_THM_PC12: S + A - Pa (Thumb32)
  static inline typename This::Status
  static inline typename This::Status
  thm_pc12(unsigned char* view,
  thm_pc12(unsigned char* view,
           const Sized_relobj_file<32, big_endian>* object,
           const Sized_relobj_file<32, big_endian>* object,
           const Symbol_value<32>* psymval,
           const Symbol_value<32>* psymval,
           Arm_address address)
           Arm_address address)
  {
  {
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Reltype insn = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
    Reltype insn = (elfcpp::Swap<16, big_endian>::readval(wv) << 16)
                   | elfcpp::Swap<16, big_endian>::readval(wv + 1);
                   | elfcpp::Swap<16, big_endian>::readval(wv + 1);
    // Determine a sign for the addend (positive if the U bit is 1).
    // Determine a sign for the addend (positive if the U bit is 1).
    const int sign = (insn & 0x00800000) ? 1 : -1;
    const int sign = (insn & 0x00800000) ? 1 : -1;
    int32_t addend = (insn & 0xfff);
    int32_t addend = (insn & 0xfff);
    // Apply a sign to the added.
    // Apply a sign to the added.
    addend *= sign;
    addend *= sign;
 
 
    int32_t x = (psymval->value(object, addend) - (address & 0xfffffffc));
    int32_t x = (psymval->value(object, addend) - (address & 0xfffffffc));
    Reltype val = abs(x);
    Reltype val = abs(x);
    // Mask out and apply the value and the U bit.
    // Mask out and apply the value and the U bit.
    insn = (insn & 0xff7ff000) | (val & 0xfff);
    insn = (insn & 0xff7ff000) | (val & 0xfff);
    // Set the U bit according to whether the value to go in the
    // Set the U bit according to whether the value to go in the
    // place is positive.
    // place is positive.
    if (x >= 0)
    if (x >= 0)
      insn |= 0x00800000;
      insn |= 0x00800000;
 
 
    elfcpp::Swap<16, big_endian>::writeval(wv, insn >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv, insn >> 16);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, insn & 0xffff);
    elfcpp::Swap<16, big_endian>::writeval(wv + 1, insn & 0xffff);
    return ((val > 0xfff) ?
    return ((val > 0xfff) ?
            This::STATUS_OVERFLOW : This::STATUS_OKAY);
            This::STATUS_OVERFLOW : This::STATUS_OKAY);
  }
  }
 
 
  // R_ARM_V4BX
  // R_ARM_V4BX
  static inline typename This::Status
  static inline typename This::Status
  v4bx(const Relocate_info<32, big_endian>* relinfo,
  v4bx(const Relocate_info<32, big_endian>* relinfo,
       unsigned char* view,
       unsigned char* view,
       const Arm_relobj<big_endian>* object,
       const Arm_relobj<big_endian>* object,
       const Arm_address address,
       const Arm_address address,
       const bool is_interworking)
       const bool is_interworking)
  {
  {
 
 
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
    // Ensure that we have a BX instruction.
    // Ensure that we have a BX instruction.
    gold_assert((val & 0x0ffffff0) == 0x012fff10);
    gold_assert((val & 0x0ffffff0) == 0x012fff10);
    const uint32_t reg = (val & 0xf);
    const uint32_t reg = (val & 0xf);
    if (is_interworking && reg != 0xf)
    if (is_interworking && reg != 0xf)
      {
      {
        Stub_table<big_endian>* stub_table =
        Stub_table<big_endian>* stub_table =
            object->stub_table(relinfo->data_shndx);
            object->stub_table(relinfo->data_shndx);
        gold_assert(stub_table != NULL);
        gold_assert(stub_table != NULL);
 
 
        Arm_v4bx_stub* stub = stub_table->find_arm_v4bx_stub(reg);
        Arm_v4bx_stub* stub = stub_table->find_arm_v4bx_stub(reg);
        gold_assert(stub != NULL);
        gold_assert(stub != NULL);
 
 
        int32_t veneer_address =
        int32_t veneer_address =
            stub_table->address() + stub->offset() - 8 - address;
            stub_table->address() + stub->offset() - 8 - address;
        gold_assert((veneer_address <= ARM_MAX_FWD_BRANCH_OFFSET)
        gold_assert((veneer_address <= ARM_MAX_FWD_BRANCH_OFFSET)
                    && (veneer_address >= ARM_MAX_BWD_BRANCH_OFFSET));
                    && (veneer_address >= ARM_MAX_BWD_BRANCH_OFFSET));
        // Replace with a branch to veneer (B <addr>)
        // Replace with a branch to veneer (B <addr>)
        val = (val & 0xf0000000) | 0x0a000000
        val = (val & 0xf0000000) | 0x0a000000
              | ((veneer_address >> 2) & 0x00ffffff);
              | ((veneer_address >> 2) & 0x00ffffff);
      }
      }
    else
    else
      {
      {
        // Preserve Rm (lowest four bits) and the condition code
        // Preserve Rm (lowest four bits) and the condition code
        // (highest four bits). Other bits encode MOV PC,Rm.
        // (highest four bits). Other bits encode MOV PC,Rm.
        val = (val & 0xf000000f) | 0x01a0f000;
        val = (val & 0xf000000f) | 0x01a0f000;
      }
      }
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    elfcpp::Swap<32, big_endian>::writeval(wv, val);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_ALU_PC_G0_NC: ((S + A) | T) - P
  // R_ARM_ALU_PC_G0_NC: ((S + A) | T) - P
  // R_ARM_ALU_PC_G0:    ((S + A) | T) - P
  // R_ARM_ALU_PC_G0:    ((S + A) | T) - P
  // R_ARM_ALU_PC_G1_NC: ((S + A) | T) - P
  // R_ARM_ALU_PC_G1_NC: ((S + A) | T) - P
  // R_ARM_ALU_PC_G1:    ((S + A) | T) - P
  // R_ARM_ALU_PC_G1:    ((S + A) | T) - P
  // R_ARM_ALU_PC_G2:    ((S + A) | T) - P
  // R_ARM_ALU_PC_G2:    ((S + A) | T) - P
  // R_ARM_ALU_SB_G0_NC: ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G0_NC: ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G0:    ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G0:    ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G1_NC: ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G1_NC: ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G1:    ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G1:    ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G2:    ((S + A) | T) - B(S)
  // R_ARM_ALU_SB_G2:    ((S + A) | T) - B(S)
  static inline typename This::Status
  static inline typename This::Status
  arm_grp_alu(unsigned char* view,
  arm_grp_alu(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval,
        const Symbol_value<32>* psymval,
        const int group,
        const int group,
        Arm_address address,
        Arm_address address,
        Arm_address thumb_bit,
        Arm_address thumb_bit,
        bool check_overflow)
        bool check_overflow)
  {
  {
    gold_assert(group >= 0 && group < 3);
    gold_assert(group >= 0 && group < 3);
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
    // ALU group relocations are allowed only for the ADD/SUB instructions.
    // ALU group relocations are allowed only for the ADD/SUB instructions.
    // (0x00800000 - ADD, 0x00400000 - SUB)
    // (0x00800000 - ADD, 0x00400000 - SUB)
    const Valtype opcode = insn & 0x01e00000;
    const Valtype opcode = insn & 0x01e00000;
    if (opcode != 0x00800000 && opcode != 0x00400000)
    if (opcode != 0x00800000 && opcode != 0x00400000)
      return This::STATUS_BAD_RELOC;
      return This::STATUS_BAD_RELOC;
 
 
    // Determine a sign for the addend.
    // Determine a sign for the addend.
    const int sign = (opcode == 0x00800000) ? 1 : -1;
    const int sign = (opcode == 0x00800000) ? 1 : -1;
    // shifter = rotate_imm * 2
    // shifter = rotate_imm * 2
    const uint32_t shifter = (insn & 0xf00) >> 7;
    const uint32_t shifter = (insn & 0xf00) >> 7;
    // Initial addend value.
    // Initial addend value.
    int32_t addend = insn & 0xff;
    int32_t addend = insn & 0xff;
    // Rotate addend right by shifter.
    // Rotate addend right by shifter.
    addend = (addend >> shifter) | (addend << (32 - shifter));
    addend = (addend >> shifter) | (addend << (32 - shifter));
    // Apply a sign to the added.
    // Apply a sign to the added.
    addend *= sign;
    addend *= sign;
 
 
    int32_t x = ((psymval->value(object, addend) | thumb_bit) - address);
    int32_t x = ((psymval->value(object, addend) | thumb_bit) - address);
    Valtype gn = Arm_relocate_functions::calc_grp_gn(abs(x), group);
    Valtype gn = Arm_relocate_functions::calc_grp_gn(abs(x), group);
    // Check for overflow if required
    // Check for overflow if required
    if (check_overflow
    if (check_overflow
        && (Arm_relocate_functions::calc_grp_residual(abs(x), group) != 0))
        && (Arm_relocate_functions::calc_grp_residual(abs(x), group) != 0))
      return This::STATUS_OVERFLOW;
      return This::STATUS_OVERFLOW;
 
 
    // Mask out the value and the ADD/SUB part of the opcode; take care
    // Mask out the value and the ADD/SUB part of the opcode; take care
    // not to destroy the S bit.
    // not to destroy the S bit.
    insn &= 0xff1ff000;
    insn &= 0xff1ff000;
    // Set the opcode according to whether the value to go in the
    // Set the opcode according to whether the value to go in the
    // place is negative.
    // place is negative.
    insn |= ((x < 0) ? 0x00400000 : 0x00800000);
    insn |= ((x < 0) ? 0x00400000 : 0x00800000);
    // Encode the offset (encoded Gn).
    // Encode the offset (encoded Gn).
    insn |= gn;
    insn |= gn;
 
 
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_LDR_PC_G0: S + A - P
  // R_ARM_LDR_PC_G0: S + A - P
  // R_ARM_LDR_PC_G1: S + A - P
  // R_ARM_LDR_PC_G1: S + A - P
  // R_ARM_LDR_PC_G2: S + A - P
  // R_ARM_LDR_PC_G2: S + A - P
  // R_ARM_LDR_SB_G0: S + A - B(S)
  // R_ARM_LDR_SB_G0: S + A - B(S)
  // R_ARM_LDR_SB_G1: S + A - B(S)
  // R_ARM_LDR_SB_G1: S + A - B(S)
  // R_ARM_LDR_SB_G2: S + A - B(S)
  // R_ARM_LDR_SB_G2: S + A - B(S)
  static inline typename This::Status
  static inline typename This::Status
  arm_grp_ldr(unsigned char* view,
  arm_grp_ldr(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval,
        const Symbol_value<32>* psymval,
        const int group,
        const int group,
        Arm_address address)
        Arm_address address)
  {
  {
    gold_assert(group >= 0 && group < 3);
    gold_assert(group >= 0 && group < 3);
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
    const int sign = (insn & 0x00800000) ? 1 : -1;
    const int sign = (insn & 0x00800000) ? 1 : -1;
    int32_t addend = (insn & 0xfff) * sign;
    int32_t addend = (insn & 0xfff) * sign;
    int32_t x = (psymval->value(object, addend) - address);
    int32_t x = (psymval->value(object, addend) - address);
    // Calculate the relevant G(n-1) value to obtain this stage residual.
    // Calculate the relevant G(n-1) value to obtain this stage residual.
    Valtype residual =
    Valtype residual =
        Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
        Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
    if (residual >= 0x1000)
    if (residual >= 0x1000)
      return This::STATUS_OVERFLOW;
      return This::STATUS_OVERFLOW;
 
 
    // Mask out the value and U bit.
    // Mask out the value and U bit.
    insn &= 0xff7ff000;
    insn &= 0xff7ff000;
    // Set the U bit for non-negative values.
    // Set the U bit for non-negative values.
    if (x >= 0)
    if (x >= 0)
      insn |= 0x00800000;
      insn |= 0x00800000;
    insn |= residual;
    insn |= residual;
 
 
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_LDRS_PC_G0: S + A - P
  // R_ARM_LDRS_PC_G0: S + A - P
  // R_ARM_LDRS_PC_G1: S + A - P
  // R_ARM_LDRS_PC_G1: S + A - P
  // R_ARM_LDRS_PC_G2: S + A - P
  // R_ARM_LDRS_PC_G2: S + A - P
  // R_ARM_LDRS_SB_G0: S + A - B(S)
  // R_ARM_LDRS_SB_G0: S + A - B(S)
  // R_ARM_LDRS_SB_G1: S + A - B(S)
  // R_ARM_LDRS_SB_G1: S + A - B(S)
  // R_ARM_LDRS_SB_G2: S + A - B(S)
  // R_ARM_LDRS_SB_G2: S + A - B(S)
  static inline typename This::Status
  static inline typename This::Status
  arm_grp_ldrs(unsigned char* view,
  arm_grp_ldrs(unsigned char* view,
        const Sized_relobj_file<32, big_endian>* object,
        const Sized_relobj_file<32, big_endian>* object,
        const Symbol_value<32>* psymval,
        const Symbol_value<32>* psymval,
        const int group,
        const int group,
        Arm_address address)
        Arm_address address)
  {
  {
    gold_assert(group >= 0 && group < 3);
    gold_assert(group >= 0 && group < 3);
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
    const int sign = (insn & 0x00800000) ? 1 : -1;
    const int sign = (insn & 0x00800000) ? 1 : -1;
    int32_t addend = (((insn & 0xf00) >> 4) + (insn & 0xf)) * sign;
    int32_t addend = (((insn & 0xf00) >> 4) + (insn & 0xf)) * sign;
    int32_t x = (psymval->value(object, addend) - address);
    int32_t x = (psymval->value(object, addend) - address);
    // Calculate the relevant G(n-1) value to obtain this stage residual.
    // Calculate the relevant G(n-1) value to obtain this stage residual.
    Valtype residual =
    Valtype residual =
        Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
        Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
   if (residual >= 0x100)
   if (residual >= 0x100)
      return This::STATUS_OVERFLOW;
      return This::STATUS_OVERFLOW;
 
 
    // Mask out the value and U bit.
    // Mask out the value and U bit.
    insn &= 0xff7ff0f0;
    insn &= 0xff7ff0f0;
    // Set the U bit for non-negative values.
    // Set the U bit for non-negative values.
    if (x >= 0)
    if (x >= 0)
      insn |= 0x00800000;
      insn |= 0x00800000;
    insn |= ((residual & 0xf0) << 4) | (residual & 0xf);
    insn |= ((residual & 0xf0) << 4) | (residual & 0xf);
 
 
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
 
 
  // R_ARM_LDC_PC_G0: S + A - P
  // R_ARM_LDC_PC_G0: S + A - P
  // R_ARM_LDC_PC_G1: S + A - P
  // R_ARM_LDC_PC_G1: S + A - P
  // R_ARM_LDC_PC_G2: S + A - P
  // R_ARM_LDC_PC_G2: S + A - P
  // R_ARM_LDC_SB_G0: S + A - B(S)
  // R_ARM_LDC_SB_G0: S + A - B(S)
  // R_ARM_LDC_SB_G1: S + A - B(S)
  // R_ARM_LDC_SB_G1: S + A - B(S)
  // R_ARM_LDC_SB_G2: S + A - B(S)
  // R_ARM_LDC_SB_G2: S + A - B(S)
  static inline typename This::Status
  static inline typename This::Status
  arm_grp_ldc(unsigned char* view,
  arm_grp_ldc(unsigned char* view,
      const Sized_relobj_file<32, big_endian>* object,
      const Sized_relobj_file<32, big_endian>* object,
      const Symbol_value<32>* psymval,
      const Symbol_value<32>* psymval,
      const int group,
      const int group,
      Arm_address address)
      Arm_address address)
  {
  {
    gold_assert(group >= 0 && group < 3);
    gold_assert(group >= 0 && group < 3);
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype* wv = reinterpret_cast<Valtype*>(view);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
    const int sign = (insn & 0x00800000) ? 1 : -1;
    const int sign = (insn & 0x00800000) ? 1 : -1;
    int32_t addend = ((insn & 0xff) << 2) * sign;
    int32_t addend = ((insn & 0xff) << 2) * sign;
    int32_t x = (psymval->value(object, addend) - address);
    int32_t x = (psymval->value(object, addend) - address);
    // Calculate the relevant G(n-1) value to obtain this stage residual.
    // Calculate the relevant G(n-1) value to obtain this stage residual.
    Valtype residual =
    Valtype residual =
      Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
      Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
    if ((residual & 0x3) != 0 || residual >= 0x400)
    if ((residual & 0x3) != 0 || residual >= 0x400)
      return This::STATUS_OVERFLOW;
      return This::STATUS_OVERFLOW;
 
 
    // Mask out the value and U bit.
    // Mask out the value and U bit.
    insn &= 0xff7fff00;
    insn &= 0xff7fff00;
    // Set the U bit for non-negative values.
    // Set the U bit for non-negative values.
    if (x >= 0)
    if (x >= 0)
      insn |= 0x00800000;
      insn |= 0x00800000;
    insn |= (residual >> 2);
    insn |= (residual >> 2);
 
 
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
    return This::STATUS_OKAY;
    return This::STATUS_OKAY;
  }
  }
};
};
 
 
// Relocate ARM long branches.  This handles relocation types
// Relocate ARM long branches.  This handles relocation types
// R_ARM_CALL, R_ARM_JUMP24, R_ARM_PLT32 and R_ARM_XPC25.
// R_ARM_CALL, R_ARM_JUMP24, R_ARM_PLT32 and R_ARM_XPC25.
// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
// undefined and we do not use PLT in this relocation.  In such a case,
// undefined and we do not use PLT in this relocation.  In such a case,
// the branch is converted into an NOP.
// the branch is converted into an NOP.
 
 
template<bool big_endian>
template<bool big_endian>
typename Arm_relocate_functions<big_endian>::Status
typename Arm_relocate_functions<big_endian>::Status
Arm_relocate_functions<big_endian>::arm_branch_common(
Arm_relocate_functions<big_endian>::arm_branch_common(
    unsigned int r_type,
    unsigned int r_type,
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned char* view,
    unsigned char* view,
    const Sized_symbol<32>* gsym,
    const Sized_symbol<32>* gsym,
    const Arm_relobj<big_endian>* object,
    const Arm_relobj<big_endian>* object,
    unsigned int r_sym,
    unsigned int r_sym,
    const Symbol_value<32>* psymval,
    const Symbol_value<32>* psymval,
    Arm_address address,
    Arm_address address,
    Arm_address thumb_bit,
    Arm_address thumb_bit,
    bool is_weakly_undefined_without_plt)
    bool is_weakly_undefined_without_plt)
{
{
  typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
  typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
  Valtype* wv = reinterpret_cast<Valtype*>(view);
  Valtype* wv = reinterpret_cast<Valtype*>(view);
  Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
  Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
  bool insn_is_b = (((val >> 28) & 0xf) <= 0xe)
  bool insn_is_b = (((val >> 28) & 0xf) <= 0xe)
                    && ((val & 0x0f000000UL) == 0x0a000000UL);
                    && ((val & 0x0f000000UL) == 0x0a000000UL);
  bool insn_is_uncond_bl = (val & 0xff000000UL) == 0xeb000000UL;
  bool insn_is_uncond_bl = (val & 0xff000000UL) == 0xeb000000UL;
  bool insn_is_cond_bl = (((val >> 28) & 0xf) < 0xe)
  bool insn_is_cond_bl = (((val >> 28) & 0xf) < 0xe)
                          && ((val & 0x0f000000UL) == 0x0b000000UL);
                          && ((val & 0x0f000000UL) == 0x0b000000UL);
  bool insn_is_blx = (val & 0xfe000000UL) == 0xfa000000UL;
  bool insn_is_blx = (val & 0xfe000000UL) == 0xfa000000UL;
  bool insn_is_any_branch = (val & 0x0e000000UL) == 0x0a000000UL;
  bool insn_is_any_branch = (val & 0x0e000000UL) == 0x0a000000UL;
 
 
  // Check that the instruction is valid.
  // Check that the instruction is valid.
  if (r_type == elfcpp::R_ARM_CALL)
  if (r_type == elfcpp::R_ARM_CALL)
    {
    {
      if (!insn_is_uncond_bl && !insn_is_blx)
      if (!insn_is_uncond_bl && !insn_is_blx)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
    }
    }
  else if (r_type == elfcpp::R_ARM_JUMP24)
  else if (r_type == elfcpp::R_ARM_JUMP24)
    {
    {
      if (!insn_is_b && !insn_is_cond_bl)
      if (!insn_is_b && !insn_is_cond_bl)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
    }
    }
  else if (r_type == elfcpp::R_ARM_PLT32)
  else if (r_type == elfcpp::R_ARM_PLT32)
    {
    {
      if (!insn_is_any_branch)
      if (!insn_is_any_branch)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
    }
    }
  else if (r_type == elfcpp::R_ARM_XPC25)
  else if (r_type == elfcpp::R_ARM_XPC25)
    {
    {
      // FIXME: AAELF document IH0044C does not say much about it other
      // FIXME: AAELF document IH0044C does not say much about it other
      // than it being obsolete.
      // than it being obsolete.
      if (!insn_is_any_branch)
      if (!insn_is_any_branch)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
    }
    }
  else
  else
    gold_unreachable();
    gold_unreachable();
 
 
  // A branch to an undefined weak symbol is turned into a jump to
  // A branch to an undefined weak symbol is turned into a jump to
  // the next instruction unless a PLT entry will be created.
  // the next instruction unless a PLT entry will be created.
  // Do the same for local undefined symbols.
  // Do the same for local undefined symbols.
  // The jump to the next instruction is optimized as a NOP depending
  // The jump to the next instruction is optimized as a NOP depending
  // on the architecture.
  // on the architecture.
  const Target_arm<big_endian>* arm_target =
  const Target_arm<big_endian>* arm_target =
    Target_arm<big_endian>::default_target();
    Target_arm<big_endian>::default_target();
  if (is_weakly_undefined_without_plt)
  if (is_weakly_undefined_without_plt)
    {
    {
      gold_assert(!parameters->options().relocatable());
      gold_assert(!parameters->options().relocatable());
      Valtype cond = val & 0xf0000000U;
      Valtype cond = val & 0xf0000000U;
      if (arm_target->may_use_arm_nop())
      if (arm_target->may_use_arm_nop())
        val = cond | 0x0320f000;
        val = cond | 0x0320f000;
      else
      else
        val = cond | 0x01a00000;        // Using pre-UAL nop: mov r0, r0.
        val = cond | 0x01a00000;        // Using pre-UAL nop: mov r0, r0.
      elfcpp::Swap<32, big_endian>::writeval(wv, val);
      elfcpp::Swap<32, big_endian>::writeval(wv, val);
      return This::STATUS_OKAY;
      return This::STATUS_OKAY;
    }
    }
 
 
  Valtype addend = utils::sign_extend<26>(val << 2);
  Valtype addend = Bits<26>::sign_extend32(val << 2);
  Valtype branch_target = psymval->value(object, addend);
  Valtype branch_target = psymval->value(object, addend);
  int32_t branch_offset = branch_target - address;
  int32_t branch_offset = branch_target - address;
 
 
  // We need a stub if the branch offset is too large or if we need
  // We need a stub if the branch offset is too large or if we need
  // to switch mode.
  // to switch mode.
  bool may_use_blx = arm_target->may_use_v5t_interworking();
  bool may_use_blx = arm_target->may_use_v5t_interworking();
  Reloc_stub* stub = NULL;
  Reloc_stub* stub = NULL;
 
 
  if (!parameters->options().relocatable()
  if (!parameters->options().relocatable()
      && (utils::has_overflow<26>(branch_offset)
      && (Bits<26>::has_overflow32(branch_offset)
          || ((thumb_bit != 0)
          || ((thumb_bit != 0)
              && !(may_use_blx && r_type == elfcpp::R_ARM_CALL))))
              && !(may_use_blx && r_type == elfcpp::R_ARM_CALL))))
    {
    {
      Valtype unadjusted_branch_target = psymval->value(object, 0);
      Valtype unadjusted_branch_target = psymval->value(object, 0);
 
 
      Stub_type stub_type =
      Stub_type stub_type =
        Reloc_stub::stub_type_for_reloc(r_type, address,
        Reloc_stub::stub_type_for_reloc(r_type, address,
                                        unadjusted_branch_target,
                                        unadjusted_branch_target,
                                        (thumb_bit != 0));
                                        (thumb_bit != 0));
      if (stub_type != arm_stub_none)
      if (stub_type != arm_stub_none)
        {
        {
          Stub_table<big_endian>* stub_table =
          Stub_table<big_endian>* stub_table =
            object->stub_table(relinfo->data_shndx);
            object->stub_table(relinfo->data_shndx);
          gold_assert(stub_table != NULL);
          gold_assert(stub_table != NULL);
 
 
          Reloc_stub::Key stub_key(stub_type, gsym, object, r_sym, addend);
          Reloc_stub::Key stub_key(stub_type, gsym, object, r_sym, addend);
          stub = stub_table->find_reloc_stub(stub_key);
          stub = stub_table->find_reloc_stub(stub_key);
          gold_assert(stub != NULL);
          gold_assert(stub != NULL);
          thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
          thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
          branch_target = stub_table->address() + stub->offset() + addend;
          branch_target = stub_table->address() + stub->offset() + addend;
          branch_offset = branch_target - address;
          branch_offset = branch_target - address;
          gold_assert(!utils::has_overflow<26>(branch_offset));
          gold_assert(!Bits<26>::has_overflow32(branch_offset));
        }
        }
    }
    }
 
 
  // At this point, if we still need to switch mode, the instruction
  // At this point, if we still need to switch mode, the instruction
  // must either be a BLX or a BL that can be converted to a BLX.
  // must either be a BLX or a BL that can be converted to a BLX.
  if (thumb_bit != 0)
  if (thumb_bit != 0)
    {
    {
      // Turn BL to BLX.
      // Turn BL to BLX.
      gold_assert(may_use_blx && r_type == elfcpp::R_ARM_CALL);
      gold_assert(may_use_blx && r_type == elfcpp::R_ARM_CALL);
      val = (val & 0xffffff) | 0xfa000000 | ((branch_offset & 2) << 23);
      val = (val & 0xffffff) | 0xfa000000 | ((branch_offset & 2) << 23);
    }
    }
 
 
  val = utils::bit_select(val, (branch_offset >> 2), 0xffffffUL);
  val = Bits<32>::bit_select32(val, (branch_offset >> 2), 0xffffffUL);
  elfcpp::Swap<32, big_endian>::writeval(wv, val);
  elfcpp::Swap<32, big_endian>::writeval(wv, val);
  return (utils::has_overflow<26>(branch_offset)
  return (Bits<26>::has_overflow32(branch_offset)
          ? This::STATUS_OVERFLOW : This::STATUS_OKAY);
          ? This::STATUS_OVERFLOW
 
          : This::STATUS_OKAY);
}
}
 
 
// Relocate THUMB long branches.  This handles relocation types
// Relocate THUMB long branches.  This handles relocation types
// R_ARM_THM_CALL, R_ARM_THM_JUMP24 and R_ARM_THM_XPC22.
// R_ARM_THM_CALL, R_ARM_THM_JUMP24 and R_ARM_THM_XPC22.
// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
// undefined and we do not use PLT in this relocation.  In such a case,
// undefined and we do not use PLT in this relocation.  In such a case,
// the branch is converted into an NOP.
// the branch is converted into an NOP.
 
 
template<bool big_endian>
template<bool big_endian>
typename Arm_relocate_functions<big_endian>::Status
typename Arm_relocate_functions<big_endian>::Status
Arm_relocate_functions<big_endian>::thumb_branch_common(
Arm_relocate_functions<big_endian>::thumb_branch_common(
    unsigned int r_type,
    unsigned int r_type,
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned char* view,
    unsigned char* view,
    const Sized_symbol<32>* gsym,
    const Sized_symbol<32>* gsym,
    const Arm_relobj<big_endian>* object,
    const Arm_relobj<big_endian>* object,
    unsigned int r_sym,
    unsigned int r_sym,
    const Symbol_value<32>* psymval,
    const Symbol_value<32>* psymval,
    Arm_address address,
    Arm_address address,
    Arm_address thumb_bit,
    Arm_address thumb_bit,
    bool is_weakly_undefined_without_plt)
    bool is_weakly_undefined_without_plt)
{
{
  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
  Valtype* wv = reinterpret_cast<Valtype*>(view);
  Valtype* wv = reinterpret_cast<Valtype*>(view);
  uint32_t upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
  uint32_t upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
  uint32_t lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
  uint32_t lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
 
 
  // FIXME: These tests are too loose and do not take THUMB/THUMB-2 difference
  // FIXME: These tests are too loose and do not take THUMB/THUMB-2 difference
  // into account.
  // into account.
  bool is_bl_insn = (lower_insn & 0x1000U) == 0x1000U;
  bool is_bl_insn = (lower_insn & 0x1000U) == 0x1000U;
  bool is_blx_insn = (lower_insn & 0x1000U) == 0x0000U;
  bool is_blx_insn = (lower_insn & 0x1000U) == 0x0000U;
 
 
  // Check that the instruction is valid.
  // Check that the instruction is valid.
  if (r_type == elfcpp::R_ARM_THM_CALL)
  if (r_type == elfcpp::R_ARM_THM_CALL)
    {
    {
      if (!is_bl_insn && !is_blx_insn)
      if (!is_bl_insn && !is_blx_insn)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
    }
    }
  else if (r_type == elfcpp::R_ARM_THM_JUMP24)
  else if (r_type == elfcpp::R_ARM_THM_JUMP24)
    {
    {
      // This cannot be a BLX.
      // This cannot be a BLX.
      if (!is_bl_insn)
      if (!is_bl_insn)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
    }
    }
  else if (r_type == elfcpp::R_ARM_THM_XPC22)
  else if (r_type == elfcpp::R_ARM_THM_XPC22)
    {
    {
      // Check for Thumb to Thumb call.
      // Check for Thumb to Thumb call.
      if (!is_blx_insn)
      if (!is_blx_insn)
        return This::STATUS_BAD_RELOC;
        return This::STATUS_BAD_RELOC;
      if (thumb_bit != 0)
      if (thumb_bit != 0)
        {
        {
          gold_warning(_("%s: Thumb BLX instruction targets "
          gold_warning(_("%s: Thumb BLX instruction targets "
                         "thumb function '%s'."),
                         "thumb function '%s'."),
                         object->name().c_str(),
                         object->name().c_str(),
                         (gsym ? gsym->name() : "(local)"));
                         (gsym ? gsym->name() : "(local)"));
          // Convert BLX to BL.
          // Convert BLX to BL.
          lower_insn |= 0x1000U;
          lower_insn |= 0x1000U;
        }
        }
    }
    }
  else
  else
    gold_unreachable();
    gold_unreachable();
 
 
  // A branch to an undefined weak symbol is turned into a jump to
  // A branch to an undefined weak symbol is turned into a jump to
  // the next instruction unless a PLT entry will be created.
  // the next instruction unless a PLT entry will be created.
  // The jump to the next instruction is optimized as a NOP.W for
  // The jump to the next instruction is optimized as a NOP.W for
  // Thumb-2 enabled architectures.
  // Thumb-2 enabled architectures.
  const Target_arm<big_endian>* arm_target =
  const Target_arm<big_endian>* arm_target =
    Target_arm<big_endian>::default_target();
    Target_arm<big_endian>::default_target();
  if (is_weakly_undefined_without_plt)
  if (is_weakly_undefined_without_plt)
    {
    {
      gold_assert(!parameters->options().relocatable());
      gold_assert(!parameters->options().relocatable());
      if (arm_target->may_use_thumb2_nop())
      if (arm_target->may_use_thumb2_nop())
        {
        {
          elfcpp::Swap<16, big_endian>::writeval(wv, 0xf3af);
          elfcpp::Swap<16, big_endian>::writeval(wv, 0xf3af);
          elfcpp::Swap<16, big_endian>::writeval(wv + 1, 0x8000);
          elfcpp::Swap<16, big_endian>::writeval(wv + 1, 0x8000);
        }
        }
      else
      else
        {
        {
          elfcpp::Swap<16, big_endian>::writeval(wv, 0xe000);
          elfcpp::Swap<16, big_endian>::writeval(wv, 0xe000);
          elfcpp::Swap<16, big_endian>::writeval(wv + 1, 0xbf00);
          elfcpp::Swap<16, big_endian>::writeval(wv + 1, 0xbf00);
        }
        }
      return This::STATUS_OKAY;
      return This::STATUS_OKAY;
    }
    }
 
 
  int32_t addend = This::thumb32_branch_offset(upper_insn, lower_insn);
  int32_t addend = This::thumb32_branch_offset(upper_insn, lower_insn);
  Arm_address branch_target = psymval->value(object, addend);
  Arm_address branch_target = psymval->value(object, addend);
 
 
  // For BLX, bit 1 of target address comes from bit 1 of base address.
  // For BLX, bit 1 of target address comes from bit 1 of base address.
  bool may_use_blx = arm_target->may_use_v5t_interworking();
  bool may_use_blx = arm_target->may_use_v5t_interworking();
  if (thumb_bit == 0 && may_use_blx)
  if (thumb_bit == 0 && may_use_blx)
    branch_target = utils::bit_select(branch_target, address, 0x2);
    branch_target = Bits<32>::bit_select32(branch_target, address, 0x2);
 
 
  int32_t branch_offset = branch_target - address;
  int32_t branch_offset = branch_target - address;
 
 
  // We need a stub if the branch offset is too large or if we need
  // We need a stub if the branch offset is too large or if we need
  // to switch mode.
  // to switch mode.
  bool thumb2 = arm_target->using_thumb2();
  bool thumb2 = arm_target->using_thumb2();
  if (!parameters->options().relocatable()
  if (!parameters->options().relocatable()
      && ((!thumb2 && utils::has_overflow<23>(branch_offset))
      && ((!thumb2 && Bits<23>::has_overflow32(branch_offset))
          || (thumb2 && utils::has_overflow<25>(branch_offset))
          || (thumb2 && Bits<25>::has_overflow32(branch_offset))
          || ((thumb_bit == 0)
          || ((thumb_bit == 0)
              && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
              && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
                  || r_type == elfcpp::R_ARM_THM_JUMP24))))
                  || r_type == elfcpp::R_ARM_THM_JUMP24))))
    {
    {
      Arm_address unadjusted_branch_target = psymval->value(object, 0);
      Arm_address unadjusted_branch_target = psymval->value(object, 0);
 
 
      Stub_type stub_type =
      Stub_type stub_type =
        Reloc_stub::stub_type_for_reloc(r_type, address,
        Reloc_stub::stub_type_for_reloc(r_type, address,
                                        unadjusted_branch_target,
                                        unadjusted_branch_target,
                                        (thumb_bit != 0));
                                        (thumb_bit != 0));
 
 
      if (stub_type != arm_stub_none)
      if (stub_type != arm_stub_none)
        {
        {
          Stub_table<big_endian>* stub_table =
          Stub_table<big_endian>* stub_table =
            object->stub_table(relinfo->data_shndx);
            object->stub_table(relinfo->data_shndx);
          gold_assert(stub_table != NULL);
          gold_assert(stub_table != NULL);
 
 
          Reloc_stub::Key stub_key(stub_type, gsym, object, r_sym, addend);
          Reloc_stub::Key stub_key(stub_type, gsym, object, r_sym, addend);
          Reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
          Reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
          gold_assert(stub != NULL);
          gold_assert(stub != NULL);
          thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
          thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
          branch_target = stub_table->address() + stub->offset() + addend;
          branch_target = stub_table->address() + stub->offset() + addend;
          if (thumb_bit == 0 && may_use_blx)
          if (thumb_bit == 0 && may_use_blx)
            branch_target = utils::bit_select(branch_target, address, 0x2);
            branch_target = Bits<32>::bit_select32(branch_target, address, 0x2);
          branch_offset = branch_target - address;
          branch_offset = branch_target - address;
        }
        }
    }
    }
 
 
  // At this point, if we still need to switch mode, the instruction
  // At this point, if we still need to switch mode, the instruction
  // must either be a BLX or a BL that can be converted to a BLX.
  // must either be a BLX or a BL that can be converted to a BLX.
  if (thumb_bit == 0)
  if (thumb_bit == 0)
    {
    {
      gold_assert(may_use_blx
      gold_assert(may_use_blx
                  && (r_type == elfcpp::R_ARM_THM_CALL
                  && (r_type == elfcpp::R_ARM_THM_CALL
                      || r_type == elfcpp::R_ARM_THM_XPC22));
                      || r_type == elfcpp::R_ARM_THM_XPC22));
      // Make sure this is a BLX.
      // Make sure this is a BLX.
      lower_insn &= ~0x1000U;
      lower_insn &= ~0x1000U;
    }
    }
  else
  else
    {
    {
      // Make sure this is a BL.
      // Make sure this is a BL.
      lower_insn |= 0x1000U;
      lower_insn |= 0x1000U;
    }
    }
 
 
  // For a BLX instruction, make sure that the relocation is rounded up
  // For a BLX instruction, make sure that the relocation is rounded up
  // to a word boundary.  This follows the semantics of the instruction
  // to a word boundary.  This follows the semantics of the instruction
  // which specifies that bit 1 of the target address will come from bit
  // which specifies that bit 1 of the target address will come from bit
  // 1 of the base address.
  // 1 of the base address.
  if ((lower_insn & 0x5000U) == 0x4000U)
  if ((lower_insn & 0x5000U) == 0x4000U)
    gold_assert((branch_offset & 3) == 0);
    gold_assert((branch_offset & 3) == 0);
 
 
  // Put BRANCH_OFFSET back into the insn.  Assumes two's complement.
  // Put BRANCH_OFFSET back into the insn.  Assumes two's complement.
  // We use the Thumb-2 encoding, which is safe even if dealing with
  // We use the Thumb-2 encoding, which is safe even if dealing with
  // a Thumb-1 instruction by virtue of our overflow check above.  */
  // a Thumb-1 instruction by virtue of our overflow check above.  */
  upper_insn = This::thumb32_branch_upper(upper_insn, branch_offset);
  upper_insn = This::thumb32_branch_upper(upper_insn, branch_offset);
  lower_insn = This::thumb32_branch_lower(lower_insn, branch_offset);
  lower_insn = This::thumb32_branch_lower(lower_insn, branch_offset);
 
 
  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
 
 
  gold_assert(!utils::has_overflow<25>(branch_offset));
  gold_assert(!Bits<25>::has_overflow32(branch_offset));
 
 
  return ((thumb2
  return ((thumb2
           ? utils::has_overflow<25>(branch_offset)
           ? Bits<25>::has_overflow32(branch_offset)
           : utils::has_overflow<23>(branch_offset))
           : Bits<23>::has_overflow32(branch_offset))
          ? This::STATUS_OVERFLOW
          ? This::STATUS_OVERFLOW
          : This::STATUS_OKAY);
          : This::STATUS_OKAY);
}
}
 
 
// Relocate THUMB-2 long conditional branches.
// Relocate THUMB-2 long conditional branches.
// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
// undefined and we do not use PLT in this relocation.  In such a case,
// undefined and we do not use PLT in this relocation.  In such a case,
// the branch is converted into an NOP.
// the branch is converted into an NOP.
 
 
template<bool big_endian>
template<bool big_endian>
typename Arm_relocate_functions<big_endian>::Status
typename Arm_relocate_functions<big_endian>::Status
Arm_relocate_functions<big_endian>::thm_jump19(
Arm_relocate_functions<big_endian>::thm_jump19(
    unsigned char* view,
    unsigned char* view,
    const Arm_relobj<big_endian>* object,
    const Arm_relobj<big_endian>* object,
    const Symbol_value<32>* psymval,
    const Symbol_value<32>* psymval,
    Arm_address address,
    Arm_address address,
    Arm_address thumb_bit)
    Arm_address thumb_bit)
{
{
  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
  Valtype* wv = reinterpret_cast<Valtype*>(view);
  Valtype* wv = reinterpret_cast<Valtype*>(view);
  uint32_t upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
  uint32_t upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
  uint32_t lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
  uint32_t lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
  int32_t addend = This::thumb32_cond_branch_offset(upper_insn, lower_insn);
  int32_t addend = This::thumb32_cond_branch_offset(upper_insn, lower_insn);
 
 
  Arm_address branch_target = psymval->value(object, addend);
  Arm_address branch_target = psymval->value(object, addend);
  int32_t branch_offset = branch_target - address;
  int32_t branch_offset = branch_target - address;
 
 
  // ??? Should handle interworking?  GCC might someday try to
  // ??? Should handle interworking?  GCC might someday try to
  // use this for tail calls.
  // use this for tail calls.
  // FIXME: We do support thumb entry to PLT yet.
  // FIXME: We do support thumb entry to PLT yet.
  if (thumb_bit == 0)
  if (thumb_bit == 0)
    {
    {
      gold_error(_("conditional branch to PLT in THUMB-2 not supported yet."));
      gold_error(_("conditional branch to PLT in THUMB-2 not supported yet."));
      return This::STATUS_BAD_RELOC;
      return This::STATUS_BAD_RELOC;
    }
    }
 
 
  // Put RELOCATION back into the insn.
  // Put RELOCATION back into the insn.
  upper_insn = This::thumb32_cond_branch_upper(upper_insn, branch_offset);
  upper_insn = This::thumb32_cond_branch_upper(upper_insn, branch_offset);
  lower_insn = This::thumb32_cond_branch_lower(lower_insn, branch_offset);
  lower_insn = This::thumb32_cond_branch_lower(lower_insn, branch_offset);
 
 
  // Put the relocated value back in the object file:
  // Put the relocated value back in the object file:
  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
 
 
  return (utils::has_overflow<21>(branch_offset)
  return (Bits<21>::has_overflow32(branch_offset)
          ? This::STATUS_OVERFLOW
          ? This::STATUS_OVERFLOW
          : This::STATUS_OKAY);
          : This::STATUS_OKAY);
}
}
 
 
// Get the GOT section, creating it if necessary.
// Get the GOT section, creating it if necessary.
 
 
template<bool big_endian>
template<bool big_endian>
Arm_output_data_got<big_endian>*
Arm_output_data_got<big_endian>*
Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
{
{
  if (this->got_ == NULL)
  if (this->got_ == NULL)
    {
    {
      gold_assert(symtab != NULL && layout != NULL);
      gold_assert(symtab != NULL && layout != NULL);
 
 
      this->got_ = new Arm_output_data_got<big_endian>(symtab, layout);
      this->got_ = new Arm_output_data_got<big_endian>(symtab, layout);
 
 
      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
                                      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
                                      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
                                      this->got_, ORDER_DATA, false);
                                      this->got_, ORDER_DATA, false);
 
 
      // The old GNU linker creates a .got.plt section.  We just
      // The old GNU linker creates a .got.plt section.  We just
      // create another set of data in the .got section.  Note that we
      // create another set of data in the .got section.  Note that we
      // always create a PLT if we create a GOT, although the PLT
      // always create a PLT if we create a GOT, although the PLT
      // might be empty.
      // might be empty.
      this->got_plt_ = new Output_data_space(4, "** GOT PLT");
      this->got_plt_ = new Output_data_space(4, "** GOT PLT");
      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
                                      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
                                      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
                                      this->got_plt_, ORDER_DATA, false);
                                      this->got_plt_, ORDER_DATA, false);
 
 
      // The first three entries are reserved.
      // The first three entries are reserved.
      this->got_plt_->set_current_data_size(3 * 4);
      this->got_plt_->set_current_data_size(3 * 4);
 
 
      // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT.
      // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT.
      symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
      symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
                                    Symbol_table::PREDEFINED,
                                    Symbol_table::PREDEFINED,
                                    this->got_plt_,
                                    this->got_plt_,
                                    0, 0, elfcpp::STT_OBJECT,
                                    0, 0, elfcpp::STT_OBJECT,
                                    elfcpp::STB_LOCAL,
                                    elfcpp::STB_LOCAL,
                                    elfcpp::STV_HIDDEN, 0,
                                    elfcpp::STV_HIDDEN, 0,
                                    false, false);
                                    false, false);
    }
    }
  return this->got_;
  return this->got_;
}
}
 
 
// Get the dynamic reloc section, creating it if necessary.
// Get the dynamic reloc section, creating it if necessary.
 
 
template<bool big_endian>
template<bool big_endian>
typename Target_arm<big_endian>::Reloc_section*
typename Target_arm<big_endian>::Reloc_section*
Target_arm<big_endian>::rel_dyn_section(Layout* layout)
Target_arm<big_endian>::rel_dyn_section(Layout* layout)
{
{
  if (this->rel_dyn_ == NULL)
  if (this->rel_dyn_ == NULL)
    {
    {
      gold_assert(layout != NULL);
      gold_assert(layout != NULL);
      this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
      this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
                                      elfcpp::SHF_ALLOC, this->rel_dyn_,
                                      elfcpp::SHF_ALLOC, this->rel_dyn_,
                                      ORDER_DYNAMIC_RELOCS, false);
                                      ORDER_DYNAMIC_RELOCS, false);
    }
    }
  return this->rel_dyn_;
  return this->rel_dyn_;
}
}
 
 
// Insn_template methods.
// Insn_template methods.
 
 
// Return byte size of an instruction template.
// Return byte size of an instruction template.
 
 
size_t
size_t
Insn_template::size() const
Insn_template::size() const
{
{
  switch (this->type())
  switch (this->type())
    {
    {
    case THUMB16_TYPE:
    case THUMB16_TYPE:
    case THUMB16_SPECIAL_TYPE:
    case THUMB16_SPECIAL_TYPE:
      return 2;
      return 2;
    case ARM_TYPE:
    case ARM_TYPE:
    case THUMB32_TYPE:
    case THUMB32_TYPE:
    case DATA_TYPE:
    case DATA_TYPE:
      return 4;
      return 4;
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
}
}
 
 
// Return alignment of an instruction template.
// Return alignment of an instruction template.
 
 
unsigned
unsigned
Insn_template::alignment() const
Insn_template::alignment() const
{
{
  switch (this->type())
  switch (this->type())
    {
    {
    case THUMB16_TYPE:
    case THUMB16_TYPE:
    case THUMB16_SPECIAL_TYPE:
    case THUMB16_SPECIAL_TYPE:
    case THUMB32_TYPE:
    case THUMB32_TYPE:
      return 2;
      return 2;
    case ARM_TYPE:
    case ARM_TYPE:
    case DATA_TYPE:
    case DATA_TYPE:
      return 4;
      return 4;
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
}
}
 
 
// Stub_template methods.
// Stub_template methods.
 
 
Stub_template::Stub_template(
Stub_template::Stub_template(
    Stub_type type, const Insn_template* insns,
    Stub_type type, const Insn_template* insns,
     size_t insn_count)
     size_t insn_count)
  : type_(type), insns_(insns), insn_count_(insn_count), alignment_(1),
  : type_(type), insns_(insns), insn_count_(insn_count), alignment_(1),
    entry_in_thumb_mode_(false), relocs_()
    entry_in_thumb_mode_(false), relocs_()
{
{
  off_t offset = 0;
  off_t offset = 0;
 
 
  // Compute byte size and alignment of stub template.
  // Compute byte size and alignment of stub template.
  for (size_t i = 0; i < insn_count; i++)
  for (size_t i = 0; i < insn_count; i++)
    {
    {
      unsigned insn_alignment = insns[i].alignment();
      unsigned insn_alignment = insns[i].alignment();
      size_t insn_size = insns[i].size();
      size_t insn_size = insns[i].size();
      gold_assert((offset & (insn_alignment - 1)) == 0);
      gold_assert((offset & (insn_alignment - 1)) == 0);
      this->alignment_ = std::max(this->alignment_, insn_alignment);
      this->alignment_ = std::max(this->alignment_, insn_alignment);
      switch (insns[i].type())
      switch (insns[i].type())
        {
        {
        case Insn_template::THUMB16_TYPE:
        case Insn_template::THUMB16_TYPE:
        case Insn_template::THUMB16_SPECIAL_TYPE:
        case Insn_template::THUMB16_SPECIAL_TYPE:
          if (i == 0)
          if (i == 0)
            this->entry_in_thumb_mode_ = true;
            this->entry_in_thumb_mode_ = true;
          break;
          break;
 
 
        case Insn_template::THUMB32_TYPE:
        case Insn_template::THUMB32_TYPE:
          if (insns[i].r_type() != elfcpp::R_ARM_NONE)
          if (insns[i].r_type() != elfcpp::R_ARM_NONE)
            this->relocs_.push_back(Reloc(i, offset));
            this->relocs_.push_back(Reloc(i, offset));
          if (i == 0)
          if (i == 0)
            this->entry_in_thumb_mode_ = true;
            this->entry_in_thumb_mode_ = true;
          break;
          break;
 
 
        case Insn_template::ARM_TYPE:
        case Insn_template::ARM_TYPE:
          // Handle cases where the target is encoded within the
          // Handle cases where the target is encoded within the
          // instruction.
          // instruction.
          if (insns[i].r_type() == elfcpp::R_ARM_JUMP24)
          if (insns[i].r_type() == elfcpp::R_ARM_JUMP24)
            this->relocs_.push_back(Reloc(i, offset));
            this->relocs_.push_back(Reloc(i, offset));
          break;
          break;
 
 
        case Insn_template::DATA_TYPE:
        case Insn_template::DATA_TYPE:
          // Entry point cannot be data.
          // Entry point cannot be data.
          gold_assert(i != 0);
          gold_assert(i != 0);
          this->relocs_.push_back(Reloc(i, offset));
          this->relocs_.push_back(Reloc(i, offset));
          break;
          break;
 
 
        default:
        default:
          gold_unreachable();
          gold_unreachable();
        }
        }
      offset += insn_size;
      offset += insn_size;
    }
    }
  this->size_ = offset;
  this->size_ = offset;
}
}
 
 
// Stub methods.
// Stub methods.
 
 
// Template to implement do_write for a specific target endianness.
// Template to implement do_write for a specific target endianness.
 
 
template<bool big_endian>
template<bool big_endian>
void inline
void inline
Stub::do_fixed_endian_write(unsigned char* view, section_size_type view_size)
Stub::do_fixed_endian_write(unsigned char* view, section_size_type view_size)
{
{
  const Stub_template* stub_template = this->stub_template();
  const Stub_template* stub_template = this->stub_template();
  const Insn_template* insns = stub_template->insns();
  const Insn_template* insns = stub_template->insns();
 
 
  // FIXME:  We do not handle BE8 encoding yet.
  // FIXME:  We do not handle BE8 encoding yet.
  unsigned char* pov = view;
  unsigned char* pov = view;
  for (size_t i = 0; i < stub_template->insn_count(); i++)
  for (size_t i = 0; i < stub_template->insn_count(); i++)
    {
    {
      switch (insns[i].type())
      switch (insns[i].type())
        {
        {
        case Insn_template::THUMB16_TYPE:
        case Insn_template::THUMB16_TYPE:
          elfcpp::Swap<16, big_endian>::writeval(pov, insns[i].data() & 0xffff);
          elfcpp::Swap<16, big_endian>::writeval(pov, insns[i].data() & 0xffff);
          break;
          break;
        case Insn_template::THUMB16_SPECIAL_TYPE:
        case Insn_template::THUMB16_SPECIAL_TYPE:
          elfcpp::Swap<16, big_endian>::writeval(
          elfcpp::Swap<16, big_endian>::writeval(
              pov,
              pov,
              this->thumb16_special(i));
              this->thumb16_special(i));
          break;
          break;
        case Insn_template::THUMB32_TYPE:
        case Insn_template::THUMB32_TYPE:
          {
          {
            uint32_t hi = (insns[i].data() >> 16) & 0xffff;
            uint32_t hi = (insns[i].data() >> 16) & 0xffff;
            uint32_t lo = insns[i].data() & 0xffff;
            uint32_t lo = insns[i].data() & 0xffff;
            elfcpp::Swap<16, big_endian>::writeval(pov, hi);
            elfcpp::Swap<16, big_endian>::writeval(pov, hi);
            elfcpp::Swap<16, big_endian>::writeval(pov + 2, lo);
            elfcpp::Swap<16, big_endian>::writeval(pov + 2, lo);
          }
          }
          break;
          break;
        case Insn_template::ARM_TYPE:
        case Insn_template::ARM_TYPE:
        case Insn_template::DATA_TYPE:
        case Insn_template::DATA_TYPE:
          elfcpp::Swap<32, big_endian>::writeval(pov, insns[i].data());
          elfcpp::Swap<32, big_endian>::writeval(pov, insns[i].data());
          break;
          break;
        default:
        default:
          gold_unreachable();
          gold_unreachable();
        }
        }
      pov += insns[i].size();
      pov += insns[i].size();
    }
    }
  gold_assert(static_cast<section_size_type>(pov - view) == view_size);
  gold_assert(static_cast<section_size_type>(pov - view) == view_size);
}
}
 
 
// Reloc_stub::Key methods.
// Reloc_stub::Key methods.
 
 
// Dump a Key as a string for debugging.
// Dump a Key as a string for debugging.
 
 
std::string
std::string
Reloc_stub::Key::name() const
Reloc_stub::Key::name() const
{
{
  if (this->r_sym_ == invalid_index)
  if (this->r_sym_ == invalid_index)
    {
    {
      // Global symbol key name
      // Global symbol key name
      // <stub-type>:<symbol name>:<addend>.
      // <stub-type>:<symbol name>:<addend>.
      const std::string sym_name = this->u_.symbol->name();
      const std::string sym_name = this->u_.symbol->name();
      // We need to print two hex number and two colons.  So just add 100 bytes
      // We need to print two hex number and two colons.  So just add 100 bytes
      // to the symbol name size.
      // to the symbol name size.
      size_t len = sym_name.size() + 100;
      size_t len = sym_name.size() + 100;
      char* buffer = new char[len];
      char* buffer = new char[len];
      int c = snprintf(buffer, len, "%d:%s:%x", this->stub_type_,
      int c = snprintf(buffer, len, "%d:%s:%x", this->stub_type_,
                       sym_name.c_str(), this->addend_);
                       sym_name.c_str(), this->addend_);
      gold_assert(c > 0 && c < static_cast<int>(len));
      gold_assert(c > 0 && c < static_cast<int>(len));
      delete[] buffer;
      delete[] buffer;
      return std::string(buffer);
      return std::string(buffer);
    }
    }
  else
  else
    {
    {
      // local symbol key name
      // local symbol key name
      // <stub-type>:<object>:<r_sym>:<addend>.
      // <stub-type>:<object>:<r_sym>:<addend>.
      const size_t len = 200;
      const size_t len = 200;
      char buffer[len];
      char buffer[len];
      int c = snprintf(buffer, len, "%d:%p:%u:%x", this->stub_type_,
      int c = snprintf(buffer, len, "%d:%p:%u:%x", this->stub_type_,
                       this->u_.relobj, this->r_sym_, this->addend_);
                       this->u_.relobj, this->r_sym_, this->addend_);
      gold_assert(c > 0 && c < static_cast<int>(len));
      gold_assert(c > 0 && c < static_cast<int>(len));
      return std::string(buffer);
      return std::string(buffer);
    }
    }
}
}
 
 
// Reloc_stub methods.
// Reloc_stub methods.
 
 
// Determine the type of stub needed, if any, for a relocation of R_TYPE at
// Determine the type of stub needed, if any, for a relocation of R_TYPE at
// LOCATION to DESTINATION.
// LOCATION to DESTINATION.
// This code is based on the arm_type_of_stub function in
// This code is based on the arm_type_of_stub function in
// bfd/elf32-arm.c.  We have changed the interface a little to keep the Stub
// bfd/elf32-arm.c.  We have changed the interface a little to keep the Stub
// class simple.
// class simple.
 
 
Stub_type
Stub_type
Reloc_stub::stub_type_for_reloc(
Reloc_stub::stub_type_for_reloc(
   unsigned int r_type,
   unsigned int r_type,
   Arm_address location,
   Arm_address location,
   Arm_address destination,
   Arm_address destination,
   bool target_is_thumb)
   bool target_is_thumb)
{
{
  Stub_type stub_type = arm_stub_none;
  Stub_type stub_type = arm_stub_none;
 
 
  // This is a bit ugly but we want to avoid using a templated class for
  // This is a bit ugly but we want to avoid using a templated class for
  // big and little endianities.
  // big and little endianities.
  bool may_use_blx;
  bool may_use_blx;
  bool should_force_pic_veneer;
  bool should_force_pic_veneer;
  bool thumb2;
  bool thumb2;
  bool thumb_only;
  bool thumb_only;
  if (parameters->target().is_big_endian())
  if (parameters->target().is_big_endian())
    {
    {
      const Target_arm<true>* big_endian_target =
      const Target_arm<true>* big_endian_target =
        Target_arm<true>::default_target();
        Target_arm<true>::default_target();
      may_use_blx = big_endian_target->may_use_v5t_interworking();
      may_use_blx = big_endian_target->may_use_v5t_interworking();
      should_force_pic_veneer = big_endian_target->should_force_pic_veneer();
      should_force_pic_veneer = big_endian_target->should_force_pic_veneer();
      thumb2 = big_endian_target->using_thumb2();
      thumb2 = big_endian_target->using_thumb2();
      thumb_only = big_endian_target->using_thumb_only();
      thumb_only = big_endian_target->using_thumb_only();
    }
    }
  else
  else
    {
    {
      const Target_arm<false>* little_endian_target =
      const Target_arm<false>* little_endian_target =
        Target_arm<false>::default_target();
        Target_arm<false>::default_target();
      may_use_blx = little_endian_target->may_use_v5t_interworking();
      may_use_blx = little_endian_target->may_use_v5t_interworking();
      should_force_pic_veneer = little_endian_target->should_force_pic_veneer();
      should_force_pic_veneer = little_endian_target->should_force_pic_veneer();
      thumb2 = little_endian_target->using_thumb2();
      thumb2 = little_endian_target->using_thumb2();
      thumb_only = little_endian_target->using_thumb_only();
      thumb_only = little_endian_target->using_thumb_only();
    }
    }
 
 
  int64_t branch_offset;
  int64_t branch_offset;
 
  bool output_is_position_independent =
 
      parameters->options().output_is_position_independent();
  if (r_type == elfcpp::R_ARM_THM_CALL || r_type == elfcpp::R_ARM_THM_JUMP24)
  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
      // For THUMB BLX instruction, bit 1 of target comes from bit 1 of the
      // base address (instruction address + 4).
      // base address (instruction address + 4).
      if ((r_type == elfcpp::R_ARM_THM_CALL) && may_use_blx && !target_is_thumb)
      if ((r_type == elfcpp::R_ARM_THM_CALL) && may_use_blx && !target_is_thumb)
        destination = utils::bit_select(destination, location, 0x2);
        destination = Bits<32>::bit_select32(destination, location, 0x2);
      branch_offset = static_cast<int64_t>(destination) - location;
      branch_offset = static_cast<int64_t>(destination) - location;
 
 
      // Handle cases where:
      // Handle cases where:
      // - this call goes too far (different Thumb/Thumb2 max
      // - this call goes too far (different Thumb/Thumb2 max
      //   distance)
      //   distance)
      // - it's a Thumb->Arm call and blx is not available, or it's a
      // - it's a Thumb->Arm call and blx is not available, or it's a
      //   Thumb->Arm branch (not bl). A stub is needed in this case.
      //   Thumb->Arm branch (not bl). A stub is needed in this case.
      if ((!thumb2
      if ((!thumb2
            && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
            && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
                || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
                || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
          || (thumb2
          || (thumb2
              && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
              && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
                  || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
                  || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
          || ((!target_is_thumb)
          || ((!target_is_thumb)
              && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
              && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
                  || (r_type == elfcpp::R_ARM_THM_JUMP24))))
                  || (r_type == elfcpp::R_ARM_THM_JUMP24))))
        {
        {
          if (target_is_thumb)
          if (target_is_thumb)
            {
            {
              // Thumb to thumb.
              // Thumb to thumb.
              if (!thumb_only)
              if (!thumb_only)
                {
                {
                  stub_type = (parameters->options().shared()
                  stub_type = (output_is_position_independent
                               || should_force_pic_veneer)
                               || should_force_pic_veneer)
                    // PIC stubs.
                    // PIC stubs.
                    ? ((may_use_blx
                    ? ((may_use_blx
                        && (r_type == elfcpp::R_ARM_THM_CALL))
                        && (r_type == elfcpp::R_ARM_THM_CALL))
                       // V5T and above. Stub starts with ARM code, so
                       // V5T and above. Stub starts with ARM code, so
                       // we must be able to switch mode before
                       // we must be able to switch mode before
                       // reaching it, which is only possible for 'bl'
                       // reaching it, which is only possible for 'bl'
                       // (ie R_ARM_THM_CALL relocation).
                       // (ie R_ARM_THM_CALL relocation).
                       ? arm_stub_long_branch_any_thumb_pic
                       ? arm_stub_long_branch_any_thumb_pic
                       // On V4T, use Thumb code only.
                       // On V4T, use Thumb code only.
                       : arm_stub_long_branch_v4t_thumb_thumb_pic)
                       : arm_stub_long_branch_v4t_thumb_thumb_pic)
 
 
                    // non-PIC stubs.
                    // non-PIC stubs.
                    : ((may_use_blx
                    : ((may_use_blx
                        && (r_type == elfcpp::R_ARM_THM_CALL))
                        && (r_type == elfcpp::R_ARM_THM_CALL))
                       ? arm_stub_long_branch_any_any // V5T and above.
                       ? arm_stub_long_branch_any_any // V5T and above.
                       : arm_stub_long_branch_v4t_thumb_thumb); // V4T.
                       : arm_stub_long_branch_v4t_thumb_thumb); // V4T.
                }
                }
              else
              else
                {
                {
                  stub_type = (parameters->options().shared()
                  stub_type = (output_is_position_independent
                               || should_force_pic_veneer)
                               || should_force_pic_veneer)
                    ? arm_stub_long_branch_thumb_only_pic       // PIC stub.
                    ? arm_stub_long_branch_thumb_only_pic       // PIC stub.
                    : arm_stub_long_branch_thumb_only;  // non-PIC stub.
                    : arm_stub_long_branch_thumb_only;  // non-PIC stub.
                }
                }
            }
            }
          else
          else
            {
            {
              // Thumb to arm.
              // Thumb to arm.
 
 
              // FIXME: We should check that the input section is from an
              // FIXME: We should check that the input section is from an
              // object that has interwork enabled.
              // object that has interwork enabled.
 
 
              stub_type = (parameters->options().shared()
              stub_type = (output_is_position_independent
                           || should_force_pic_veneer)
                           || should_force_pic_veneer)
                // PIC stubs.
                // PIC stubs.
                ? ((may_use_blx
                ? ((may_use_blx
                    && (r_type == elfcpp::R_ARM_THM_CALL))
                    && (r_type == elfcpp::R_ARM_THM_CALL))
                   ? arm_stub_long_branch_any_arm_pic   // V5T and above.
                   ? arm_stub_long_branch_any_arm_pic   // V5T and above.
                   : arm_stub_long_branch_v4t_thumb_arm_pic)    // V4T.
                   : arm_stub_long_branch_v4t_thumb_arm_pic)    // V4T.
 
 
                // non-PIC stubs.
                // non-PIC stubs.
                : ((may_use_blx
                : ((may_use_blx
                    && (r_type == elfcpp::R_ARM_THM_CALL))
                    && (r_type == elfcpp::R_ARM_THM_CALL))
                   ? arm_stub_long_branch_any_any       // V5T and above.
                   ? arm_stub_long_branch_any_any       // V5T and above.
                   : arm_stub_long_branch_v4t_thumb_arm);       // V4T.
                   : arm_stub_long_branch_v4t_thumb_arm);       // V4T.
 
 
              // Handle v4t short branches.
              // Handle v4t short branches.
              if ((stub_type == arm_stub_long_branch_v4t_thumb_arm)
              if ((stub_type == arm_stub_long_branch_v4t_thumb_arm)
                  && (branch_offset <= THM_MAX_FWD_BRANCH_OFFSET)
                  && (branch_offset <= THM_MAX_FWD_BRANCH_OFFSET)
                  && (branch_offset >= THM_MAX_BWD_BRANCH_OFFSET))
                  && (branch_offset >= THM_MAX_BWD_BRANCH_OFFSET))
                stub_type = arm_stub_short_branch_v4t_thumb_arm;
                stub_type = arm_stub_short_branch_v4t_thumb_arm;
            }
            }
        }
        }
    }
    }
  else if (r_type == elfcpp::R_ARM_CALL
  else if (r_type == elfcpp::R_ARM_CALL
           || r_type == elfcpp::R_ARM_JUMP24
           || r_type == elfcpp::R_ARM_JUMP24
           || r_type == elfcpp::R_ARM_PLT32)
           || r_type == elfcpp::R_ARM_PLT32)
    {
    {
      branch_offset = static_cast<int64_t>(destination) - location;
      branch_offset = static_cast<int64_t>(destination) - location;
      if (target_is_thumb)
      if (target_is_thumb)
        {
        {
          // Arm to thumb.
          // Arm to thumb.
 
 
          // FIXME: We should check that the input section is from an
          // FIXME: We should check that the input section is from an
          // object that has interwork enabled.
          // object that has interwork enabled.
 
 
          // We have an extra 2-bytes reach because of
          // We have an extra 2-bytes reach because of
          // the mode change (bit 24 (H) of BLX encoding).
          // the mode change (bit 24 (H) of BLX encoding).
          if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET + 2)
          if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET + 2)
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
              || ((r_type == elfcpp::R_ARM_CALL) && !may_use_blx)
              || ((r_type == elfcpp::R_ARM_CALL) && !may_use_blx)
              || (r_type == elfcpp::R_ARM_JUMP24)
              || (r_type == elfcpp::R_ARM_JUMP24)
              || (r_type == elfcpp::R_ARM_PLT32))
              || (r_type == elfcpp::R_ARM_PLT32))
            {
            {
              stub_type = (parameters->options().shared()
              stub_type = (output_is_position_independent
                           || should_force_pic_veneer)
                           || should_force_pic_veneer)
                // PIC stubs.
                // PIC stubs.
                ? (may_use_blx
                ? (may_use_blx
                   ? arm_stub_long_branch_any_thumb_pic// V5T and above.
                   ? arm_stub_long_branch_any_thumb_pic// V5T and above.
                   : arm_stub_long_branch_v4t_arm_thumb_pic)    // V4T stub.
                   : arm_stub_long_branch_v4t_arm_thumb_pic)    // V4T stub.
 
 
                // non-PIC stubs.
                // non-PIC stubs.
                : (may_use_blx
                : (may_use_blx
                   ? arm_stub_long_branch_any_any       // V5T and above.
                   ? arm_stub_long_branch_any_any       // V5T and above.
                   : arm_stub_long_branch_v4t_arm_thumb);       // V4T.
                   : arm_stub_long_branch_v4t_arm_thumb);       // V4T.
            }
            }
        }
        }
      else
      else
        {
        {
          // Arm to arm.
          // Arm to arm.
          if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
          if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
            {
            {
              stub_type = (parameters->options().shared()
              stub_type = (output_is_position_independent
                           || should_force_pic_veneer)
                           || should_force_pic_veneer)
                ? arm_stub_long_branch_any_arm_pic      // PIC stubs.
                ? arm_stub_long_branch_any_arm_pic      // PIC stubs.
                : arm_stub_long_branch_any_any;         /// non-PIC.
                : arm_stub_long_branch_any_any;         /// non-PIC.
            }
            }
        }
        }
    }
    }
 
 
  return stub_type;
  return stub_type;
}
}
 
 
// Cortex_a8_stub methods.
// Cortex_a8_stub methods.
 
 
// Return the instruction for a THUMB16_SPECIAL_TYPE instruction template.
// Return the instruction for a THUMB16_SPECIAL_TYPE instruction template.
// I is the position of the instruction template in the stub template.
// I is the position of the instruction template in the stub template.
 
 
uint16_t
uint16_t
Cortex_a8_stub::do_thumb16_special(size_t i)
Cortex_a8_stub::do_thumb16_special(size_t i)
{
{
  // The only use of this is to copy condition code from a conditional
  // The only use of this is to copy condition code from a conditional
  // branch being worked around to the corresponding conditional branch in
  // branch being worked around to the corresponding conditional branch in
  // to the stub.
  // to the stub.
  gold_assert(this->stub_template()->type() == arm_stub_a8_veneer_b_cond
  gold_assert(this->stub_template()->type() == arm_stub_a8_veneer_b_cond
              && i == 0);
              && i == 0);
  uint16_t data = this->stub_template()->insns()[i].data();
  uint16_t data = this->stub_template()->insns()[i].data();
  gold_assert((data & 0xff00U) == 0xd000U);
  gold_assert((data & 0xff00U) == 0xd000U);
  data |= ((this->original_insn_ >> 22) & 0xf) << 8;
  data |= ((this->original_insn_ >> 22) & 0xf) << 8;
  return data;
  return data;
}
}
 
 
// Stub_factory methods.
// Stub_factory methods.
 
 
Stub_factory::Stub_factory()
Stub_factory::Stub_factory()
{
{
  // The instruction template sequences are declared as static
  // The instruction template sequences are declared as static
  // objects and initialized first time the constructor runs.
  // objects and initialized first time the constructor runs.
 
 
  // Arm/Thumb -> Arm/Thumb long branch stub. On V5T and above, use blx
  // Arm/Thumb -> Arm/Thumb long branch stub. On V5T and above, use blx
  // to reach the stub if necessary.
  // to reach the stub if necessary.
  static const Insn_template elf32_arm_stub_long_branch_any_any[] =
  static const Insn_template elf32_arm_stub_long_branch_any_any[] =
    {
    {
      Insn_template::arm_insn(0xe51ff004),      // ldr   pc, [pc, #-4]
      Insn_template::arm_insn(0xe51ff004),      // ldr   pc, [pc, #-4]
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
                                                // dcd   R_ARM_ABS32(X)
                                                // dcd   R_ARM_ABS32(X)
    };
    };
 
 
  // V4T Arm -> Thumb long branch stub. Used on V4T where blx is not
  // V4T Arm -> Thumb long branch stub. Used on V4T where blx is not
  // available.
  // available.
  static const Insn_template elf32_arm_stub_long_branch_v4t_arm_thumb[] =
  static const Insn_template elf32_arm_stub_long_branch_v4t_arm_thumb[] =
    {
    {
      Insn_template::arm_insn(0xe59fc000),      // ldr   ip, [pc, #0]
      Insn_template::arm_insn(0xe59fc000),      // ldr   ip, [pc, #0]
      Insn_template::arm_insn(0xe12fff1c),      // bx    ip
      Insn_template::arm_insn(0xe12fff1c),      // bx    ip
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
                                                // dcd   R_ARM_ABS32(X)
                                                // dcd   R_ARM_ABS32(X)
    };
    };
 
 
  // Thumb -> Thumb long branch stub. Used on M-profile architectures.
  // Thumb -> Thumb long branch stub. Used on M-profile architectures.
  static const Insn_template elf32_arm_stub_long_branch_thumb_only[] =
  static const Insn_template elf32_arm_stub_long_branch_thumb_only[] =
    {
    {
      Insn_template::thumb16_insn(0xb401),      // push {r0}
      Insn_template::thumb16_insn(0xb401),      // push {r0}
      Insn_template::thumb16_insn(0x4802),      // ldr  r0, [pc, #8]
      Insn_template::thumb16_insn(0x4802),      // ldr  r0, [pc, #8]
      Insn_template::thumb16_insn(0x4684),      // mov  ip, r0
      Insn_template::thumb16_insn(0x4684),      // mov  ip, r0
      Insn_template::thumb16_insn(0xbc01),      // pop  {r0}
      Insn_template::thumb16_insn(0xbc01),      // pop  {r0}
      Insn_template::thumb16_insn(0x4760),      // bx   ip
      Insn_template::thumb16_insn(0x4760),      // bx   ip
      Insn_template::thumb16_insn(0xbf00),      // nop
      Insn_template::thumb16_insn(0xbf00),      // nop
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
                                                // dcd  R_ARM_ABS32(X)
                                                // dcd  R_ARM_ABS32(X)
    };
    };
 
 
  // V4T Thumb -> Thumb long branch stub. Using the stack is not
  // V4T Thumb -> Thumb long branch stub. Using the stack is not
  // allowed.
  // allowed.
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_thumb[] =
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_thumb[] =
    {
    {
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::arm_insn(0xe59fc000),      // ldr  ip, [pc, #0]
      Insn_template::arm_insn(0xe59fc000),      // ldr  ip, [pc, #0]
      Insn_template::arm_insn(0xe12fff1c),      // bx   ip
      Insn_template::arm_insn(0xe12fff1c),      // bx   ip
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
                                                // dcd  R_ARM_ABS32(X)
                                                // dcd  R_ARM_ABS32(X)
    };
    };
 
 
  // V4T Thumb -> ARM long branch stub. Used on V4T where blx is not
  // V4T Thumb -> ARM long branch stub. Used on V4T where blx is not
  // available.
  // available.
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_arm[] =
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_arm[] =
    {
    {
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::arm_insn(0xe51ff004),      // ldr   pc, [pc, #-4]
      Insn_template::arm_insn(0xe51ff004),      // ldr   pc, [pc, #-4]
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_ABS32, 0),
                                                // dcd   R_ARM_ABS32(X)
                                                // dcd   R_ARM_ABS32(X)
    };
    };
 
 
  // V4T Thumb -> ARM short branch stub. Shorter variant of the above
  // V4T Thumb -> ARM short branch stub. Shorter variant of the above
  // one, when the destination is close enough.
  // one, when the destination is close enough.
  static const Insn_template elf32_arm_stub_short_branch_v4t_thumb_arm[] =
  static const Insn_template elf32_arm_stub_short_branch_v4t_thumb_arm[] =
    {
    {
      Insn_template::thumb16_insn(0x4778),              // bx   pc
      Insn_template::thumb16_insn(0x4778),              // bx   pc
      Insn_template::thumb16_insn(0x46c0),              // nop
      Insn_template::thumb16_insn(0x46c0),              // nop
      Insn_template::arm_rel_insn(0xea000000, -8),      // b    (X-8)
      Insn_template::arm_rel_insn(0xea000000, -8),      // b    (X-8)
    };
    };
 
 
  // ARM/Thumb -> ARM long branch stub, PIC.  On V5T and above, use
  // ARM/Thumb -> ARM long branch stub, PIC.  On V5T and above, use
  // blx to reach the stub if necessary.
  // blx to reach the stub if necessary.
  static const Insn_template elf32_arm_stub_long_branch_any_arm_pic[] =
  static const Insn_template elf32_arm_stub_long_branch_any_arm_pic[] =
    {
    {
      Insn_template::arm_insn(0xe59fc000),      // ldr   r12, [pc]
      Insn_template::arm_insn(0xe59fc000),      // ldr   r12, [pc]
      Insn_template::arm_insn(0xe08ff00c),      // add   pc, pc, ip
      Insn_template::arm_insn(0xe08ff00c),      // add   pc, pc, ip
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, -4),
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, -4),
                                                // dcd   R_ARM_REL32(X-4)
                                                // dcd   R_ARM_REL32(X-4)
    };
    };
 
 
  // ARM/Thumb -> Thumb long branch stub, PIC.  On V5T and above, use
  // ARM/Thumb -> Thumb long branch stub, PIC.  On V5T and above, use
  // blx to reach the stub if necessary.  We can not add into pc;
  // blx to reach the stub if necessary.  We can not add into pc;
  // it is not guaranteed to mode switch (different in ARMv6 and
  // it is not guaranteed to mode switch (different in ARMv6 and
  // ARMv7).
  // ARMv7).
  static const Insn_template elf32_arm_stub_long_branch_any_thumb_pic[] =
  static const Insn_template elf32_arm_stub_long_branch_any_thumb_pic[] =
    {
    {
      Insn_template::arm_insn(0xe59fc004),      // ldr   r12, [pc, #4]
      Insn_template::arm_insn(0xe59fc004),      // ldr   r12, [pc, #4]
      Insn_template::arm_insn(0xe08fc00c),      // add   ip, pc, ip
      Insn_template::arm_insn(0xe08fc00c),      // add   ip, pc, ip
      Insn_template::arm_insn(0xe12fff1c),      // bx    ip
      Insn_template::arm_insn(0xe12fff1c),      // bx    ip
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 0),
                                                // dcd   R_ARM_REL32(X)
                                                // dcd   R_ARM_REL32(X)
    };
    };
 
 
  // V4T ARM -> ARM long branch stub, PIC.
  // V4T ARM -> ARM long branch stub, PIC.
  static const Insn_template elf32_arm_stub_long_branch_v4t_arm_thumb_pic[] =
  static const Insn_template elf32_arm_stub_long_branch_v4t_arm_thumb_pic[] =
    {
    {
      Insn_template::arm_insn(0xe59fc004),      // ldr   ip, [pc, #4]
      Insn_template::arm_insn(0xe59fc004),      // ldr   ip, [pc, #4]
      Insn_template::arm_insn(0xe08fc00c),      // add   ip, pc, ip
      Insn_template::arm_insn(0xe08fc00c),      // add   ip, pc, ip
      Insn_template::arm_insn(0xe12fff1c),      // bx    ip
      Insn_template::arm_insn(0xe12fff1c),      // bx    ip
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 0),
                                                // dcd   R_ARM_REL32(X)
                                                // dcd   R_ARM_REL32(X)
    };
    };
 
 
  // V4T Thumb -> ARM long branch stub, PIC.
  // V4T Thumb -> ARM long branch stub, PIC.
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_arm_pic[] =
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_arm_pic[] =
    {
    {
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::arm_insn(0xe59fc000),      // ldr  ip, [pc, #0]
      Insn_template::arm_insn(0xe59fc000),      // ldr  ip, [pc, #0]
      Insn_template::arm_insn(0xe08cf00f),      // add  pc, ip, pc
      Insn_template::arm_insn(0xe08cf00f),      // add  pc, ip, pc
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, -4),
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, -4),
                                                // dcd  R_ARM_REL32(X)
                                                // dcd  R_ARM_REL32(X)
    };
    };
 
 
  // Thumb -> Thumb long branch stub, PIC. Used on M-profile
  // Thumb -> Thumb long branch stub, PIC. Used on M-profile
  // architectures.
  // architectures.
  static const Insn_template elf32_arm_stub_long_branch_thumb_only_pic[] =
  static const Insn_template elf32_arm_stub_long_branch_thumb_only_pic[] =
    {
    {
      Insn_template::thumb16_insn(0xb401),      // push {r0}
      Insn_template::thumb16_insn(0xb401),      // push {r0}
      Insn_template::thumb16_insn(0x4802),      // ldr  r0, [pc, #8]
      Insn_template::thumb16_insn(0x4802),      // ldr  r0, [pc, #8]
      Insn_template::thumb16_insn(0x46fc),      // mov  ip, pc
      Insn_template::thumb16_insn(0x46fc),      // mov  ip, pc
      Insn_template::thumb16_insn(0x4484),      // add  ip, r0
      Insn_template::thumb16_insn(0x4484),      // add  ip, r0
      Insn_template::thumb16_insn(0xbc01),      // pop  {r0}
      Insn_template::thumb16_insn(0xbc01),      // pop  {r0}
      Insn_template::thumb16_insn(0x4760),      // bx   ip
      Insn_template::thumb16_insn(0x4760),      // bx   ip
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 4),
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 4),
                                                // dcd  R_ARM_REL32(X)
                                                // dcd  R_ARM_REL32(X)
    };
    };
 
 
  // V4T Thumb -> Thumb long branch stub, PIC. Using the stack is not
  // V4T Thumb -> Thumb long branch stub, PIC. Using the stack is not
  // allowed.
  // allowed.
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_thumb_pic[] =
  static const Insn_template elf32_arm_stub_long_branch_v4t_thumb_thumb_pic[] =
    {
    {
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x4778),      // bx   pc
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::thumb16_insn(0x46c0),      // nop
      Insn_template::arm_insn(0xe59fc004),      // ldr  ip, [pc, #4]
      Insn_template::arm_insn(0xe59fc004),      // ldr  ip, [pc, #4]
      Insn_template::arm_insn(0xe08fc00c),      // add   ip, pc, ip
      Insn_template::arm_insn(0xe08fc00c),      // add   ip, pc, ip
      Insn_template::arm_insn(0xe12fff1c),      // bx   ip
      Insn_template::arm_insn(0xe12fff1c),      // bx   ip
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 0),
      Insn_template::data_word(0, elfcpp::R_ARM_REL32, 0),
                                                // dcd  R_ARM_REL32(X)
                                                // dcd  R_ARM_REL32(X)
    };
    };
 
 
  // Cortex-A8 erratum-workaround stubs.
  // Cortex-A8 erratum-workaround stubs.
 
 
  // Stub used for conditional branches (which may be beyond +/-1MB away,
  // Stub used for conditional branches (which may be beyond +/-1MB away,
  // so we can't use a conditional branch to reach this stub).
  // so we can't use a conditional branch to reach this stub).
 
 
  // original code:
  // original code:
  //
  //
  //    b<cond> X
  //    b<cond> X
  // after:
  // after:
  //
  //
  static const Insn_template elf32_arm_stub_a8_veneer_b_cond[] =
  static const Insn_template elf32_arm_stub_a8_veneer_b_cond[] =
    {
    {
      Insn_template::thumb16_bcond_insn(0xd001),        //      b<cond>.n true
      Insn_template::thumb16_bcond_insn(0xd001),        //      b<cond>.n true
      Insn_template::thumb32_b_insn(0xf000b800, -4),    //      b.w after
      Insn_template::thumb32_b_insn(0xf000b800, -4),    //      b.w after
      Insn_template::thumb32_b_insn(0xf000b800, -4)     // true:
      Insn_template::thumb32_b_insn(0xf000b800, -4)     // true:
                                                        //      b.w X
                                                        //      b.w X
    };
    };
 
 
  // Stub used for b.w and bl.w instructions.
  // Stub used for b.w and bl.w instructions.
 
 
  static const Insn_template elf32_arm_stub_a8_veneer_b[] =
  static const Insn_template elf32_arm_stub_a8_veneer_b[] =
    {
    {
      Insn_template::thumb32_b_insn(0xf000b800, -4)     // b.w dest
      Insn_template::thumb32_b_insn(0xf000b800, -4)     // b.w dest
    };
    };
 
 
  static const Insn_template elf32_arm_stub_a8_veneer_bl[] =
  static const Insn_template elf32_arm_stub_a8_veneer_bl[] =
    {
    {
      Insn_template::thumb32_b_insn(0xf000b800, -4)     // b.w dest
      Insn_template::thumb32_b_insn(0xf000b800, -4)     // b.w dest
    };
    };
 
 
  // Stub used for Thumb-2 blx.w instructions.  We modified the original blx.w
  // Stub used for Thumb-2 blx.w instructions.  We modified the original blx.w
  // instruction (which switches to ARM mode) to point to this stub.  Jump to
  // instruction (which switches to ARM mode) to point to this stub.  Jump to
  // the real destination using an ARM-mode branch.
  // the real destination using an ARM-mode branch.
  static const Insn_template elf32_arm_stub_a8_veneer_blx[] =
  static const Insn_template elf32_arm_stub_a8_veneer_blx[] =
    {
    {
      Insn_template::arm_rel_insn(0xea000000, -8)       // b dest
      Insn_template::arm_rel_insn(0xea000000, -8)       // b dest
    };
    };
 
 
  // Stub used to provide an interworking for R_ARM_V4BX relocation
  // Stub used to provide an interworking for R_ARM_V4BX relocation
  // (bx r[n] instruction).
  // (bx r[n] instruction).
  static const Insn_template elf32_arm_stub_v4_veneer_bx[] =
  static const Insn_template elf32_arm_stub_v4_veneer_bx[] =
    {
    {
      Insn_template::arm_insn(0xe3100001),              // tst   r<n>, #1
      Insn_template::arm_insn(0xe3100001),              // tst   r<n>, #1
      Insn_template::arm_insn(0x01a0f000),              // moveq pc, r<n>
      Insn_template::arm_insn(0x01a0f000),              // moveq pc, r<n>
      Insn_template::arm_insn(0xe12fff10)               // bx    r<n>
      Insn_template::arm_insn(0xe12fff10)               // bx    r<n>
    };
    };
 
 
  // Fill in the stub template look-up table.  Stub templates are constructed
  // Fill in the stub template look-up table.  Stub templates are constructed
  // per instance of Stub_factory for fast look-up without locking
  // per instance of Stub_factory for fast look-up without locking
  // in a thread-enabled environment.
  // in a thread-enabled environment.
 
 
  this->stub_templates_[arm_stub_none] =
  this->stub_templates_[arm_stub_none] =
    new Stub_template(arm_stub_none, NULL, 0);
    new Stub_template(arm_stub_none, NULL, 0);
 
 
#define DEF_STUB(x)     \
#define DEF_STUB(x)     \
  do \
  do \
    { \
    { \
      size_t array_size \
      size_t array_size \
        = sizeof(elf32_arm_stub_##x) / sizeof(elf32_arm_stub_##x[0]); \
        = sizeof(elf32_arm_stub_##x) / sizeof(elf32_arm_stub_##x[0]); \
      Stub_type type = arm_stub_##x; \
      Stub_type type = arm_stub_##x; \
      this->stub_templates_[type] = \
      this->stub_templates_[type] = \
        new Stub_template(type, elf32_arm_stub_##x, array_size); \
        new Stub_template(type, elf32_arm_stub_##x, array_size); \
    } \
    } \
  while (0);
  while (0);
 
 
  DEF_STUBS
  DEF_STUBS
#undef DEF_STUB
#undef DEF_STUB
}
}
 
 
// Stub_table methods.
// Stub_table methods.
 
 
// Remove all Cortex-A8 stub.
// Remove all Cortex-A8 stub.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Stub_table<big_endian>::remove_all_cortex_a8_stubs()
Stub_table<big_endian>::remove_all_cortex_a8_stubs()
{
{
  for (Cortex_a8_stub_list::iterator p = this->cortex_a8_stubs_.begin();
  for (Cortex_a8_stub_list::iterator p = this->cortex_a8_stubs_.begin();
       p != this->cortex_a8_stubs_.end();
       p != this->cortex_a8_stubs_.end();
       ++p)
       ++p)
    delete p->second;
    delete p->second;
  this->cortex_a8_stubs_.clear();
  this->cortex_a8_stubs_.clear();
}
}
 
 
// Relocate one stub.  This is a helper for Stub_table::relocate_stubs().
// Relocate one stub.  This is a helper for Stub_table::relocate_stubs().
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Stub_table<big_endian>::relocate_stub(
Stub_table<big_endian>::relocate_stub(
    Stub* stub,
    Stub* stub,
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    Target_arm<big_endian>* arm_target,
    Target_arm<big_endian>* arm_target,
    Output_section* output_section,
    Output_section* output_section,
    unsigned char* view,
    unsigned char* view,
    Arm_address address,
    Arm_address address,
    section_size_type view_size)
    section_size_type view_size)
{
{
  const Stub_template* stub_template = stub->stub_template();
  const Stub_template* stub_template = stub->stub_template();
  if (stub_template->reloc_count() != 0)
  if (stub_template->reloc_count() != 0)
    {
    {
      // Adjust view to cover the stub only.
      // Adjust view to cover the stub only.
      section_size_type offset = stub->offset();
      section_size_type offset = stub->offset();
      section_size_type stub_size = stub_template->size();
      section_size_type stub_size = stub_template->size();
      gold_assert(offset + stub_size <= view_size);
      gold_assert(offset + stub_size <= view_size);
 
 
      arm_target->relocate_stub(stub, relinfo, output_section, view + offset,
      arm_target->relocate_stub(stub, relinfo, output_section, view + offset,
                                address + offset, stub_size);
                                address + offset, stub_size);
    }
    }
}
}
 
 
// Relocate all stubs in this stub table.
// Relocate all stubs in this stub table.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Stub_table<big_endian>::relocate_stubs(
Stub_table<big_endian>::relocate_stubs(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    Target_arm<big_endian>* arm_target,
    Target_arm<big_endian>* arm_target,
    Output_section* output_section,
    Output_section* output_section,
    unsigned char* view,
    unsigned char* view,
    Arm_address address,
    Arm_address address,
    section_size_type view_size)
    section_size_type view_size)
{
{
  // If we are passed a view bigger than the stub table's.  we need to
  // If we are passed a view bigger than the stub table's.  we need to
  // adjust the view.
  // adjust the view.
  gold_assert(address == this->address()
  gold_assert(address == this->address()
              && (view_size
              && (view_size
                  == static_cast<section_size_type>(this->data_size())));
                  == static_cast<section_size_type>(this->data_size())));
 
 
  // Relocate all relocation stubs.
  // Relocate all relocation stubs.
  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
      p != this->reloc_stubs_.end();
      p != this->reloc_stubs_.end();
      ++p)
      ++p)
    this->relocate_stub(p->second, relinfo, arm_target, output_section, view,
    this->relocate_stub(p->second, relinfo, arm_target, output_section, view,
                        address, view_size);
                        address, view_size);
 
 
  // Relocate all Cortex-A8 stubs.
  // Relocate all Cortex-A8 stubs.
  for (Cortex_a8_stub_list::iterator p = this->cortex_a8_stubs_.begin();
  for (Cortex_a8_stub_list::iterator p = this->cortex_a8_stubs_.begin();
       p != this->cortex_a8_stubs_.end();
       p != this->cortex_a8_stubs_.end();
       ++p)
       ++p)
    this->relocate_stub(p->second, relinfo, arm_target, output_section, view,
    this->relocate_stub(p->second, relinfo, arm_target, output_section, view,
                        address, view_size);
                        address, view_size);
 
 
  // Relocate all ARM V4BX stubs.
  // Relocate all ARM V4BX stubs.
  for (Arm_v4bx_stub_list::iterator p = this->arm_v4bx_stubs_.begin();
  for (Arm_v4bx_stub_list::iterator p = this->arm_v4bx_stubs_.begin();
       p != this->arm_v4bx_stubs_.end();
       p != this->arm_v4bx_stubs_.end();
       ++p)
       ++p)
    {
    {
      if (*p != NULL)
      if (*p != NULL)
        this->relocate_stub(*p, relinfo, arm_target, output_section, view,
        this->relocate_stub(*p, relinfo, arm_target, output_section, view,
                            address, view_size);
                            address, view_size);
    }
    }
}
}
 
 
// Write out the stubs to file.
// Write out the stubs to file.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Stub_table<big_endian>::do_write(Output_file* of)
Stub_table<big_endian>::do_write(Output_file* of)
{
{
  off_t offset = this->offset();
  off_t offset = this->offset();
  const section_size_type oview_size =
  const section_size_type oview_size =
    convert_to_section_size_type(this->data_size());
    convert_to_section_size_type(this->data_size());
  unsigned char* const oview = of->get_output_view(offset, oview_size);
  unsigned char* const oview = of->get_output_view(offset, oview_size);
 
 
  // Write relocation stubs.
  // Write relocation stubs.
  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
      p != this->reloc_stubs_.end();
      p != this->reloc_stubs_.end();
      ++p)
      ++p)
    {
    {
      Reloc_stub* stub = p->second;
      Reloc_stub* stub = p->second;
      Arm_address address = this->address() + stub->offset();
      Arm_address address = this->address() + stub->offset();
      gold_assert(address
      gold_assert(address
                  == align_address(address,
                  == align_address(address,
                                   stub->stub_template()->alignment()));
                                   stub->stub_template()->alignment()));
      stub->write(oview + stub->offset(), stub->stub_template()->size(),
      stub->write(oview + stub->offset(), stub->stub_template()->size(),
                  big_endian);
                  big_endian);
    }
    }
 
 
  // Write Cortex-A8 stubs.
  // Write Cortex-A8 stubs.
  for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
  for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
       p != this->cortex_a8_stubs_.end();
       p != this->cortex_a8_stubs_.end();
       ++p)
       ++p)
    {
    {
      Cortex_a8_stub* stub = p->second;
      Cortex_a8_stub* stub = p->second;
      Arm_address address = this->address() + stub->offset();
      Arm_address address = this->address() + stub->offset();
      gold_assert(address
      gold_assert(address
                  == align_address(address,
                  == align_address(address,
                                   stub->stub_template()->alignment()));
                                   stub->stub_template()->alignment()));
      stub->write(oview + stub->offset(), stub->stub_template()->size(),
      stub->write(oview + stub->offset(), stub->stub_template()->size(),
                  big_endian);
                  big_endian);
    }
    }
 
 
  // Write ARM V4BX relocation stubs.
  // Write ARM V4BX relocation stubs.
  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
       p != this->arm_v4bx_stubs_.end();
       p != this->arm_v4bx_stubs_.end();
       ++p)
       ++p)
    {
    {
      if (*p == NULL)
      if (*p == NULL)
        continue;
        continue;
 
 
      Arm_address address = this->address() + (*p)->offset();
      Arm_address address = this->address() + (*p)->offset();
      gold_assert(address
      gold_assert(address
                  == align_address(address,
                  == align_address(address,
                                   (*p)->stub_template()->alignment()));
                                   (*p)->stub_template()->alignment()));
      (*p)->write(oview + (*p)->offset(), (*p)->stub_template()->size(),
      (*p)->write(oview + (*p)->offset(), (*p)->stub_template()->size(),
                  big_endian);
                  big_endian);
    }
    }
 
 
  of->write_output_view(this->offset(), oview_size, oview);
  of->write_output_view(this->offset(), oview_size, oview);
}
}
 
 
// Update the data size and address alignment of the stub table at the end
// Update the data size and address alignment of the stub table at the end
// of a relaxation pass.   Return true if either the data size or the
// of a relaxation pass.   Return true if either the data size or the
// alignment changed in this relaxation pass.
// alignment changed in this relaxation pass.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Stub_table<big_endian>::update_data_size_and_addralign()
Stub_table<big_endian>::update_data_size_and_addralign()
{
{
  // Go over all stubs in table to compute data size and address alignment.
  // Go over all stubs in table to compute data size and address alignment.
  off_t size = this->reloc_stubs_size_;
  off_t size = this->reloc_stubs_size_;
  unsigned addralign = this->reloc_stubs_addralign_;
  unsigned addralign = this->reloc_stubs_addralign_;
 
 
  for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
  for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
       p != this->cortex_a8_stubs_.end();
       p != this->cortex_a8_stubs_.end();
       ++p)
       ++p)
    {
    {
      const Stub_template* stub_template = p->second->stub_template();
      const Stub_template* stub_template = p->second->stub_template();
      addralign = std::max(addralign, stub_template->alignment());
      addralign = std::max(addralign, stub_template->alignment());
      size = (align_address(size, stub_template->alignment())
      size = (align_address(size, stub_template->alignment())
              + stub_template->size());
              + stub_template->size());
    }
    }
 
 
  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
       p != this->arm_v4bx_stubs_.end();
       p != this->arm_v4bx_stubs_.end();
       ++p)
       ++p)
    {
    {
      if (*p == NULL)
      if (*p == NULL)
        continue;
        continue;
 
 
      const Stub_template* stub_template = (*p)->stub_template();
      const Stub_template* stub_template = (*p)->stub_template();
      addralign = std::max(addralign, stub_template->alignment());
      addralign = std::max(addralign, stub_template->alignment());
      size = (align_address(size, stub_template->alignment())
      size = (align_address(size, stub_template->alignment())
              + stub_template->size());
              + stub_template->size());
    }
    }
 
 
  // Check if either data size or alignment changed in this pass.
  // Check if either data size or alignment changed in this pass.
  // Update prev_data_size_ and prev_addralign_.  These will be used
  // Update prev_data_size_ and prev_addralign_.  These will be used
  // as the current data size and address alignment for the next pass.
  // as the current data size and address alignment for the next pass.
  bool changed = size != this->prev_data_size_;
  bool changed = size != this->prev_data_size_;
  this->prev_data_size_ = size;
  this->prev_data_size_ = size;
 
 
  if (addralign != this->prev_addralign_)
  if (addralign != this->prev_addralign_)
    changed = true;
    changed = true;
  this->prev_addralign_ = addralign;
  this->prev_addralign_ = addralign;
 
 
  return changed;
  return changed;
}
}
 
 
// Finalize the stubs.  This sets the offsets of the stubs within the stub
// Finalize the stubs.  This sets the offsets of the stubs within the stub
// table.  It also marks all input sections needing Cortex-A8 workaround.
// table.  It also marks all input sections needing Cortex-A8 workaround.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Stub_table<big_endian>::finalize_stubs()
Stub_table<big_endian>::finalize_stubs()
{
{
  off_t off = this->reloc_stubs_size_;
  off_t off = this->reloc_stubs_size_;
  for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
  for (Cortex_a8_stub_list::const_iterator p = this->cortex_a8_stubs_.begin();
       p != this->cortex_a8_stubs_.end();
       p != this->cortex_a8_stubs_.end();
       ++p)
       ++p)
    {
    {
      Cortex_a8_stub* stub = p->second;
      Cortex_a8_stub* stub = p->second;
      const Stub_template* stub_template = stub->stub_template();
      const Stub_template* stub_template = stub->stub_template();
      uint64_t stub_addralign = stub_template->alignment();
      uint64_t stub_addralign = stub_template->alignment();
      off = align_address(off, stub_addralign);
      off = align_address(off, stub_addralign);
      stub->set_offset(off);
      stub->set_offset(off);
      off += stub_template->size();
      off += stub_template->size();
 
 
      // Mark input section so that we can determine later if a code section
      // Mark input section so that we can determine later if a code section
      // needs the Cortex-A8 workaround quickly.
      // needs the Cortex-A8 workaround quickly.
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(stub->relobj());
        Arm_relobj<big_endian>::as_arm_relobj(stub->relobj());
      arm_relobj->mark_section_for_cortex_a8_workaround(stub->shndx());
      arm_relobj->mark_section_for_cortex_a8_workaround(stub->shndx());
    }
    }
 
 
  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
      p != this->arm_v4bx_stubs_.end();
      p != this->arm_v4bx_stubs_.end();
      ++p)
      ++p)
    {
    {
      if (*p == NULL)
      if (*p == NULL)
        continue;
        continue;
 
 
      const Stub_template* stub_template = (*p)->stub_template();
      const Stub_template* stub_template = (*p)->stub_template();
      uint64_t stub_addralign = stub_template->alignment();
      uint64_t stub_addralign = stub_template->alignment();
      off = align_address(off, stub_addralign);
      off = align_address(off, stub_addralign);
      (*p)->set_offset(off);
      (*p)->set_offset(off);
      off += stub_template->size();
      off += stub_template->size();
    }
    }
 
 
  gold_assert(off <= this->prev_data_size_);
  gold_assert(off <= this->prev_data_size_);
}
}
 
 
// Apply Cortex-A8 workaround to an address range between VIEW_ADDRESS
// Apply Cortex-A8 workaround to an address range between VIEW_ADDRESS
// and VIEW_ADDRESS + VIEW_SIZE - 1.  VIEW points to the mapped address
// and VIEW_ADDRESS + VIEW_SIZE - 1.  VIEW points to the mapped address
// of the address range seen by the linker.
// of the address range seen by the linker.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Stub_table<big_endian>::apply_cortex_a8_workaround_to_address_range(
Stub_table<big_endian>::apply_cortex_a8_workaround_to_address_range(
    Target_arm<big_endian>* arm_target,
    Target_arm<big_endian>* arm_target,
    unsigned char* view,
    unsigned char* view,
    Arm_address view_address,
    Arm_address view_address,
    section_size_type view_size)
    section_size_type view_size)
{
{
  // Cortex-A8 stubs are sorted by addresses of branches being fixed up.
  // Cortex-A8 stubs are sorted by addresses of branches being fixed up.
  for (Cortex_a8_stub_list::const_iterator p =
  for (Cortex_a8_stub_list::const_iterator p =
         this->cortex_a8_stubs_.lower_bound(view_address);
         this->cortex_a8_stubs_.lower_bound(view_address);
       ((p != this->cortex_a8_stubs_.end())
       ((p != this->cortex_a8_stubs_.end())
        && (p->first < (view_address + view_size)));
        && (p->first < (view_address + view_size)));
       ++p)
       ++p)
    {
    {
      // We do not store the THUMB bit in the LSB of either the branch address
      // We do not store the THUMB bit in the LSB of either the branch address
      // or the stub offset.  There is no need to strip the LSB.
      // or the stub offset.  There is no need to strip the LSB.
      Arm_address branch_address = p->first;
      Arm_address branch_address = p->first;
      const Cortex_a8_stub* stub = p->second;
      const Cortex_a8_stub* stub = p->second;
      Arm_address stub_address = this->address() + stub->offset();
      Arm_address stub_address = this->address() + stub->offset();
 
 
      // Offset of the branch instruction relative to this view.
      // Offset of the branch instruction relative to this view.
      section_size_type offset =
      section_size_type offset =
        convert_to_section_size_type(branch_address - view_address);
        convert_to_section_size_type(branch_address - view_address);
      gold_assert((offset + 4) <= view_size);
      gold_assert((offset + 4) <= view_size);
 
 
      arm_target->apply_cortex_a8_workaround(stub, stub_address,
      arm_target->apply_cortex_a8_workaround(stub, stub_address,
                                             view + offset, branch_address);
                                             view + offset, branch_address);
    }
    }
}
}
 
 
// Arm_input_section methods.
// Arm_input_section methods.
 
 
// Initialize an Arm_input_section.
// Initialize an Arm_input_section.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_input_section<big_endian>::init()
Arm_input_section<big_endian>::init()
{
{
  Relobj* relobj = this->relobj();
  Relobj* relobj = this->relobj();
  unsigned int shndx = this->shndx();
  unsigned int shndx = this->shndx();
 
 
  // We have to cache original size, alignment and contents to avoid locking
  // We have to cache original size, alignment and contents to avoid locking
  // the original file.
  // the original file.
  this->original_addralign_ =
  this->original_addralign_ =
    convert_types<uint32_t, uint64_t>(relobj->section_addralign(shndx));
    convert_types<uint32_t, uint64_t>(relobj->section_addralign(shndx));
 
 
  // This is not efficient but we expect only a small number of relaxed
  // This is not efficient but we expect only a small number of relaxed
  // input sections for stubs.
  // input sections for stubs.
  section_size_type section_size;
  section_size_type section_size;
  const unsigned char* section_contents =
  const unsigned char* section_contents =
    relobj->section_contents(shndx, &section_size, false);
    relobj->section_contents(shndx, &section_size, false);
  this->original_size_ =
  this->original_size_ =
    convert_types<uint32_t, uint64_t>(relobj->section_size(shndx));
    convert_types<uint32_t, uint64_t>(relobj->section_size(shndx));
 
 
  gold_assert(this->original_contents_ == NULL);
  gold_assert(this->original_contents_ == NULL);
  this->original_contents_ = new unsigned char[section_size];
  this->original_contents_ = new unsigned char[section_size];
  memcpy(this->original_contents_, section_contents, section_size);
  memcpy(this->original_contents_, section_contents, section_size);
 
 
  // We want to make this look like the original input section after
  // We want to make this look like the original input section after
  // output sections are finalized.
  // output sections are finalized.
  Output_section* os = relobj->output_section(shndx);
  Output_section* os = relobj->output_section(shndx);
  off_t offset = relobj->output_section_offset(shndx);
  off_t offset = relobj->output_section_offset(shndx);
  gold_assert(os != NULL && !relobj->is_output_section_offset_invalid(shndx));
  gold_assert(os != NULL && !relobj->is_output_section_offset_invalid(shndx));
  this->set_address(os->address() + offset);
  this->set_address(os->address() + offset);
  this->set_file_offset(os->offset() + offset);
  this->set_file_offset(os->offset() + offset);
 
 
  this->set_current_data_size(this->original_size_);
  this->set_current_data_size(this->original_size_);
  this->finalize_data_size();
  this->finalize_data_size();
}
}
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_input_section<big_endian>::do_write(Output_file* of)
Arm_input_section<big_endian>::do_write(Output_file* of)
{
{
  // We have to write out the original section content.
  // We have to write out the original section content.
  gold_assert(this->original_contents_ != NULL);
  gold_assert(this->original_contents_ != NULL);
  of->write(this->offset(), this->original_contents_,
  of->write(this->offset(), this->original_contents_,
            this->original_size_);
            this->original_size_);
 
 
  // If this owns a stub table and it is not empty, write it.
  // If this owns a stub table and it is not empty, write it.
  if (this->is_stub_table_owner() && !this->stub_table_->empty())
  if (this->is_stub_table_owner() && !this->stub_table_->empty())
    this->stub_table_->write(of);
    this->stub_table_->write(of);
}
}
 
 
// Finalize data size.
// Finalize data size.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_input_section<big_endian>::set_final_data_size()
Arm_input_section<big_endian>::set_final_data_size()
{
{
  off_t off = convert_types<off_t, uint64_t>(this->original_size_);
  off_t off = convert_types<off_t, uint64_t>(this->original_size_);
 
 
  if (this->is_stub_table_owner())
  if (this->is_stub_table_owner())
    {
    {
      this->stub_table_->finalize_data_size();
      this->stub_table_->finalize_data_size();
      off = align_address(off, this->stub_table_->addralign());
      off = align_address(off, this->stub_table_->addralign());
      off += this->stub_table_->data_size();
      off += this->stub_table_->data_size();
    }
    }
  this->set_data_size(off);
  this->set_data_size(off);
}
}
 
 
// Reset address and file offset.
// Reset address and file offset.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_input_section<big_endian>::do_reset_address_and_file_offset()
Arm_input_section<big_endian>::do_reset_address_and_file_offset()
{
{
  // Size of the original input section contents.
  // Size of the original input section contents.
  off_t off = convert_types<off_t, uint64_t>(this->original_size_);
  off_t off = convert_types<off_t, uint64_t>(this->original_size_);
 
 
  // If this is a stub table owner, account for the stub table size.
  // If this is a stub table owner, account for the stub table size.
  if (this->is_stub_table_owner())
  if (this->is_stub_table_owner())
    {
    {
      Stub_table<big_endian>* stub_table = this->stub_table_;
      Stub_table<big_endian>* stub_table = this->stub_table_;
 
 
      // Reset the stub table's address and file offset.  The
      // Reset the stub table's address and file offset.  The
      // current data size for child will be updated after that.
      // current data size for child will be updated after that.
      stub_table_->reset_address_and_file_offset();
      stub_table_->reset_address_and_file_offset();
      off = align_address(off, stub_table_->addralign());
      off = align_address(off, stub_table_->addralign());
      off += stub_table->current_data_size();
      off += stub_table->current_data_size();
    }
    }
 
 
  this->set_current_data_size(off);
  this->set_current_data_size(off);
}
}
 
 
// Arm_exidx_cantunwind methods.
// Arm_exidx_cantunwind methods.
 
 
// Write this to Output file OF for a fixed endianness.
// Write this to Output file OF for a fixed endianness.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_exidx_cantunwind::do_fixed_endian_write(Output_file* of)
Arm_exidx_cantunwind::do_fixed_endian_write(Output_file* of)
{
{
  off_t offset = this->offset();
  off_t offset = this->offset();
  const section_size_type oview_size = 8;
  const section_size_type oview_size = 8;
  unsigned char* const oview = of->get_output_view(offset, oview_size);
  unsigned char* const oview = of->get_output_view(offset, oview_size);
 
 
  typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
  typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
 
 
  Output_section* os = this->relobj_->output_section(this->shndx_);
  Output_section* os = this->relobj_->output_section(this->shndx_);
  gold_assert(os != NULL);
  gold_assert(os != NULL);
 
 
  Arm_relobj<big_endian>* arm_relobj =
  Arm_relobj<big_endian>* arm_relobj =
    Arm_relobj<big_endian>::as_arm_relobj(this->relobj_);
    Arm_relobj<big_endian>::as_arm_relobj(this->relobj_);
  Arm_address output_offset =
  Arm_address output_offset =
    arm_relobj->get_output_section_offset(this->shndx_);
    arm_relobj->get_output_section_offset(this->shndx_);
  Arm_address section_start;
  Arm_address section_start;
  section_size_type section_size;
  section_size_type section_size;
 
 
  // Find out the end of the text section referred by this.
  // Find out the end of the text section referred by this.
  if (output_offset != Arm_relobj<big_endian>::invalid_address)
  if (output_offset != Arm_relobj<big_endian>::invalid_address)
    {
    {
      section_start = os->address() + output_offset;
      section_start = os->address() + output_offset;
      const Arm_exidx_input_section* exidx_input_section =
      const Arm_exidx_input_section* exidx_input_section =
        arm_relobj->exidx_input_section_by_link(this->shndx_);
        arm_relobj->exidx_input_section_by_link(this->shndx_);
      gold_assert(exidx_input_section != NULL);
      gold_assert(exidx_input_section != NULL);
      section_size =
      section_size =
        convert_to_section_size_type(exidx_input_section->text_size());
        convert_to_section_size_type(exidx_input_section->text_size());
    }
    }
  else
  else
    {
    {
      // Currently this only happens for a relaxed section.
      // Currently this only happens for a relaxed section.
      const Output_relaxed_input_section* poris =
      const Output_relaxed_input_section* poris =
        os->find_relaxed_input_section(this->relobj_, this->shndx_);
        os->find_relaxed_input_section(this->relobj_, this->shndx_);
      gold_assert(poris != NULL);
      gold_assert(poris != NULL);
      section_start = poris->address();
      section_start = poris->address();
      section_size = convert_to_section_size_type(poris->data_size());
      section_size = convert_to_section_size_type(poris->data_size());
    }
    }
 
 
  // We always append this to the end of an EXIDX section.
  // We always append this to the end of an EXIDX section.
  Arm_address output_address = section_start + section_size;
  Arm_address output_address = section_start + section_size;
 
 
  // Write out the entry.  The first word either points to the beginning
  // Write out the entry.  The first word either points to the beginning
  // or after the end of a text section.  The second word is the special
  // or after the end of a text section.  The second word is the special
  // EXIDX_CANTUNWIND value.
  // EXIDX_CANTUNWIND value.
  uint32_t prel31_offset = output_address - this->address();
  uint32_t prel31_offset = output_address - this->address();
  if (utils::has_overflow<31>(offset))
  if (Bits<31>::has_overflow32(offset))
    gold_error(_("PREL31 overflow in EXIDX_CANTUNWIND entry"));
    gold_error(_("PREL31 overflow in EXIDX_CANTUNWIND entry"));
  elfcpp::Swap_unaligned<32, big_endian>::writeval(oview,
  elfcpp::Swap_unaligned<32, big_endian>::writeval(oview,
                                                   prel31_offset & 0x7fffffffU);
                                                   prel31_offset & 0x7fffffffU);
  elfcpp::Swap_unaligned<32, big_endian>::writeval(oview + 4,
  elfcpp::Swap_unaligned<32, big_endian>::writeval(oview + 4,
                                                   elfcpp::EXIDX_CANTUNWIND);
                                                   elfcpp::EXIDX_CANTUNWIND);
 
 
  of->write_output_view(this->offset(), oview_size, oview);
  of->write_output_view(this->offset(), oview_size, oview);
}
}
 
 
// Arm_exidx_merged_section methods.
// Arm_exidx_merged_section methods.
 
 
// Constructor for Arm_exidx_merged_section.
// Constructor for Arm_exidx_merged_section.
// EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
// EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
// SECTION_OFFSET_MAP points to a section offset map describing how
// SECTION_OFFSET_MAP points to a section offset map describing how
// parts of the input section are mapped to output.  DELETED_BYTES is
// parts of the input section are mapped to output.  DELETED_BYTES is
// the number of bytes deleted from the EXIDX input section.
// the number of bytes deleted from the EXIDX input section.
 
 
Arm_exidx_merged_section::Arm_exidx_merged_section(
Arm_exidx_merged_section::Arm_exidx_merged_section(
    const Arm_exidx_input_section& exidx_input_section,
    const Arm_exidx_input_section& exidx_input_section,
    const Arm_exidx_section_offset_map& section_offset_map,
    const Arm_exidx_section_offset_map& section_offset_map,
    uint32_t deleted_bytes)
    uint32_t deleted_bytes)
  : Output_relaxed_input_section(exidx_input_section.relobj(),
  : Output_relaxed_input_section(exidx_input_section.relobj(),
                                 exidx_input_section.shndx(),
                                 exidx_input_section.shndx(),
                                 exidx_input_section.addralign()),
                                 exidx_input_section.addralign()),
    exidx_input_section_(exidx_input_section),
    exidx_input_section_(exidx_input_section),
    section_offset_map_(section_offset_map)
    section_offset_map_(section_offset_map)
{
{
  // If we retain or discard the whole EXIDX input section,  we would
  // If we retain or discard the whole EXIDX input section,  we would
  // not be here.
  // not be here.
  gold_assert(deleted_bytes != 0
  gold_assert(deleted_bytes != 0
              && deleted_bytes != this->exidx_input_section_.size());
              && deleted_bytes != this->exidx_input_section_.size());
 
 
  // Fix size here so that we do not need to implement set_final_data_size.
  // Fix size here so that we do not need to implement set_final_data_size.
  uint32_t size = exidx_input_section.size() - deleted_bytes;
  uint32_t size = exidx_input_section.size() - deleted_bytes;
  this->set_data_size(size);
  this->set_data_size(size);
  this->fix_data_size();
  this->fix_data_size();
 
 
  // Allocate buffer for section contents and build contents.
  // Allocate buffer for section contents and build contents.
  this->section_contents_ = new unsigned char[size];
  this->section_contents_ = new unsigned char[size];
}
}
 
 
// Build the contents of a merged EXIDX output section.
// Build the contents of a merged EXIDX output section.
 
 
void
void
Arm_exidx_merged_section::build_contents(
Arm_exidx_merged_section::build_contents(
    const unsigned char* original_contents,
    const unsigned char* original_contents,
    section_size_type original_size)
    section_size_type original_size)
{
{
  // Go over spans of input offsets and write only those that are not
  // Go over spans of input offsets and write only those that are not
  // discarded.
  // discarded.
  section_offset_type in_start = 0;
  section_offset_type in_start = 0;
  section_offset_type out_start = 0;
  section_offset_type out_start = 0;
  section_offset_type in_max =
  section_offset_type in_max =
    convert_types<section_offset_type>(original_size);
    convert_types<section_offset_type>(original_size);
  section_offset_type out_max =
  section_offset_type out_max =
    convert_types<section_offset_type>(this->data_size());
    convert_types<section_offset_type>(this->data_size());
  for (Arm_exidx_section_offset_map::const_iterator p =
  for (Arm_exidx_section_offset_map::const_iterator p =
        this->section_offset_map_.begin();
        this->section_offset_map_.begin();
      p != this->section_offset_map_.end();
      p != this->section_offset_map_.end();
      ++p)
      ++p)
    {
    {
      section_offset_type in_end = p->first;
      section_offset_type in_end = p->first;
      gold_assert(in_end >= in_start);
      gold_assert(in_end >= in_start);
      section_offset_type out_end = p->second;
      section_offset_type out_end = p->second;
      size_t in_chunk_size = convert_types<size_t>(in_end - in_start + 1);
      size_t in_chunk_size = convert_types<size_t>(in_end - in_start + 1);
      if (out_end != -1)
      if (out_end != -1)
        {
        {
          size_t out_chunk_size =
          size_t out_chunk_size =
            convert_types<size_t>(out_end - out_start + 1);
            convert_types<size_t>(out_end - out_start + 1);
 
 
          gold_assert(out_chunk_size == in_chunk_size
          gold_assert(out_chunk_size == in_chunk_size
                      && in_end < in_max && out_end < out_max);
                      && in_end < in_max && out_end < out_max);
 
 
          memcpy(this->section_contents_ + out_start,
          memcpy(this->section_contents_ + out_start,
                 original_contents + in_start,
                 original_contents + in_start,
                 out_chunk_size);
                 out_chunk_size);
          out_start += out_chunk_size;
          out_start += out_chunk_size;
        }
        }
      in_start += in_chunk_size;
      in_start += in_chunk_size;
    }
    }
}
}
 
 
// Given an input OBJECT, an input section index SHNDX within that
// Given an input OBJECT, an input section index SHNDX within that
// object, and an OFFSET relative to the start of that input
// object, and an OFFSET relative to the start of that input
// section, return whether or not the corresponding offset within
// section, return whether or not the corresponding offset within
// the output section is known.  If this function returns true, it
// the output section is known.  If this function returns true, it
// sets *POUTPUT to the output offset.  The value -1 indicates that
// sets *POUTPUT to the output offset.  The value -1 indicates that
// this input offset is being discarded.
// this input offset is being discarded.
 
 
bool
bool
Arm_exidx_merged_section::do_output_offset(
Arm_exidx_merged_section::do_output_offset(
    const Relobj* relobj,
    const Relobj* relobj,
    unsigned int shndx,
    unsigned int shndx,
    section_offset_type offset,
    section_offset_type offset,
    section_offset_type* poutput) const
    section_offset_type* poutput) const
{
{
  // We only handle offsets for the original EXIDX input section.
  // We only handle offsets for the original EXIDX input section.
  if (relobj != this->exidx_input_section_.relobj()
  if (relobj != this->exidx_input_section_.relobj()
      || shndx != this->exidx_input_section_.shndx())
      || shndx != this->exidx_input_section_.shndx())
    return false;
    return false;
 
 
  section_offset_type section_size =
  section_offset_type section_size =
    convert_types<section_offset_type>(this->exidx_input_section_.size());
    convert_types<section_offset_type>(this->exidx_input_section_.size());
  if (offset < 0 || offset >= section_size)
  if (offset < 0 || offset >= section_size)
    // Input offset is out of valid range.
    // Input offset is out of valid range.
    *poutput = -1;
    *poutput = -1;
  else
  else
    {
    {
      // We need to look up the section offset map to determine the output
      // We need to look up the section offset map to determine the output
      // offset.  Find the reference point in map that is first offset
      // offset.  Find the reference point in map that is first offset
      // bigger than or equal to this offset.
      // bigger than or equal to this offset.
      Arm_exidx_section_offset_map::const_iterator p =
      Arm_exidx_section_offset_map::const_iterator p =
        this->section_offset_map_.lower_bound(offset);
        this->section_offset_map_.lower_bound(offset);
 
 
      // The section offset maps are build such that this should not happen if
      // The section offset maps are build such that this should not happen if
      // input offset is in the valid range.
      // input offset is in the valid range.
      gold_assert(p != this->section_offset_map_.end());
      gold_assert(p != this->section_offset_map_.end());
 
 
      // We need to check if this is dropped.
      // We need to check if this is dropped.
     section_offset_type ref = p->first;
     section_offset_type ref = p->first;
     section_offset_type mapped_ref = p->second;
     section_offset_type mapped_ref = p->second;
 
 
      if (mapped_ref != Arm_exidx_input_section::invalid_offset)
      if (mapped_ref != Arm_exidx_input_section::invalid_offset)
        // Offset is present in output.
        // Offset is present in output.
        *poutput = mapped_ref + (offset - ref);
        *poutput = mapped_ref + (offset - ref);
      else
      else
        // Offset is discarded owing to EXIDX entry merging.
        // Offset is discarded owing to EXIDX entry merging.
        *poutput = -1;
        *poutput = -1;
    }
    }
 
 
  return true;
  return true;
}
}
 
 
// Write this to output file OF.
// Write this to output file OF.
 
 
void
void
Arm_exidx_merged_section::do_write(Output_file* of)
Arm_exidx_merged_section::do_write(Output_file* of)
{
{
  off_t offset = this->offset();
  off_t offset = this->offset();
  const section_size_type oview_size = this->data_size();
  const section_size_type oview_size = this->data_size();
  unsigned char* const oview = of->get_output_view(offset, oview_size);
  unsigned char* const oview = of->get_output_view(offset, oview_size);
 
 
  Output_section* os = this->relobj()->output_section(this->shndx());
  Output_section* os = this->relobj()->output_section(this->shndx());
  gold_assert(os != NULL);
  gold_assert(os != NULL);
 
 
  memcpy(oview, this->section_contents_, oview_size);
  memcpy(oview, this->section_contents_, oview_size);
  of->write_output_view(this->offset(), oview_size, oview);
  of->write_output_view(this->offset(), oview_size, oview);
}
}
 
 
// Arm_exidx_fixup methods.
// Arm_exidx_fixup methods.
 
 
// Append an EXIDX_CANTUNWIND in the current output section if the last entry
// Append an EXIDX_CANTUNWIND in the current output section if the last entry
// is not an EXIDX_CANTUNWIND entry already.  The new EXIDX_CANTUNWIND entry
// is not an EXIDX_CANTUNWIND entry already.  The new EXIDX_CANTUNWIND entry
// points to the end of the last seen EXIDX section.
// points to the end of the last seen EXIDX section.
 
 
void
void
Arm_exidx_fixup::add_exidx_cantunwind_as_needed()
Arm_exidx_fixup::add_exidx_cantunwind_as_needed()
{
{
  if (this->last_unwind_type_ != UT_EXIDX_CANTUNWIND
  if (this->last_unwind_type_ != UT_EXIDX_CANTUNWIND
      && this->last_input_section_ != NULL)
      && this->last_input_section_ != NULL)
    {
    {
      Relobj* relobj = this->last_input_section_->relobj();
      Relobj* relobj = this->last_input_section_->relobj();
      unsigned int text_shndx = this->last_input_section_->link();
      unsigned int text_shndx = this->last_input_section_->link();
      Arm_exidx_cantunwind* cantunwind =
      Arm_exidx_cantunwind* cantunwind =
        new Arm_exidx_cantunwind(relobj, text_shndx);
        new Arm_exidx_cantunwind(relobj, text_shndx);
      this->exidx_output_section_->add_output_section_data(cantunwind);
      this->exidx_output_section_->add_output_section_data(cantunwind);
      this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
      this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
    }
    }
}
}
 
 
// Process an EXIDX section entry in input.  Return whether this entry
// Process an EXIDX section entry in input.  Return whether this entry
// can be deleted in the output.  SECOND_WORD in the second word of the
// can be deleted in the output.  SECOND_WORD in the second word of the
// EXIDX entry.
// EXIDX entry.
 
 
bool
bool
Arm_exidx_fixup::process_exidx_entry(uint32_t second_word)
Arm_exidx_fixup::process_exidx_entry(uint32_t second_word)
{
{
  bool delete_entry;
  bool delete_entry;
  if (second_word == elfcpp::EXIDX_CANTUNWIND)
  if (second_word == elfcpp::EXIDX_CANTUNWIND)
    {
    {
      // Merge if previous entry is also an EXIDX_CANTUNWIND.
      // Merge if previous entry is also an EXIDX_CANTUNWIND.
      delete_entry = this->last_unwind_type_ == UT_EXIDX_CANTUNWIND;
      delete_entry = this->last_unwind_type_ == UT_EXIDX_CANTUNWIND;
      this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
      this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
    }
    }
  else if ((second_word & 0x80000000) != 0)
  else if ((second_word & 0x80000000) != 0)
    {
    {
      // Inlined unwinding data.  Merge if equal to previous.
      // Inlined unwinding data.  Merge if equal to previous.
      delete_entry = (merge_exidx_entries_
      delete_entry = (merge_exidx_entries_
                      && this->last_unwind_type_ == UT_INLINED_ENTRY
                      && this->last_unwind_type_ == UT_INLINED_ENTRY
                      && this->last_inlined_entry_ == second_word);
                      && this->last_inlined_entry_ == second_word);
      this->last_unwind_type_ = UT_INLINED_ENTRY;
      this->last_unwind_type_ = UT_INLINED_ENTRY;
      this->last_inlined_entry_ = second_word;
      this->last_inlined_entry_ = second_word;
    }
    }
  else
  else
    {
    {
      // Normal table entry.  In theory we could merge these too,
      // Normal table entry.  In theory we could merge these too,
      // but duplicate entries are likely to be much less common.
      // but duplicate entries are likely to be much less common.
      delete_entry = false;
      delete_entry = false;
      this->last_unwind_type_ = UT_NORMAL_ENTRY;
      this->last_unwind_type_ = UT_NORMAL_ENTRY;
    }
    }
  return delete_entry;
  return delete_entry;
}
}
 
 
// Update the current section offset map during EXIDX section fix-up.
// Update the current section offset map during EXIDX section fix-up.
// If there is no map, create one.  INPUT_OFFSET is the offset of a
// If there is no map, create one.  INPUT_OFFSET is the offset of a
// reference point, DELETED_BYTES is the number of deleted by in the
// reference point, DELETED_BYTES is the number of deleted by in the
// section so far.  If DELETE_ENTRY is true, the reference point and
// section so far.  If DELETE_ENTRY is true, the reference point and
// all offsets after the previous reference point are discarded.
// all offsets after the previous reference point are discarded.
 
 
void
void
Arm_exidx_fixup::update_offset_map(
Arm_exidx_fixup::update_offset_map(
    section_offset_type input_offset,
    section_offset_type input_offset,
    section_size_type deleted_bytes,
    section_size_type deleted_bytes,
    bool delete_entry)
    bool delete_entry)
{
{
  if (this->section_offset_map_ == NULL)
  if (this->section_offset_map_ == NULL)
    this->section_offset_map_ = new Arm_exidx_section_offset_map();
    this->section_offset_map_ = new Arm_exidx_section_offset_map();
  section_offset_type output_offset;
  section_offset_type output_offset;
  if (delete_entry)
  if (delete_entry)
    output_offset = Arm_exidx_input_section::invalid_offset;
    output_offset = Arm_exidx_input_section::invalid_offset;
  else
  else
    output_offset = input_offset - deleted_bytes;
    output_offset = input_offset - deleted_bytes;
  (*this->section_offset_map_)[input_offset] = output_offset;
  (*this->section_offset_map_)[input_offset] = output_offset;
}
}
 
 
// Process EXIDX_INPUT_SECTION for EXIDX entry merging.  Return the number of
// Process EXIDX_INPUT_SECTION for EXIDX entry merging.  Return the number of
// bytes deleted.  SECTION_CONTENTS points to the contents of the EXIDX
// bytes deleted.  SECTION_CONTENTS points to the contents of the EXIDX
// section and SECTION_SIZE is the number of bytes pointed by SECTION_CONTENTS.
// section and SECTION_SIZE is the number of bytes pointed by SECTION_CONTENTS.
// If some entries are merged, also store a pointer to a newly created
// If some entries are merged, also store a pointer to a newly created
// Arm_exidx_section_offset_map object in *PSECTION_OFFSET_MAP.  The caller
// Arm_exidx_section_offset_map object in *PSECTION_OFFSET_MAP.  The caller
// owns the map and is responsible for releasing it after use.
// owns the map and is responsible for releasing it after use.
 
 
template<bool big_endian>
template<bool big_endian>
uint32_t
uint32_t
Arm_exidx_fixup::process_exidx_section(
Arm_exidx_fixup::process_exidx_section(
    const Arm_exidx_input_section* exidx_input_section,
    const Arm_exidx_input_section* exidx_input_section,
    const unsigned char* section_contents,
    const unsigned char* section_contents,
    section_size_type section_size,
    section_size_type section_size,
    Arm_exidx_section_offset_map** psection_offset_map)
    Arm_exidx_section_offset_map** psection_offset_map)
{
{
  Relobj* relobj = exidx_input_section->relobj();
  Relobj* relobj = exidx_input_section->relobj();
  unsigned shndx = exidx_input_section->shndx();
  unsigned shndx = exidx_input_section->shndx();
 
 
  if ((section_size % 8) != 0)
  if ((section_size % 8) != 0)
    {
    {
      // Something is wrong with this section.  Better not touch it.
      // Something is wrong with this section.  Better not touch it.
      gold_error(_("uneven .ARM.exidx section size in %s section %u"),
      gold_error(_("uneven .ARM.exidx section size in %s section %u"),
                 relobj->name().c_str(), shndx);
                 relobj->name().c_str(), shndx);
      this->last_input_section_ = exidx_input_section;
      this->last_input_section_ = exidx_input_section;
      this->last_unwind_type_ = UT_NONE;
      this->last_unwind_type_ = UT_NONE;
      return 0;
      return 0;
    }
    }
 
 
  uint32_t deleted_bytes = 0;
  uint32_t deleted_bytes = 0;
  bool prev_delete_entry = false;
  bool prev_delete_entry = false;
  gold_assert(this->section_offset_map_ == NULL);
  gold_assert(this->section_offset_map_ == NULL);
 
 
  for (section_size_type i = 0; i < section_size; i += 8)
  for (section_size_type i = 0; i < section_size; i += 8)
    {
    {
      typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
      typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
      const Valtype* wv =
      const Valtype* wv =
          reinterpret_cast<const Valtype*>(section_contents + i + 4);
          reinterpret_cast<const Valtype*>(section_contents + i + 4);
      uint32_t second_word = elfcpp::Swap<32, big_endian>::readval(wv);
      uint32_t second_word = elfcpp::Swap<32, big_endian>::readval(wv);
 
 
      bool delete_entry = this->process_exidx_entry(second_word);
      bool delete_entry = this->process_exidx_entry(second_word);
 
 
      // Entry deletion causes changes in output offsets.  We use a std::map
      // Entry deletion causes changes in output offsets.  We use a std::map
      // to record these.  And entry (x, y) means input offset x
      // to record these.  And entry (x, y) means input offset x
      // is mapped to output offset y.  If y is invalid_offset, then x is
      // is mapped to output offset y.  If y is invalid_offset, then x is
      // dropped in the output.  Because of the way std::map::lower_bound
      // dropped in the output.  Because of the way std::map::lower_bound
      // works, we record the last offset in a region w.r.t to keeping or
      // works, we record the last offset in a region w.r.t to keeping or
      // dropping.  If there is no entry (x0, y0) for an input offset x0,
      // dropping.  If there is no entry (x0, y0) for an input offset x0,
      // the output offset y0 of it is determined by the output offset y1 of
      // the output offset y0 of it is determined by the output offset y1 of
      // the smallest input offset x1 > x0 that there is an (x1, y1) entry
      // the smallest input offset x1 > x0 that there is an (x1, y1) entry
      // in the map.  If y1 is not -1, then y0 = y1 + x0 - x1.  Otherwise, y1
      // in the map.  If y1 is not -1, then y0 = y1 + x0 - x1.  Otherwise, y1
      // y0 is also -1.
      // y0 is also -1.
      if (delete_entry != prev_delete_entry && i != 0)
      if (delete_entry != prev_delete_entry && i != 0)
        this->update_offset_map(i - 1, deleted_bytes, prev_delete_entry);
        this->update_offset_map(i - 1, deleted_bytes, prev_delete_entry);
 
 
      // Update total deleted bytes for this entry.
      // Update total deleted bytes for this entry.
      if (delete_entry)
      if (delete_entry)
        deleted_bytes += 8;
        deleted_bytes += 8;
 
 
      prev_delete_entry = delete_entry;
      prev_delete_entry = delete_entry;
    }
    }
 
 
  // If section offset map is not NULL, make an entry for the end of
  // If section offset map is not NULL, make an entry for the end of
  // section.
  // section.
  if (this->section_offset_map_ != NULL)
  if (this->section_offset_map_ != NULL)
    update_offset_map(section_size - 1, deleted_bytes, prev_delete_entry);
    update_offset_map(section_size - 1, deleted_bytes, prev_delete_entry);
 
 
  *psection_offset_map = this->section_offset_map_;
  *psection_offset_map = this->section_offset_map_;
  this->section_offset_map_ = NULL;
  this->section_offset_map_ = NULL;
  this->last_input_section_ = exidx_input_section;
  this->last_input_section_ = exidx_input_section;
 
 
  // Set the first output text section so that we can link the EXIDX output
  // Set the first output text section so that we can link the EXIDX output
  // section to it.  Ignore any EXIDX input section that is completely merged.
  // section to it.  Ignore any EXIDX input section that is completely merged.
  if (this->first_output_text_section_ == NULL
  if (this->first_output_text_section_ == NULL
      && deleted_bytes != section_size)
      && deleted_bytes != section_size)
    {
    {
      unsigned int link = exidx_input_section->link();
      unsigned int link = exidx_input_section->link();
      Output_section* os = relobj->output_section(link);
      Output_section* os = relobj->output_section(link);
      gold_assert(os != NULL);
      gold_assert(os != NULL);
      this->first_output_text_section_ = os;
      this->first_output_text_section_ = os;
    }
    }
 
 
  return deleted_bytes;
  return deleted_bytes;
}
}
 
 
// Arm_output_section methods.
// Arm_output_section methods.
 
 
// Create a stub group for input sections from BEGIN to END.  OWNER
// Create a stub group for input sections from BEGIN to END.  OWNER
// points to the input section to be the owner a new stub table.
// points to the input section to be the owner a new stub table.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_section<big_endian>::create_stub_group(
Arm_output_section<big_endian>::create_stub_group(
  Input_section_list::const_iterator begin,
  Input_section_list::const_iterator begin,
  Input_section_list::const_iterator end,
  Input_section_list::const_iterator end,
  Input_section_list::const_iterator owner,
  Input_section_list::const_iterator owner,
  Target_arm<big_endian>* target,
  Target_arm<big_endian>* target,
  std::vector<Output_relaxed_input_section*>* new_relaxed_sections,
  std::vector<Output_relaxed_input_section*>* new_relaxed_sections,
  const Task* task)
  const Task* task)
{
{
  // We use a different kind of relaxed section in an EXIDX section.
  // We use a different kind of relaxed section in an EXIDX section.
  // The static casting from Output_relaxed_input_section to
  // The static casting from Output_relaxed_input_section to
  // Arm_input_section is invalid in an EXIDX section.  We are okay
  // Arm_input_section is invalid in an EXIDX section.  We are okay
  // because we should not be calling this for an EXIDX section. 
  // because we should not be calling this for an EXIDX section. 
  gold_assert(this->type() != elfcpp::SHT_ARM_EXIDX);
  gold_assert(this->type() != elfcpp::SHT_ARM_EXIDX);
 
 
  // Currently we convert ordinary input sections into relaxed sections only
  // Currently we convert ordinary input sections into relaxed sections only
  // at this point but we may want to support creating relaxed input section
  // at this point but we may want to support creating relaxed input section
  // very early.  So we check here to see if owner is already a relaxed
  // very early.  So we check here to see if owner is already a relaxed
  // section.
  // section.
 
 
  Arm_input_section<big_endian>* arm_input_section;
  Arm_input_section<big_endian>* arm_input_section;
  if (owner->is_relaxed_input_section())
  if (owner->is_relaxed_input_section())
    {
    {
      arm_input_section =
      arm_input_section =
        Arm_input_section<big_endian>::as_arm_input_section(
        Arm_input_section<big_endian>::as_arm_input_section(
          owner->relaxed_input_section());
          owner->relaxed_input_section());
    }
    }
  else
  else
    {
    {
      gold_assert(owner->is_input_section());
      gold_assert(owner->is_input_section());
      // Create a new relaxed input section.  We need to lock the original
      // Create a new relaxed input section.  We need to lock the original
      // file.
      // file.
      Task_lock_obj<Object> tl(task, owner->relobj());
      Task_lock_obj<Object> tl(task, owner->relobj());
      arm_input_section =
      arm_input_section =
        target->new_arm_input_section(owner->relobj(), owner->shndx());
        target->new_arm_input_section(owner->relobj(), owner->shndx());
      new_relaxed_sections->push_back(arm_input_section);
      new_relaxed_sections->push_back(arm_input_section);
    }
    }
 
 
  // Create a stub table.
  // Create a stub table.
  Stub_table<big_endian>* stub_table =
  Stub_table<big_endian>* stub_table =
    target->new_stub_table(arm_input_section);
    target->new_stub_table(arm_input_section);
 
 
  arm_input_section->set_stub_table(stub_table);
  arm_input_section->set_stub_table(stub_table);
 
 
  Input_section_list::const_iterator p = begin;
  Input_section_list::const_iterator p = begin;
  Input_section_list::const_iterator prev_p;
  Input_section_list::const_iterator prev_p;
 
 
  // Look for input sections or relaxed input sections in [begin ... end].
  // Look for input sections or relaxed input sections in [begin ... end].
  do
  do
    {
    {
      if (p->is_input_section() || p->is_relaxed_input_section())
      if (p->is_input_section() || p->is_relaxed_input_section())
        {
        {
          // The stub table information for input sections live
          // The stub table information for input sections live
          // in their objects.
          // in their objects.
          Arm_relobj<big_endian>* arm_relobj =
          Arm_relobj<big_endian>* arm_relobj =
            Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
            Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
          arm_relobj->set_stub_table(p->shndx(), stub_table);
          arm_relobj->set_stub_table(p->shndx(), stub_table);
        }
        }
      prev_p = p++;
      prev_p = p++;
    }
    }
  while (prev_p != end);
  while (prev_p != end);
}
}
 
 
// Group input sections for stub generation.  GROUP_SIZE is roughly the limit
// Group input sections for stub generation.  GROUP_SIZE is roughly the limit
// of stub groups.  We grow a stub group by adding input section until the
// of stub groups.  We grow a stub group by adding input section until the
// size is just below GROUP_SIZE.  The last input section will be converted
// size is just below GROUP_SIZE.  The last input section will be converted
// into a stub table.  If STUB_ALWAYS_AFTER_BRANCH is false, we also add
// into a stub table.  If STUB_ALWAYS_AFTER_BRANCH is false, we also add
// input section after the stub table, effectively double the group size.
// input section after the stub table, effectively double the group size.
// 
// 
// This is similar to the group_sections() function in elf32-arm.c but is
// This is similar to the group_sections() function in elf32-arm.c but is
// implemented differently.
// implemented differently.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_section<big_endian>::group_sections(
Arm_output_section<big_endian>::group_sections(
    section_size_type group_size,
    section_size_type group_size,
    bool stubs_always_after_branch,
    bool stubs_always_after_branch,
    Target_arm<big_endian>* target,
    Target_arm<big_endian>* target,
    const Task* task)
    const Task* task)
{
{
  // We only care about sections containing code.
  // We only care about sections containing code.
  if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0)
  if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0)
    return;
    return;
 
 
  // States for grouping.
  // States for grouping.
  typedef enum
  typedef enum
  {
  {
    // No group is being built.
    // No group is being built.
    NO_GROUP,
    NO_GROUP,
    // A group is being built but the stub table is not found yet.
    // A group is being built but the stub table is not found yet.
    // We keep group a stub group until the size is just under GROUP_SIZE.
    // We keep group a stub group until the size is just under GROUP_SIZE.
    // The last input section in the group will be used as the stub table.
    // The last input section in the group will be used as the stub table.
    FINDING_STUB_SECTION,
    FINDING_STUB_SECTION,
    // A group is being built and we have already found a stub table.
    // A group is being built and we have already found a stub table.
    // We enter this state to grow a stub group by adding input section
    // We enter this state to grow a stub group by adding input section
    // after the stub table.  This effectively doubles the group size.
    // after the stub table.  This effectively doubles the group size.
    HAS_STUB_SECTION
    HAS_STUB_SECTION
  } State;
  } State;
 
 
  // Any newly created relaxed sections are stored here.
  // Any newly created relaxed sections are stored here.
  std::vector<Output_relaxed_input_section*> new_relaxed_sections;
  std::vector<Output_relaxed_input_section*> new_relaxed_sections;
 
 
  State state = NO_GROUP;
  State state = NO_GROUP;
  section_size_type off = 0;
  section_size_type off = 0;
  section_size_type group_begin_offset = 0;
  section_size_type group_begin_offset = 0;
  section_size_type group_end_offset = 0;
  section_size_type group_end_offset = 0;
  section_size_type stub_table_end_offset = 0;
  section_size_type stub_table_end_offset = 0;
  Input_section_list::const_iterator group_begin =
  Input_section_list::const_iterator group_begin =
    this->input_sections().end();
    this->input_sections().end();
  Input_section_list::const_iterator stub_table =
  Input_section_list::const_iterator stub_table =
    this->input_sections().end();
    this->input_sections().end();
  Input_section_list::const_iterator group_end = this->input_sections().end();
  Input_section_list::const_iterator group_end = this->input_sections().end();
  for (Input_section_list::const_iterator p = this->input_sections().begin();
  for (Input_section_list::const_iterator p = this->input_sections().begin();
       p != this->input_sections().end();
       p != this->input_sections().end();
       ++p)
       ++p)
    {
    {
      section_size_type section_begin_offset =
      section_size_type section_begin_offset =
        align_address(off, p->addralign());
        align_address(off, p->addralign());
      section_size_type section_end_offset =
      section_size_type section_end_offset =
        section_begin_offset + p->data_size();
        section_begin_offset + p->data_size();
 
 
      // Check to see if we should group the previously seen sections.
      // Check to see if we should group the previously seen sections.
      switch (state)
      switch (state)
        {
        {
        case NO_GROUP:
        case NO_GROUP:
          break;
          break;
 
 
        case FINDING_STUB_SECTION:
        case FINDING_STUB_SECTION:
          // Adding this section makes the group larger than GROUP_SIZE.
          // Adding this section makes the group larger than GROUP_SIZE.
          if (section_end_offset - group_begin_offset >= group_size)
          if (section_end_offset - group_begin_offset >= group_size)
            {
            {
              if (stubs_always_after_branch)
              if (stubs_always_after_branch)
                {
                {
                  gold_assert(group_end != this->input_sections().end());
                  gold_assert(group_end != this->input_sections().end());
                  this->create_stub_group(group_begin, group_end, group_end,
                  this->create_stub_group(group_begin, group_end, group_end,
                                          target, &new_relaxed_sections,
                                          target, &new_relaxed_sections,
                                          task);
                                          task);
                  state = NO_GROUP;
                  state = NO_GROUP;
                }
                }
              else
              else
                {
                {
                  // But wait, there's more!  Input sections up to
                  // But wait, there's more!  Input sections up to
                  // stub_group_size bytes after the stub table can be
                  // stub_group_size bytes after the stub table can be
                  // handled by it too.
                  // handled by it too.
                  state = HAS_STUB_SECTION;
                  state = HAS_STUB_SECTION;
                  stub_table = group_end;
                  stub_table = group_end;
                  stub_table_end_offset = group_end_offset;
                  stub_table_end_offset = group_end_offset;
                }
                }
            }
            }
            break;
            break;
 
 
        case HAS_STUB_SECTION:
        case HAS_STUB_SECTION:
          // Adding this section makes the post stub-section group larger
          // Adding this section makes the post stub-section group larger
          // than GROUP_SIZE.
          // than GROUP_SIZE.
          if (section_end_offset - stub_table_end_offset >= group_size)
          if (section_end_offset - stub_table_end_offset >= group_size)
           {
           {
             gold_assert(group_end != this->input_sections().end());
             gold_assert(group_end != this->input_sections().end());
             this->create_stub_group(group_begin, group_end, stub_table,
             this->create_stub_group(group_begin, group_end, stub_table,
                                     target, &new_relaxed_sections, task);
                                     target, &new_relaxed_sections, task);
             state = NO_GROUP;
             state = NO_GROUP;
           }
           }
           break;
           break;
 
 
          default:
          default:
            gold_unreachable();
            gold_unreachable();
        }
        }
 
 
      // If we see an input section and currently there is no group, start
      // If we see an input section and currently there is no group, start
      // a new one.  Skip any empty sections.  We look at the data size
      // a new one.  Skip any empty sections.  We look at the data size
      // instead of calling p->relobj()->section_size() to avoid locking.
      // instead of calling p->relobj()->section_size() to avoid locking.
      if ((p->is_input_section() || p->is_relaxed_input_section())
      if ((p->is_input_section() || p->is_relaxed_input_section())
          && (p->data_size() != 0))
          && (p->data_size() != 0))
        {
        {
          if (state == NO_GROUP)
          if (state == NO_GROUP)
            {
            {
              state = FINDING_STUB_SECTION;
              state = FINDING_STUB_SECTION;
              group_begin = p;
              group_begin = p;
              group_begin_offset = section_begin_offset;
              group_begin_offset = section_begin_offset;
            }
            }
 
 
          // Keep track of the last input section seen.
          // Keep track of the last input section seen.
          group_end = p;
          group_end = p;
          group_end_offset = section_end_offset;
          group_end_offset = section_end_offset;
        }
        }
 
 
      off = section_end_offset;
      off = section_end_offset;
    }
    }
 
 
  // Create a stub group for any ungrouped sections.
  // Create a stub group for any ungrouped sections.
  if (state == FINDING_STUB_SECTION || state == HAS_STUB_SECTION)
  if (state == FINDING_STUB_SECTION || state == HAS_STUB_SECTION)
    {
    {
      gold_assert(group_end != this->input_sections().end());
      gold_assert(group_end != this->input_sections().end());
      this->create_stub_group(group_begin, group_end,
      this->create_stub_group(group_begin, group_end,
                              (state == FINDING_STUB_SECTION
                              (state == FINDING_STUB_SECTION
                               ? group_end
                               ? group_end
                               : stub_table),
                               : stub_table),
                               target, &new_relaxed_sections, task);
                               target, &new_relaxed_sections, task);
    }
    }
 
 
  // Convert input section into relaxed input section in a batch.
  // Convert input section into relaxed input section in a batch.
  if (!new_relaxed_sections.empty())
  if (!new_relaxed_sections.empty())
    this->convert_input_sections_to_relaxed_sections(new_relaxed_sections);
    this->convert_input_sections_to_relaxed_sections(new_relaxed_sections);
 
 
  // Update the section offsets
  // Update the section offsets
  for (size_t i = 0; i < new_relaxed_sections.size(); ++i)
  for (size_t i = 0; i < new_relaxed_sections.size(); ++i)
    {
    {
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(
        Arm_relobj<big_endian>::as_arm_relobj(
          new_relaxed_sections[i]->relobj());
          new_relaxed_sections[i]->relobj());
      unsigned int shndx = new_relaxed_sections[i]->shndx();
      unsigned int shndx = new_relaxed_sections[i]->shndx();
      // Tell Arm_relobj that this input section is converted.
      // Tell Arm_relobj that this input section is converted.
      arm_relobj->convert_input_section_to_relaxed_section(shndx);
      arm_relobj->convert_input_section_to_relaxed_section(shndx);
    }
    }
}
}
 
 
// Append non empty text sections in this to LIST in ascending
// Append non empty text sections in this to LIST in ascending
// order of their position in this.
// order of their position in this.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_section<big_endian>::append_text_sections_to_list(
Arm_output_section<big_endian>::append_text_sections_to_list(
    Text_section_list* list)
    Text_section_list* list)
{
{
  gold_assert((this->flags() & elfcpp::SHF_ALLOC) != 0);
  gold_assert((this->flags() & elfcpp::SHF_ALLOC) != 0);
 
 
  for (Input_section_list::const_iterator p = this->input_sections().begin();
  for (Input_section_list::const_iterator p = this->input_sections().begin();
       p != this->input_sections().end();
       p != this->input_sections().end();
       ++p)
       ++p)
    {
    {
      // We only care about plain or relaxed input sections.  We also
      // We only care about plain or relaxed input sections.  We also
      // ignore any merged sections.
      // ignore any merged sections.
      if (p->is_input_section() || p->is_relaxed_input_section())
      if (p->is_input_section() || p->is_relaxed_input_section())
        list->push_back(Text_section_list::value_type(p->relobj(),
        list->push_back(Text_section_list::value_type(p->relobj(),
                                                      p->shndx()));
                                                      p->shndx()));
    }
    }
}
}
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_section<big_endian>::fix_exidx_coverage(
Arm_output_section<big_endian>::fix_exidx_coverage(
    Layout* layout,
    Layout* layout,
    const Text_section_list& sorted_text_sections,
    const Text_section_list& sorted_text_sections,
    Symbol_table* symtab,
    Symbol_table* symtab,
    bool merge_exidx_entries,
    bool merge_exidx_entries,
    const Task* task)
    const Task* task)
{
{
  // We should only do this for the EXIDX output section.
  // We should only do this for the EXIDX output section.
  gold_assert(this->type() == elfcpp::SHT_ARM_EXIDX);
  gold_assert(this->type() == elfcpp::SHT_ARM_EXIDX);
 
 
  // We don't want the relaxation loop to undo these changes, so we discard
  // We don't want the relaxation loop to undo these changes, so we discard
  // the current saved states and take another one after the fix-up.
  // the current saved states and take another one after the fix-up.
  this->discard_states();
  this->discard_states();
 
 
  // Remove all input sections.
  // Remove all input sections.
  uint64_t address = this->address();
  uint64_t address = this->address();
  typedef std::list<Output_section::Input_section> Input_section_list;
  typedef std::list<Output_section::Input_section> Input_section_list;
  Input_section_list input_sections;
  Input_section_list input_sections;
  this->reset_address_and_file_offset();
  this->reset_address_and_file_offset();
  this->get_input_sections(address, std::string(""), &input_sections);
  this->get_input_sections(address, std::string(""), &input_sections);
 
 
  if (!this->input_sections().empty())
  if (!this->input_sections().empty())
    gold_error(_("Found non-EXIDX input sections in EXIDX output section"));
    gold_error(_("Found non-EXIDX input sections in EXIDX output section"));
 
 
  // Go through all the known input sections and record them.
  // Go through all the known input sections and record them.
  typedef Unordered_set<Section_id, Section_id_hash> Section_id_set;
  typedef Unordered_set<Section_id, Section_id_hash> Section_id_set;
  typedef Unordered_map<Section_id, const Output_section::Input_section*,
  typedef Unordered_map<Section_id, const Output_section::Input_section*,
                        Section_id_hash> Text_to_exidx_map;
                        Section_id_hash> Text_to_exidx_map;
  Text_to_exidx_map text_to_exidx_map;
  Text_to_exidx_map text_to_exidx_map;
  for (Input_section_list::const_iterator p = input_sections.begin();
  for (Input_section_list::const_iterator p = input_sections.begin();
       p != input_sections.end();
       p != input_sections.end();
       ++p)
       ++p)
    {
    {
      // This should never happen.  At this point, we should only see
      // This should never happen.  At this point, we should only see
      // plain EXIDX input sections.
      // plain EXIDX input sections.
      gold_assert(!p->is_relaxed_input_section());
      gold_assert(!p->is_relaxed_input_section());
      text_to_exidx_map[Section_id(p->relobj(), p->shndx())] = &(*p);
      text_to_exidx_map[Section_id(p->relobj(), p->shndx())] = &(*p);
    }
    }
 
 
  Arm_exidx_fixup exidx_fixup(this, merge_exidx_entries);
  Arm_exidx_fixup exidx_fixup(this, merge_exidx_entries);
 
 
  // Go over the sorted text sections.
  // Go over the sorted text sections.
  typedef Unordered_set<Section_id, Section_id_hash> Section_id_set;
  typedef Unordered_set<Section_id, Section_id_hash> Section_id_set;
  Section_id_set processed_input_sections;
  Section_id_set processed_input_sections;
  for (Text_section_list::const_iterator p = sorted_text_sections.begin();
  for (Text_section_list::const_iterator p = sorted_text_sections.begin();
       p != sorted_text_sections.end();
       p != sorted_text_sections.end();
       ++p)
       ++p)
    {
    {
      Relobj* relobj = p->first;
      Relobj* relobj = p->first;
      unsigned int shndx = p->second;
      unsigned int shndx = p->second;
 
 
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
         Arm_relobj<big_endian>::as_arm_relobj(relobj);
         Arm_relobj<big_endian>::as_arm_relobj(relobj);
      const Arm_exidx_input_section* exidx_input_section =
      const Arm_exidx_input_section* exidx_input_section =
         arm_relobj->exidx_input_section_by_link(shndx);
         arm_relobj->exidx_input_section_by_link(shndx);
 
 
      // If this text section has no EXIDX section or if the EXIDX section
      // If this text section has no EXIDX section or if the EXIDX section
      // has errors, force an EXIDX_CANTUNWIND entry pointing to the end
      // has errors, force an EXIDX_CANTUNWIND entry pointing to the end
      // of the last seen EXIDX section.
      // of the last seen EXIDX section.
      if (exidx_input_section == NULL || exidx_input_section->has_errors())
      if (exidx_input_section == NULL || exidx_input_section->has_errors())
        {
        {
          exidx_fixup.add_exidx_cantunwind_as_needed();
          exidx_fixup.add_exidx_cantunwind_as_needed();
          continue;
          continue;
        }
        }
 
 
      Relobj* exidx_relobj = exidx_input_section->relobj();
      Relobj* exidx_relobj = exidx_input_section->relobj();
      unsigned int exidx_shndx = exidx_input_section->shndx();
      unsigned int exidx_shndx = exidx_input_section->shndx();
      Section_id sid(exidx_relobj, exidx_shndx);
      Section_id sid(exidx_relobj, exidx_shndx);
      Text_to_exidx_map::const_iterator iter = text_to_exidx_map.find(sid);
      Text_to_exidx_map::const_iterator iter = text_to_exidx_map.find(sid);
      if (iter == text_to_exidx_map.end())
      if (iter == text_to_exidx_map.end())
        {
        {
          // This is odd.  We have not seen this EXIDX input section before.
          // This is odd.  We have not seen this EXIDX input section before.
          // We cannot do fix-up.  If we saw a SECTIONS clause in a script,
          // 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
          // issue a warning instead.  We assume the user knows what he
          // or she is doing.  Otherwise, this is an error.
          // or she is doing.  Otherwise, this is an error.
          if (layout->script_options()->saw_sections_clause())
          if (layout->script_options()->saw_sections_clause())
            gold_warning(_("unwinding may not work because EXIDX input section"
            gold_warning(_("unwinding may not work because EXIDX input section"
                           " %u of %s is not in EXIDX output section"),
                           " %u of %s is not in EXIDX output section"),
                         exidx_shndx, exidx_relobj->name().c_str());
                         exidx_shndx, exidx_relobj->name().c_str());
          else
          else
            gold_error(_("unwinding may not work because EXIDX input section"
            gold_error(_("unwinding may not work because EXIDX input section"
                         " %u of %s is not in EXIDX output section"),
                         " %u of %s is not in EXIDX output section"),
                       exidx_shndx, exidx_relobj->name().c_str());
                       exidx_shndx, exidx_relobj->name().c_str());
 
 
          exidx_fixup.add_exidx_cantunwind_as_needed();
          exidx_fixup.add_exidx_cantunwind_as_needed();
          continue;
          continue;
        }
        }
 
 
      // We need to access the contents of the EXIDX section, lock the
      // We need to access the contents of the EXIDX section, lock the
      // object here.
      // object here.
      Task_lock_obj<Object> tl(task, exidx_relobj);
      Task_lock_obj<Object> tl(task, exidx_relobj);
      section_size_type exidx_size;
      section_size_type exidx_size;
      const unsigned char* exidx_contents =
      const unsigned char* exidx_contents =
        exidx_relobj->section_contents(exidx_shndx, &exidx_size, false);
        exidx_relobj->section_contents(exidx_shndx, &exidx_size, false);
 
 
      // Fix up coverage and append input section to output data list.
      // Fix up coverage and append input section to output data list.
      Arm_exidx_section_offset_map* section_offset_map = NULL;
      Arm_exidx_section_offset_map* section_offset_map = NULL;
      uint32_t deleted_bytes =
      uint32_t deleted_bytes =
        exidx_fixup.process_exidx_section<big_endian>(exidx_input_section,
        exidx_fixup.process_exidx_section<big_endian>(exidx_input_section,
                                                      exidx_contents,
                                                      exidx_contents,
                                                      exidx_size,
                                                      exidx_size,
                                                      &section_offset_map);
                                                      &section_offset_map);
 
 
      if (deleted_bytes == exidx_input_section->size())
      if (deleted_bytes == exidx_input_section->size())
        {
        {
          // The whole EXIDX section got merged.  Remove it from output.
          // The whole EXIDX section got merged.  Remove it from output.
          gold_assert(section_offset_map == NULL);
          gold_assert(section_offset_map == NULL);
          exidx_relobj->set_output_section(exidx_shndx, NULL);
          exidx_relobj->set_output_section(exidx_shndx, NULL);
 
 
          // All local symbols defined in this input section will be dropped.
          // All local symbols defined in this input section will be dropped.
          // We need to adjust output local symbol count.
          // We need to adjust output local symbol count.
          arm_relobj->set_output_local_symbol_count_needs_update();
          arm_relobj->set_output_local_symbol_count_needs_update();
        }
        }
      else if (deleted_bytes > 0)
      else if (deleted_bytes > 0)
        {
        {
          // Some entries are merged.  We need to convert this EXIDX input
          // Some entries are merged.  We need to convert this EXIDX input
          // section into a relaxed section.
          // section into a relaxed section.
          gold_assert(section_offset_map != NULL);
          gold_assert(section_offset_map != NULL);
 
 
          Arm_exidx_merged_section* merged_section =
          Arm_exidx_merged_section* merged_section =
            new Arm_exidx_merged_section(*exidx_input_section,
            new Arm_exidx_merged_section(*exidx_input_section,
                                         *section_offset_map, deleted_bytes);
                                         *section_offset_map, deleted_bytes);
          merged_section->build_contents(exidx_contents, exidx_size);
          merged_section->build_contents(exidx_contents, exidx_size);
 
 
          const std::string secname = exidx_relobj->section_name(exidx_shndx);
          const std::string secname = exidx_relobj->section_name(exidx_shndx);
          this->add_relaxed_input_section(layout, merged_section, secname);
          this->add_relaxed_input_section(layout, merged_section, secname);
          arm_relobj->convert_input_section_to_relaxed_section(exidx_shndx);
          arm_relobj->convert_input_section_to_relaxed_section(exidx_shndx);
 
 
          // All local symbols defined in discarded portions of this input
          // All local symbols defined in discarded portions of this input
          // section will be dropped.  We need to adjust output local symbol
          // section will be dropped.  We need to adjust output local symbol
          // count.
          // count.
          arm_relobj->set_output_local_symbol_count_needs_update();
          arm_relobj->set_output_local_symbol_count_needs_update();
        }
        }
      else
      else
        {
        {
          // Just add back the EXIDX input section.
          // Just add back the EXIDX input section.
          gold_assert(section_offset_map == NULL);
          gold_assert(section_offset_map == NULL);
          const Output_section::Input_section* pis = iter->second;
          const Output_section::Input_section* pis = iter->second;
          gold_assert(pis->is_input_section());
          gold_assert(pis->is_input_section());
          this->add_script_input_section(*pis);
          this->add_script_input_section(*pis);
        }
        }
 
 
      processed_input_sections.insert(Section_id(exidx_relobj, exidx_shndx));
      processed_input_sections.insert(Section_id(exidx_relobj, exidx_shndx));
    }
    }
 
 
  // Insert an EXIDX_CANTUNWIND entry at the end of output if necessary.
  // Insert an EXIDX_CANTUNWIND entry at the end of output if necessary.
  exidx_fixup.add_exidx_cantunwind_as_needed();
  exidx_fixup.add_exidx_cantunwind_as_needed();
 
 
  // Remove any known EXIDX input sections that are not processed.
  // Remove any known EXIDX input sections that are not processed.
  for (Input_section_list::const_iterator p = input_sections.begin();
  for (Input_section_list::const_iterator p = input_sections.begin();
       p != input_sections.end();
       p != input_sections.end();
       ++p)
       ++p)
    {
    {
      if (processed_input_sections.find(Section_id(p->relobj(), p->shndx()))
      if (processed_input_sections.find(Section_id(p->relobj(), p->shndx()))
          == processed_input_sections.end())
          == processed_input_sections.end())
        {
        {
          // We discard a known EXIDX section because its linked
          // We discard a known EXIDX section because its linked
          // text section has been folded by ICF.  We also discard an
          // text section has been folded by ICF.  We also discard an
          // EXIDX section with error, the output does not matter in this
          // EXIDX section with error, the output does not matter in this
          // case.  We do this to avoid triggering asserts.
          // case.  We do this to avoid triggering asserts.
          Arm_relobj<big_endian>* arm_relobj =
          Arm_relobj<big_endian>* arm_relobj =
            Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
            Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
          const Arm_exidx_input_section* exidx_input_section =
          const Arm_exidx_input_section* exidx_input_section =
            arm_relobj->exidx_input_section_by_shndx(p->shndx());
            arm_relobj->exidx_input_section_by_shndx(p->shndx());
          gold_assert(exidx_input_section != NULL);
          gold_assert(exidx_input_section != NULL);
          if (!exidx_input_section->has_errors())
          if (!exidx_input_section->has_errors())
            {
            {
              unsigned int text_shndx = exidx_input_section->link();
              unsigned int text_shndx = exidx_input_section->link();
              gold_assert(symtab->is_section_folded(p->relobj(), text_shndx));
              gold_assert(symtab->is_section_folded(p->relobj(), text_shndx));
            }
            }
 
 
          // Remove this from link.  We also need to recount the
          // Remove this from link.  We also need to recount the
          // local symbols.
          // local symbols.
          p->relobj()->set_output_section(p->shndx(), NULL);
          p->relobj()->set_output_section(p->shndx(), NULL);
          arm_relobj->set_output_local_symbol_count_needs_update();
          arm_relobj->set_output_local_symbol_count_needs_update();
        }
        }
    }
    }
 
 
  // Link exidx output section to the first seen output section and
  // Link exidx output section to the first seen output section and
  // set correct entry size.
  // set correct entry size.
  this->set_link_section(exidx_fixup.first_output_text_section());
  this->set_link_section(exidx_fixup.first_output_text_section());
  this->set_entsize(8);
  this->set_entsize(8);
 
 
  // Make changes permanent.
  // Make changes permanent.
  this->save_states();
  this->save_states();
  this->set_section_offsets_need_adjustment();
  this->set_section_offsets_need_adjustment();
}
}
 
 
// Link EXIDX output sections to text output sections.
// Link EXIDX output sections to text output sections.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_section<big_endian>::set_exidx_section_link()
Arm_output_section<big_endian>::set_exidx_section_link()
{
{
  gold_assert(this->type() == elfcpp::SHT_ARM_EXIDX);
  gold_assert(this->type() == elfcpp::SHT_ARM_EXIDX);
  if (!this->input_sections().empty())
  if (!this->input_sections().empty())
    {
    {
      Input_section_list::const_iterator p = this->input_sections().begin();
      Input_section_list::const_iterator p = this->input_sections().begin();
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
        Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
      unsigned exidx_shndx = p->shndx();
      unsigned exidx_shndx = p->shndx();
      const Arm_exidx_input_section* exidx_input_section =
      const Arm_exidx_input_section* exidx_input_section =
        arm_relobj->exidx_input_section_by_shndx(exidx_shndx);
        arm_relobj->exidx_input_section_by_shndx(exidx_shndx);
      gold_assert(exidx_input_section != NULL);
      gold_assert(exidx_input_section != NULL);
      unsigned int text_shndx = exidx_input_section->link();
      unsigned int text_shndx = exidx_input_section->link();
      Output_section* os = arm_relobj->output_section(text_shndx);
      Output_section* os = arm_relobj->output_section(text_shndx);
      this->set_link_section(os);
      this->set_link_section(os);
    }
    }
}
}
 
 
// Arm_relobj methods.
// Arm_relobj methods.
 
 
// Determine if an input section is scannable for stub processing.  SHDR is
// Determine if an input section is scannable for stub processing.  SHDR is
// the header of the section and SHNDX is the section index.  OS is the output
// the header of the section and SHNDX is the section index.  OS is the output
// section for the input section and SYMTAB is the global symbol table used to
// section for the input section and SYMTAB is the global symbol table used to
// look up ICF information.
// look up ICF information.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Arm_relobj<big_endian>::section_is_scannable(
Arm_relobj<big_endian>::section_is_scannable(
    const elfcpp::Shdr<32, big_endian>& shdr,
    const elfcpp::Shdr<32, big_endian>& shdr,
    unsigned int shndx,
    unsigned int shndx,
    const Output_section* os,
    const Output_section* os,
    const Symbol_table* symtab)
    const Symbol_table* symtab)
{
{
  // Skip any empty sections, unallocated sections or sections whose
  // Skip any empty sections, unallocated sections or sections whose
  // type are not SHT_PROGBITS.
  // type are not SHT_PROGBITS.
  if (shdr.get_sh_size() == 0
  if (shdr.get_sh_size() == 0
      || (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0
      || (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0
      || shdr.get_sh_type() != elfcpp::SHT_PROGBITS)
      || shdr.get_sh_type() != elfcpp::SHT_PROGBITS)
    return false;
    return false;
 
 
  // Skip any discarded or ICF'ed sections.
  // Skip any discarded or ICF'ed sections.
  if (os == NULL || symtab->is_section_folded(this, shndx))
  if (os == NULL || symtab->is_section_folded(this, shndx))
    return false;
    return false;
 
 
  // If this requires special offset handling, check to see if it is
  // If this requires special offset handling, check to see if it is
  // a relaxed section.  If this is not, then it is a merged section that
  // a relaxed section.  If this is not, then it is a merged section that
  // we cannot handle.
  // we cannot handle.
  if (this->is_output_section_offset_invalid(shndx))
  if (this->is_output_section_offset_invalid(shndx))
    {
    {
      const Output_relaxed_input_section* poris =
      const Output_relaxed_input_section* poris =
        os->find_relaxed_input_section(this, shndx);
        os->find_relaxed_input_section(this, shndx);
      if (poris == NULL)
      if (poris == NULL)
        return false;
        return false;
    }
    }
 
 
  return true;
  return true;
}
}
 
 
// Determine if we want to scan the SHNDX-th section for relocation stubs.
// Determine if we want to scan the SHNDX-th section for relocation stubs.
// This is a helper for Arm_relobj::scan_sections_for_stubs() below.
// This is a helper for Arm_relobj::scan_sections_for_stubs() below.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Arm_relobj<big_endian>::section_needs_reloc_stub_scanning(
Arm_relobj<big_endian>::section_needs_reloc_stub_scanning(
    const elfcpp::Shdr<32, big_endian>& shdr,
    const elfcpp::Shdr<32, big_endian>& shdr,
    const Relobj::Output_sections& out_sections,
    const Relobj::Output_sections& out_sections,
    const Symbol_table* symtab,
    const Symbol_table* symtab,
    const unsigned char* pshdrs)
    const unsigned char* pshdrs)
{
{
  unsigned int sh_type = shdr.get_sh_type();
  unsigned int sh_type = shdr.get_sh_type();
  if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA)
  if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA)
    return false;
    return false;
 
 
  // Ignore empty section.
  // Ignore empty section.
  off_t sh_size = shdr.get_sh_size();
  off_t sh_size = shdr.get_sh_size();
  if (sh_size == 0)
  if (sh_size == 0)
    return false;
    return false;
 
 
  // Ignore reloc section with unexpected symbol table.  The
  // Ignore reloc section with unexpected symbol table.  The
  // error will be reported in the final link.
  // error will be reported in the final link.
  if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx())
  if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx())
    return false;
    return false;
 
 
  unsigned int reloc_size;
  unsigned int reloc_size;
  if (sh_type == elfcpp::SHT_REL)
  if (sh_type == elfcpp::SHT_REL)
    reloc_size = elfcpp::Elf_sizes<32>::rel_size;
    reloc_size = elfcpp::Elf_sizes<32>::rel_size;
  else
  else
    reloc_size = elfcpp::Elf_sizes<32>::rela_size;
    reloc_size = elfcpp::Elf_sizes<32>::rela_size;
 
 
  // Ignore reloc section with unexpected entsize or uneven size.
  // Ignore reloc section with unexpected entsize or uneven size.
  // The error will be reported in the final link.
  // The error will be reported in the final link.
  if (reloc_size != shdr.get_sh_entsize() || sh_size % reloc_size != 0)
  if (reloc_size != shdr.get_sh_entsize() || sh_size % reloc_size != 0)
    return false;
    return false;
 
 
  // Ignore reloc section with bad info.  This error will be
  // Ignore reloc section with bad info.  This error will be
  // reported in the final link.
  // reported in the final link.
  unsigned int index = this->adjust_shndx(shdr.get_sh_info());
  unsigned int index = this->adjust_shndx(shdr.get_sh_info());
  if (index >= this->shnum())
  if (index >= this->shnum())
    return false;
    return false;
 
 
  const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const elfcpp::Shdr<32, big_endian> text_shdr(pshdrs + index * shdr_size);
  const elfcpp::Shdr<32, big_endian> text_shdr(pshdrs + index * shdr_size);
  return this->section_is_scannable(text_shdr, index,
  return this->section_is_scannable(text_shdr, index,
                                   out_sections[index], symtab);
                                   out_sections[index], symtab);
}
}
 
 
// Return the output address of either a plain input section or a relaxed
// Return the output address of either a plain input section or a relaxed
// input section.  SHNDX is the section index.  We define and use this
// input section.  SHNDX is the section index.  We define and use this
// instead of calling Output_section::output_address because that is slow
// instead of calling Output_section::output_address because that is slow
// for large output.
// for large output.
 
 
template<bool big_endian>
template<bool big_endian>
Arm_address
Arm_address
Arm_relobj<big_endian>::simple_input_section_output_address(
Arm_relobj<big_endian>::simple_input_section_output_address(
    unsigned int shndx,
    unsigned int shndx,
    Output_section* os)
    Output_section* os)
{
{
  if (this->is_output_section_offset_invalid(shndx))
  if (this->is_output_section_offset_invalid(shndx))
    {
    {
      const Output_relaxed_input_section* poris =
      const Output_relaxed_input_section* poris =
        os->find_relaxed_input_section(this, shndx);
        os->find_relaxed_input_section(this, shndx);
      // We do not handle merged sections here.
      // We do not handle merged sections here.
      gold_assert(poris != NULL);
      gold_assert(poris != NULL);
      return poris->address();
      return poris->address();
    }
    }
  else
  else
    return os->address() + this->get_output_section_offset(shndx);
    return os->address() + this->get_output_section_offset(shndx);
}
}
 
 
// Determine if we want to scan the SHNDX-th section for non-relocation stubs.
// Determine if we want to scan the SHNDX-th section for non-relocation stubs.
// This is a helper for Arm_relobj::scan_sections_for_stubs() below.
// This is a helper for Arm_relobj::scan_sections_for_stubs() below.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Arm_relobj<big_endian>::section_needs_cortex_a8_stub_scanning(
Arm_relobj<big_endian>::section_needs_cortex_a8_stub_scanning(
    const elfcpp::Shdr<32, big_endian>& shdr,
    const elfcpp::Shdr<32, big_endian>& shdr,
    unsigned int shndx,
    unsigned int shndx,
    Output_section* os,
    Output_section* os,
    const Symbol_table* symtab)
    const Symbol_table* symtab)
{
{
  if (!this->section_is_scannable(shdr, shndx, os, symtab))
  if (!this->section_is_scannable(shdr, shndx, os, symtab))
    return false;
    return false;
 
 
  // If the section does not cross any 4K-boundaries, it does not need to
  // If the section does not cross any 4K-boundaries, it does not need to
  // be scanned.
  // be scanned.
  Arm_address address = this->simple_input_section_output_address(shndx, os);
  Arm_address address = this->simple_input_section_output_address(shndx, os);
  if ((address & ~0xfffU) == ((address + shdr.get_sh_size() - 1) & ~0xfffU))
  if ((address & ~0xfffU) == ((address + shdr.get_sh_size() - 1) & ~0xfffU))
    return false;
    return false;
 
 
  return true;
  return true;
}
}
 
 
// Scan a section for Cortex-A8 workaround.
// Scan a section for Cortex-A8 workaround.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::scan_section_for_cortex_a8_erratum(
Arm_relobj<big_endian>::scan_section_for_cortex_a8_erratum(
    const elfcpp::Shdr<32, big_endian>& shdr,
    const elfcpp::Shdr<32, big_endian>& shdr,
    unsigned int shndx,
    unsigned int shndx,
    Output_section* os,
    Output_section* os,
    Target_arm<big_endian>* arm_target)
    Target_arm<big_endian>* arm_target)
{
{
  // Look for the first mapping symbol in this section.  It should be
  // Look for the first mapping symbol in this section.  It should be
  // at (shndx, 0).
  // at (shndx, 0).
  Mapping_symbol_position section_start(shndx, 0);
  Mapping_symbol_position section_start(shndx, 0);
  typename Mapping_symbols_info::const_iterator p =
  typename Mapping_symbols_info::const_iterator p =
    this->mapping_symbols_info_.lower_bound(section_start);
    this->mapping_symbols_info_.lower_bound(section_start);
 
 
  // There are no mapping symbols for this section.  Treat it as a data-only
  // There are no mapping symbols for this section.  Treat it as a data-only
  // section.  Issue a warning if section is marked as containing
  // section.  Issue a warning if section is marked as containing
  // instructions.
  // instructions.
  if (p == this->mapping_symbols_info_.end() || p->first.first != shndx)
  if (p == this->mapping_symbols_info_.end() || p->first.first != shndx)
    {
    {
      if ((this->section_flags(shndx) & elfcpp::SHF_EXECINSTR) != 0)
      if ((this->section_flags(shndx) & elfcpp::SHF_EXECINSTR) != 0)
        gold_warning(_("cannot scan executable section %u of %s for Cortex-A8 "
        gold_warning(_("cannot scan executable section %u of %s for Cortex-A8 "
                       "erratum because it has no mapping symbols."),
                       "erratum because it has no mapping symbols."),
                     shndx, this->name().c_str());
                     shndx, this->name().c_str());
      return;
      return;
    }
    }
 
 
  Arm_address output_address =
  Arm_address output_address =
    this->simple_input_section_output_address(shndx, os);
    this->simple_input_section_output_address(shndx, os);
 
 
  // Get the section contents.
  // Get the section contents.
  section_size_type input_view_size = 0;
  section_size_type input_view_size = 0;
  const unsigned char* input_view =
  const unsigned char* input_view =
    this->section_contents(shndx, &input_view_size, false);
    this->section_contents(shndx, &input_view_size, false);
 
 
  // We need to go through the mapping symbols to determine what to
  // We need to go through the mapping symbols to determine what to
  // scan.  There are two reasons.  First, we should look at THUMB code and
  // scan.  There are two reasons.  First, we should look at THUMB code and
  // THUMB code only.  Second, we only want to look at the 4K-page boundary
  // THUMB code only.  Second, we only want to look at the 4K-page boundary
  // to speed up the scanning.
  // to speed up the scanning.
 
 
  while (p != this->mapping_symbols_info_.end()
  while (p != this->mapping_symbols_info_.end()
        && p->first.first == shndx)
        && p->first.first == shndx)
    {
    {
      typename Mapping_symbols_info::const_iterator next =
      typename Mapping_symbols_info::const_iterator next =
        this->mapping_symbols_info_.upper_bound(p->first);
        this->mapping_symbols_info_.upper_bound(p->first);
 
 
      // Only scan part of a section with THUMB code.
      // Only scan part of a section with THUMB code.
      if (p->second == 't')
      if (p->second == 't')
        {
        {
          // Determine the end of this range.
          // Determine the end of this range.
          section_size_type span_start =
          section_size_type span_start =
            convert_to_section_size_type(p->first.second);
            convert_to_section_size_type(p->first.second);
          section_size_type span_end;
          section_size_type span_end;
          if (next != this->mapping_symbols_info_.end()
          if (next != this->mapping_symbols_info_.end()
              && next->first.first == shndx)
              && next->first.first == shndx)
            span_end = convert_to_section_size_type(next->first.second);
            span_end = convert_to_section_size_type(next->first.second);
          else
          else
            span_end = convert_to_section_size_type(shdr.get_sh_size());
            span_end = convert_to_section_size_type(shdr.get_sh_size());
 
 
          if (((span_start + output_address) & ~0xfffUL)
          if (((span_start + output_address) & ~0xfffUL)
              != ((span_end + output_address - 1) & ~0xfffUL))
              != ((span_end + output_address - 1) & ~0xfffUL))
            {
            {
              arm_target->scan_span_for_cortex_a8_erratum(this, shndx,
              arm_target->scan_span_for_cortex_a8_erratum(this, shndx,
                                                          span_start, span_end,
                                                          span_start, span_end,
                                                          input_view,
                                                          input_view,
                                                          output_address);
                                                          output_address);
            }
            }
        }
        }
 
 
      p = next;
      p = next;
    }
    }
}
}
 
 
// Scan relocations for stub generation.
// Scan relocations for stub generation.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::scan_sections_for_stubs(
Arm_relobj<big_endian>::scan_sections_for_stubs(
    Target_arm<big_endian>* arm_target,
    Target_arm<big_endian>* arm_target,
    const Symbol_table* symtab,
    const Symbol_table* symtab,
    const Layout* layout)
    const Layout* layout)
{
{
  unsigned int shnum = this->shnum();
  unsigned int shnum = this->shnum();
  const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
 
 
  // Read the section headers.
  // Read the section headers.
  const unsigned char* pshdrs = this->get_view(this->elf_file()->shoff(),
  const unsigned char* pshdrs = this->get_view(this->elf_file()->shoff(),
                                               shnum * shdr_size,
                                               shnum * shdr_size,
                                               true, true);
                                               true, true);
 
 
  // To speed up processing, we set up hash tables for fast lookup of
  // To speed up processing, we set up hash tables for fast lookup of
  // input offsets to output addresses.
  // input offsets to output addresses.
  this->initialize_input_to_output_maps();
  this->initialize_input_to_output_maps();
 
 
  const Relobj::Output_sections& out_sections(this->output_sections());
  const Relobj::Output_sections& out_sections(this->output_sections());
 
 
  Relocate_info<32, big_endian> relinfo;
  Relocate_info<32, big_endian> relinfo;
  relinfo.symtab = symtab;
  relinfo.symtab = symtab;
  relinfo.layout = layout;
  relinfo.layout = layout;
  relinfo.object = this;
  relinfo.object = this;
 
 
  // Do relocation stubs scanning.
  // Do relocation stubs scanning.
  const unsigned char* p = pshdrs + shdr_size;
  const unsigned char* p = pshdrs + shdr_size;
  for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
  for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
    {
    {
      const elfcpp::Shdr<32, big_endian> shdr(p);
      const elfcpp::Shdr<32, big_endian> shdr(p);
      if (this->section_needs_reloc_stub_scanning(shdr, out_sections, symtab,
      if (this->section_needs_reloc_stub_scanning(shdr, out_sections, symtab,
                                                  pshdrs))
                                                  pshdrs))
        {
        {
          unsigned int index = this->adjust_shndx(shdr.get_sh_info());
          unsigned int index = this->adjust_shndx(shdr.get_sh_info());
          Arm_address output_offset = this->get_output_section_offset(index);
          Arm_address output_offset = this->get_output_section_offset(index);
          Arm_address output_address;
          Arm_address output_address;
          if (output_offset != invalid_address)
          if (output_offset != invalid_address)
            output_address = out_sections[index]->address() + output_offset;
            output_address = out_sections[index]->address() + output_offset;
          else
          else
            {
            {
              // Currently this only happens for a relaxed section.
              // Currently this only happens for a relaxed section.
              const Output_relaxed_input_section* poris =
              const Output_relaxed_input_section* poris =
              out_sections[index]->find_relaxed_input_section(this, index);
              out_sections[index]->find_relaxed_input_section(this, index);
              gold_assert(poris != NULL);
              gold_assert(poris != NULL);
              output_address = poris->address();
              output_address = poris->address();
            }
            }
 
 
          // Get the relocations.
          // Get the relocations.
          const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(),
          const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(),
                                                        shdr.get_sh_size(),
                                                        shdr.get_sh_size(),
                                                        true, false);
                                                        true, false);
 
 
          // Get the section contents.  This does work for the case in which
          // Get the section contents.  This does work for the case in which
          // we modify the contents of an input section.  We need to pass the
          // we modify the contents of an input section.  We need to pass the
          // output view under such circumstances.
          // output view under such circumstances.
          section_size_type input_view_size = 0;
          section_size_type input_view_size = 0;
          const unsigned char* input_view =
          const unsigned char* input_view =
            this->section_contents(index, &input_view_size, false);
            this->section_contents(index, &input_view_size, false);
 
 
          relinfo.reloc_shndx = i;
          relinfo.reloc_shndx = i;
          relinfo.data_shndx = index;
          relinfo.data_shndx = index;
          unsigned int sh_type = shdr.get_sh_type();
          unsigned int sh_type = shdr.get_sh_type();
          unsigned int reloc_size;
          unsigned int reloc_size;
          if (sh_type == elfcpp::SHT_REL)
          if (sh_type == elfcpp::SHT_REL)
            reloc_size = elfcpp::Elf_sizes<32>::rel_size;
            reloc_size = elfcpp::Elf_sizes<32>::rel_size;
          else
          else
            reloc_size = elfcpp::Elf_sizes<32>::rela_size;
            reloc_size = elfcpp::Elf_sizes<32>::rela_size;
 
 
          Output_section* os = out_sections[index];
          Output_section* os = out_sections[index];
          arm_target->scan_section_for_stubs(&relinfo, sh_type, prelocs,
          arm_target->scan_section_for_stubs(&relinfo, sh_type, prelocs,
                                             shdr.get_sh_size() / reloc_size,
                                             shdr.get_sh_size() / reloc_size,
                                             os,
                                             os,
                                             output_offset == invalid_address,
                                             output_offset == invalid_address,
                                             input_view, output_address,
                                             input_view, output_address,
                                             input_view_size);
                                             input_view_size);
        }
        }
    }
    }
 
 
  // Do Cortex-A8 erratum stubs scanning.  This has to be done for a section
  // Do Cortex-A8 erratum stubs scanning.  This has to be done for a section
  // after its relocation section, if there is one, is processed for
  // after its relocation section, if there is one, is processed for
  // relocation stubs.  Merging this loop with the one above would have been
  // relocation stubs.  Merging this loop with the one above would have been
  // complicated since we would have had to make sure that relocation stub
  // complicated since we would have had to make sure that relocation stub
  // scanning is done first.
  // scanning is done first.
  if (arm_target->fix_cortex_a8())
  if (arm_target->fix_cortex_a8())
    {
    {
      const unsigned char* p = pshdrs + shdr_size;
      const unsigned char* p = pshdrs + shdr_size;
      for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
      for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
        {
        {
          const elfcpp::Shdr<32, big_endian> shdr(p);
          const elfcpp::Shdr<32, big_endian> shdr(p);
          if (this->section_needs_cortex_a8_stub_scanning(shdr, i,
          if (this->section_needs_cortex_a8_stub_scanning(shdr, i,
                                                          out_sections[i],
                                                          out_sections[i],
                                                          symtab))
                                                          symtab))
            this->scan_section_for_cortex_a8_erratum(shdr, i, out_sections[i],
            this->scan_section_for_cortex_a8_erratum(shdr, i, out_sections[i],
                                                     arm_target);
                                                     arm_target);
        }
        }
    }
    }
 
 
  // After we've done the relocations, we release the hash tables,
  // After we've done the relocations, we release the hash tables,
  // since we no longer need them.
  // since we no longer need them.
  this->free_input_to_output_maps();
  this->free_input_to_output_maps();
}
}
 
 
// Count the local symbols.  The ARM backend needs to know if a symbol
// Count the local symbols.  The ARM backend needs to know if a symbol
// is a THUMB function or not.  For global symbols, it is easy because
// is a THUMB function or not.  For global symbols, it is easy because
// the Symbol object keeps the ELF symbol type.  For local symbol it is
// the Symbol object keeps the ELF symbol type.  For local symbol it is
// harder because we cannot access this information.   So we override the
// harder because we cannot access this information.   So we override the
// do_count_local_symbol in parent and scan local symbols to mark
// do_count_local_symbol in parent and scan local symbols to mark
// THUMB functions.  This is not the most efficient way but I do not want to
// THUMB functions.  This is not the most efficient way but I do not want to
// slow down other ports by calling a per symbol target hook inside
// slow down other ports by calling a per symbol target hook inside
// Sized_relobj_file<size, big_endian>::do_count_local_symbols. 
// Sized_relobj_file<size, big_endian>::do_count_local_symbols. 
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::do_count_local_symbols(
Arm_relobj<big_endian>::do_count_local_symbols(
    Stringpool_template<char>* pool,
    Stringpool_template<char>* pool,
    Stringpool_template<char>* dynpool)
    Stringpool_template<char>* dynpool)
{
{
  // We need to fix-up the values of any local symbols whose type are
  // We need to fix-up the values of any local symbols whose type are
  // STT_ARM_TFUNC.
  // STT_ARM_TFUNC.
 
 
  // Ask parent to count the local symbols.
  // Ask parent to count the local symbols.
  Sized_relobj_file<32, big_endian>::do_count_local_symbols(pool, dynpool);
  Sized_relobj_file<32, big_endian>::do_count_local_symbols(pool, dynpool);
  const unsigned int loccount = this->local_symbol_count();
  const unsigned int loccount = this->local_symbol_count();
  if (loccount == 0)
  if (loccount == 0)
    return;
    return;
 
 
  // Initialize the thumb function bit-vector.
  // Initialize the thumb function bit-vector.
  std::vector<bool> empty_vector(loccount, false);
  std::vector<bool> empty_vector(loccount, false);
  this->local_symbol_is_thumb_function_.swap(empty_vector);
  this->local_symbol_is_thumb_function_.swap(empty_vector);
 
 
  // Read the symbol table section header.
  // Read the symbol table section header.
  const unsigned int symtab_shndx = this->symtab_shndx();
  const unsigned int symtab_shndx = this->symtab_shndx();
  elfcpp::Shdr<32, big_endian>
  elfcpp::Shdr<32, big_endian>
      symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
      symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
  gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
  gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
 
 
  // Read the local symbols.
  // Read the local symbols.
  const int sym_size =elfcpp::Elf_sizes<32>::sym_size;
  const int sym_size =elfcpp::Elf_sizes<32>::sym_size;
  gold_assert(loccount == symtabshdr.get_sh_info());
  gold_assert(loccount == symtabshdr.get_sh_info());
  off_t locsize = loccount * sym_size;
  off_t locsize = loccount * sym_size;
  const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
  const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
                                              locsize, true, true);
                                              locsize, true, true);
 
 
  // For mapping symbol processing, we need to read the symbol names.
  // For mapping symbol processing, we need to read the symbol names.
  unsigned int strtab_shndx = this->adjust_shndx(symtabshdr.get_sh_link());
  unsigned int strtab_shndx = this->adjust_shndx(symtabshdr.get_sh_link());
  if (strtab_shndx >= this->shnum())
  if (strtab_shndx >= this->shnum())
    {
    {
      this->error(_("invalid symbol table name index: %u"), strtab_shndx);
      this->error(_("invalid symbol table name index: %u"), strtab_shndx);
      return;
      return;
    }
    }
 
 
  elfcpp::Shdr<32, big_endian>
  elfcpp::Shdr<32, big_endian>
    strtabshdr(this, this->elf_file()->section_header(strtab_shndx));
    strtabshdr(this, this->elf_file()->section_header(strtab_shndx));
  if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
  if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
    {
    {
      this->error(_("symbol table name section has wrong type: %u"),
      this->error(_("symbol table name section has wrong type: %u"),
                  static_cast<unsigned int>(strtabshdr.get_sh_type()));
                  static_cast<unsigned int>(strtabshdr.get_sh_type()));
      return;
      return;
    }
    }
  const char* pnames =
  const char* pnames =
    reinterpret_cast<const char*>(this->get_view(strtabshdr.get_sh_offset(),
    reinterpret_cast<const char*>(this->get_view(strtabshdr.get_sh_offset(),
                                                 strtabshdr.get_sh_size(),
                                                 strtabshdr.get_sh_size(),
                                                 false, false));
                                                 false, false));
 
 
  // Loop over the local symbols and mark any local symbols pointing
  // Loop over the local symbols and mark any local symbols pointing
  // to THUMB functions.
  // to THUMB functions.
 
 
  // Skip the first dummy symbol.
  // Skip the first dummy symbol.
  psyms += sym_size;
  psyms += sym_size;
  typename Sized_relobj_file<32, big_endian>::Local_values* plocal_values =
  typename Sized_relobj_file<32, big_endian>::Local_values* plocal_values =
    this->local_values();
    this->local_values();
  for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
  for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
    {
    {
      elfcpp::Sym<32, big_endian> sym(psyms);
      elfcpp::Sym<32, big_endian> sym(psyms);
      elfcpp::STT st_type = sym.get_st_type();
      elfcpp::STT st_type = sym.get_st_type();
      Symbol_value<32>& lv((*plocal_values)[i]);
      Symbol_value<32>& lv((*plocal_values)[i]);
      Arm_address input_value = lv.input_value();
      Arm_address input_value = lv.input_value();
 
 
      // Check to see if this is a mapping symbol.
      // Check to see if this is a mapping symbol.
      const char* sym_name = pnames + sym.get_st_name();
      const char* sym_name = pnames + sym.get_st_name();
      if (Target_arm<big_endian>::is_mapping_symbol_name(sym_name))
      if (Target_arm<big_endian>::is_mapping_symbol_name(sym_name))
        {
        {
          bool is_ordinary;
          bool is_ordinary;
          unsigned int input_shndx =
          unsigned int input_shndx =
            this->adjust_sym_shndx(i, sym.get_st_shndx(), &is_ordinary);
            this->adjust_sym_shndx(i, sym.get_st_shndx(), &is_ordinary);
          gold_assert(is_ordinary);
          gold_assert(is_ordinary);
 
 
          // Strip of LSB in case this is a THUMB symbol.
          // Strip of LSB in case this is a THUMB symbol.
          Mapping_symbol_position msp(input_shndx, input_value & ~1U);
          Mapping_symbol_position msp(input_shndx, input_value & ~1U);
          this->mapping_symbols_info_[msp] = sym_name[1];
          this->mapping_symbols_info_[msp] = sym_name[1];
        }
        }
 
 
      if (st_type == elfcpp::STT_ARM_TFUNC
      if (st_type == elfcpp::STT_ARM_TFUNC
          || (st_type == elfcpp::STT_FUNC && ((input_value & 1) != 0)))
          || (st_type == elfcpp::STT_FUNC && ((input_value & 1) != 0)))
        {
        {
          // This is a THUMB function.  Mark this and canonicalize the
          // This is a THUMB function.  Mark this and canonicalize the
          // symbol value by setting LSB.
          // symbol value by setting LSB.
          this->local_symbol_is_thumb_function_[i] = true;
          this->local_symbol_is_thumb_function_[i] = true;
          if ((input_value & 1) == 0)
          if ((input_value & 1) == 0)
            lv.set_input_value(input_value | 1);
            lv.set_input_value(input_value | 1);
        }
        }
    }
    }
}
}
 
 
// Relocate sections.
// Relocate sections.
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::do_relocate_sections(
Arm_relobj<big_endian>::do_relocate_sections(
    const Symbol_table* symtab,
    const Symbol_table* symtab,
    const Layout* layout,
    const Layout* layout,
    const unsigned char* pshdrs,
    const unsigned char* pshdrs,
    Output_file* of,
    Output_file* of,
    typename Sized_relobj_file<32, big_endian>::Views* pviews)
    typename Sized_relobj_file<32, big_endian>::Views* pviews)
{
{
  // Call parent to relocate sections.
  // Call parent to relocate sections.
  Sized_relobj_file<32, big_endian>::do_relocate_sections(symtab, layout,
  Sized_relobj_file<32, big_endian>::do_relocate_sections(symtab, layout,
                                                          pshdrs, of, pviews);
                                                          pshdrs, of, pviews);
 
 
  // We do not generate stubs if doing a relocatable link.
  // We do not generate stubs if doing a relocatable link.
  if (parameters->options().relocatable())
  if (parameters->options().relocatable())
    return;
    return;
 
 
  // Relocate stub tables.
  // Relocate stub tables.
  unsigned int shnum = this->shnum();
  unsigned int shnum = this->shnum();
 
 
  Target_arm<big_endian>* arm_target =
  Target_arm<big_endian>* arm_target =
    Target_arm<big_endian>::default_target();
    Target_arm<big_endian>::default_target();
 
 
  Relocate_info<32, big_endian> relinfo;
  Relocate_info<32, big_endian> relinfo;
  relinfo.symtab = symtab;
  relinfo.symtab = symtab;
  relinfo.layout = layout;
  relinfo.layout = layout;
  relinfo.object = this;
  relinfo.object = this;
 
 
  for (unsigned int i = 1; i < shnum; ++i)
  for (unsigned int i = 1; i < shnum; ++i)
    {
    {
      Arm_input_section<big_endian>* arm_input_section =
      Arm_input_section<big_endian>* arm_input_section =
        arm_target->find_arm_input_section(this, i);
        arm_target->find_arm_input_section(this, i);
 
 
      if (arm_input_section != NULL
      if (arm_input_section != NULL
          && arm_input_section->is_stub_table_owner()
          && arm_input_section->is_stub_table_owner()
          && !arm_input_section->stub_table()->empty())
          && !arm_input_section->stub_table()->empty())
        {
        {
          // We cannot discard a section if it owns a stub table.
          // We cannot discard a section if it owns a stub table.
          Output_section* os = this->output_section(i);
          Output_section* os = this->output_section(i);
          gold_assert(os != NULL);
          gold_assert(os != NULL);
 
 
          relinfo.reloc_shndx = elfcpp::SHN_UNDEF;
          relinfo.reloc_shndx = elfcpp::SHN_UNDEF;
          relinfo.reloc_shdr = NULL;
          relinfo.reloc_shdr = NULL;
          relinfo.data_shndx = i;
          relinfo.data_shndx = i;
          relinfo.data_shdr = pshdrs + i * elfcpp::Elf_sizes<32>::shdr_size;
          relinfo.data_shdr = pshdrs + i * elfcpp::Elf_sizes<32>::shdr_size;
 
 
          gold_assert((*pviews)[i].view != NULL);
          gold_assert((*pviews)[i].view != NULL);
 
 
          // We are passed the output section view.  Adjust it to cover the
          // We are passed the output section view.  Adjust it to cover the
          // stub table only.
          // stub table only.
          Stub_table<big_endian>* stub_table = arm_input_section->stub_table();
          Stub_table<big_endian>* stub_table = arm_input_section->stub_table();
          gold_assert((stub_table->address() >= (*pviews)[i].address)
          gold_assert((stub_table->address() >= (*pviews)[i].address)
                      && ((stub_table->address() + stub_table->data_size())
                      && ((stub_table->address() + stub_table->data_size())
                          <= (*pviews)[i].address + (*pviews)[i].view_size));
                          <= (*pviews)[i].address + (*pviews)[i].view_size));
 
 
          off_t offset = stub_table->address() - (*pviews)[i].address;
          off_t offset = stub_table->address() - (*pviews)[i].address;
          unsigned char* view = (*pviews)[i].view + offset;
          unsigned char* view = (*pviews)[i].view + offset;
          Arm_address address = stub_table->address();
          Arm_address address = stub_table->address();
          section_size_type view_size = stub_table->data_size();
          section_size_type view_size = stub_table->data_size();
 
 
          stub_table->relocate_stubs(&relinfo, arm_target, os, view, address,
          stub_table->relocate_stubs(&relinfo, arm_target, os, view, address,
                                     view_size);
                                     view_size);
        }
        }
 
 
      // Apply Cortex A8 workaround if applicable.
      // Apply Cortex A8 workaround if applicable.
      if (this->section_has_cortex_a8_workaround(i))
      if (this->section_has_cortex_a8_workaround(i))
        {
        {
          unsigned char* view = (*pviews)[i].view;
          unsigned char* view = (*pviews)[i].view;
          Arm_address view_address = (*pviews)[i].address;
          Arm_address view_address = (*pviews)[i].address;
          section_size_type view_size = (*pviews)[i].view_size;
          section_size_type view_size = (*pviews)[i].view_size;
          Stub_table<big_endian>* stub_table = this->stub_tables_[i];
          Stub_table<big_endian>* stub_table = this->stub_tables_[i];
 
 
          // Adjust view to cover section.
          // Adjust view to cover section.
          Output_section* os = this->output_section(i);
          Output_section* os = this->output_section(i);
          gold_assert(os != NULL);
          gold_assert(os != NULL);
          Arm_address section_address =
          Arm_address section_address =
            this->simple_input_section_output_address(i, os);
            this->simple_input_section_output_address(i, os);
          uint64_t section_size = this->section_size(i);
          uint64_t section_size = this->section_size(i);
 
 
          gold_assert(section_address >= view_address
          gold_assert(section_address >= view_address
                      && ((section_address + section_size)
                      && ((section_address + section_size)
                          <= (view_address + view_size)));
                          <= (view_address + view_size)));
 
 
          unsigned char* section_view = view + (section_address - view_address);
          unsigned char* section_view = view + (section_address - view_address);
 
 
          // Apply the Cortex-A8 workaround to the output address range
          // Apply the Cortex-A8 workaround to the output address range
          // corresponding to this input section.
          // corresponding to this input section.
          stub_table->apply_cortex_a8_workaround_to_address_range(
          stub_table->apply_cortex_a8_workaround_to_address_range(
              arm_target,
              arm_target,
              section_view,
              section_view,
              section_address,
              section_address,
              section_size);
              section_size);
        }
        }
    }
    }
}
}
 
 
// Find the linked text section of an EXIDX section by looking at the first
// Find the linked text section of an EXIDX section by looking at the first
// relocation.  4.4.1 of the EHABI specifications says that an EXIDX section
// relocation.  4.4.1 of the EHABI specifications says that an EXIDX section
// must be linked to its associated code section via the sh_link field of
// must be linked to its associated code section via the sh_link field of
// its section header.  However, some tools are broken and the link is not
// its section header.  However, some tools are broken and the link is not
// always set.  LD just drops such an EXIDX section silently, causing the
// always set.  LD just drops such an EXIDX section silently, causing the
// associated code not unwindabled.   Here we try a little bit harder to
// associated code not unwindabled.   Here we try a little bit harder to
// discover the linked code section.
// discover the linked code section.
//
//
// PSHDR points to the section header of a relocation section of an EXIDX
// PSHDR points to the section header of a relocation section of an EXIDX
// section.  If we can find a linked text section, return true and
// section.  If we can find a linked text section, return true and
// store the text section index in the location PSHNDX.  Otherwise
// store the text section index in the location PSHNDX.  Otherwise
// return false.
// return false.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Arm_relobj<big_endian>::find_linked_text_section(
Arm_relobj<big_endian>::find_linked_text_section(
    const unsigned char* pshdr,
    const unsigned char* pshdr,
    const unsigned char* psyms,
    const unsigned char* psyms,
    unsigned int* pshndx)
    unsigned int* pshndx)
{
{
  elfcpp::Shdr<32, big_endian> shdr(pshdr);
  elfcpp::Shdr<32, big_endian> shdr(pshdr);
 
 
  // If there is no relocation, we cannot find the linked text section.
  // If there is no relocation, we cannot find the linked text section.
  size_t reloc_size;
  size_t reloc_size;
  if (shdr.get_sh_type() == elfcpp::SHT_REL)
  if (shdr.get_sh_type() == elfcpp::SHT_REL)
      reloc_size = elfcpp::Elf_sizes<32>::rel_size;
      reloc_size = elfcpp::Elf_sizes<32>::rel_size;
  else
  else
      reloc_size = elfcpp::Elf_sizes<32>::rela_size;
      reloc_size = elfcpp::Elf_sizes<32>::rela_size;
  size_t reloc_count = shdr.get_sh_size() / reloc_size;
  size_t reloc_count = shdr.get_sh_size() / reloc_size;
 
 
  // Get the relocations.
  // Get the relocations.
  const unsigned char* prelocs =
  const unsigned char* prelocs =
      this->get_view(shdr.get_sh_offset(), shdr.get_sh_size(), true, false);
      this->get_view(shdr.get_sh_offset(), shdr.get_sh_size(), true, false);
 
 
  // Find the REL31 relocation for the first word of the first EXIDX entry.
  // Find the REL31 relocation for the first word of the first EXIDX entry.
  for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
  for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
    {
    {
      Arm_address r_offset;
      Arm_address r_offset;
      typename elfcpp::Elf_types<32>::Elf_WXword r_info;
      typename elfcpp::Elf_types<32>::Elf_WXword r_info;
      if (shdr.get_sh_type() == elfcpp::SHT_REL)
      if (shdr.get_sh_type() == elfcpp::SHT_REL)
        {
        {
          typename elfcpp::Rel<32, big_endian> reloc(prelocs);
          typename elfcpp::Rel<32, big_endian> reloc(prelocs);
          r_info = reloc.get_r_info();
          r_info = reloc.get_r_info();
          r_offset = reloc.get_r_offset();
          r_offset = reloc.get_r_offset();
        }
        }
      else
      else
        {
        {
          typename elfcpp::Rela<32, big_endian> reloc(prelocs);
          typename elfcpp::Rela<32, big_endian> reloc(prelocs);
          r_info = reloc.get_r_info();
          r_info = reloc.get_r_info();
          r_offset = reloc.get_r_offset();
          r_offset = reloc.get_r_offset();
        }
        }
 
 
      unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
      unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
      if (r_type != elfcpp::R_ARM_PREL31 && r_type != elfcpp::R_ARM_SBREL31)
      if (r_type != elfcpp::R_ARM_PREL31 && r_type != elfcpp::R_ARM_SBREL31)
        continue;
        continue;
 
 
      unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
      unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
      if (r_sym == 0
      if (r_sym == 0
          || r_sym >= this->local_symbol_count()
          || r_sym >= this->local_symbol_count()
          || r_offset != 0)
          || r_offset != 0)
        continue;
        continue;
 
 
      // This is the relocation for the first word of the first EXIDX entry.
      // This is the relocation for the first word of the first EXIDX entry.
      // We expect to see a local section symbol.
      // We expect to see a local section symbol.
      const int sym_size = elfcpp::Elf_sizes<32>::sym_size;
      const int sym_size = elfcpp::Elf_sizes<32>::sym_size;
      elfcpp::Sym<32, big_endian> sym(psyms + r_sym * sym_size);
      elfcpp::Sym<32, big_endian> sym(psyms + r_sym * sym_size);
      if (sym.get_st_type() == elfcpp::STT_SECTION)
      if (sym.get_st_type() == elfcpp::STT_SECTION)
        {
        {
          bool is_ordinary;
          bool is_ordinary;
          *pshndx =
          *pshndx =
            this->adjust_sym_shndx(r_sym, sym.get_st_shndx(), &is_ordinary);
            this->adjust_sym_shndx(r_sym, sym.get_st_shndx(), &is_ordinary);
          gold_assert(is_ordinary);
          gold_assert(is_ordinary);
          return true;
          return true;
        }
        }
      else
      else
        return false;
        return false;
    }
    }
 
 
  return false;
  return false;
}
}
 
 
// Make an EXIDX input section object for an EXIDX section whose index is
// Make an EXIDX input section object for an EXIDX section whose index is
// SHNDX.  SHDR is the section header of the EXIDX section and TEXT_SHNDX
// SHNDX.  SHDR is the section header of the EXIDX section and TEXT_SHNDX
// is the section index of the linked text section.
// is the section index of the linked text section.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::make_exidx_input_section(
Arm_relobj<big_endian>::make_exidx_input_section(
    unsigned int shndx,
    unsigned int shndx,
    const elfcpp::Shdr<32, big_endian>& shdr,
    const elfcpp::Shdr<32, big_endian>& shdr,
    unsigned int text_shndx,
    unsigned int text_shndx,
    const elfcpp::Shdr<32, big_endian>& text_shdr)
    const elfcpp::Shdr<32, big_endian>& text_shdr)
{
{
  // Create an Arm_exidx_input_section object for this EXIDX section.
  // Create an Arm_exidx_input_section object for this EXIDX section.
  Arm_exidx_input_section* exidx_input_section =
  Arm_exidx_input_section* exidx_input_section =
    new Arm_exidx_input_section(this, shndx, text_shndx, shdr.get_sh_size(),
    new Arm_exidx_input_section(this, shndx, text_shndx, shdr.get_sh_size(),
                                shdr.get_sh_addralign(),
                                shdr.get_sh_addralign(),
                                text_shdr.get_sh_size());
                                text_shdr.get_sh_size());
 
 
  gold_assert(this->exidx_section_map_[shndx] == NULL);
  gold_assert(this->exidx_section_map_[shndx] == NULL);
  this->exidx_section_map_[shndx] = exidx_input_section;
  this->exidx_section_map_[shndx] = exidx_input_section;
 
 
  if (text_shndx == elfcpp::SHN_UNDEF || text_shndx >= this->shnum())
  if (text_shndx == elfcpp::SHN_UNDEF || text_shndx >= this->shnum())
    {
    {
      gold_error(_("EXIDX section %s(%u) links to invalid section %u in %s"),
      gold_error(_("EXIDX section %s(%u) links to invalid section %u in %s"),
                 this->section_name(shndx).c_str(), shndx, text_shndx,
                 this->section_name(shndx).c_str(), shndx, text_shndx,
                 this->name().c_str());
                 this->name().c_str());
      exidx_input_section->set_has_errors();
      exidx_input_section->set_has_errors();
    }
    }
  else if (this->exidx_section_map_[text_shndx] != NULL)
  else if (this->exidx_section_map_[text_shndx] != NULL)
    {
    {
      unsigned other_exidx_shndx =
      unsigned other_exidx_shndx =
        this->exidx_section_map_[text_shndx]->shndx();
        this->exidx_section_map_[text_shndx]->shndx();
      gold_error(_("EXIDX sections %s(%u) and %s(%u) both link to text section"
      gold_error(_("EXIDX sections %s(%u) and %s(%u) both link to text section"
                   "%s(%u) in %s"),
                   "%s(%u) in %s"),
                 this->section_name(shndx).c_str(), shndx,
                 this->section_name(shndx).c_str(), shndx,
                 this->section_name(other_exidx_shndx).c_str(),
                 this->section_name(other_exidx_shndx).c_str(),
                 other_exidx_shndx, this->section_name(text_shndx).c_str(),
                 other_exidx_shndx, this->section_name(text_shndx).c_str(),
                 text_shndx, this->name().c_str());
                 text_shndx, this->name().c_str());
      exidx_input_section->set_has_errors();
      exidx_input_section->set_has_errors();
    }
    }
  else
  else
     this->exidx_section_map_[text_shndx] = exidx_input_section;
     this->exidx_section_map_[text_shndx] = exidx_input_section;
 
 
  // Check section flags of text section.
  // Check section flags of text section.
  if ((text_shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
  if ((text_shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
    {
    {
      gold_error(_("EXIDX section %s(%u) links to non-allocated section %s(%u) "
      gold_error(_("EXIDX section %s(%u) links to non-allocated section %s(%u) "
                   " in %s"),
                   " in %s"),
                 this->section_name(shndx).c_str(), shndx,
                 this->section_name(shndx).c_str(), shndx,
                 this->section_name(text_shndx).c_str(), text_shndx,
                 this->section_name(text_shndx).c_str(), text_shndx,
                 this->name().c_str());
                 this->name().c_str());
      exidx_input_section->set_has_errors();
      exidx_input_section->set_has_errors();
    }
    }
  else if ((text_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) == 0)
  else if ((text_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) == 0)
    // I would like to make this an error but currently ld just ignores
    // I would like to make this an error but currently ld just ignores
    // this.
    // this.
    gold_warning(_("EXIDX section %s(%u) links to non-executable section "
    gold_warning(_("EXIDX section %s(%u) links to non-executable section "
                   "%s(%u) in %s"),
                   "%s(%u) in %s"),
                 this->section_name(shndx).c_str(), shndx,
                 this->section_name(shndx).c_str(), shndx,
                 this->section_name(text_shndx).c_str(), text_shndx,
                 this->section_name(text_shndx).c_str(), text_shndx,
                 this->name().c_str());
                 this->name().c_str());
}
}
 
 
// Read the symbol information.
// Read the symbol information.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
Arm_relobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
{
{
  // Call parent class to read symbol information.
  // Call parent class to read symbol information.
  Sized_relobj_file<32, big_endian>::do_read_symbols(sd);
  Sized_relobj_file<32, big_endian>::do_read_symbols(sd);
 
 
  // If this input file is a binary file, it has no processor
  // If this input file is a binary file, it has no processor
  // specific flags and attributes section.
  // specific flags and attributes section.
  Input_file::Format format = this->input_file()->format();
  Input_file::Format format = this->input_file()->format();
  if (format != Input_file::FORMAT_ELF)
  if (format != Input_file::FORMAT_ELF)
    {
    {
      gold_assert(format == Input_file::FORMAT_BINARY);
      gold_assert(format == Input_file::FORMAT_BINARY);
      this->merge_flags_and_attributes_ = false;
      this->merge_flags_and_attributes_ = false;
      return;
      return;
    }
    }
 
 
  // Read processor-specific flags in ELF file header.
  // Read processor-specific flags in ELF file header.
  const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
  const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
                                              elfcpp::Elf_sizes<32>::ehdr_size,
                                              elfcpp::Elf_sizes<32>::ehdr_size,
                                              true, false);
                                              true, false);
  elfcpp::Ehdr<32, big_endian> ehdr(pehdr);
  elfcpp::Ehdr<32, big_endian> ehdr(pehdr);
  this->processor_specific_flags_ = ehdr.get_e_flags();
  this->processor_specific_flags_ = ehdr.get_e_flags();
 
 
  // Go over the section headers and look for .ARM.attributes and .ARM.exidx
  // Go over the section headers and look for .ARM.attributes and .ARM.exidx
  // sections.
  // sections.
  std::vector<unsigned int> deferred_exidx_sections;
  std::vector<unsigned int> deferred_exidx_sections;
  const size_t shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const size_t shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const unsigned char* pshdrs = sd->section_headers->data();
  const unsigned char* pshdrs = sd->section_headers->data();
  const unsigned char* ps = pshdrs + shdr_size;
  const unsigned char* ps = pshdrs + shdr_size;
  bool must_merge_flags_and_attributes = false;
  bool must_merge_flags_and_attributes = false;
  for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size)
  for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size)
    {
    {
      elfcpp::Shdr<32, big_endian> shdr(ps);
      elfcpp::Shdr<32, big_endian> shdr(ps);
 
 
      // Sometimes an object has no contents except the section name string
      // Sometimes an object has no contents except the section name string
      // table and an empty symbol table with the undefined symbol.  We
      // table and an empty symbol table with the undefined symbol.  We
      // don't want to merge processor-specific flags from such an object.
      // don't want to merge processor-specific flags from such an object.
      if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
      if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
        {
        {
          // Symbol table is not empty.
          // Symbol table is not empty.
          const elfcpp::Elf_types<32>::Elf_WXword sym_size =
          const elfcpp::Elf_types<32>::Elf_WXword sym_size =
             elfcpp::Elf_sizes<32>::sym_size;
             elfcpp::Elf_sizes<32>::sym_size;
          if (shdr.get_sh_size() > sym_size)
          if (shdr.get_sh_size() > sym_size)
            must_merge_flags_and_attributes = true;
            must_merge_flags_and_attributes = true;
        }
        }
      else if (shdr.get_sh_type() != elfcpp::SHT_STRTAB)
      else if (shdr.get_sh_type() != elfcpp::SHT_STRTAB)
        // If this is neither an empty symbol table nor a string table,
        // If this is neither an empty symbol table nor a string table,
        // be conservative.
        // be conservative.
        must_merge_flags_and_attributes = true;
        must_merge_flags_and_attributes = true;
 
 
      if (shdr.get_sh_type() == elfcpp::SHT_ARM_ATTRIBUTES)
      if (shdr.get_sh_type() == elfcpp::SHT_ARM_ATTRIBUTES)
        {
        {
          gold_assert(this->attributes_section_data_ == NULL);
          gold_assert(this->attributes_section_data_ == NULL);
          section_offset_type section_offset = shdr.get_sh_offset();
          section_offset_type section_offset = shdr.get_sh_offset();
          section_size_type section_size =
          section_size_type section_size =
            convert_to_section_size_type(shdr.get_sh_size());
            convert_to_section_size_type(shdr.get_sh_size());
          const unsigned char* view =
          const unsigned char* view =
             this->get_view(section_offset, section_size, true, false);
             this->get_view(section_offset, section_size, true, false);
          this->attributes_section_data_ =
          this->attributes_section_data_ =
            new Attributes_section_data(view, section_size);
            new Attributes_section_data(view, section_size);
        }
        }
      else if (shdr.get_sh_type() == elfcpp::SHT_ARM_EXIDX)
      else if (shdr.get_sh_type() == elfcpp::SHT_ARM_EXIDX)
        {
        {
          unsigned int text_shndx = this->adjust_shndx(shdr.get_sh_link());
          unsigned int text_shndx = this->adjust_shndx(shdr.get_sh_link());
          if (text_shndx == elfcpp::SHN_UNDEF)
          if (text_shndx == elfcpp::SHN_UNDEF)
            deferred_exidx_sections.push_back(i);
            deferred_exidx_sections.push_back(i);
          else
          else
            {
            {
              elfcpp::Shdr<32, big_endian> text_shdr(pshdrs
              elfcpp::Shdr<32, big_endian> text_shdr(pshdrs
                                                     + text_shndx * shdr_size);
                                                     + text_shndx * shdr_size);
              this->make_exidx_input_section(i, shdr, text_shndx, text_shdr);
              this->make_exidx_input_section(i, shdr, text_shndx, text_shdr);
            }
            }
          // EHABI 4.4.1 requires that SHF_LINK_ORDER flag to be set.
          // EHABI 4.4.1 requires that SHF_LINK_ORDER flag to be set.
          if ((shdr.get_sh_flags() & elfcpp::SHF_LINK_ORDER) == 0)
          if ((shdr.get_sh_flags() & elfcpp::SHF_LINK_ORDER) == 0)
            gold_warning(_("SHF_LINK_ORDER not set in EXIDX section %s of %s"),
            gold_warning(_("SHF_LINK_ORDER not set in EXIDX section %s of %s"),
                         this->section_name(i).c_str(), this->name().c_str());
                         this->section_name(i).c_str(), this->name().c_str());
        }
        }
    }
    }
 
 
  // This is rare.
  // This is rare.
  if (!must_merge_flags_and_attributes)
  if (!must_merge_flags_and_attributes)
    {
    {
      gold_assert(deferred_exidx_sections.empty());
      gold_assert(deferred_exidx_sections.empty());
      this->merge_flags_and_attributes_ = false;
      this->merge_flags_and_attributes_ = false;
      return;
      return;
    }
    }
 
 
  // Some tools are broken and they do not set the link of EXIDX sections. 
  // Some tools are broken and they do not set the link of EXIDX sections. 
  // We look at the first relocation to figure out the linked sections.
  // We look at the first relocation to figure out the linked sections.
  if (!deferred_exidx_sections.empty())
  if (!deferred_exidx_sections.empty())
    {
    {
      // We need to go over the section headers again to find the mapping
      // We need to go over the section headers again to find the mapping
      // from sections being relocated to their relocation sections.  This is
      // from sections being relocated to their relocation sections.  This is
      // a bit inefficient as we could do that in the loop above.  However,
      // a bit inefficient as we could do that in the loop above.  However,
      // we do not expect any deferred EXIDX sections normally.  So we do not
      // we do not expect any deferred EXIDX sections normally.  So we do not
      // want to slow down the most common path.
      // want to slow down the most common path.
      typedef Unordered_map<unsigned int, unsigned int> Reloc_map;
      typedef Unordered_map<unsigned int, unsigned int> Reloc_map;
      Reloc_map reloc_map;
      Reloc_map reloc_map;
      ps = pshdrs + shdr_size;
      ps = pshdrs + shdr_size;
      for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size)
      for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size)
        {
        {
          elfcpp::Shdr<32, big_endian> shdr(ps);
          elfcpp::Shdr<32, big_endian> shdr(ps);
          elfcpp::Elf_Word sh_type = shdr.get_sh_type();
          elfcpp::Elf_Word sh_type = shdr.get_sh_type();
          if (sh_type == elfcpp::SHT_REL || sh_type == elfcpp::SHT_RELA)
          if (sh_type == elfcpp::SHT_REL || sh_type == elfcpp::SHT_RELA)
            {
            {
              unsigned int info_shndx = this->adjust_shndx(shdr.get_sh_info());
              unsigned int info_shndx = this->adjust_shndx(shdr.get_sh_info());
              if (info_shndx >= this->shnum())
              if (info_shndx >= this->shnum())
                gold_error(_("relocation section %u has invalid info %u"),
                gold_error(_("relocation section %u has invalid info %u"),
                           i, info_shndx);
                           i, info_shndx);
              Reloc_map::value_type value(info_shndx, i);
              Reloc_map::value_type value(info_shndx, i);
              std::pair<Reloc_map::iterator, bool> result =
              std::pair<Reloc_map::iterator, bool> result =
                reloc_map.insert(value);
                reloc_map.insert(value);
              if (!result.second)
              if (!result.second)
                gold_error(_("section %u has multiple relocation sections "
                gold_error(_("section %u has multiple relocation sections "
                             "%u and %u"),
                             "%u and %u"),
                           info_shndx, i, reloc_map[info_shndx]);
                           info_shndx, i, reloc_map[info_shndx]);
            }
            }
        }
        }
 
 
      // Read the symbol table section header.
      // Read the symbol table section header.
      const unsigned int symtab_shndx = this->symtab_shndx();
      const unsigned int symtab_shndx = this->symtab_shndx();
      elfcpp::Shdr<32, big_endian>
      elfcpp::Shdr<32, big_endian>
          symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
          symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
      gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
      gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
 
 
      // Read the local symbols.
      // Read the local symbols.
      const int sym_size =elfcpp::Elf_sizes<32>::sym_size;
      const int sym_size =elfcpp::Elf_sizes<32>::sym_size;
      const unsigned int loccount = this->local_symbol_count();
      const unsigned int loccount = this->local_symbol_count();
      gold_assert(loccount == symtabshdr.get_sh_info());
      gold_assert(loccount == symtabshdr.get_sh_info());
      off_t locsize = loccount * sym_size;
      off_t locsize = loccount * sym_size;
      const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
      const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
                                                  locsize, true, true);
                                                  locsize, true, true);
 
 
      // Process the deferred EXIDX sections. 
      // Process the deferred EXIDX sections. 
      for (unsigned int i = 0; i < deferred_exidx_sections.size(); ++i)
      for (unsigned int i = 0; i < deferred_exidx_sections.size(); ++i)
        {
        {
          unsigned int shndx = deferred_exidx_sections[i];
          unsigned int shndx = deferred_exidx_sections[i];
          elfcpp::Shdr<32, big_endian> shdr(pshdrs + shndx * shdr_size);
          elfcpp::Shdr<32, big_endian> shdr(pshdrs + shndx * shdr_size);
          unsigned int text_shndx = elfcpp::SHN_UNDEF;
          unsigned int text_shndx = elfcpp::SHN_UNDEF;
          Reloc_map::const_iterator it = reloc_map.find(shndx);
          Reloc_map::const_iterator it = reloc_map.find(shndx);
          if (it != reloc_map.end())
          if (it != reloc_map.end())
            find_linked_text_section(pshdrs + it->second * shdr_size,
            find_linked_text_section(pshdrs + it->second * shdr_size,
                                     psyms, &text_shndx);
                                     psyms, &text_shndx);
          elfcpp::Shdr<32, big_endian> text_shdr(pshdrs
          elfcpp::Shdr<32, big_endian> text_shdr(pshdrs
                                                 + text_shndx * shdr_size);
                                                 + text_shndx * shdr_size);
          this->make_exidx_input_section(shndx, shdr, text_shndx, text_shdr);
          this->make_exidx_input_section(shndx, shdr, text_shndx, text_shdr);
        }
        }
    }
    }
}
}
 
 
// Process relocations for garbage collection.  The ARM target uses .ARM.exidx
// Process relocations for garbage collection.  The ARM target uses .ARM.exidx
// sections for unwinding.  These sections are referenced implicitly by 
// sections for unwinding.  These sections are referenced implicitly by 
// text sections linked in the section headers.  If we ignore these implicit
// text sections linked in the section headers.  If we ignore these implicit
// references, the .ARM.exidx sections and any .ARM.extab sections they use
// references, the .ARM.exidx sections and any .ARM.extab sections they use
// will be garbage-collected incorrectly.  Hence we override the same function
// will be garbage-collected incorrectly.  Hence we override the same function
// in the base class to handle these implicit references.
// in the base class to handle these implicit references.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::do_gc_process_relocs(Symbol_table* symtab,
Arm_relobj<big_endian>::do_gc_process_relocs(Symbol_table* symtab,
                                             Layout* layout,
                                             Layout* layout,
                                             Read_relocs_data* rd)
                                             Read_relocs_data* rd)
{
{
  // First, call base class method to process relocations in this object.
  // First, call base class method to process relocations in this object.
  Sized_relobj_file<32, big_endian>::do_gc_process_relocs(symtab, layout, rd);
  Sized_relobj_file<32, big_endian>::do_gc_process_relocs(symtab, layout, rd);
 
 
  // If --gc-sections is not specified, there is nothing more to do.
  // If --gc-sections is not specified, there is nothing more to do.
  // This happens when --icf is used but --gc-sections is not.
  // This happens when --icf is used but --gc-sections is not.
  if (!parameters->options().gc_sections())
  if (!parameters->options().gc_sections())
    return;
    return;
 
 
  unsigned int shnum = this->shnum();
  unsigned int shnum = this->shnum();
  const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const unsigned char* pshdrs = this->get_view(this->elf_file()->shoff(),
  const unsigned char* pshdrs = this->get_view(this->elf_file()->shoff(),
                                               shnum * shdr_size,
                                               shnum * shdr_size,
                                               true, true);
                                               true, true);
 
 
  // Scan section headers for sections of type SHT_ARM_EXIDX.  Add references
  // Scan section headers for sections of type SHT_ARM_EXIDX.  Add references
  // to these from the linked text sections.
  // to these from the linked text sections.
  const unsigned char* ps = pshdrs + shdr_size;
  const unsigned char* ps = pshdrs + shdr_size;
  for (unsigned int i = 1; i < shnum; ++i, ps += shdr_size)
  for (unsigned int i = 1; i < shnum; ++i, ps += shdr_size)
    {
    {
      elfcpp::Shdr<32, big_endian> shdr(ps);
      elfcpp::Shdr<32, big_endian> shdr(ps);
      if (shdr.get_sh_type() == elfcpp::SHT_ARM_EXIDX)
      if (shdr.get_sh_type() == elfcpp::SHT_ARM_EXIDX)
        {
        {
          // Found an .ARM.exidx section, add it to the set of reachable
          // Found an .ARM.exidx section, add it to the set of reachable
          // sections from its linked text section.
          // sections from its linked text section.
          unsigned int text_shndx = this->adjust_shndx(shdr.get_sh_link());
          unsigned int text_shndx = this->adjust_shndx(shdr.get_sh_link());
          symtab->gc()->add_reference(this, text_shndx, this, i);
          symtab->gc()->add_reference(this, text_shndx, this, i);
        }
        }
    }
    }
}
}
 
 
// Update output local symbol count.  Owing to EXIDX entry merging, some local
// Update output local symbol count.  Owing to EXIDX entry merging, some local
// symbols  will be removed in output.  Adjust output local symbol count
// symbols  will be removed in output.  Adjust output local symbol count
// accordingly.  We can only changed the static output local symbol count.  It
// accordingly.  We can only changed the static output local symbol count.  It
// is too late to change the dynamic symbols.
// is too late to change the dynamic symbols.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_relobj<big_endian>::update_output_local_symbol_count()
Arm_relobj<big_endian>::update_output_local_symbol_count()
{
{
  // Caller should check that this needs updating.  We want caller checking
  // Caller should check that this needs updating.  We want caller checking
  // because output_local_symbol_count_needs_update() is most likely inlined.
  // because output_local_symbol_count_needs_update() is most likely inlined.
  gold_assert(this->output_local_symbol_count_needs_update_);
  gold_assert(this->output_local_symbol_count_needs_update_);
 
 
  gold_assert(this->symtab_shndx() != -1U);
  gold_assert(this->symtab_shndx() != -1U);
  if (this->symtab_shndx() == 0)
  if (this->symtab_shndx() == 0)
    {
    {
      // This object has no symbols.  Weird but legal.
      // This object has no symbols.  Weird but legal.
      return;
      return;
    }
    }
 
 
  // Read the symbol table section header.
  // Read the symbol table section header.
  const unsigned int symtab_shndx = this->symtab_shndx();
  const unsigned int symtab_shndx = this->symtab_shndx();
  elfcpp::Shdr<32, big_endian>
  elfcpp::Shdr<32, big_endian>
    symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
    symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
  gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
  gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
 
 
  // Read the local symbols.
  // Read the local symbols.
  const int sym_size = elfcpp::Elf_sizes<32>::sym_size;
  const int sym_size = elfcpp::Elf_sizes<32>::sym_size;
  const unsigned int loccount = this->local_symbol_count();
  const unsigned int loccount = this->local_symbol_count();
  gold_assert(loccount == symtabshdr.get_sh_info());
  gold_assert(loccount == symtabshdr.get_sh_info());
  off_t locsize = loccount * sym_size;
  off_t locsize = loccount * sym_size;
  const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
  const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
                                              locsize, true, true);
                                              locsize, true, true);
 
 
  // Loop over the local symbols.
  // Loop over the local symbols.
 
 
  typedef typename Sized_relobj_file<32, big_endian>::Output_sections
  typedef typename Sized_relobj_file<32, big_endian>::Output_sections
     Output_sections;
     Output_sections;
  const Output_sections& out_sections(this->output_sections());
  const Output_sections& out_sections(this->output_sections());
  unsigned int shnum = this->shnum();
  unsigned int shnum = this->shnum();
  unsigned int count = 0;
  unsigned int count = 0;
  // Skip the first, dummy, symbol.
  // Skip the first, dummy, symbol.
  psyms += sym_size;
  psyms += sym_size;
  for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
  for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
    {
    {
      elfcpp::Sym<32, big_endian> sym(psyms);
      elfcpp::Sym<32, big_endian> sym(psyms);
 
 
      Symbol_value<32>& lv((*this->local_values())[i]);
      Symbol_value<32>& lv((*this->local_values())[i]);
 
 
      // This local symbol was already discarded by do_count_local_symbols.
      // This local symbol was already discarded by do_count_local_symbols.
      if (lv.is_output_symtab_index_set() && !lv.has_output_symtab_entry())
      if (lv.is_output_symtab_index_set() && !lv.has_output_symtab_entry())
        continue;
        continue;
 
 
      bool is_ordinary;
      bool is_ordinary;
      unsigned int shndx = this->adjust_sym_shndx(i, sym.get_st_shndx(),
      unsigned int shndx = this->adjust_sym_shndx(i, sym.get_st_shndx(),
                                                  &is_ordinary);
                                                  &is_ordinary);
 
 
      if (shndx < shnum)
      if (shndx < shnum)
        {
        {
          Output_section* os = out_sections[shndx];
          Output_section* os = out_sections[shndx];
 
 
          // This local symbol no longer has an output section.  Discard it.
          // This local symbol no longer has an output section.  Discard it.
          if (os == NULL)
          if (os == NULL)
            {
            {
              lv.set_no_output_symtab_entry();
              lv.set_no_output_symtab_entry();
              continue;
              continue;
            }
            }
 
 
          // Currently we only discard parts of EXIDX input sections.
          // Currently we only discard parts of EXIDX input sections.
          // We explicitly check for a merged EXIDX input section to avoid
          // We explicitly check for a merged EXIDX input section to avoid
          // calling Output_section_data::output_offset unless necessary.
          // calling Output_section_data::output_offset unless necessary.
          if ((this->get_output_section_offset(shndx) == invalid_address)
          if ((this->get_output_section_offset(shndx) == invalid_address)
              && (this->exidx_input_section_by_shndx(shndx) != NULL))
              && (this->exidx_input_section_by_shndx(shndx) != NULL))
            {
            {
              section_offset_type output_offset =
              section_offset_type output_offset =
                os->output_offset(this, shndx, lv.input_value());
                os->output_offset(this, shndx, lv.input_value());
              if (output_offset == -1)
              if (output_offset == -1)
                {
                {
                  // This symbol is defined in a part of an EXIDX input section
                  // This symbol is defined in a part of an EXIDX input section
                  // that is discarded due to entry merging.
                  // that is discarded due to entry merging.
                  lv.set_no_output_symtab_entry();
                  lv.set_no_output_symtab_entry();
                  continue;
                  continue;
                }
                }
            }
            }
        }
        }
 
 
      ++count;
      ++count;
    }
    }
 
 
  this->set_output_local_symbol_count(count);
  this->set_output_local_symbol_count(count);
  this->output_local_symbol_count_needs_update_ = false;
  this->output_local_symbol_count_needs_update_ = false;
}
}
 
 
// Arm_dynobj methods.
// Arm_dynobj methods.
 
 
// Read the symbol information.
// Read the symbol information.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_dynobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
Arm_dynobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
{
{
  // Call parent class to read symbol information.
  // Call parent class to read symbol information.
  Sized_dynobj<32, big_endian>::do_read_symbols(sd);
  Sized_dynobj<32, big_endian>::do_read_symbols(sd);
 
 
  // Read processor-specific flags in ELF file header.
  // Read processor-specific flags in ELF file header.
  const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
  const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
                                              elfcpp::Elf_sizes<32>::ehdr_size,
                                              elfcpp::Elf_sizes<32>::ehdr_size,
                                              true, false);
                                              true, false);
  elfcpp::Ehdr<32, big_endian> ehdr(pehdr);
  elfcpp::Ehdr<32, big_endian> ehdr(pehdr);
  this->processor_specific_flags_ = ehdr.get_e_flags();
  this->processor_specific_flags_ = ehdr.get_e_flags();
 
 
  // Read the attributes section if there is one.
  // Read the attributes section if there is one.
  // We read from the end because gas seems to put it near the end of
  // We read from the end because gas seems to put it near the end of
  // the section headers.
  // the section headers.
  const size_t shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const size_t shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
  const unsigned char* ps =
  const unsigned char* ps =
    sd->section_headers->data() + shdr_size * (this->shnum() - 1);
    sd->section_headers->data() + shdr_size * (this->shnum() - 1);
  for (unsigned int i = this->shnum(); i > 0; --i, ps -= shdr_size)
  for (unsigned int i = this->shnum(); i > 0; --i, ps -= shdr_size)
    {
    {
      elfcpp::Shdr<32, big_endian> shdr(ps);
      elfcpp::Shdr<32, big_endian> shdr(ps);
      if (shdr.get_sh_type() == elfcpp::SHT_ARM_ATTRIBUTES)
      if (shdr.get_sh_type() == elfcpp::SHT_ARM_ATTRIBUTES)
        {
        {
          section_offset_type section_offset = shdr.get_sh_offset();
          section_offset_type section_offset = shdr.get_sh_offset();
          section_size_type section_size =
          section_size_type section_size =
            convert_to_section_size_type(shdr.get_sh_size());
            convert_to_section_size_type(shdr.get_sh_size());
          const unsigned char* view =
          const unsigned char* view =
            this->get_view(section_offset, section_size, true, false);
            this->get_view(section_offset, section_size, true, false);
          this->attributes_section_data_ =
          this->attributes_section_data_ =
            new Attributes_section_data(view, section_size);
            new Attributes_section_data(view, section_size);
          break;
          break;
        }
        }
    }
    }
}
}
 
 
// Stub_addend_reader methods.
// Stub_addend_reader methods.
 
 
// Read the addend of a REL relocation of type R_TYPE at VIEW.
// Read the addend of a REL relocation of type R_TYPE at VIEW.
 
 
template<bool big_endian>
template<bool big_endian>
elfcpp::Elf_types<32>::Elf_Swxword
elfcpp::Elf_types<32>::Elf_Swxword
Stub_addend_reader<elfcpp::SHT_REL, big_endian>::operator()(
Stub_addend_reader<elfcpp::SHT_REL, big_endian>::operator()(
    unsigned int r_type,
    unsigned int r_type,
    const unsigned char* view,
    const unsigned char* view,
    const typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc&) const
    const typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc&) const
{
{
  typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
  typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
 
 
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
      {
      {
        typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
        typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
        const Valtype* wv = reinterpret_cast<const Valtype*>(view);
        const Valtype* wv = reinterpret_cast<const Valtype*>(view);
        Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
        Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
        return utils::sign_extend<26>(val << 2);
        return Bits<26>::sign_extend32(val << 2);
      }
      }
 
 
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_XPC22:
      {
      {
        typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
        typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
        const Valtype* wv = reinterpret_cast<const Valtype*>(view);
        const Valtype* wv = reinterpret_cast<const Valtype*>(view);
        Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
        Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
        Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
        Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
        return RelocFuncs::thumb32_branch_offset(upper_insn, lower_insn);
        return RelocFuncs::thumb32_branch_offset(upper_insn, lower_insn);
      }
      }
 
 
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
      {
      {
        typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
        typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
        const Valtype* wv = reinterpret_cast<const Valtype*>(view);
        const Valtype* wv = reinterpret_cast<const Valtype*>(view);
        Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
        Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
        Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
        Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
        return RelocFuncs::thumb32_cond_branch_offset(upper_insn, lower_insn);
        return RelocFuncs::thumb32_cond_branch_offset(upper_insn, lower_insn);
      }
      }
 
 
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
}
}
 
 
// Arm_output_data_got methods.
// Arm_output_data_got methods.
 
 
// Add a GOT pair for R_ARM_TLS_GD32.  The creates a pair of GOT entries.
// 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 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
// 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
// R_ARM_TLS_DTPOFF32 will be created for the second GOT entry and will
// be applied by gold.  GSYM is a global symbol.
// be applied by gold.  GSYM is a global symbol.
//
//
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_data_got<big_endian>::add_tls_gd32_with_static_reloc(
Arm_output_data_got<big_endian>::add_tls_gd32_with_static_reloc(
    unsigned int got_type,
    unsigned int got_type,
    Symbol* gsym)
    Symbol* gsym)
{
{
  if (gsym->has_got_offset(got_type))
  if (gsym->has_got_offset(got_type))
    return;
    return;
 
 
  // We are doing a static link.  Just mark it as belong to module 1,
  // We are doing a static link.  Just mark it as belong to module 1,
  // the executable.
  // the executable.
  unsigned int got_offset = this->add_constant(1);
  unsigned int got_offset = this->add_constant(1);
  gsym->set_got_offset(got_type, got_offset);
  gsym->set_got_offset(got_type, got_offset);
  got_offset = this->add_constant(0);
  got_offset = this->add_constant(0);
  this->static_relocs_.push_back(Static_reloc(got_offset,
  this->static_relocs_.push_back(Static_reloc(got_offset,
                                              elfcpp::R_ARM_TLS_DTPOFF32,
                                              elfcpp::R_ARM_TLS_DTPOFF32,
                                              gsym));
                                              gsym));
}
}
 
 
// Same as the above but for a local symbol.
// Same as the above but for a local symbol.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_data_got<big_endian>::add_tls_gd32_with_static_reloc(
Arm_output_data_got<big_endian>::add_tls_gd32_with_static_reloc(
  unsigned int got_type,
  unsigned int got_type,
  Sized_relobj_file<32, big_endian>* object,
  Sized_relobj_file<32, big_endian>* object,
  unsigned int index)
  unsigned int index)
{
{
  if (object->local_has_got_offset(index, got_type))
  if (object->local_has_got_offset(index, got_type))
    return;
    return;
 
 
  // We are doing a static link.  Just mark it as belong to module 1,
  // We are doing a static link.  Just mark it as belong to module 1,
  // the executable.
  // the executable.
  unsigned int got_offset = this->add_constant(1);
  unsigned int got_offset = this->add_constant(1);
  object->set_local_got_offset(index, got_type, got_offset);
  object->set_local_got_offset(index, got_type, got_offset);
  got_offset = this->add_constant(0);
  got_offset = this->add_constant(0);
  this->static_relocs_.push_back(Static_reloc(got_offset,
  this->static_relocs_.push_back(Static_reloc(got_offset,
                                              elfcpp::R_ARM_TLS_DTPOFF32,
                                              elfcpp::R_ARM_TLS_DTPOFF32,
                                              object, index));
                                              object, index));
}
}
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Arm_output_data_got<big_endian>::do_write(Output_file* of)
Arm_output_data_got<big_endian>::do_write(Output_file* of)
{
{
  // Call parent to write out GOT.
  // Call parent to write out GOT.
  Output_data_got<32, big_endian>::do_write(of);
  Output_data_got<32, big_endian>::do_write(of);
 
 
  // We are done if there is no fix up.
  // We are done if there is no fix up.
  if (this->static_relocs_.empty())
  if (this->static_relocs_.empty())
    return;
    return;
 
 
  gold_assert(parameters->doing_static_link());
  gold_assert(parameters->doing_static_link());
 
 
  const off_t offset = this->offset();
  const off_t offset = this->offset();
  const section_size_type oview_size =
  const section_size_type oview_size =
    convert_to_section_size_type(this->data_size());
    convert_to_section_size_type(this->data_size());
  unsigned char* const oview = of->get_output_view(offset, oview_size);
  unsigned char* const oview = of->get_output_view(offset, oview_size);
 
 
  Output_segment* tls_segment = this->layout_->tls_segment();
  Output_segment* tls_segment = this->layout_->tls_segment();
  gold_assert(tls_segment != NULL);
  gold_assert(tls_segment != NULL);
 
 
  // The thread pointer $tp points to the TCB, which is followed by the
  // 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.
  // TLS.  So we need to adjust $tp relative addressing by this amount.
  Arm_address aligned_tcb_size =
  Arm_address aligned_tcb_size =
    align_address(ARM_TCB_SIZE, tls_segment->maximum_alignment());
    align_address(ARM_TCB_SIZE, tls_segment->maximum_alignment());
 
 
  for (size_t i = 0; i < this->static_relocs_.size(); ++i)
  for (size_t i = 0; i < this->static_relocs_.size(); ++i)
    {
    {
      Static_reloc& reloc(this->static_relocs_[i]);
      Static_reloc& reloc(this->static_relocs_[i]);
 
 
      Arm_address value;
      Arm_address value;
      if (!reloc.symbol_is_global())
      if (!reloc.symbol_is_global())
        {
        {
          Sized_relobj_file<32, big_endian>* object = reloc.relobj();
          Sized_relobj_file<32, big_endian>* object = reloc.relobj();
          const Symbol_value<32>* psymval =
          const Symbol_value<32>* psymval =
            reloc.relobj()->local_symbol(reloc.index());
            reloc.relobj()->local_symbol(reloc.index());
 
 
          // We are doing static linking.  Issue an error and skip this
          // We are doing static linking.  Issue an error and skip this
          // relocation if the symbol is undefined or in a discarded_section.
          // relocation if the symbol is undefined or in a discarded_section.
          bool is_ordinary;
          bool is_ordinary;
          unsigned int shndx = psymval->input_shndx(&is_ordinary);
          unsigned int shndx = psymval->input_shndx(&is_ordinary);
          if ((shndx == elfcpp::SHN_UNDEF)
          if ((shndx == elfcpp::SHN_UNDEF)
              || (is_ordinary
              || (is_ordinary
                  && shndx != elfcpp::SHN_UNDEF
                  && shndx != elfcpp::SHN_UNDEF
                  && !object->is_section_included(shndx)
                  && !object->is_section_included(shndx)
                  && !this->symbol_table_->is_section_folded(object, shndx)))
                  && !this->symbol_table_->is_section_folded(object, shndx)))
            {
            {
              gold_error(_("undefined or discarded local symbol %u from "
              gold_error(_("undefined or discarded local symbol %u from "
                           " object %s in GOT"),
                           " object %s in GOT"),
                         reloc.index(), reloc.relobj()->name().c_str());
                         reloc.index(), reloc.relobj()->name().c_str());
              continue;
              continue;
            }
            }
 
 
          value = psymval->value(object, 0);
          value = psymval->value(object, 0);
        }
        }
      else
      else
        {
        {
          const Symbol* gsym = reloc.symbol();
          const Symbol* gsym = reloc.symbol();
          gold_assert(gsym != NULL);
          gold_assert(gsym != NULL);
          if (gsym->is_forwarder())
          if (gsym->is_forwarder())
            gsym = this->symbol_table_->resolve_forwards(gsym);
            gsym = this->symbol_table_->resolve_forwards(gsym);
 
 
          // We are doing static linking.  Issue an error and skip this
          // We are doing static linking.  Issue an error and skip this
          // relocation if the symbol is undefined or in a discarded_section
          // relocation if the symbol is undefined or in a discarded_section
          // unless it is a weakly_undefined symbol.
          // unless it is a weakly_undefined symbol.
          if ((gsym->is_defined_in_discarded_section()
          if ((gsym->is_defined_in_discarded_section()
               || gsym->is_undefined())
               || gsym->is_undefined())
              && !gsym->is_weak_undefined())
              && !gsym->is_weak_undefined())
            {
            {
              gold_error(_("undefined or discarded symbol %s in GOT"),
              gold_error(_("undefined or discarded symbol %s in GOT"),
                         gsym->name());
                         gsym->name());
              continue;
              continue;
            }
            }
 
 
          if (!gsym->is_weak_undefined())
          if (!gsym->is_weak_undefined())
            {
            {
              const Sized_symbol<32>* sym =
              const Sized_symbol<32>* sym =
                static_cast<const Sized_symbol<32>*>(gsym);
                static_cast<const Sized_symbol<32>*>(gsym);
              value = sym->value();
              value = sym->value();
            }
            }
          else
          else
              value = 0;
              value = 0;
        }
        }
 
 
      unsigned got_offset = reloc.got_offset();
      unsigned got_offset = reloc.got_offset();
      gold_assert(got_offset < oview_size);
      gold_assert(got_offset < oview_size);
 
 
      typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
      typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
      Valtype* wv = reinterpret_cast<Valtype*>(oview + got_offset);
      Valtype* wv = reinterpret_cast<Valtype*>(oview + got_offset);
      Valtype x;
      Valtype x;
      switch (reloc.r_type())
      switch (reloc.r_type())
        {
        {
        case elfcpp::R_ARM_TLS_DTPOFF32:
        case elfcpp::R_ARM_TLS_DTPOFF32:
          x = value;
          x = value;
          break;
          break;
        case elfcpp::R_ARM_TLS_TPOFF32:
        case elfcpp::R_ARM_TLS_TPOFF32:
          x = value + aligned_tcb_size;
          x = value + aligned_tcb_size;
          break;
          break;
        default:
        default:
          gold_unreachable();
          gold_unreachable();
        }
        }
      elfcpp::Swap<32, big_endian>::writeval(wv, x);
      elfcpp::Swap<32, big_endian>::writeval(wv, x);
    }
    }
 
 
  of->write_output_view(offset, oview_size, oview);
  of->write_output_view(offset, oview_size, oview);
}
}
 
 
// A class to handle the PLT data.
// A class to handle the PLT data.
 
 
template<bool big_endian>
template<bool big_endian>
class Output_data_plt_arm : public Output_section_data
class Output_data_plt_arm : public Output_section_data
{
{
 public:
 public:
  typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
  typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
    Reloc_section;
    Reloc_section;
 
 
  Output_data_plt_arm(Layout*, Output_data_space*);
  Output_data_plt_arm(Layout*, Output_data_space*);
 
 
  // Add an entry to the PLT.
  // Add an entry to the PLT.
  void
  void
  add_entry(Symbol* gsym);
  add_entry(Symbol* gsym);
 
 
  // Return the .rel.plt section data.
  // Return the .rel.plt section data.
  const Reloc_section*
  const Reloc_section*
  rel_plt() const
  rel_plt() const
  { return this->rel_; }
  { return this->rel_; }
 
 
  // Return the number of PLT entries.
  // Return the number of PLT entries.
  unsigned int
  unsigned int
  entry_count() const
  entry_count() const
  { return this->count_; }
  { return this->count_; }
 
 
  // Return the offset of the first non-reserved PLT entry.
  // Return the offset of the first non-reserved PLT entry.
  static unsigned int
  static unsigned int
  first_plt_entry_offset()
  first_plt_entry_offset()
  { return sizeof(first_plt_entry); }
  { return sizeof(first_plt_entry); }
 
 
  // Return the size of a PLT entry.
  // Return the size of a PLT entry.
  static unsigned int
  static unsigned int
  get_plt_entry_size()
  get_plt_entry_size()
  { return sizeof(plt_entry); }
  { return sizeof(plt_entry); }
 
 
 protected:
 protected:
  void
  void
  do_adjust_output_section(Output_section* os);
  do_adjust_output_section(Output_section* os);
 
 
  // Write to a map file.
  // Write to a map file.
  void
  void
  do_print_to_mapfile(Mapfile* mapfile) const
  do_print_to_mapfile(Mapfile* mapfile) const
  { mapfile->print_output_data(this, _("** PLT")); }
  { mapfile->print_output_data(this, _("** PLT")); }
 
 
 private:
 private:
  // Template for the first PLT entry.
  // Template for the first PLT entry.
  static const uint32_t first_plt_entry[5];
  static const uint32_t first_plt_entry[5];
 
 
  // Template for subsequent PLT entries. 
  // Template for subsequent PLT entries. 
  static const uint32_t plt_entry[3];
  static const uint32_t plt_entry[3];
 
 
  // Set the final size.
  // Set the final size.
  void
  void
  set_final_data_size()
  set_final_data_size()
  {
  {
    this->set_data_size(sizeof(first_plt_entry)
    this->set_data_size(sizeof(first_plt_entry)
                        + this->count_ * sizeof(plt_entry));
                        + this->count_ * sizeof(plt_entry));
  }
  }
 
 
  // Write out the PLT data.
  // Write out the PLT data.
  void
  void
  do_write(Output_file*);
  do_write(Output_file*);
 
 
  // The reloc section.
  // The reloc section.
  Reloc_section* rel_;
  Reloc_section* rel_;
  // The .got.plt section.
  // The .got.plt section.
  Output_data_space* got_plt_;
  Output_data_space* got_plt_;
  // The number of PLT entries.
  // The number of PLT entries.
  unsigned int count_;
  unsigned int count_;
};
};
 
 
// Create the PLT section.  The ordinary .got section is an argument,
// Create the PLT section.  The ordinary .got section is an argument,
// since we need to refer to the start.  We also create our own .got
// since we need to refer to the start.  We also create our own .got
// section just for PLT entries.
// section just for PLT entries.
 
 
template<bool big_endian>
template<bool big_endian>
Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout,
Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout,
                                                     Output_data_space* got_plt)
                                                     Output_data_space* got_plt)
  : Output_section_data(4), got_plt_(got_plt), count_(0)
  : Output_section_data(4), got_plt_(got_plt), count_(0)
{
{
  this->rel_ = new Reloc_section(false);
  this->rel_ = new Reloc_section(false);
  layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
  layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
                                  elfcpp::SHF_ALLOC, this->rel_,
                                  elfcpp::SHF_ALLOC, this->rel_,
                                  ORDER_DYNAMIC_PLT_RELOCS, false);
                                  ORDER_DYNAMIC_PLT_RELOCS, false);
}
}
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os)
Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os)
{
{
  os->set_entsize(0);
  os->set_entsize(0);
}
}
 
 
// Add an entry to the PLT.
// Add an entry to the PLT.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
{
{
  gold_assert(!gsym->has_plt_offset());
  gold_assert(!gsym->has_plt_offset());
 
 
  // Note that when setting the PLT offset we skip the initial
  // Note that when setting the PLT offset we skip the initial
  // reserved PLT entry.
  // reserved PLT entry.
  gsym->set_plt_offset((this->count_) * sizeof(plt_entry)
  gsym->set_plt_offset((this->count_) * sizeof(plt_entry)
                       + sizeof(first_plt_entry));
                       + sizeof(first_plt_entry));
 
 
  ++this->count_;
  ++this->count_;
 
 
  section_offset_type got_offset = this->got_plt_->current_data_size();
  section_offset_type got_offset = this->got_plt_->current_data_size();
 
 
  // Every PLT entry needs a GOT entry which points back to the PLT
  // Every PLT entry needs a GOT entry which points back to the PLT
  // entry (this will be changed by the dynamic linker, normally
  // entry (this will be changed by the dynamic linker, normally
  // lazily when the function is called).
  // lazily when the function is called).
  this->got_plt_->set_current_data_size(got_offset + 4);
  this->got_plt_->set_current_data_size(got_offset + 4);
 
 
  // Every PLT entry needs a reloc.
  // Every PLT entry needs a reloc.
  gsym->set_needs_dynsym_entry();
  gsym->set_needs_dynsym_entry();
  this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
  this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
                         got_offset);
                         got_offset);
 
 
  // Note that we don't need to save the symbol.  The contents of the
  // Note that we don't need to save the symbol.  The contents of the
  // PLT are independent of which symbols are used.  The symbols only
  // PLT are independent of which symbols are used.  The symbols only
  // appear in the relocations.
  // appear in the relocations.
}
}
 
 
// ARM PLTs.
// ARM PLTs.
// FIXME:  This is not very flexible.  Right now this has only been tested
// FIXME:  This is not very flexible.  Right now this has only been tested
// on armv5te.  If we are to support additional architecture features like
// on armv5te.  If we are to support additional architecture features like
// Thumb-2 or BE8, we need to make this more flexible like GNU ld.
// Thumb-2 or BE8, we need to make this more flexible like GNU ld.
 
 
// The first entry in the PLT.
// The first entry in the PLT.
template<bool big_endian>
template<bool big_endian>
const uint32_t Output_data_plt_arm<big_endian>::first_plt_entry[5] =
const uint32_t Output_data_plt_arm<big_endian>::first_plt_entry[5] =
{
{
  0xe52de004,   // str   lr, [sp, #-4]!
  0xe52de004,   // str   lr, [sp, #-4]!
  0xe59fe004,   // ldr   lr, [pc, #4]
  0xe59fe004,   // ldr   lr, [pc, #4]
  0xe08fe00e,   // add   lr, pc, lr 
  0xe08fe00e,   // add   lr, pc, lr 
  0xe5bef008,   // ldr   pc, [lr, #8]!
  0xe5bef008,   // ldr   pc, [lr, #8]!
  0x00000000,   // &GOT[0] - .
  0x00000000,   // &GOT[0] - .
};
};
 
 
// Subsequent entries in the PLT.
// Subsequent entries in the PLT.
 
 
template<bool big_endian>
template<bool big_endian>
const uint32_t Output_data_plt_arm<big_endian>::plt_entry[3] =
const uint32_t Output_data_plt_arm<big_endian>::plt_entry[3] =
{
{
  0xe28fc600,   // add   ip, pc, #0xNN00000
  0xe28fc600,   // add   ip, pc, #0xNN00000
  0xe28cca00,   // add   ip, ip, #0xNN000
  0xe28cca00,   // add   ip, ip, #0xNN000
  0xe5bcf000,   // ldr   pc, [ip, #0xNNN]!
  0xe5bcf000,   // ldr   pc, [ip, #0xNNN]!
};
};
 
 
// Write out the PLT.  This uses the hand-coded instructions above,
// Write out the PLT.  This uses the hand-coded instructions above,
// and adjusts them as needed.  This is all specified by the arm ELF
// and adjusts them as needed.  This is all specified by the arm ELF
// Processor Supplement.
// Processor Supplement.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Output_data_plt_arm<big_endian>::do_write(Output_file* of)
Output_data_plt_arm<big_endian>::do_write(Output_file* of)
{
{
  const off_t offset = this->offset();
  const off_t offset = this->offset();
  const section_size_type oview_size =
  const section_size_type oview_size =
    convert_to_section_size_type(this->data_size());
    convert_to_section_size_type(this->data_size());
  unsigned char* const oview = of->get_output_view(offset, oview_size);
  unsigned char* const oview = of->get_output_view(offset, oview_size);
 
 
  const off_t got_file_offset = this->got_plt_->offset();
  const off_t got_file_offset = this->got_plt_->offset();
  const section_size_type got_size =
  const section_size_type got_size =
    convert_to_section_size_type(this->got_plt_->data_size());
    convert_to_section_size_type(this->got_plt_->data_size());
  unsigned char* const got_view = of->get_output_view(got_file_offset,
  unsigned char* const got_view = of->get_output_view(got_file_offset,
                                                      got_size);
                                                      got_size);
  unsigned char* pov = oview;
  unsigned char* pov = oview;
 
 
  Arm_address plt_address = this->address();
  Arm_address plt_address = this->address();
  Arm_address got_address = this->got_plt_->address();
  Arm_address got_address = this->got_plt_->address();
 
 
  // Write first PLT entry.  All but the last word are constants.
  // Write first PLT entry.  All but the last word are constants.
  const size_t num_first_plt_words = (sizeof(first_plt_entry)
  const size_t num_first_plt_words = (sizeof(first_plt_entry)
                                      / sizeof(plt_entry[0]));
                                      / sizeof(plt_entry[0]));
  for (size_t i = 0; i < num_first_plt_words - 1; i++)
  for (size_t i = 0; i < num_first_plt_words - 1; i++)
    elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
    elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
  // Last word in first PLT entry is &GOT[0] - .
  // Last word in first PLT entry is &GOT[0] - .
  elfcpp::Swap<32, big_endian>::writeval(pov + 16,
  elfcpp::Swap<32, big_endian>::writeval(pov + 16,
                                         got_address - (plt_address + 16));
                                         got_address - (plt_address + 16));
  pov += sizeof(first_plt_entry);
  pov += sizeof(first_plt_entry);
 
 
  unsigned char* got_pov = got_view;
  unsigned char* got_pov = got_view;
 
 
  memset(got_pov, 0, 12);
  memset(got_pov, 0, 12);
  got_pov += 12;
  got_pov += 12;
 
 
  const int rel_size = elfcpp::Elf_sizes<32>::rel_size;
  const int rel_size = elfcpp::Elf_sizes<32>::rel_size;
  unsigned int plt_offset = sizeof(first_plt_entry);
  unsigned int plt_offset = sizeof(first_plt_entry);
  unsigned int plt_rel_offset = 0;
  unsigned int plt_rel_offset = 0;
  unsigned int got_offset = 12;
  unsigned int got_offset = 12;
  const unsigned int count = this->count_;
  const unsigned int count = this->count_;
  for (unsigned int i = 0;
  for (unsigned int i = 0;
       i < count;
       i < count;
       ++i,
       ++i,
         pov += sizeof(plt_entry),
         pov += sizeof(plt_entry),
         got_pov += 4,
         got_pov += 4,
         plt_offset += sizeof(plt_entry),
         plt_offset += sizeof(plt_entry),
         plt_rel_offset += rel_size,
         plt_rel_offset += rel_size,
         got_offset += 4)
         got_offset += 4)
    {
    {
      // Set and adjust the PLT entry itself.
      // Set and adjust the PLT entry itself.
      int32_t offset = ((got_address + got_offset)
      int32_t offset = ((got_address + got_offset)
                         - (plt_address + plt_offset + 8));
                         - (plt_address + plt_offset + 8));
 
 
      gold_assert(offset >= 0 && offset < 0x0fffffff);
      gold_assert(offset >= 0 && offset < 0x0fffffff);
      uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
      uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
      elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
      elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
      uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
      uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
      elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
      elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
      uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff);
      uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff);
      elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
      elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
 
 
      // Set the entry in the GOT.
      // Set the entry in the GOT.
      elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
      elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
    }
    }
 
 
  gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
  gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
  gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size);
  gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size);
 
 
  of->write_output_view(offset, oview_size, oview);
  of->write_output_view(offset, oview_size, oview);
  of->write_output_view(got_file_offset, got_size, got_view);
  of->write_output_view(got_file_offset, got_size, got_view);
}
}
 
 
// Create a PLT entry for a global symbol.
// Create a PLT entry for a global symbol.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
                                       Symbol* gsym)
                                       Symbol* gsym)
{
{
  if (gsym->has_plt_offset())
  if (gsym->has_plt_offset())
    return;
    return;
 
 
  if (this->plt_ == NULL)
  if (this->plt_ == NULL)
    {
    {
      // Create the GOT sections first.
      // Create the GOT sections first.
      this->got_section(symtab, layout);
      this->got_section(symtab, layout);
 
 
      this->plt_ = new Output_data_plt_arm<big_endian>(layout, this->got_plt_);
      this->plt_ = new Output_data_plt_arm<big_endian>(layout, this->got_plt_);
      layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
      layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
                                      (elfcpp::SHF_ALLOC
                                      (elfcpp::SHF_ALLOC
                                       | elfcpp::SHF_EXECINSTR),
                                       | elfcpp::SHF_EXECINSTR),
                                      this->plt_, ORDER_PLT, false);
                                      this->plt_, ORDER_PLT, false);
    }
    }
  this->plt_->add_entry(gsym);
  this->plt_->add_entry(gsym);
}
}
 
 
// Return the number of entries in the PLT.
// Return the number of entries in the PLT.
 
 
template<bool big_endian>
template<bool big_endian>
unsigned int
unsigned int
Target_arm<big_endian>::plt_entry_count() const
Target_arm<big_endian>::plt_entry_count() const
{
{
  if (this->plt_ == NULL)
  if (this->plt_ == NULL)
    return 0;
    return 0;
  return this->plt_->entry_count();
  return this->plt_->entry_count();
}
}
 
 
// Return the offset of the first non-reserved PLT entry.
// Return the offset of the first non-reserved PLT entry.
 
 
template<bool big_endian>
template<bool big_endian>
unsigned int
unsigned int
Target_arm<big_endian>::first_plt_entry_offset() const
Target_arm<big_endian>::first_plt_entry_offset() const
{
{
  return Output_data_plt_arm<big_endian>::first_plt_entry_offset();
  return Output_data_plt_arm<big_endian>::first_plt_entry_offset();
}
}
 
 
// Return the size of each PLT entry.
// Return the size of each PLT entry.
 
 
template<bool big_endian>
template<bool big_endian>
unsigned int
unsigned int
Target_arm<big_endian>::plt_entry_size() const
Target_arm<big_endian>::plt_entry_size() const
{
{
  return Output_data_plt_arm<big_endian>::get_plt_entry_size();
  return Output_data_plt_arm<big_endian>::get_plt_entry_size();
}
}
 
 
// Get the section to use for TLS_DESC relocations.
// Get the section to use for TLS_DESC relocations.
 
 
template<bool big_endian>
template<bool big_endian>
typename Target_arm<big_endian>::Reloc_section*
typename Target_arm<big_endian>::Reloc_section*
Target_arm<big_endian>::rel_tls_desc_section(Layout* layout) const
Target_arm<big_endian>::rel_tls_desc_section(Layout* layout) const
{
{
  return this->plt_section()->rel_tls_desc(layout);
  return this->plt_section()->rel_tls_desc(layout);
}
}
 
 
// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::define_tls_base_symbol(
Target_arm<big_endian>::define_tls_base_symbol(
    Symbol_table* symtab,
    Symbol_table* symtab,
    Layout* layout)
    Layout* layout)
{
{
  if (this->tls_base_symbol_defined_)
  if (this->tls_base_symbol_defined_)
    return;
    return;
 
 
  Output_segment* tls_segment = layout->tls_segment();
  Output_segment* tls_segment = layout->tls_segment();
  if (tls_segment != NULL)
  if (tls_segment != NULL)
    {
    {
      bool is_exec = parameters->options().output_is_executable();
      bool is_exec = parameters->options().output_is_executable();
      symtab->define_in_output_segment("_TLS_MODULE_BASE_", NULL,
      symtab->define_in_output_segment("_TLS_MODULE_BASE_", NULL,
                                       Symbol_table::PREDEFINED,
                                       Symbol_table::PREDEFINED,
                                       tls_segment, 0, 0,
                                       tls_segment, 0, 0,
                                       elfcpp::STT_TLS,
                                       elfcpp::STT_TLS,
                                       elfcpp::STB_LOCAL,
                                       elfcpp::STB_LOCAL,
                                       elfcpp::STV_HIDDEN, 0,
                                       elfcpp::STV_HIDDEN, 0,
                                       (is_exec
                                       (is_exec
                                        ? Symbol::SEGMENT_END
                                        ? Symbol::SEGMENT_END
                                        : Symbol::SEGMENT_START),
                                        : Symbol::SEGMENT_START),
                                       true);
                                       true);
    }
    }
  this->tls_base_symbol_defined_ = true;
  this->tls_base_symbol_defined_ = true;
}
}
 
 
// Create a GOT entry for the TLS module index.
// Create a GOT entry for the TLS module index.
 
 
template<bool big_endian>
template<bool big_endian>
unsigned int
unsigned int
Target_arm<big_endian>::got_mod_index_entry(
Target_arm<big_endian>::got_mod_index_entry(
    Symbol_table* symtab,
    Symbol_table* symtab,
    Layout* layout,
    Layout* layout,
    Sized_relobj_file<32, big_endian>* object)
    Sized_relobj_file<32, big_endian>* object)
{
{
  if (this->got_mod_index_offset_ == -1U)
  if (this->got_mod_index_offset_ == -1U)
    {
    {
      gold_assert(symtab != NULL && layout != NULL && object != NULL);
      gold_assert(symtab != NULL && layout != NULL && object != NULL);
      Arm_output_data_got<big_endian>* got = this->got_section(symtab, layout);
      Arm_output_data_got<big_endian>* got = this->got_section(symtab, layout);
      unsigned int got_offset;
      unsigned int got_offset;
      if (!parameters->doing_static_link())
      if (!parameters->doing_static_link())
        {
        {
          got_offset = got->add_constant(0);
          got_offset = got->add_constant(0);
          Reloc_section* rel_dyn = this->rel_dyn_section(layout);
          Reloc_section* rel_dyn = this->rel_dyn_section(layout);
          rel_dyn->add_local(object, 0, elfcpp::R_ARM_TLS_DTPMOD32, got,
          rel_dyn->add_local(object, 0, elfcpp::R_ARM_TLS_DTPMOD32, got,
                             got_offset);
                             got_offset);
        }
        }
      else
      else
        {
        {
          // We are doing a static link.  Just mark it as belong to module 1,
          // We are doing a static link.  Just mark it as belong to module 1,
          // the executable.
          // the executable.
          got_offset = got->add_constant(1);
          got_offset = got->add_constant(1);
        }
        }
 
 
      got->add_constant(0);
      got->add_constant(0);
      this->got_mod_index_offset_ = got_offset;
      this->got_mod_index_offset_ = got_offset;
    }
    }
  return this->got_mod_index_offset_;
  return this->got_mod_index_offset_;
}
}
 
 
// Optimize the TLS relocation type based on what we know about the
// 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
// symbol.  IS_FINAL is true if the final address of this symbol is
// known at link time.
// known at link time.
 
 
template<bool big_endian>
template<bool big_endian>
tls::Tls_optimization
tls::Tls_optimization
Target_arm<big_endian>::optimize_tls_reloc(bool, int)
Target_arm<big_endian>::optimize_tls_reloc(bool, int)
{
{
  // FIXME: Currently we do not do any TLS optimization.
  // FIXME: Currently we do not do any TLS optimization.
  return tls::TLSOPT_NONE;
  return tls::TLSOPT_NONE;
}
}
 
 
// Get the Reference_flags for a particular relocation.
// Get the Reference_flags for a particular relocation.
 
 
template<bool big_endian>
template<bool big_endian>
int
int
Target_arm<big_endian>::Scan::get_reference_flags(unsigned int r_type)
Target_arm<big_endian>::Scan::get_reference_flags(unsigned int r_type)
{
{
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_GNU_VTENTRY:
    case elfcpp::R_ARM_GNU_VTENTRY:
    case elfcpp::R_ARM_GNU_VTINHERIT:
    case elfcpp::R_ARM_GNU_VTINHERIT:
      // No symbol reference.
      // No symbol reference.
      return 0;
      return 0;
 
 
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_ABS32_NOI:
    case elfcpp::R_ARM_ABS32_NOI:
      return Symbol::ABSOLUTE_REF;
      return Symbol::ABSOLUTE_REF;
 
 
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_SBREL32:
    case elfcpp::R_ARM_SBREL32:
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_REL32_NOI:
    case elfcpp::R_ARM_REL32_NOI:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF12:
    case elfcpp::R_ARM_GOTOFF12:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_SBREL31:
      return Symbol::RELATIVE_REF;
      return Symbol::RELATIVE_REF;
 
 
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP8:
    case elfcpp::R_ARM_THM_JUMP8:
    // R_ARM_PREL31 is not used to relocate call/jump instructions but
    // R_ARM_PREL31 is not used to relocate call/jump instructions but
    // in unwind tables. It may point to functions via PLTs.
    // in unwind tables. It may point to functions via PLTs.
    // So we treat it like call/jump relocations above.
    // So we treat it like call/jump relocations above.
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_PREL31:
      return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
      return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
 
 
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_ABS:
    case elfcpp::R_ARM_GOT_ABS:
    case elfcpp::R_ARM_GOT_PREL:
    case elfcpp::R_ARM_GOT_PREL:
      // Absolute in GOT.
      // Absolute in GOT.
      return Symbol::ABSOLUTE_REF;
      return Symbol::ABSOLUTE_REF;
 
 
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
      return Symbol::TLS_REF;
      return Symbol::TLS_REF;
 
 
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET2:
    case elfcpp::R_ARM_TARGET2:
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_RELATIVE:
    case elfcpp::R_ARM_RELATIVE:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    default:
    default:
      // Not expected.  We will give an error later.
      // Not expected.  We will give an error later.
      return 0;
      return 0;
    }
    }
}
}
 
 
// Report an unsupported relocation against a local symbol.
// Report an unsupported relocation against a local symbol.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::Scan::unsupported_reloc_local(
Target_arm<big_endian>::Scan::unsupported_reloc_local(
    Sized_relobj_file<32, big_endian>* object,
    Sized_relobj_file<32, big_endian>* object,
    unsigned int r_type)
    unsigned int r_type)
{
{
  gold_error(_("%s: unsupported reloc %u against local symbol"),
  gold_error(_("%s: unsupported reloc %u against local symbol"),
             object->name().c_str(), r_type);
             object->name().c_str(), r_type);
}
}
 
 
// We are about to emit a dynamic relocation of type R_TYPE.  If the
// We are about to emit a dynamic relocation of type R_TYPE.  If the
// dynamic linker does not support it, issue an error.  The GNU linker
// dynamic linker does not support it, issue an error.  The GNU linker
// only issues a non-PIC error for an allocated read-only section.
// only issues a non-PIC error for an allocated read-only section.
// Here we know the section is allocated, but we don't know that it is
// Here we know the section is allocated, but we don't know that it is
// read-only.  But we check for all the relocation types which the
// read-only.  But we check for all the relocation types which the
// glibc dynamic linker supports, so it seems appropriate to issue an
// glibc dynamic linker supports, so it seems appropriate to issue an
// error even if the section is not read-only.
// error even if the section is not read-only.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
                                            unsigned int r_type)
                                            unsigned int r_type)
{
{
  switch (r_type)
  switch (r_type)
    {
    {
    // These are the relocation types supported by glibc for ARM.
    // These are the relocation types supported by glibc for ARM.
    case elfcpp::R_ARM_RELATIVE:
    case elfcpp::R_ARM_RELATIVE:
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32_NOI:
    case elfcpp::R_ARM_ABS32_NOI:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_PC24:
    // FIXME: The following 3 types are not supported by Android's dynamic
    // FIXME: The following 3 types are not supported by Android's dynamic
    // linker.
    // linker.
    case elfcpp::R_ARM_TLS_DTPMOD32:
    case elfcpp::R_ARM_TLS_DTPMOD32:
    case elfcpp::R_ARM_TLS_DTPOFF32:
    case elfcpp::R_ARM_TLS_DTPOFF32:
    case elfcpp::R_ARM_TLS_TPOFF32:
    case elfcpp::R_ARM_TLS_TPOFF32:
      return;
      return;
 
 
    default:
    default:
      {
      {
        // This prevents us from issuing more than one error per reloc
        // This prevents us from issuing more than one error per reloc
        // section.  But we can still wind up issuing more than one
        // section.  But we can still wind up issuing more than one
        // error per object file.
        // error per object file.
        if (this->issued_non_pic_error_)
        if (this->issued_non_pic_error_)
          return;
          return;
        const Arm_reloc_property* reloc_property =
        const Arm_reloc_property* reloc_property =
          arm_reloc_property_table->get_reloc_property(r_type);
          arm_reloc_property_table->get_reloc_property(r_type);
        gold_assert(reloc_property != NULL);
        gold_assert(reloc_property != NULL);
        object->error(_("requires unsupported dynamic reloc %s; "
        object->error(_("requires unsupported dynamic reloc %s; "
                      "recompile with -fPIC"),
                      "recompile with -fPIC"),
                      reloc_property->name().c_str());
                      reloc_property->name().c_str());
        this->issued_non_pic_error_ = true;
        this->issued_non_pic_error_ = true;
        return;
        return;
      }
      }
 
 
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_NONE:
      gold_unreachable();
      gold_unreachable();
    }
    }
}
}
 
 
// Scan a relocation for a local symbol.
// Scan a relocation for a local symbol.
// FIXME: This only handles a subset of relocation types used by Android
// FIXME: This only handles a subset of relocation types used by Android
// on ARM v5te devices.
// on ARM v5te devices.
 
 
template<bool big_endian>
template<bool big_endian>
inline void
inline void
Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
                                    Layout* layout,
                                    Layout* layout,
                                    Target_arm* target,
                                    Target_arm* target,
                                    Sized_relobj_file<32, big_endian>* object,
                                    Sized_relobj_file<32, big_endian>* object,
                                    unsigned int data_shndx,
                                    unsigned int data_shndx,
                                    Output_section* output_section,
                                    Output_section* output_section,
                                    const elfcpp::Rel<32, big_endian>& reloc,
                                    const elfcpp::Rel<32, big_endian>& reloc,
                                    unsigned int r_type,
                                    unsigned int r_type,
                                    const elfcpp::Sym<32, big_endian>& lsym)
                                    const elfcpp::Sym<32, big_endian>& lsym)
{
{
  r_type = get_real_reloc_type(r_type);
  r_type = get_real_reloc_type(r_type);
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_GNU_VTENTRY:
    case elfcpp::R_ARM_GNU_VTENTRY:
    case elfcpp::R_ARM_GNU_VTINHERIT:
    case elfcpp::R_ARM_GNU_VTINHERIT:
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32_NOI:
    case elfcpp::R_ARM_ABS32_NOI:
      // If building a shared library (or a position-independent
      // If building a shared library (or a position-independent
      // executable), we need to create a dynamic relocation for
      // executable), we need to create a dynamic relocation for
      // this location. The relocation applied at link time will
      // this location. The relocation applied at link time will
      // apply the link-time value, so we flag the location with
      // apply the link-time value, so we flag the location with
      // an R_ARM_RELATIVE relocation so the dynamic loader can
      // an R_ARM_RELATIVE relocation so the dynamic loader can
      // relocate it easily.
      // relocate it easily.
      if (parameters->options().output_is_position_independent())
      if (parameters->options().output_is_position_independent())
        {
        {
          Reloc_section* rel_dyn = target->rel_dyn_section(layout);
          Reloc_section* rel_dyn = target->rel_dyn_section(layout);
          unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
          unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
          // If we are to add more other reloc types than R_ARM_ABS32,
          // If we are to add more other reloc types than R_ARM_ABS32,
          // we need to add check_non_pic(object, r_type) here.
          // we need to add check_non_pic(object, r_type) here.
          rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE,
          rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE,
                                      output_section, data_shndx,
                                      output_section, data_shndx,
                                      reloc.get_r_offset());
                                      reloc.get_r_offset());
        }
        }
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVT_ABS:
      // If building a shared library (or a position-independent
      // If building a shared library (or a position-independent
      // executable), we need to create a dynamic relocation for
      // executable), we need to create a dynamic relocation for
      // this location. Because the addend needs to remain in the
      // this location. Because the addend needs to remain in the
      // data section, we need to be careful not to apply this
      // data section, we need to be careful not to apply this
      // relocation statically.
      // relocation statically.
      if (parameters->options().output_is_position_independent())
      if (parameters->options().output_is_position_independent())
        {
        {
          check_non_pic(object, r_type);
          check_non_pic(object, r_type);
          Reloc_section* rel_dyn = target->rel_dyn_section(layout);
          Reloc_section* rel_dyn = target->rel_dyn_section(layout);
          unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
          unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
          if (lsym.get_st_type() != elfcpp::STT_SECTION)
          if (lsym.get_st_type() != elfcpp::STT_SECTION)
            rel_dyn->add_local(object, r_sym, r_type, output_section,
            rel_dyn->add_local(object, r_sym, r_type, output_section,
                               data_shndx, reloc.get_r_offset());
                               data_shndx, reloc.get_r_offset());
          else
          else
            {
            {
              gold_assert(lsym.get_st_value() == 0);
              gold_assert(lsym.get_st_value() == 0);
              unsigned int shndx = lsym.get_st_shndx();
              unsigned int shndx = lsym.get_st_shndx();
              bool is_ordinary;
              bool is_ordinary;
              shndx = object->adjust_sym_shndx(r_sym, shndx,
              shndx = object->adjust_sym_shndx(r_sym, shndx,
                                               &is_ordinary);
                                               &is_ordinary);
              if (!is_ordinary)
              if (!is_ordinary)
                object->error(_("section symbol %u has bad shndx %u"),
                object->error(_("section symbol %u has bad shndx %u"),
                              r_sym, shndx);
                              r_sym, shndx);
              else
              else
                rel_dyn->add_local_section(object, shndx,
                rel_dyn->add_local_section(object, shndx,
                                           r_type, output_section,
                                           r_type, output_section,
                                           data_shndx, reloc.get_r_offset());
                                           data_shndx, reloc.get_r_offset());
            }
            }
        }
        }
      break;
      break;
 
 
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_SBREL32:
    case elfcpp::R_ARM_SBREL32:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_REL32_NOI:
    case elfcpp::R_ARM_REL32_NOI:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP8:
    case elfcpp::R_ARM_THM_JUMP8:
      // We don't need to do anything for a relative addressing relocation
      // We don't need to do anything for a relative addressing relocation
      // against a local symbol if it does not reference the GOT.
      // against a local symbol if it does not reference the GOT.
      break;
      break;
 
 
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF12:
    case elfcpp::R_ARM_GOTOFF12:
      // We need a GOT section:
      // We need a GOT section:
      target->got_section(symtab, layout);
      target->got_section(symtab, layout);
      break;
      break;
 
 
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_PREL:
    case elfcpp::R_ARM_GOT_PREL:
      {
      {
        // The symbol requires a GOT entry.
        // The symbol requires a GOT entry.
        Arm_output_data_got<big_endian>* got =
        Arm_output_data_got<big_endian>* got =
          target->got_section(symtab, layout);
          target->got_section(symtab, layout);
        unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
        unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
        if (got->add_local(object, r_sym, GOT_TYPE_STANDARD))
        if (got->add_local(object, r_sym, GOT_TYPE_STANDARD))
          {
          {
            // If we are generating a shared object, we need to add a
            // If we are generating a shared object, we need to add a
            // dynamic RELATIVE relocation for this symbol's GOT entry.
            // dynamic RELATIVE relocation for this symbol's GOT entry.
            if (parameters->options().output_is_position_independent())
            if (parameters->options().output_is_position_independent())
              {
              {
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
                rel_dyn->add_local_relative(
                rel_dyn->add_local_relative(
                    object, r_sym, elfcpp::R_ARM_RELATIVE, got,
                    object, r_sym, elfcpp::R_ARM_RELATIVE, got,
                    object->local_got_offset(r_sym, GOT_TYPE_STANDARD));
                    object->local_got_offset(r_sym, GOT_TYPE_STANDARD));
              }
              }
          }
          }
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET2:
    case elfcpp::R_ARM_TARGET2:
      // This should have been mapped to another type already.
      // This should have been mapped to another type already.
      // Fall through.
      // Fall through.
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_RELATIVE:
    case elfcpp::R_ARM_RELATIVE:
      // These are relocations which should only be seen by the
      // These are relocations which should only be seen by the
      // dynamic linker, and should never be seen here.
      // dynamic linker, and should never be seen here.
      gold_error(_("%s: unexpected reloc %u in object file"),
      gold_error(_("%s: unexpected reloc %u in object file"),
                 object->name().c_str(), r_type);
                 object->name().c_str(), r_type);
      break;
      break;
 
 
 
 
      // These are initial TLS relocs, which are expected when
      // These are initial TLS relocs, which are expected when
      // linking.
      // linking.
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
      {
      {
        bool output_is_shared = parameters->options().shared();
        bool output_is_shared = parameters->options().shared();
        const tls::Tls_optimization optimized_type
        const tls::Tls_optimization optimized_type
            = Target_arm<big_endian>::optimize_tls_reloc(!output_is_shared,
            = Target_arm<big_endian>::optimize_tls_reloc(!output_is_shared,
                                                         r_type);
                                                         r_type);
        switch (r_type)
        switch (r_type)
          {
          {
          case elfcpp::R_ARM_TLS_GD32:          // Global-dynamic
          case elfcpp::R_ARM_TLS_GD32:          // Global-dynamic
            if (optimized_type == tls::TLSOPT_NONE)
            if (optimized_type == tls::TLSOPT_NONE)
              {
              {
                // Create a pair of GOT entries for the module index and
                // Create a pair of GOT entries for the module index and
                // dtv-relative offset.
                // dtv-relative offset.
                Arm_output_data_got<big_endian>* got
                Arm_output_data_got<big_endian>* got
                    = target->got_section(symtab, layout);
                    = target->got_section(symtab, layout);
                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
                unsigned int shndx = lsym.get_st_shndx();
                unsigned int shndx = lsym.get_st_shndx();
                bool is_ordinary;
                bool is_ordinary;
                shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
                shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
                if (!is_ordinary)
                if (!is_ordinary)
                  {
                  {
                    object->error(_("local symbol %u has bad shndx %u"),
                    object->error(_("local symbol %u has bad shndx %u"),
                                  r_sym, shndx);
                                  r_sym, shndx);
                    break;
                    break;
                  }
                  }
 
 
                if (!parameters->doing_static_link())
                if (!parameters->doing_static_link())
                  got->add_local_pair_with_rel(object, r_sym, shndx,
                  got->add_local_pair_with_rel(object, r_sym, shndx,
                                               GOT_TYPE_TLS_PAIR,
                                               GOT_TYPE_TLS_PAIR,
                                               target->rel_dyn_section(layout),
                                               target->rel_dyn_section(layout),
                                               elfcpp::R_ARM_TLS_DTPMOD32, 0);
                                               elfcpp::R_ARM_TLS_DTPMOD32, 0);
                else
                else
                  got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR,
                  got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR,
                                                      object, r_sym);
                                                      object, r_sym);
              }
              }
            else
            else
              // FIXME: TLS optimization not supported yet.
              // FIXME: TLS optimization not supported yet.
              gold_unreachable();
              gold_unreachable();
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_LDM32:         // Local-dynamic
          case elfcpp::R_ARM_TLS_LDM32:         // Local-dynamic
            if (optimized_type == tls::TLSOPT_NONE)
            if (optimized_type == tls::TLSOPT_NONE)
              {
              {
                // Create a GOT entry for the module index.
                // Create a GOT entry for the module index.
                target->got_mod_index_entry(symtab, layout, object);
                target->got_mod_index_entry(symtab, layout, object);
              }
              }
            else
            else
              // FIXME: TLS optimization not supported yet.
              // FIXME: TLS optimization not supported yet.
              gold_unreachable();
              gold_unreachable();
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_LDO32:         // Alternate local-dynamic
          case elfcpp::R_ARM_TLS_LDO32:         // Alternate local-dynamic
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_IE32:          // Initial-exec
          case elfcpp::R_ARM_TLS_IE32:          // Initial-exec
            layout->set_has_static_tls();
            layout->set_has_static_tls();
            if (optimized_type == tls::TLSOPT_NONE)
            if (optimized_type == tls::TLSOPT_NONE)
              {
              {
                // Create a GOT entry for the tp-relative offset.
                // Create a GOT entry for the tp-relative offset.
                Arm_output_data_got<big_endian>* got
                Arm_output_data_got<big_endian>* got
                  = target->got_section(symtab, layout);
                  = target->got_section(symtab, layout);
                unsigned int r_sym =
                unsigned int r_sym =
                   elfcpp::elf_r_sym<32>(reloc.get_r_info());
                   elfcpp::elf_r_sym<32>(reloc.get_r_info());
                if (!parameters->doing_static_link())
                if (!parameters->doing_static_link())
                    got->add_local_with_rel(object, r_sym, GOT_TYPE_TLS_OFFSET,
                    got->add_local_with_rel(object, r_sym, GOT_TYPE_TLS_OFFSET,
                                            target->rel_dyn_section(layout),
                                            target->rel_dyn_section(layout),
                                            elfcpp::R_ARM_TLS_TPOFF32);
                                            elfcpp::R_ARM_TLS_TPOFF32);
                else if (!object->local_has_got_offset(r_sym,
                else if (!object->local_has_got_offset(r_sym,
                                                       GOT_TYPE_TLS_OFFSET))
                                                       GOT_TYPE_TLS_OFFSET))
                  {
                  {
                    got->add_local(object, r_sym, GOT_TYPE_TLS_OFFSET);
                    got->add_local(object, r_sym, GOT_TYPE_TLS_OFFSET);
                    unsigned int got_offset =
                    unsigned int got_offset =
                      object->local_got_offset(r_sym, GOT_TYPE_TLS_OFFSET);
                      object->local_got_offset(r_sym, GOT_TYPE_TLS_OFFSET);
                    got->add_static_reloc(got_offset,
                    got->add_static_reloc(got_offset,
                                          elfcpp::R_ARM_TLS_TPOFF32, object,
                                          elfcpp::R_ARM_TLS_TPOFF32, object,
                                          r_sym);
                                          r_sym);
                  }
                  }
              }
              }
            else
            else
              // FIXME: TLS optimization not supported yet.
              // FIXME: TLS optimization not supported yet.
              gold_unreachable();
              gold_unreachable();
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_LE32:          // Local-exec
          case elfcpp::R_ARM_TLS_LE32:          // Local-exec
            layout->set_has_static_tls();
            layout->set_has_static_tls();
            if (output_is_shared)
            if (output_is_shared)
              {
              {
                // We need to create a dynamic relocation.
                // We need to create a dynamic relocation.
                gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION);
                gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION);
                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                rel_dyn->add_local(object, r_sym, elfcpp::R_ARM_TLS_TPOFF32,
                rel_dyn->add_local(object, r_sym, elfcpp::R_ARM_TLS_TPOFF32,
                                   output_section, data_shndx,
                                   output_section, data_shndx,
                                   reloc.get_r_offset());
                                   reloc.get_r_offset());
              }
              }
            break;
            break;
 
 
          default:
          default:
            gold_unreachable();
            gold_unreachable();
          }
          }
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    default:
    default:
      unsupported_reloc_local(object, r_type);
      unsupported_reloc_local(object, r_type);
      break;
      break;
    }
    }
}
}
 
 
// Report an unsupported relocation against a global symbol.
// Report an unsupported relocation against a global symbol.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::Scan::unsupported_reloc_global(
Target_arm<big_endian>::Scan::unsupported_reloc_global(
    Sized_relobj_file<32, big_endian>* object,
    Sized_relobj_file<32, big_endian>* object,
    unsigned int r_type,
    unsigned int r_type,
    Symbol* gsym)
    Symbol* gsym)
{
{
  gold_error(_("%s: unsupported reloc %u against global symbol %s"),
  gold_error(_("%s: unsupported reloc %u against global symbol %s"),
             object->name().c_str(), r_type, gsym->demangled_name().c_str());
             object->name().c_str(), r_type, gsym->demangled_name().c_str());
}
}
 
 
template<bool big_endian>
template<bool big_endian>
inline bool
inline bool
Target_arm<big_endian>::Scan::possible_function_pointer_reloc(
Target_arm<big_endian>::Scan::possible_function_pointer_reloc(
    unsigned int r_type)
    unsigned int r_type)
{
{
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP8:
    case elfcpp::R_ARM_THM_JUMP8:
      // All the relocations above are branches except SBREL31 and PREL31.
      // All the relocations above are branches except SBREL31 and PREL31.
      return false;
      return false;
 
 
    default:
    default:
      // Be conservative and assume this is a function pointer.
      // Be conservative and assume this is a function pointer.
      return true;
      return true;
    }
    }
}
}
 
 
template<bool big_endian>
template<bool big_endian>
inline bool
inline bool
Target_arm<big_endian>::Scan::local_reloc_may_be_function_pointer(
Target_arm<big_endian>::Scan::local_reloc_may_be_function_pointer(
  Symbol_table*,
  Symbol_table*,
  Layout*,
  Layout*,
  Target_arm<big_endian>* target,
  Target_arm<big_endian>* target,
  Sized_relobj_file<32, big_endian>*,
  Sized_relobj_file<32, big_endian>*,
  unsigned int,
  unsigned int,
  Output_section*,
  Output_section*,
  const elfcpp::Rel<32, big_endian>&,
  const elfcpp::Rel<32, big_endian>&,
  unsigned int r_type,
  unsigned int r_type,
  const elfcpp::Sym<32, big_endian>&)
  const elfcpp::Sym<32, big_endian>&)
{
{
  r_type = target->get_real_reloc_type(r_type);
  r_type = target->get_real_reloc_type(r_type);
  return possible_function_pointer_reloc(r_type);
  return possible_function_pointer_reloc(r_type);
}
}
 
 
template<bool big_endian>
template<bool big_endian>
inline bool
inline bool
Target_arm<big_endian>::Scan::global_reloc_may_be_function_pointer(
Target_arm<big_endian>::Scan::global_reloc_may_be_function_pointer(
  Symbol_table*,
  Symbol_table*,
  Layout*,
  Layout*,
  Target_arm<big_endian>* target,
  Target_arm<big_endian>* target,
  Sized_relobj_file<32, big_endian>*,
  Sized_relobj_file<32, big_endian>*,
  unsigned int,
  unsigned int,
  Output_section*,
  Output_section*,
  const elfcpp::Rel<32, big_endian>&,
  const elfcpp::Rel<32, big_endian>&,
  unsigned int r_type,
  unsigned int r_type,
  Symbol* gsym)
  Symbol* gsym)
{
{
  // GOT is not a function.
  // GOT is not a function.
  if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
  if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
    return false;
    return false;
 
 
  r_type = target->get_real_reloc_type(r_type);
  r_type = target->get_real_reloc_type(r_type);
  return possible_function_pointer_reloc(r_type);
  return possible_function_pointer_reloc(r_type);
}
}
 
 
// Scan a relocation for a global symbol.
// Scan a relocation for a global symbol.
 
 
template<bool big_endian>
template<bool big_endian>
inline void
inline void
Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
                                     Layout* layout,
                                     Layout* layout,
                                     Target_arm* target,
                                     Target_arm* target,
                                     Sized_relobj_file<32, big_endian>* object,
                                     Sized_relobj_file<32, big_endian>* object,
                                     unsigned int data_shndx,
                                     unsigned int data_shndx,
                                     Output_section* output_section,
                                     Output_section* output_section,
                                     const elfcpp::Rel<32, big_endian>& reloc,
                                     const elfcpp::Rel<32, big_endian>& reloc,
                                     unsigned int r_type,
                                     unsigned int r_type,
                                     Symbol* gsym)
                                     Symbol* gsym)
{
{
  // A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
  // A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
  // section.  We check here to avoid creating a dynamic reloc against
  // section.  We check here to avoid creating a dynamic reloc against
  // _GLOBAL_OFFSET_TABLE_.
  // _GLOBAL_OFFSET_TABLE_.
  if (!target->has_got_section()
  if (!target->has_got_section()
      && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
      && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
    target->got_section(symtab, layout);
    target->got_section(symtab, layout);
 
 
  r_type = get_real_reloc_type(r_type);
  r_type = get_real_reloc_type(r_type);
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_GNU_VTENTRY:
    case elfcpp::R_ARM_GNU_VTENTRY:
    case elfcpp::R_ARM_GNU_VTINHERIT:
    case elfcpp::R_ARM_GNU_VTINHERIT:
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_ABS32_NOI:
    case elfcpp::R_ARM_ABS32_NOI:
      // Absolute addressing relocations.
      // Absolute addressing relocations.
      {
      {
        // Make a PLT entry if necessary.
        // Make a PLT entry if necessary.
        if (this->symbol_needs_plt_entry(gsym))
        if (this->symbol_needs_plt_entry(gsym))
          {
          {
            target->make_plt_entry(symtab, layout, gsym);
            target->make_plt_entry(symtab, layout, gsym);
            // Since this is not a PC-relative relocation, we may be
            // Since this is not a PC-relative relocation, we may be
            // taking the address of a function. In that case we need to
            // taking the address of a function. In that case we need to
            // set the entry in the dynamic symbol table to the address of
            // set the entry in the dynamic symbol table to the address of
            // the PLT entry.
            // the PLT entry.
            if (gsym->is_from_dynobj() && !parameters->options().shared())
            if (gsym->is_from_dynobj() && !parameters->options().shared())
              gsym->set_needs_dynsym_value();
              gsym->set_needs_dynsym_value();
          }
          }
        // Make a dynamic relocation if necessary.
        // Make a dynamic relocation if necessary.
        if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
        if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
          {
          {
            if (gsym->may_need_copy_reloc())
            if (gsym->may_need_copy_reloc())
              {
              {
                target->copy_reloc(symtab, layout, object,
                target->copy_reloc(symtab, layout, object,
                                   data_shndx, output_section, gsym, reloc);
                                   data_shndx, output_section, gsym, reloc);
              }
              }
            else if ((r_type == elfcpp::R_ARM_ABS32
            else if ((r_type == elfcpp::R_ARM_ABS32
                      || r_type == elfcpp::R_ARM_ABS32_NOI)
                      || r_type == elfcpp::R_ARM_ABS32_NOI)
                     && gsym->can_use_relative_reloc(false))
                     && gsym->can_use_relative_reloc(false))
              {
              {
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                rel_dyn->add_global_relative(gsym, elfcpp::R_ARM_RELATIVE,
                rel_dyn->add_global_relative(gsym, elfcpp::R_ARM_RELATIVE,
                                             output_section, object,
                                             output_section, object,
                                             data_shndx, reloc.get_r_offset());
                                             data_shndx, reloc.get_r_offset());
              }
              }
            else
            else
              {
              {
                check_non_pic(object, r_type);
                check_non_pic(object, r_type);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                rel_dyn->add_global(gsym, r_type, output_section, object,
                rel_dyn->add_global(gsym, r_type, output_section, object,
                                    data_shndx, reloc.get_r_offset());
                                    data_shndx, reloc.get_r_offset());
              }
              }
          }
          }
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF12:
    case elfcpp::R_ARM_GOTOFF12:
      // We need a GOT section.
      // We need a GOT section.
      target->got_section(symtab, layout);
      target->got_section(symtab, layout);
      break;
      break;
 
 
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_SBREL32:
    case elfcpp::R_ARM_SBREL32:
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_REL32_NOI:
    case elfcpp::R_ARM_REL32_NOI:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
      // Relative addressing relocations.
      // Relative addressing relocations.
      {
      {
        // Make a dynamic relocation if necessary.
        // Make a dynamic relocation if necessary.
        if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
        if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
          {
          {
            if (target->may_need_copy_reloc(gsym))
            if (target->may_need_copy_reloc(gsym))
              {
              {
                target->copy_reloc(symtab, layout, object,
                target->copy_reloc(symtab, layout, object,
                                   data_shndx, output_section, gsym, reloc);
                                   data_shndx, output_section, gsym, reloc);
              }
              }
            else
            else
              {
              {
                check_non_pic(object, r_type);
                check_non_pic(object, r_type);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                rel_dyn->add_global(gsym, r_type, output_section, object,
                rel_dyn->add_global(gsym, r_type, output_section, object,
                                    data_shndx, reloc.get_r_offset());
                                    data_shndx, reloc.get_r_offset());
              }
              }
          }
          }
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_SBREL31:
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP8:
    case elfcpp::R_ARM_THM_JUMP8:
      // All the relocation above are branches except for the PREL31 ones.
      // All the relocation above are branches except for the PREL31 ones.
      // A PREL31 relocation can point to a personality function in a shared
      // A PREL31 relocation can point to a personality function in a shared
      // library.  In that case we want to use a PLT because we want to
      // library.  In that case we want to use a PLT because we want to
      // call the personality routine and the dynamic linkers we care about
      // call the personality routine and the dynamic linkers we care about
      // do not support dynamic PREL31 relocations. An REL31 relocation may
      // do not support dynamic PREL31 relocations. An REL31 relocation may
      // point to a function whose unwinding behaviour is being described but
      // point to a function whose unwinding behaviour is being described but
      // we will not mistakenly generate a PLT for that because we should use
      // we will not mistakenly generate a PLT for that because we should use
      // a local section symbol.
      // a local section symbol.
 
 
      // If the symbol is fully resolved, this is just a relative
      // If the symbol is fully resolved, this is just a relative
      // local reloc.  Otherwise we need a PLT entry.
      // local reloc.  Otherwise we need a PLT entry.
      if (gsym->final_value_is_known())
      if (gsym->final_value_is_known())
        break;
        break;
      // If building a shared library, we can also skip the PLT entry
      // If building a shared library, we can also skip the PLT entry
      // if the symbol is defined in the output file and is protected
      // if the symbol is defined in the output file and is protected
      // or hidden.
      // or hidden.
      if (gsym->is_defined()
      if (gsym->is_defined()
          && !gsym->is_from_dynobj()
          && !gsym->is_from_dynobj()
          && !gsym->is_preemptible())
          && !gsym->is_preemptible())
        break;
        break;
      target->make_plt_entry(symtab, layout, gsym);
      target->make_plt_entry(symtab, layout, gsym);
      break;
      break;
 
 
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_ABS:
    case elfcpp::R_ARM_GOT_ABS:
    case elfcpp::R_ARM_GOT_PREL:
    case elfcpp::R_ARM_GOT_PREL:
      {
      {
        // The symbol requires a GOT entry.
        // The symbol requires a GOT entry.
        Arm_output_data_got<big_endian>* got =
        Arm_output_data_got<big_endian>* got =
          target->got_section(symtab, layout);
          target->got_section(symtab, layout);
        if (gsym->final_value_is_known())
        if (gsym->final_value_is_known())
          got->add_global(gsym, GOT_TYPE_STANDARD);
          got->add_global(gsym, GOT_TYPE_STANDARD);
        else
        else
          {
          {
            // If this symbol is not fully resolved, we need to add a
            // If this symbol is not fully resolved, we need to add a
            // GOT entry with a dynamic relocation.
            // GOT entry with a dynamic relocation.
            Reloc_section* rel_dyn = target->rel_dyn_section(layout);
            Reloc_section* rel_dyn = target->rel_dyn_section(layout);
            if (gsym->is_from_dynobj()
            if (gsym->is_from_dynobj()
                || gsym->is_undefined()
                || gsym->is_undefined()
                || gsym->is_preemptible())
                || gsym->is_preemptible())
              got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
              got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
                                       rel_dyn, elfcpp::R_ARM_GLOB_DAT);
                                       rel_dyn, elfcpp::R_ARM_GLOB_DAT);
            else
            else
              {
              {
                if (got->add_global(gsym, GOT_TYPE_STANDARD))
                if (got->add_global(gsym, GOT_TYPE_STANDARD))
                  rel_dyn->add_global_relative(
                  rel_dyn->add_global_relative(
                      gsym, elfcpp::R_ARM_RELATIVE, got,
                      gsym, elfcpp::R_ARM_RELATIVE, got,
                      gsym->got_offset(GOT_TYPE_STANDARD));
                      gsym->got_offset(GOT_TYPE_STANDARD));
              }
              }
          }
          }
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET2:
    case elfcpp::R_ARM_TARGET2:
      // These should have been mapped to other types already.
      // These should have been mapped to other types already.
      // Fall through.
      // Fall through.
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_COPY:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_GLOB_DAT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_JUMP_SLOT:
    case elfcpp::R_ARM_RELATIVE:
    case elfcpp::R_ARM_RELATIVE:
      // These are relocations which should only be seen by the
      // These are relocations which should only be seen by the
      // dynamic linker, and should never be seen here.
      // dynamic linker, and should never be seen here.
      gold_error(_("%s: unexpected reloc %u in object file"),
      gold_error(_("%s: unexpected reloc %u in object file"),
                 object->name().c_str(), r_type);
                 object->name().c_str(), r_type);
      break;
      break;
 
 
      // These are initial tls relocs, which are expected when
      // These are initial tls relocs, which are expected when
      // linking.
      // linking.
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
      {
      {
        const bool is_final = gsym->final_value_is_known();
        const bool is_final = gsym->final_value_is_known();
        const tls::Tls_optimization optimized_type
        const tls::Tls_optimization optimized_type
            = Target_arm<big_endian>::optimize_tls_reloc(is_final, r_type);
            = Target_arm<big_endian>::optimize_tls_reloc(is_final, r_type);
        switch (r_type)
        switch (r_type)
          {
          {
          case elfcpp::R_ARM_TLS_GD32:          // Global-dynamic
          case elfcpp::R_ARM_TLS_GD32:          // Global-dynamic
            if (optimized_type == tls::TLSOPT_NONE)
            if (optimized_type == tls::TLSOPT_NONE)
              {
              {
                // Create a pair of GOT entries for the module index and
                // Create a pair of GOT entries for the module index and
                // dtv-relative offset.
                // dtv-relative offset.
                Arm_output_data_got<big_endian>* got
                Arm_output_data_got<big_endian>* got
                    = target->got_section(symtab, layout);
                    = target->got_section(symtab, layout);
                if (!parameters->doing_static_link())
                if (!parameters->doing_static_link())
                  got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR,
                  got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR,
                                                target->rel_dyn_section(layout),
                                                target->rel_dyn_section(layout),
                                                elfcpp::R_ARM_TLS_DTPMOD32,
                                                elfcpp::R_ARM_TLS_DTPMOD32,
                                                elfcpp::R_ARM_TLS_DTPOFF32);
                                                elfcpp::R_ARM_TLS_DTPOFF32);
                else
                else
                  got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR, gsym);
                  got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR, gsym);
              }
              }
            else
            else
              // FIXME: TLS optimization not supported yet.
              // FIXME: TLS optimization not supported yet.
              gold_unreachable();
              gold_unreachable();
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_LDM32:         // Local-dynamic
          case elfcpp::R_ARM_TLS_LDM32:         // Local-dynamic
            if (optimized_type == tls::TLSOPT_NONE)
            if (optimized_type == tls::TLSOPT_NONE)
              {
              {
                // Create a GOT entry for the module index.
                // Create a GOT entry for the module index.
                target->got_mod_index_entry(symtab, layout, object);
                target->got_mod_index_entry(symtab, layout, object);
              }
              }
            else
            else
              // FIXME: TLS optimization not supported yet.
              // FIXME: TLS optimization not supported yet.
              gold_unreachable();
              gold_unreachable();
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_LDO32:         // Alternate local-dynamic
          case elfcpp::R_ARM_TLS_LDO32:         // Alternate local-dynamic
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_IE32:          // Initial-exec
          case elfcpp::R_ARM_TLS_IE32:          // Initial-exec
            layout->set_has_static_tls();
            layout->set_has_static_tls();
            if (optimized_type == tls::TLSOPT_NONE)
            if (optimized_type == tls::TLSOPT_NONE)
              {
              {
                // Create a GOT entry for the tp-relative offset.
                // Create a GOT entry for the tp-relative offset.
                Arm_output_data_got<big_endian>* got
                Arm_output_data_got<big_endian>* got
                  = target->got_section(symtab, layout);
                  = target->got_section(symtab, layout);
                if (!parameters->doing_static_link())
                if (!parameters->doing_static_link())
                  got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET,
                  got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET,
                                           target->rel_dyn_section(layout),
                                           target->rel_dyn_section(layout),
                                           elfcpp::R_ARM_TLS_TPOFF32);
                                           elfcpp::R_ARM_TLS_TPOFF32);
                else if (!gsym->has_got_offset(GOT_TYPE_TLS_OFFSET))
                else if (!gsym->has_got_offset(GOT_TYPE_TLS_OFFSET))
                  {
                  {
                    got->add_global(gsym, GOT_TYPE_TLS_OFFSET);
                    got->add_global(gsym, GOT_TYPE_TLS_OFFSET);
                    unsigned int got_offset =
                    unsigned int got_offset =
                       gsym->got_offset(GOT_TYPE_TLS_OFFSET);
                       gsym->got_offset(GOT_TYPE_TLS_OFFSET);
                    got->add_static_reloc(got_offset,
                    got->add_static_reloc(got_offset,
                                          elfcpp::R_ARM_TLS_TPOFF32, gsym);
                                          elfcpp::R_ARM_TLS_TPOFF32, gsym);
                  }
                  }
              }
              }
            else
            else
              // FIXME: TLS optimization not supported yet.
              // FIXME: TLS optimization not supported yet.
              gold_unreachable();
              gold_unreachable();
            break;
            break;
 
 
          case elfcpp::R_ARM_TLS_LE32:  // Local-exec
          case elfcpp::R_ARM_TLS_LE32:  // Local-exec
            layout->set_has_static_tls();
            layout->set_has_static_tls();
            if (parameters->options().shared())
            if (parameters->options().shared())
              {
              {
                // We need to create a dynamic relocation.
                // We need to create a dynamic relocation.
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                rel_dyn->add_global(gsym, elfcpp::R_ARM_TLS_TPOFF32,
                rel_dyn->add_global(gsym, elfcpp::R_ARM_TLS_TPOFF32,
                                    output_section, object,
                                    output_section, object,
                                    data_shndx, reloc.get_r_offset());
                                    data_shndx, reloc.get_r_offset());
              }
              }
            break;
            break;
 
 
          default:
          default:
            gold_unreachable();
            gold_unreachable();
          }
          }
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    default:
    default:
      unsupported_reloc_global(object, r_type, gsym);
      unsupported_reloc_global(object, r_type, gsym);
      break;
      break;
    }
    }
}
}
 
 
// Process relocations for gc.
// Process relocations for gc.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::gc_process_relocs(
Target_arm<big_endian>::gc_process_relocs(
    Symbol_table* symtab,
    Symbol_table* symtab,
    Layout* layout,
    Layout* layout,
    Sized_relobj_file<32, big_endian>* object,
    Sized_relobj_file<32, big_endian>* object,
    unsigned int data_shndx,
    unsigned int data_shndx,
    unsigned int,
    unsigned int,
    const unsigned char* prelocs,
    const unsigned char* prelocs,
    size_t reloc_count,
    size_t reloc_count,
    Output_section* output_section,
    Output_section* output_section,
    bool needs_special_offset_handling,
    bool needs_special_offset_handling,
    size_t local_symbol_count,
    size_t local_symbol_count,
    const unsigned char* plocal_symbols)
    const unsigned char* plocal_symbols)
{
{
  typedef Target_arm<big_endian> Arm;
  typedef Target_arm<big_endian> Arm;
  typedef typename Target_arm<big_endian>::Scan Scan;
  typedef typename Target_arm<big_endian>::Scan Scan;
 
 
  gold::gc_process_relocs<32, big_endian, Arm, elfcpp::SHT_REL, Scan,
  gold::gc_process_relocs<32, big_endian, Arm, elfcpp::SHT_REL, Scan,
                          typename Target_arm::Relocatable_size_for_reloc>(
                          typename Target_arm::Relocatable_size_for_reloc>(
    symtab,
    symtab,
    layout,
    layout,
    this,
    this,
    object,
    object,
    data_shndx,
    data_shndx,
    prelocs,
    prelocs,
    reloc_count,
    reloc_count,
    output_section,
    output_section,
    needs_special_offset_handling,
    needs_special_offset_handling,
    local_symbol_count,
    local_symbol_count,
    plocal_symbols);
    plocal_symbols);
}
}
 
 
// Scan relocations for a section.
// Scan relocations for a section.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::scan_relocs(Symbol_table* symtab,
Target_arm<big_endian>::scan_relocs(Symbol_table* symtab,
                                    Layout* layout,
                                    Layout* layout,
                                    Sized_relobj_file<32, big_endian>* object,
                                    Sized_relobj_file<32, big_endian>* object,
                                    unsigned int data_shndx,
                                    unsigned int data_shndx,
                                    unsigned int sh_type,
                                    unsigned int sh_type,
                                    const unsigned char* prelocs,
                                    const unsigned char* prelocs,
                                    size_t reloc_count,
                                    size_t reloc_count,
                                    Output_section* output_section,
                                    Output_section* output_section,
                                    bool needs_special_offset_handling,
                                    bool needs_special_offset_handling,
                                    size_t local_symbol_count,
                                    size_t local_symbol_count,
                                    const unsigned char* plocal_symbols)
                                    const unsigned char* plocal_symbols)
{
{
  typedef typename Target_arm<big_endian>::Scan Scan;
  typedef typename Target_arm<big_endian>::Scan Scan;
  if (sh_type == elfcpp::SHT_RELA)
  if (sh_type == elfcpp::SHT_RELA)
    {
    {
      gold_error(_("%s: unsupported RELA reloc section"),
      gold_error(_("%s: unsupported RELA reloc section"),
                 object->name().c_str());
                 object->name().c_str());
      return;
      return;
    }
    }
 
 
  gold::scan_relocs<32, big_endian, Target_arm, elfcpp::SHT_REL, Scan>(
  gold::scan_relocs<32, big_endian, Target_arm, elfcpp::SHT_REL, Scan>(
    symtab,
    symtab,
    layout,
    layout,
    this,
    this,
    object,
    object,
    data_shndx,
    data_shndx,
    prelocs,
    prelocs,
    reloc_count,
    reloc_count,
    output_section,
    output_section,
    needs_special_offset_handling,
    needs_special_offset_handling,
    local_symbol_count,
    local_symbol_count,
    plocal_symbols);
    plocal_symbols);
}
}
 
 
// Finalize the sections.
// Finalize the sections.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::do_finalize_sections(
Target_arm<big_endian>::do_finalize_sections(
    Layout* layout,
    Layout* layout,
    const Input_objects* input_objects,
    const Input_objects* input_objects,
    Symbol_table* symtab)
    Symbol_table* symtab)
{
{
  bool merged_any_attributes = false;
  bool merged_any_attributes = false;
  // Merge processor-specific flags.
  // Merge processor-specific flags.
  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
       p != input_objects->relobj_end();
       p != input_objects->relobj_end();
       ++p)
       ++p)
    {
    {
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(*p);
        Arm_relobj<big_endian>::as_arm_relobj(*p);
      if (arm_relobj->merge_flags_and_attributes())
      if (arm_relobj->merge_flags_and_attributes())
        {
        {
          this->merge_processor_specific_flags(
          this->merge_processor_specific_flags(
              arm_relobj->name(),
              arm_relobj->name(),
              arm_relobj->processor_specific_flags());
              arm_relobj->processor_specific_flags());
          this->merge_object_attributes(arm_relobj->name().c_str(),
          this->merge_object_attributes(arm_relobj->name().c_str(),
                                        arm_relobj->attributes_section_data());
                                        arm_relobj->attributes_section_data());
          merged_any_attributes = true;
          merged_any_attributes = true;
        }
        }
    }
    }
 
 
  for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin();
  for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin();
       p != input_objects->dynobj_end();
       p != input_objects->dynobj_end();
       ++p)
       ++p)
    {
    {
      Arm_dynobj<big_endian>* arm_dynobj =
      Arm_dynobj<big_endian>* arm_dynobj =
        Arm_dynobj<big_endian>::as_arm_dynobj(*p);
        Arm_dynobj<big_endian>::as_arm_dynobj(*p);
      this->merge_processor_specific_flags(
      this->merge_processor_specific_flags(
          arm_dynobj->name(),
          arm_dynobj->name(),
          arm_dynobj->processor_specific_flags());
          arm_dynobj->processor_specific_flags());
      this->merge_object_attributes(arm_dynobj->name().c_str(),
      this->merge_object_attributes(arm_dynobj->name().c_str(),
                                    arm_dynobj->attributes_section_data());
                                    arm_dynobj->attributes_section_data());
      merged_any_attributes = true;
      merged_any_attributes = true;
    }
    }
 
 
  // Create an empty uninitialized attribute section if we still don't have it
  // Create an empty uninitialized attribute section if we still don't have it
  // at this moment.  This happens if there is no attributes sections in all
  // at this moment.  This happens if there is no attributes sections in all
  // inputs.
  // inputs.
  if (this->attributes_section_data_ == NULL)
  if (this->attributes_section_data_ == NULL)
    this->attributes_section_data_ = new Attributes_section_data(NULL, 0);
    this->attributes_section_data_ = new Attributes_section_data(NULL, 0);
 
 
  const Object_attribute* cpu_arch_attr =
  const Object_attribute* cpu_arch_attr =
    this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
    this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
  // Check if we need to use Cortex-A8 workaround.
  // Check if we need to use Cortex-A8 workaround.
  if (parameters->options().user_set_fix_cortex_a8())
  if (parameters->options().user_set_fix_cortex_a8())
    this->fix_cortex_a8_ = parameters->options().fix_cortex_a8();
    this->fix_cortex_a8_ = parameters->options().fix_cortex_a8();
  else
  else
    {
    {
      // If neither --fix-cortex-a8 nor --no-fix-cortex-a8 is used, turn on
      // If neither --fix-cortex-a8 nor --no-fix-cortex-a8 is used, turn on
      // Cortex-A8 erratum workaround for ARMv7-A or ARMv7 with unknown
      // Cortex-A8 erratum workaround for ARMv7-A or ARMv7 with unknown
      // profile.  
      // profile.  
      const Object_attribute* cpu_arch_profile_attr =
      const Object_attribute* cpu_arch_profile_attr =
        this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
        this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
      this->fix_cortex_a8_ =
      this->fix_cortex_a8_ =
        (cpu_arch_attr->int_value() == elfcpp::TAG_CPU_ARCH_V7
        (cpu_arch_attr->int_value() == elfcpp::TAG_CPU_ARCH_V7
         && (cpu_arch_profile_attr->int_value() == 'A'
         && (cpu_arch_profile_attr->int_value() == 'A'
             || cpu_arch_profile_attr->int_value() == 0));
             || cpu_arch_profile_attr->int_value() == 0));
    }
    }
 
 
  // Check if we can use V4BX interworking.
  // Check if we can use V4BX interworking.
  // The V4BX interworking stub contains BX instruction,
  // The V4BX interworking stub contains BX instruction,
  // which is not specified for some profiles.
  // which is not specified for some profiles.
  if (this->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING
  if (this->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING
      && !this->may_use_v4t_interworking())
      && !this->may_use_v4t_interworking())
    gold_error(_("unable to provide V4BX reloc interworking fix up; "
    gold_error(_("unable to provide V4BX reloc interworking fix up; "
                 "the target profile does not support BX instruction"));
                 "the target profile does not support BX instruction"));
 
 
  // Fill in some more dynamic tags.
  // Fill in some more dynamic tags.
  const Reloc_section* rel_plt = (this->plt_ == NULL
  const Reloc_section* rel_plt = (this->plt_ == NULL
                                  ? NULL
                                  ? NULL
                                  : this->plt_->rel_plt());
                                  : this->plt_->rel_plt());
  layout->add_target_dynamic_tags(true, this->got_plt_, rel_plt,
  layout->add_target_dynamic_tags(true, this->got_plt_, rel_plt,
                                  this->rel_dyn_, true, false);
                                  this->rel_dyn_, true, false);
 
 
  // Emit any relocs we saved in an attempt to avoid generating COPY
  // Emit any relocs we saved in an attempt to avoid generating COPY
  // relocs.
  // relocs.
  if (this->copy_relocs_.any_saved_relocs())
  if (this->copy_relocs_.any_saved_relocs())
    this->copy_relocs_.emit(this->rel_dyn_section(layout));
    this->copy_relocs_.emit(this->rel_dyn_section(layout));
 
 
  // Handle the .ARM.exidx section.
  // Handle the .ARM.exidx section.
  Output_section* exidx_section = layout->find_output_section(".ARM.exidx");
  Output_section* exidx_section = layout->find_output_section(".ARM.exidx");
 
 
  if (!parameters->options().relocatable())
  if (!parameters->options().relocatable())
    {
    {
      if (exidx_section != NULL
      if (exidx_section != NULL
          && exidx_section->type() == elfcpp::SHT_ARM_EXIDX)
          && exidx_section->type() == elfcpp::SHT_ARM_EXIDX)
        {
        {
          // Create __exidx_start and __exidx_end symbols.
          // Create __exidx_start and __exidx_end symbols.
          symtab->define_in_output_data("__exidx_start", NULL,
          symtab->define_in_output_data("__exidx_start", NULL,
                                        Symbol_table::PREDEFINED,
                                        Symbol_table::PREDEFINED,
                                        exidx_section, 0, 0, elfcpp::STT_OBJECT,
                                        exidx_section, 0, 0, elfcpp::STT_OBJECT,
                                        elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
                                        elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
                                        0, false, true);
                                        0, false, true);
          symtab->define_in_output_data("__exidx_end", NULL,
          symtab->define_in_output_data("__exidx_end", NULL,
                                        Symbol_table::PREDEFINED,
                                        Symbol_table::PREDEFINED,
                                        exidx_section, 0, 0, elfcpp::STT_OBJECT,
                                        exidx_section, 0, 0, elfcpp::STT_OBJECT,
                                        elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
                                        elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
                                        0, true, true);
                                        0, true, true);
 
 
          // For the ARM target, we need to add a PT_ARM_EXIDX segment for
          // For the ARM target, we need to add a PT_ARM_EXIDX segment for
          // the .ARM.exidx section.
          // the .ARM.exidx section.
          if (!layout->script_options()->saw_phdrs_clause())
          if (!layout->script_options()->saw_phdrs_clause())
            {
            {
              gold_assert(layout->find_output_segment(elfcpp::PT_ARM_EXIDX, 0,
              gold_assert(layout->find_output_segment(elfcpp::PT_ARM_EXIDX, 0,
                                                      0)
                                                      0)
                          == NULL);
                          == NULL);
              Output_segment*  exidx_segment =
              Output_segment*  exidx_segment =
                layout->make_output_segment(elfcpp::PT_ARM_EXIDX, elfcpp::PF_R);
                layout->make_output_segment(elfcpp::PT_ARM_EXIDX, elfcpp::PF_R);
              exidx_segment->add_output_section_to_nonload(exidx_section,
              exidx_segment->add_output_section_to_nonload(exidx_section,
                                                           elfcpp::PF_R);
                                                           elfcpp::PF_R);
            }
            }
        }
        }
      else
      else
        {
        {
          symtab->define_as_constant("__exidx_start", NULL,
          symtab->define_as_constant("__exidx_start", NULL,
                                     Symbol_table::PREDEFINED,
                                     Symbol_table::PREDEFINED,
                                     0, 0, elfcpp::STT_OBJECT,
                                     0, 0, elfcpp::STT_OBJECT,
                                     elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0,
                                     elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0,
                                     true, false);
                                     true, false);
          symtab->define_as_constant("__exidx_end", NULL,
          symtab->define_as_constant("__exidx_end", NULL,
                                     Symbol_table::PREDEFINED,
                                     Symbol_table::PREDEFINED,
                                     0, 0, elfcpp::STT_OBJECT,
                                     0, 0, elfcpp::STT_OBJECT,
                                     elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0,
                                     elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0,
                                     true, false);
                                     true, false);
        }
        }
    }
    }
 
 
  // Create an .ARM.attributes section if we have merged any attributes
  // Create an .ARM.attributes section if we have merged any attributes
  // from inputs.
  // from inputs.
  if (merged_any_attributes)
  if (merged_any_attributes)
    {
    {
      Output_attributes_section_data* attributes_section =
      Output_attributes_section_data* attributes_section =
      new Output_attributes_section_data(*this->attributes_section_data_);
      new Output_attributes_section_data(*this->attributes_section_data_);
      layout->add_output_section_data(".ARM.attributes",
      layout->add_output_section_data(".ARM.attributes",
                                      elfcpp::SHT_ARM_ATTRIBUTES, 0,
                                      elfcpp::SHT_ARM_ATTRIBUTES, 0,
                                      attributes_section, ORDER_INVALID,
                                      attributes_section, ORDER_INVALID,
                                      false);
                                      false);
    }
    }
 
 
  // Fix up links in section EXIDX headers.
  // Fix up links in section EXIDX headers.
  for (Layout::Section_list::const_iterator p = layout->section_list().begin();
  for (Layout::Section_list::const_iterator p = layout->section_list().begin();
       p != layout->section_list().end();
       p != layout->section_list().end();
       ++p)
       ++p)
    if ((*p)->type() == elfcpp::SHT_ARM_EXIDX)
    if ((*p)->type() == elfcpp::SHT_ARM_EXIDX)
      {
      {
        Arm_output_section<big_endian>* os =
        Arm_output_section<big_endian>* os =
          Arm_output_section<big_endian>::as_arm_output_section(*p);
          Arm_output_section<big_endian>::as_arm_output_section(*p);
        os->set_exidx_section_link();
        os->set_exidx_section_link();
      }
      }
}
}
 
 
// Return whether a direct absolute static relocation needs to be applied.
// Return whether a direct absolute static relocation needs to be applied.
// In cases where Scan::local() or Scan::global() has created
// In cases where Scan::local() or Scan::global() has created
// a dynamic relocation other than R_ARM_RELATIVE, the addend
// a dynamic relocation other than R_ARM_RELATIVE, the addend
// of the relocation is carried in the data, and we must not
// of the relocation is carried in the data, and we must not
// apply the static relocation.
// apply the static relocation.
 
 
template<bool big_endian>
template<bool big_endian>
inline bool
inline bool
Target_arm<big_endian>::Relocate::should_apply_static_reloc(
Target_arm<big_endian>::Relocate::should_apply_static_reloc(
    const Sized_symbol<32>* gsym,
    const Sized_symbol<32>* gsym,
    unsigned int r_type,
    unsigned int r_type,
    bool is_32bit,
    bool is_32bit,
    Output_section* output_section)
    Output_section* output_section)
{
{
  // If the output section is not allocated, then we didn't call
  // If the output section is not allocated, then we didn't call
  // scan_relocs, we didn't create a dynamic reloc, and we must apply
  // scan_relocs, we didn't create a dynamic reloc, and we must apply
  // the reloc here.
  // the reloc here.
  if ((output_section->flags() & elfcpp::SHF_ALLOC) == 0)
  if ((output_section->flags() & elfcpp::SHF_ALLOC) == 0)
      return true;
      return true;
 
 
  int ref_flags = Scan::get_reference_flags(r_type);
  int ref_flags = Scan::get_reference_flags(r_type);
 
 
  // For local symbols, we will have created a non-RELATIVE dynamic
  // For local symbols, we will have created a non-RELATIVE dynamic
  // relocation only if (a) the output is position independent,
  // relocation only if (a) the output is position independent,
  // (b) the relocation is absolute (not pc- or segment-relative), and
  // (b) the relocation is absolute (not pc- or segment-relative), and
  // (c) the relocation is not 32 bits wide.
  // (c) the relocation is not 32 bits wide.
  if (gsym == NULL)
  if (gsym == NULL)
    return !(parameters->options().output_is_position_independent()
    return !(parameters->options().output_is_position_independent()
             && (ref_flags & Symbol::ABSOLUTE_REF)
             && (ref_flags & Symbol::ABSOLUTE_REF)
             && !is_32bit);
             && !is_32bit);
 
 
  // For global symbols, we use the same helper routines used in the
  // For global symbols, we use the same helper routines used in the
  // scan pass.  If we did not create a dynamic relocation, or if we
  // scan pass.  If we did not create a dynamic relocation, or if we
  // created a RELATIVE dynamic relocation, we should apply the static
  // created a RELATIVE dynamic relocation, we should apply the static
  // relocation.
  // relocation.
  bool has_dyn = gsym->needs_dynamic_reloc(ref_flags);
  bool has_dyn = gsym->needs_dynamic_reloc(ref_flags);
  bool is_rel = (ref_flags & Symbol::ABSOLUTE_REF)
  bool is_rel = (ref_flags & Symbol::ABSOLUTE_REF)
                 && gsym->can_use_relative_reloc(ref_flags
                 && gsym->can_use_relative_reloc(ref_flags
                                                 & Symbol::FUNCTION_CALL);
                                                 & Symbol::FUNCTION_CALL);
  return !has_dyn || is_rel;
  return !has_dyn || is_rel;
}
}
 
 
// Perform a relocation.
// Perform a relocation.
 
 
template<bool big_endian>
template<bool big_endian>
inline bool
inline bool
Target_arm<big_endian>::Relocate::relocate(
Target_arm<big_endian>::Relocate::relocate(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    Target_arm* target,
    Target_arm* target,
    Output_section* output_section,
    Output_section* output_section,
    size_t relnum,
    size_t relnum,
    const elfcpp::Rel<32, big_endian>& rel,
    const elfcpp::Rel<32, big_endian>& rel,
    unsigned int r_type,
    unsigned int r_type,
    const Sized_symbol<32>* gsym,
    const Sized_symbol<32>* gsym,
    const Symbol_value<32>* psymval,
    const Symbol_value<32>* psymval,
    unsigned char* view,
    unsigned char* view,
    Arm_address address,
    Arm_address address,
    section_size_type view_size)
    section_size_type view_size)
{
{
  typedef Arm_relocate_functions<big_endian> Arm_relocate_functions;
  typedef Arm_relocate_functions<big_endian> Arm_relocate_functions;
 
 
  r_type = get_real_reloc_type(r_type);
  r_type = get_real_reloc_type(r_type);
  const Arm_reloc_property* reloc_property =
  const Arm_reloc_property* reloc_property =
    arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
    arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
  if (reloc_property == NULL)
  if (reloc_property == NULL)
    {
    {
      std::string reloc_name =
      std::string reloc_name =
        arm_reloc_property_table->reloc_name_in_error_message(r_type);
        arm_reloc_property_table->reloc_name_in_error_message(r_type);
      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("cannot relocate %s in object file"),
                             _("cannot relocate %s in object file"),
                             reloc_name.c_str());
                             reloc_name.c_str());
      return true;
      return true;
    }
    }
 
 
  const Arm_relobj<big_endian>* object =
  const Arm_relobj<big_endian>* object =
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
 
 
  // If the final branch target of a relocation is THUMB instruction, this
  // If the final branch target of a relocation is THUMB instruction, this
  // is 1.  Otherwise it is 0.
  // is 1.  Otherwise it is 0.
  Arm_address thumb_bit = 0;
  Arm_address thumb_bit = 0;
  Symbol_value<32> symval;
  Symbol_value<32> symval;
  bool is_weakly_undefined_without_plt = false;
  bool is_weakly_undefined_without_plt = false;
  bool have_got_offset = false;
  bool have_got_offset = false;
  unsigned int got_offset = 0;
  unsigned int got_offset = 0;
 
 
  // If the relocation uses the GOT entry of a symbol instead of the symbol
  // If the relocation uses the GOT entry of a symbol instead of the symbol
  // itself, we don't care about whether the symbol is defined or what kind
  // itself, we don't care about whether the symbol is defined or what kind
  // of symbol it is.
  // of symbol it is.
  if (reloc_property->uses_got_entry())
  if (reloc_property->uses_got_entry())
    {
    {
      // Get the GOT offset.
      // Get the GOT offset.
      // The GOT pointer points to the end of the GOT section.
      // The GOT pointer points to the end of the GOT section.
      // We need to subtract the size of the GOT section to get
      // We need to subtract the size of the GOT section to get
      // the actual offset to use in the relocation.
      // the actual offset to use in the relocation.
      // TODO: We should move GOT offset computing code in TLS relocations
      // TODO: We should move GOT offset computing code in TLS relocations
      // to here.
      // to here.
      switch (r_type)
      switch (r_type)
        {
        {
        case elfcpp::R_ARM_GOT_BREL:
        case elfcpp::R_ARM_GOT_BREL:
        case elfcpp::R_ARM_GOT_PREL:
        case elfcpp::R_ARM_GOT_PREL:
          if (gsym != NULL)
          if (gsym != NULL)
            {
            {
              gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
              gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
              got_offset = (gsym->got_offset(GOT_TYPE_STANDARD)
              got_offset = (gsym->got_offset(GOT_TYPE_STANDARD)
                            - target->got_size());
                            - target->got_size());
            }
            }
          else
          else
            {
            {
              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
              gold_assert(object->local_has_got_offset(r_sym,
              gold_assert(object->local_has_got_offset(r_sym,
                                                       GOT_TYPE_STANDARD));
                                                       GOT_TYPE_STANDARD));
              got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD)
              got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD)
                            - target->got_size());
                            - target->got_size());
            }
            }
          have_got_offset = true;
          have_got_offset = true;
          break;
          break;
 
 
        default:
        default:
          break;
          break;
        }
        }
    }
    }
  else if (relnum != Target_arm<big_endian>::fake_relnum_for_stubs)
  else if (relnum != Target_arm<big_endian>::fake_relnum_for_stubs)
    {
    {
      if (gsym != NULL)
      if (gsym != NULL)
        {
        {
          // This is a global symbol.  Determine if we use PLT and if the
          // This is a global symbol.  Determine if we use PLT and if the
          // final target is THUMB.
          // final target is THUMB.
          if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
          if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
            {
            {
              // This uses a PLT, change the symbol value.
              // This uses a PLT, change the symbol value.
              symval.set_output_value(target->plt_section()->address()
              symval.set_output_value(target->plt_section()->address()
                                      + gsym->plt_offset());
                                      + gsym->plt_offset());
              psymval = &symval;
              psymval = &symval;
            }
            }
          else if (gsym->is_weak_undefined())
          else if (gsym->is_weak_undefined())
            {
            {
              // This is a weakly undefined symbol and we do not use PLT
              // This is a weakly undefined symbol and we do not use PLT
              // for this relocation.  A branch targeting this symbol will
              // for this relocation.  A branch targeting this symbol will
              // be converted into an NOP.
              // be converted into an NOP.
              is_weakly_undefined_without_plt = true;
              is_weakly_undefined_without_plt = true;
            }
            }
          else if (gsym->is_undefined() && reloc_property->uses_symbol())
          else if (gsym->is_undefined() && reloc_property->uses_symbol())
            {
            {
              // This relocation uses the symbol value but the symbol is
              // This relocation uses the symbol value but the symbol is
              // undefined.  Exit early and have the caller reporting an
              // undefined.  Exit early and have the caller reporting an
              // error.
              // error.
              return true;
              return true;
            }
            }
          else
          else
            {
            {
              // Set thumb bit if symbol:
              // Set thumb bit if symbol:
              // -Has type STT_ARM_TFUNC or
              // -Has type STT_ARM_TFUNC or
              // -Has type STT_FUNC, is defined and with LSB in value set.
              // -Has type STT_FUNC, is defined and with LSB in value set.
              thumb_bit =
              thumb_bit =
                (((gsym->type() == elfcpp::STT_ARM_TFUNC)
                (((gsym->type() == elfcpp::STT_ARM_TFUNC)
                 || (gsym->type() == elfcpp::STT_FUNC
                 || (gsym->type() == elfcpp::STT_FUNC
                     && !gsym->is_undefined()
                     && !gsym->is_undefined()
                     && ((psymval->value(object, 0) & 1) != 0)))
                     && ((psymval->value(object, 0) & 1) != 0)))
                ? 1
                ? 1
                : 0);
                : 0);
            }
            }
        }
        }
      else
      else
        {
        {
          // This is a local symbol.  Determine if the final target is THUMB.
          // This is a local symbol.  Determine if the final target is THUMB.
          // We saved this information when all the local symbols were read.
          // We saved this information when all the local symbols were read.
          elfcpp::Elf_types<32>::Elf_WXword r_info = rel.get_r_info();
          elfcpp::Elf_types<32>::Elf_WXword r_info = rel.get_r_info();
          unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
          unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
          thumb_bit = object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
          thumb_bit = object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
        }
        }
    }
    }
  else
  else
    {
    {
      // This is a fake relocation synthesized for a stub.  It does not have
      // This is a fake relocation synthesized for a stub.  It does not have
      // a real symbol.  We just look at the LSB of the symbol value to
      // a real symbol.  We just look at the LSB of the symbol value to
      // determine if the target is THUMB or not.
      // determine if the target is THUMB or not.
      thumb_bit = ((psymval->value(object, 0) & 1) != 0);
      thumb_bit = ((psymval->value(object, 0) & 1) != 0);
    }
    }
 
 
  // Strip LSB if this points to a THUMB target.
  // Strip LSB if this points to a THUMB target.
  if (thumb_bit != 0
  if (thumb_bit != 0
      && reloc_property->uses_thumb_bit()
      && reloc_property->uses_thumb_bit()
      && ((psymval->value(object, 0) & 1) != 0))
      && ((psymval->value(object, 0) & 1) != 0))
    {
    {
      Arm_address stripped_value =
      Arm_address stripped_value =
        psymval->value(object, 0) & ~static_cast<Arm_address>(1);
        psymval->value(object, 0) & ~static_cast<Arm_address>(1);
      symval.set_output_value(stripped_value);
      symval.set_output_value(stripped_value);
      psymval = &symval;
      psymval = &symval;
    }
    }
 
 
  // To look up relocation stubs, we need to pass the symbol table index of
  // To look up relocation stubs, we need to pass the symbol table index of
  // a local symbol.
  // a local symbol.
  unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
  unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
 
 
  // Get the addressing origin of the output segment defining the
  // Get the addressing origin of the output segment defining the
  // symbol gsym if needed (AAELF 4.6.1.2 Relocation types).
  // symbol gsym if needed (AAELF 4.6.1.2 Relocation types).
  Arm_address sym_origin = 0;
  Arm_address sym_origin = 0;
  if (reloc_property->uses_symbol_base())
  if (reloc_property->uses_symbol_base())
    {
    {
      if (r_type == elfcpp::R_ARM_BASE_ABS && gsym == NULL)
      if (r_type == elfcpp::R_ARM_BASE_ABS && gsym == NULL)
        // R_ARM_BASE_ABS with the NULL symbol will give the
        // R_ARM_BASE_ABS with the NULL symbol will give the
        // absolute address of the GOT origin (GOT_ORG) (see ARM IHI
        // absolute address of the GOT origin (GOT_ORG) (see ARM IHI
        // 0044C (AAELF): 4.6.1.8 Proxy generating relocations).
        // 0044C (AAELF): 4.6.1.8 Proxy generating relocations).
        sym_origin = target->got_plt_section()->address();
        sym_origin = target->got_plt_section()->address();
      else if (gsym == NULL)
      else if (gsym == NULL)
        sym_origin = 0;
        sym_origin = 0;
      else if (gsym->source() == Symbol::IN_OUTPUT_SEGMENT)
      else if (gsym->source() == Symbol::IN_OUTPUT_SEGMENT)
        sym_origin = gsym->output_segment()->vaddr();
        sym_origin = gsym->output_segment()->vaddr();
      else if (gsym->source() == Symbol::IN_OUTPUT_DATA)
      else if (gsym->source() == Symbol::IN_OUTPUT_DATA)
        sym_origin = gsym->output_data()->address();
        sym_origin = gsym->output_data()->address();
 
 
      // TODO: Assumes the segment base to be zero for the global symbols
      // TODO: Assumes the segment base to be zero for the global symbols
      // till the proper support for the segment-base-relative addressing
      // till the proper support for the segment-base-relative addressing
      // will be implemented.  This is consistent with GNU ld.
      // will be implemented.  This is consistent with GNU ld.
    }
    }
 
 
  // For relative addressing relocation, find out the relative address base.
  // For relative addressing relocation, find out the relative address base.
  Arm_address relative_address_base = 0;
  Arm_address relative_address_base = 0;
  switch(reloc_property->relative_address_base())
  switch(reloc_property->relative_address_base())
    {
    {
    case Arm_reloc_property::RAB_NONE:
    case Arm_reloc_property::RAB_NONE:
    // Relocations with relative address bases RAB_TLS and RAB_tp are
    // Relocations with relative address bases RAB_TLS and RAB_tp are
    // handled by relocate_tls.  So we do not need to do anything here.
    // handled by relocate_tls.  So we do not need to do anything here.
    case Arm_reloc_property::RAB_TLS:
    case Arm_reloc_property::RAB_TLS:
    case Arm_reloc_property::RAB_tp:
    case Arm_reloc_property::RAB_tp:
      break;
      break;
    case Arm_reloc_property::RAB_B_S:
    case Arm_reloc_property::RAB_B_S:
      relative_address_base = sym_origin;
      relative_address_base = sym_origin;
      break;
      break;
    case Arm_reloc_property::RAB_GOT_ORG:
    case Arm_reloc_property::RAB_GOT_ORG:
      relative_address_base = target->got_plt_section()->address();
      relative_address_base = target->got_plt_section()->address();
      break;
      break;
    case Arm_reloc_property::RAB_P:
    case Arm_reloc_property::RAB_P:
      relative_address_base = address;
      relative_address_base = address;
      break;
      break;
    case Arm_reloc_property::RAB_Pa:
    case Arm_reloc_property::RAB_Pa:
      relative_address_base = address & 0xfffffffcU;
      relative_address_base = address & 0xfffffffcU;
      break;
      break;
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
 
 
  typename Arm_relocate_functions::Status reloc_status =
  typename Arm_relocate_functions::Status reloc_status =
        Arm_relocate_functions::STATUS_OKAY;
        Arm_relocate_functions::STATUS_OKAY;
  bool check_overflow = reloc_property->checks_overflow();
  bool check_overflow = reloc_property->checks_overflow();
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_NONE:
    case elfcpp::R_ARM_NONE:
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_ABS8:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::abs8(view, object, psymval);
        reloc_status = Arm_relocate_functions::abs8(view, object, psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_ABS12:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::abs12(view, object, psymval);
        reloc_status = Arm_relocate_functions::abs12(view, object, psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS16:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::abs16(view, object, psymval);
        reloc_status = Arm_relocate_functions::abs16(view, object, psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS32:
    case elfcpp::R_ARM_ABS32:
      if (should_apply_static_reloc(gsym, r_type, true, output_section))
      if (should_apply_static_reloc(gsym, r_type, true, output_section))
        reloc_status = Arm_relocate_functions::abs32(view, object, psymval,
        reloc_status = Arm_relocate_functions::abs32(view, object, psymval,
                                                     thumb_bit);
                                                     thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS32_NOI:
    case elfcpp::R_ARM_ABS32_NOI:
      if (should_apply_static_reloc(gsym, r_type, true, output_section))
      if (should_apply_static_reloc(gsym, r_type, true, output_section))
        // No thumb bit for this relocation: (S + A)
        // No thumb bit for this relocation: (S + A)
        reloc_status = Arm_relocate_functions::abs32(view, object, psymval,
        reloc_status = Arm_relocate_functions::abs32(view, object, psymval,
                                                     0);
                                                     0);
      break;
      break;
 
 
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVW_ABS_NC:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::movw(view, object, psymval,
        reloc_status = Arm_relocate_functions::movw(view, object, psymval,
                                                    0, thumb_bit,
                                                    0, thumb_bit,
                                                    check_overflow);
                                                    check_overflow);
      break;
      break;
 
 
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_MOVT_ABS:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::movt(view, object, psymval, 0);
        reloc_status = Arm_relocate_functions::movt(view, object, psymval, 0);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::thm_movw(view, object, psymval,
        reloc_status = Arm_relocate_functions::thm_movw(view, object, psymval,
                                                        0, thumb_bit, false);
                                                        0, thumb_bit, false);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVT_ABS:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::thm_movt(view, object,
        reloc_status = Arm_relocate_functions::thm_movt(view, object,
                                                        psymval, 0);
                                                        psymval, 0);
      break;
      break;
 
 
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::movw(view, object, psymval,
        Arm_relocate_functions::movw(view, object, psymval,
                                     relative_address_base, thumb_bit,
                                     relative_address_base, thumb_bit,
                                     check_overflow);
                                     check_overflow);
      break;
      break;
 
 
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVT_BREL:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::movt(view, object, psymval,
        Arm_relocate_functions::movt(view, object, psymval,
                                     relative_address_base);
                                     relative_address_base);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_movw(view, object, psymval,
        Arm_relocate_functions::thm_movw(view, object, psymval,
                                         relative_address_base,
                                         relative_address_base,
                                         thumb_bit, check_overflow);
                                         thumb_bit, check_overflow);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_movt(view, object, psymval,
        Arm_relocate_functions::thm_movt(view, object, psymval,
                                         relative_address_base);
                                         relative_address_base);
      break;
      break;
 
 
    case elfcpp::R_ARM_REL32:
    case elfcpp::R_ARM_REL32:
      reloc_status = Arm_relocate_functions::rel32(view, object, psymval,
      reloc_status = Arm_relocate_functions::rel32(view, object, psymval,
                                                   address, thumb_bit);
                                                   address, thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_THM_ABS5:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::thm_abs5(view, object, psymval);
        reloc_status = Arm_relocate_functions::thm_abs5(view, object, psymval);
      break;
      break;
 
 
    // Thumb long branches.
    // Thumb long branches.
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thumb_branch_common(
        Arm_relocate_functions::thumb_branch_common(
            r_type, relinfo, view, gsym, object, r_sym, psymval, address,
            r_type, relinfo, view, gsym, object, r_sym, psymval, address,
            thumb_bit, is_weakly_undefined_without_plt);
            thumb_bit, is_weakly_undefined_without_plt);
      break;
      break;
 
 
    case elfcpp::R_ARM_GOTOFF32:
    case elfcpp::R_ARM_GOTOFF32:
      {
      {
        Arm_address got_origin;
        Arm_address got_origin;
        got_origin = target->got_plt_section()->address();
        got_origin = target->got_plt_section()->address();
        reloc_status = Arm_relocate_functions::rel32(view, object, psymval,
        reloc_status = Arm_relocate_functions::rel32(view, object, psymval,
                                                     got_origin, thumb_bit);
                                                     got_origin, thumb_bit);
      }
      }
      break;
      break;
 
 
    case elfcpp::R_ARM_BASE_PREL:
    case elfcpp::R_ARM_BASE_PREL:
      gold_assert(gsym != NULL);
      gold_assert(gsym != NULL);
      reloc_status =
      reloc_status =
          Arm_relocate_functions::base_prel(view, sym_origin, address);
          Arm_relocate_functions::base_prel(view, sym_origin, address);
      break;
      break;
 
 
    case elfcpp::R_ARM_BASE_ABS:
    case elfcpp::R_ARM_BASE_ABS:
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
      if (should_apply_static_reloc(gsym, r_type, false, output_section))
        reloc_status = Arm_relocate_functions::base_abs(view, sym_origin);
        reloc_status = Arm_relocate_functions::base_abs(view, sym_origin);
      break;
      break;
 
 
    case elfcpp::R_ARM_GOT_BREL:
    case elfcpp::R_ARM_GOT_BREL:
      gold_assert(have_got_offset);
      gold_assert(have_got_offset);
      reloc_status = Arm_relocate_functions::got_brel(view, got_offset);
      reloc_status = Arm_relocate_functions::got_brel(view, got_offset);
      break;
      break;
 
 
    case elfcpp::R_ARM_GOT_PREL:
    case elfcpp::R_ARM_GOT_PREL:
      gold_assert(have_got_offset);
      gold_assert(have_got_offset);
      // Get the address origin for GOT PLT, which is allocated right
      // Get the address origin for GOT PLT, which is allocated right
      // after the GOT section, to calculate an absolute address of
      // after the GOT section, to calculate an absolute address of
      // the symbol GOT entry (got_origin + got_offset).
      // the symbol GOT entry (got_origin + got_offset).
      Arm_address got_origin;
      Arm_address got_origin;
      got_origin = target->got_plt_section()->address();
      got_origin = target->got_plt_section()->address();
      reloc_status = Arm_relocate_functions::got_prel(view,
      reloc_status = Arm_relocate_functions::got_prel(view,
                                                      got_origin + got_offset,
                                                      got_origin + got_offset,
                                                      address);
                                                      address);
      break;
      break;
 
 
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_XPC25:
    case elfcpp::R_ARM_XPC25:
      gold_assert(gsym == NULL
      gold_assert(gsym == NULL
                  || gsym->has_plt_offset()
                  || gsym->has_plt_offset()
                  || gsym->final_value_is_known()
                  || gsym->final_value_is_known()
                  || (gsym->is_defined()
                  || (gsym->is_defined()
                      && !gsym->is_from_dynobj()
                      && !gsym->is_from_dynobj()
                      && !gsym->is_preemptible()));
                      && !gsym->is_preemptible()));
      reloc_status =
      reloc_status =
        Arm_relocate_functions::arm_branch_common(
        Arm_relocate_functions::arm_branch_common(
            r_type, relinfo, view, gsym, object, r_sym, psymval, address,
            r_type, relinfo, view, gsym, object, r_sym, psymval, address,
            thumb_bit, is_weakly_undefined_without_plt);
            thumb_bit, is_weakly_undefined_without_plt);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_jump19(view, object, psymval, address,
        Arm_relocate_functions::thm_jump19(view, object, psymval, address,
                                           thumb_bit);
                                           thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP6:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_jump6(view, object, psymval, address);
        Arm_relocate_functions::thm_jump6(view, object, psymval, address);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP8:
    case elfcpp::R_ARM_THM_JUMP8:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_jump8(view, object, psymval, address);
        Arm_relocate_functions::thm_jump8(view, object, psymval, address);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP11:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_jump11(view, object, psymval, address);
        Arm_relocate_functions::thm_jump11(view, object, psymval, address);
      break;
      break;
 
 
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_PREL31:
      reloc_status = Arm_relocate_functions::prel31(view, object, psymval,
      reloc_status = Arm_relocate_functions::prel31(view, object, psymval,
                                                    address, thumb_bit);
                                                    address, thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_V4BX:
    case elfcpp::R_ARM_V4BX:
      if (target->fix_v4bx() > General_options::FIX_V4BX_NONE)
      if (target->fix_v4bx() > General_options::FIX_V4BX_NONE)
        {
        {
          const bool is_v4bx_interworking =
          const bool is_v4bx_interworking =
              (target->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING);
              (target->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING);
          reloc_status =
          reloc_status =
            Arm_relocate_functions::v4bx(relinfo, view, object, address,
            Arm_relocate_functions::v4bx(relinfo, view, object, address,
                                         is_v4bx_interworking);
                                         is_v4bx_interworking);
        }
        }
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_THM_PC8:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_pc8(view, object, psymval, address);
        Arm_relocate_functions::thm_pc8(view, object, psymval, address);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_THM_PC12:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_pc12(view, object, psymval, address);
        Arm_relocate_functions::thm_pc12(view, object, psymval, address);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::thm_alu11(view, object, psymval, address,
        Arm_relocate_functions::thm_alu11(view, object, psymval, address,
                                          thumb_bit);
                                          thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_ALU_SB_G2:
      reloc_status =
      reloc_status =
        Arm_relocate_functions::arm_grp_alu(view, object, psymval,
        Arm_relocate_functions::arm_grp_alu(view, object, psymval,
                                            reloc_property->group_index(),
                                            reloc_property->group_index(),
                                            relative_address_base,
                                            relative_address_base,
                                            thumb_bit, check_overflow);
                                            thumb_bit, check_overflow);
      break;
      break;
 
 
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G2:
      reloc_status =
      reloc_status =
          Arm_relocate_functions::arm_grp_ldr(view, object, psymval,
          Arm_relocate_functions::arm_grp_ldr(view, object, psymval,
                                              reloc_property->group_index(),
                                              reloc_property->group_index(),
                                              relative_address_base);
                                              relative_address_base);
      break;
      break;
 
 
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G2:
      reloc_status =
      reloc_status =
          Arm_relocate_functions::arm_grp_ldrs(view, object, psymval,
          Arm_relocate_functions::arm_grp_ldrs(view, object, psymval,
                                               reloc_property->group_index(),
                                               reloc_property->group_index(),
                                               relative_address_base);
                                               relative_address_base);
      break;
      break;
 
 
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G2:
      reloc_status =
      reloc_status =
          Arm_relocate_functions::arm_grp_ldc(view, object, psymval,
          Arm_relocate_functions::arm_grp_ldc(view, object, psymval,
                                              reloc_property->group_index(),
                                              reloc_property->group_index(),
                                              relative_address_base);
                                              relative_address_base);
      break;
      break;
 
 
      // These are initial tls relocs, which are expected when
      // These are initial tls relocs, which are expected when
      // linking.
      // linking.
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
      reloc_status =
      reloc_status =
        this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval,
        this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval,
                           view, address, view_size);
                           view, address, view_size);
      break;
      break;
 
 
    // The known and unknown unsupported and/or deprecated relocations.
    // The known and unknown unsupported and/or deprecated relocations.
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_PC24:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
    default:
    default:
      // Just silently leave the method. We should get an appropriate error
      // Just silently leave the method. We should get an appropriate error
      // message in the scan methods.
      // message in the scan methods.
      break;
      break;
    }
    }
 
 
  // Report any errors.
  // Report any errors.
  switch (reloc_status)
  switch (reloc_status)
    {
    {
    case Arm_relocate_functions::STATUS_OKAY:
    case Arm_relocate_functions::STATUS_OKAY:
      break;
      break;
    case Arm_relocate_functions::STATUS_OVERFLOW:
    case Arm_relocate_functions::STATUS_OVERFLOW:
      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("relocation overflow in %s"),
                             _("relocation overflow in %s"),
                             reloc_property->name().c_str());
                             reloc_property->name().c_str());
      break;
      break;
    case Arm_relocate_functions::STATUS_BAD_RELOC:
    case Arm_relocate_functions::STATUS_BAD_RELOC:
      gold_error_at_location(
      gold_error_at_location(
        relinfo,
        relinfo,
        relnum,
        relnum,
        rel.get_r_offset(),
        rel.get_r_offset(),
        _("unexpected opcode while processing relocation %s"),
        _("unexpected opcode while processing relocation %s"),
        reloc_property->name().c_str());
        reloc_property->name().c_str());
      break;
      break;
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
 
 
  return true;
  return true;
}
}
 
 
// Perform a TLS relocation.
// Perform a TLS relocation.
 
 
template<bool big_endian>
template<bool big_endian>
inline typename Arm_relocate_functions<big_endian>::Status
inline typename Arm_relocate_functions<big_endian>::Status
Target_arm<big_endian>::Relocate::relocate_tls(
Target_arm<big_endian>::Relocate::relocate_tls(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    Target_arm<big_endian>* target,
    Target_arm<big_endian>* target,
    size_t relnum,
    size_t relnum,
    const elfcpp::Rel<32, big_endian>& rel,
    const elfcpp::Rel<32, big_endian>& rel,
    unsigned int r_type,
    unsigned int r_type,
    const Sized_symbol<32>* gsym,
    const Sized_symbol<32>* gsym,
    const Symbol_value<32>* psymval,
    const Symbol_value<32>* psymval,
    unsigned char* view,
    unsigned char* view,
    elfcpp::Elf_types<32>::Elf_Addr address,
    elfcpp::Elf_types<32>::Elf_Addr address,
    section_size_type /*view_size*/ )
    section_size_type /*view_size*/ )
{
{
  typedef Arm_relocate_functions<big_endian> ArmRelocFuncs;
  typedef Arm_relocate_functions<big_endian> ArmRelocFuncs;
  typedef Relocate_functions<32, big_endian> RelocFuncs;
  typedef Relocate_functions<32, big_endian> RelocFuncs;
  Output_segment* tls_segment = relinfo->layout->tls_segment();
  Output_segment* tls_segment = relinfo->layout->tls_segment();
 
 
  const Sized_relobj_file<32, big_endian>* object = relinfo->object;
  const Sized_relobj_file<32, big_endian>* object = relinfo->object;
 
 
  elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(object, 0);
  elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(object, 0);
 
 
  const bool is_final = (gsym == NULL
  const bool is_final = (gsym == NULL
                         ? !parameters->options().shared()
                         ? !parameters->options().shared()
                         : gsym->final_value_is_known());
                         : gsym->final_value_is_known());
  const tls::Tls_optimization optimized_type
  const tls::Tls_optimization optimized_type
      = Target_arm<big_endian>::optimize_tls_reloc(is_final, r_type);
      = Target_arm<big_endian>::optimize_tls_reloc(is_final, r_type);
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
    case elfcpp::R_ARM_TLS_GD32:        // Global-dynamic
        {
        {
          unsigned int got_type = GOT_TYPE_TLS_PAIR;
          unsigned int got_type = GOT_TYPE_TLS_PAIR;
          unsigned int got_offset;
          unsigned int got_offset;
          if (gsym != NULL)
          if (gsym != NULL)
            {
            {
              gold_assert(gsym->has_got_offset(got_type));
              gold_assert(gsym->has_got_offset(got_type));
              got_offset = gsym->got_offset(got_type) - target->got_size();
              got_offset = gsym->got_offset(got_type) - target->got_size();
            }
            }
          else
          else
            {
            {
              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
              gold_assert(object->local_has_got_offset(r_sym, got_type));
              gold_assert(object->local_has_got_offset(r_sym, got_type));
              got_offset = (object->local_got_offset(r_sym, got_type)
              got_offset = (object->local_got_offset(r_sym, got_type)
                            - target->got_size());
                            - target->got_size());
            }
            }
          if (optimized_type == tls::TLSOPT_NONE)
          if (optimized_type == tls::TLSOPT_NONE)
            {
            {
              Arm_address got_entry =
              Arm_address got_entry =
                target->got_plt_section()->address() + got_offset;
                target->got_plt_section()->address() + got_offset;
 
 
              // Relocate the field with the PC relative offset of the pair of
              // Relocate the field with the PC relative offset of the pair of
              // GOT entries.
              // GOT entries.
              RelocFuncs::pcrel32_unaligned(view, got_entry, address);
              RelocFuncs::pcrel32_unaligned(view, got_entry, address);
              return ArmRelocFuncs::STATUS_OKAY;
              return ArmRelocFuncs::STATUS_OKAY;
            }
            }
        }
        }
      break;
      break;
 
 
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
    case elfcpp::R_ARM_TLS_LDM32:       // Local-dynamic
      if (optimized_type == tls::TLSOPT_NONE)
      if (optimized_type == tls::TLSOPT_NONE)
        {
        {
          // Relocate the field with the offset of the GOT entry for
          // Relocate the field with the offset of the GOT entry for
          // the module index.
          // the module index.
          unsigned int got_offset;
          unsigned int got_offset;
          got_offset = (target->got_mod_index_entry(NULL, NULL, NULL)
          got_offset = (target->got_mod_index_entry(NULL, NULL, NULL)
                        - target->got_size());
                        - target->got_size());
          Arm_address got_entry =
          Arm_address got_entry =
            target->got_plt_section()->address() + got_offset;
            target->got_plt_section()->address() + got_offset;
 
 
          // Relocate the field with the PC relative offset of the pair of
          // Relocate the field with the PC relative offset of the pair of
          // GOT entries.
          // GOT entries.
          RelocFuncs::pcrel32_unaligned(view, got_entry, address);
          RelocFuncs::pcrel32_unaligned(view, got_entry, address);
          return ArmRelocFuncs::STATUS_OKAY;
          return ArmRelocFuncs::STATUS_OKAY;
        }
        }
      break;
      break;
 
 
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
    case elfcpp::R_ARM_TLS_LDO32:       // Alternate local-dynamic
      RelocFuncs::rel32_unaligned(view, value);
      RelocFuncs::rel32_unaligned(view, value);
      return ArmRelocFuncs::STATUS_OKAY;
      return ArmRelocFuncs::STATUS_OKAY;
 
 
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
    case elfcpp::R_ARM_TLS_IE32:        // Initial-exec
      if (optimized_type == tls::TLSOPT_NONE)
      if (optimized_type == tls::TLSOPT_NONE)
        {
        {
          // Relocate the field with the offset of the GOT entry for
          // Relocate the field with the offset of the GOT entry for
          // the tp-relative offset of the symbol.
          // the tp-relative offset of the symbol.
          unsigned int got_type = GOT_TYPE_TLS_OFFSET;
          unsigned int got_type = GOT_TYPE_TLS_OFFSET;
          unsigned int got_offset;
          unsigned int got_offset;
          if (gsym != NULL)
          if (gsym != NULL)
            {
            {
              gold_assert(gsym->has_got_offset(got_type));
              gold_assert(gsym->has_got_offset(got_type));
              got_offset = gsym->got_offset(got_type);
              got_offset = gsym->got_offset(got_type);
            }
            }
          else
          else
            {
            {
              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
              gold_assert(object->local_has_got_offset(r_sym, got_type));
              gold_assert(object->local_has_got_offset(r_sym, got_type));
              got_offset = object->local_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.
          // All GOT offsets are relative to the end of the GOT.
          got_offset -= target->got_size();
          got_offset -= target->got_size();
 
 
          Arm_address got_entry =
          Arm_address got_entry =
            target->got_plt_section()->address() + got_offset;
            target->got_plt_section()->address() + got_offset;
 
 
          // Relocate the field with the PC relative offset of the GOT entry.
          // Relocate the field with the PC relative offset of the GOT entry.
          RelocFuncs::pcrel32_unaligned(view, got_entry, address);
          RelocFuncs::pcrel32_unaligned(view, got_entry, address);
          return ArmRelocFuncs::STATUS_OKAY;
          return ArmRelocFuncs::STATUS_OKAY;
        }
        }
      break;
      break;
 
 
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
    case elfcpp::R_ARM_TLS_LE32:        // Local-exec
      // If we're creating a shared library, a dynamic relocation will
      // If we're creating a shared library, a dynamic relocation will
      // have been created for this location, so do not apply it now.
      // have been created for this location, so do not apply it now.
      if (!parameters->options().shared())
      if (!parameters->options().shared())
        {
        {
          gold_assert(tls_segment != NULL);
          gold_assert(tls_segment != NULL);
 
 
          // $tp points to the TCB, which is followed by the TLS, so we
          // $tp points to the TCB, which is followed by the TLS, so we
          // need to add TCB size to the offset.
          // need to add TCB size to the offset.
          Arm_address aligned_tcb_size =
          Arm_address aligned_tcb_size =
            align_address(ARM_TCB_SIZE, tls_segment->maximum_alignment());
            align_address(ARM_TCB_SIZE, tls_segment->maximum_alignment());
          RelocFuncs::rel32_unaligned(view, value + aligned_tcb_size);
          RelocFuncs::rel32_unaligned(view, value + aligned_tcb_size);
 
 
        }
        }
      return ArmRelocFuncs::STATUS_OKAY;
      return ArmRelocFuncs::STATUS_OKAY;
 
 
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
 
 
  gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
  gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                         _("unsupported reloc %u"),
                         _("unsupported reloc %u"),
                         r_type);
                         r_type);
  return ArmRelocFuncs::STATUS_BAD_RELOC;
  return ArmRelocFuncs::STATUS_BAD_RELOC;
}
}
 
 
// Relocate section data.
// Relocate section data.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::relocate_section(
Target_arm<big_endian>::relocate_section(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned int sh_type,
    unsigned int sh_type,
    const unsigned char* prelocs,
    const unsigned char* prelocs,
    size_t reloc_count,
    size_t reloc_count,
    Output_section* output_section,
    Output_section* output_section,
    bool needs_special_offset_handling,
    bool needs_special_offset_handling,
    unsigned char* view,
    unsigned char* view,
    Arm_address address,
    Arm_address address,
    section_size_type view_size,
    section_size_type view_size,
    const Reloc_symbol_changes* reloc_symbol_changes)
    const Reloc_symbol_changes* reloc_symbol_changes)
{
{
  typedef typename Target_arm<big_endian>::Relocate Arm_relocate;
  typedef typename Target_arm<big_endian>::Relocate Arm_relocate;
  gold_assert(sh_type == elfcpp::SHT_REL);
  gold_assert(sh_type == elfcpp::SHT_REL);
 
 
  // See if we are relocating a relaxed input section.  If so, the view
  // See if we are relocating a relaxed input section.  If so, the view
  // covers the whole output section and we need to adjust accordingly.
  // covers the whole output section and we need to adjust accordingly.
  if (needs_special_offset_handling)
  if (needs_special_offset_handling)
    {
    {
      const Output_relaxed_input_section* poris =
      const Output_relaxed_input_section* poris =
        output_section->find_relaxed_input_section(relinfo->object,
        output_section->find_relaxed_input_section(relinfo->object,
                                                   relinfo->data_shndx);
                                                   relinfo->data_shndx);
      if (poris != NULL)
      if (poris != NULL)
        {
        {
          Arm_address section_address = poris->address();
          Arm_address section_address = poris->address();
          section_size_type section_size = poris->data_size();
          section_size_type section_size = poris->data_size();
 
 
          gold_assert((section_address >= address)
          gold_assert((section_address >= address)
                      && ((section_address + section_size)
                      && ((section_address + section_size)
                          <= (address + view_size)));
                          <= (address + view_size)));
 
 
          off_t offset = section_address - address;
          off_t offset = section_address - address;
          view += offset;
          view += offset;
          address += offset;
          address += offset;
          view_size = section_size;
          view_size = section_size;
        }
        }
    }
    }
 
 
  gold::relocate_section<32, big_endian, Target_arm, elfcpp::SHT_REL,
  gold::relocate_section<32, big_endian, Target_arm, elfcpp::SHT_REL,
                         Arm_relocate>(
                         Arm_relocate>(
    relinfo,
    relinfo,
    this,
    this,
    prelocs,
    prelocs,
    reloc_count,
    reloc_count,
    output_section,
    output_section,
    needs_special_offset_handling,
    needs_special_offset_handling,
    view,
    view,
    address,
    address,
    view_size,
    view_size,
    reloc_symbol_changes);
    reloc_symbol_changes);
}
}
 
 
// Return the size of a relocation while scanning during a relocatable
// Return the size of a relocation while scanning during a relocatable
// link.
// link.
 
 
template<bool big_endian>
template<bool big_endian>
unsigned int
unsigned int
Target_arm<big_endian>::Relocatable_size_for_reloc::get_size_for_reloc(
Target_arm<big_endian>::Relocatable_size_for_reloc::get_size_for_reloc(
    unsigned int r_type,
    unsigned int r_type,
    Relobj* object)
    Relobj* object)
{
{
  r_type = get_real_reloc_type(r_type);
  r_type = get_real_reloc_type(r_type);
  const Arm_reloc_property* arp =
  const Arm_reloc_property* arp =
      arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
      arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
  if (arp != NULL)
  if (arp != NULL)
    return arp->size();
    return arp->size();
  else
  else
    {
    {
      std::string reloc_name =
      std::string reloc_name =
        arm_reloc_property_table->reloc_name_in_error_message(r_type);
        arm_reloc_property_table->reloc_name_in_error_message(r_type);
      gold_error(_("%s: unexpected %s in object file"),
      gold_error(_("%s: unexpected %s in object file"),
                 object->name().c_str(), reloc_name.c_str());
                 object->name().c_str(), reloc_name.c_str());
      return 0;
      return 0;
    }
    }
}
}
 
 
// Scan the relocs during a relocatable link.
// Scan the relocs during a relocatable link.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::scan_relocatable_relocs(
Target_arm<big_endian>::scan_relocatable_relocs(
    Symbol_table* symtab,
    Symbol_table* symtab,
    Layout* layout,
    Layout* layout,
    Sized_relobj_file<32, big_endian>* object,
    Sized_relobj_file<32, big_endian>* object,
    unsigned int data_shndx,
    unsigned int data_shndx,
    unsigned int sh_type,
    unsigned int sh_type,
    const unsigned char* prelocs,
    const unsigned char* prelocs,
    size_t reloc_count,
    size_t reloc_count,
    Output_section* output_section,
    Output_section* output_section,
    bool needs_special_offset_handling,
    bool needs_special_offset_handling,
    size_t local_symbol_count,
    size_t local_symbol_count,
    const unsigned char* plocal_symbols,
    const unsigned char* plocal_symbols,
    Relocatable_relocs* rr)
    Relocatable_relocs* rr)
{
{
  gold_assert(sh_type == elfcpp::SHT_REL);
  gold_assert(sh_type == elfcpp::SHT_REL);
 
 
  typedef Arm_scan_relocatable_relocs<big_endian, elfcpp::SHT_REL,
  typedef Arm_scan_relocatable_relocs<big_endian, elfcpp::SHT_REL,
    Relocatable_size_for_reloc> Scan_relocatable_relocs;
    Relocatable_size_for_reloc> Scan_relocatable_relocs;
 
 
  gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL,
  gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL,
      Scan_relocatable_relocs>(
      Scan_relocatable_relocs>(
    symtab,
    symtab,
    layout,
    layout,
    object,
    object,
    data_shndx,
    data_shndx,
    prelocs,
    prelocs,
    reloc_count,
    reloc_count,
    output_section,
    output_section,
    needs_special_offset_handling,
    needs_special_offset_handling,
    local_symbol_count,
    local_symbol_count,
    plocal_symbols,
    plocal_symbols,
    rr);
    rr);
}
}
 
 
// Relocate a section during a relocatable link.
// Relocate a section during a relocatable link.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::relocate_for_relocatable(
Target_arm<big_endian>::relocate_for_relocatable(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned int sh_type,
    unsigned int sh_type,
    const unsigned char* prelocs,
    const unsigned char* prelocs,
    size_t reloc_count,
    size_t reloc_count,
    Output_section* output_section,
    Output_section* output_section,
    off_t offset_in_output_section,
    off_t offset_in_output_section,
    const Relocatable_relocs* rr,
    const Relocatable_relocs* rr,
    unsigned char* view,
    unsigned char* view,
    Arm_address view_address,
    Arm_address view_address,
    section_size_type view_size,
    section_size_type view_size,
    unsigned char* reloc_view,
    unsigned char* reloc_view,
    section_size_type reloc_view_size)
    section_size_type reloc_view_size)
{
{
  gold_assert(sh_type == elfcpp::SHT_REL);
  gold_assert(sh_type == elfcpp::SHT_REL);
 
 
  gold::relocate_for_relocatable<32, big_endian, elfcpp::SHT_REL>(
  gold::relocate_for_relocatable<32, big_endian, elfcpp::SHT_REL>(
    relinfo,
    relinfo,
    prelocs,
    prelocs,
    reloc_count,
    reloc_count,
    output_section,
    output_section,
    offset_in_output_section,
    offset_in_output_section,
    rr,
    rr,
    view,
    view,
    view_address,
    view_address,
    view_size,
    view_size,
    reloc_view,
    reloc_view,
    reloc_view_size);
    reloc_view_size);
}
}
 
 
// Perform target-specific processing in a relocatable link.  This is
// Perform target-specific processing in a relocatable link.  This is
// only used if we use the relocation strategy RELOC_SPECIAL.
// only used if we use the relocation strategy RELOC_SPECIAL.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::relocate_special_relocatable(
Target_arm<big_endian>::relocate_special_relocatable(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned int sh_type,
    unsigned int sh_type,
    const unsigned char* preloc_in,
    const unsigned char* preloc_in,
    size_t relnum,
    size_t relnum,
    Output_section* output_section,
    Output_section* output_section,
    off_t offset_in_output_section,
    off_t offset_in_output_section,
    unsigned char* view,
    unsigned char* view,
    elfcpp::Elf_types<32>::Elf_Addr view_address,
    elfcpp::Elf_types<32>::Elf_Addr view_address,
    section_size_type,
    section_size_type,
    unsigned char* preloc_out)
    unsigned char* preloc_out)
{
{
  // We can only handle REL type relocation sections.
  // We can only handle REL type relocation sections.
  gold_assert(sh_type == elfcpp::SHT_REL);
  gold_assert(sh_type == elfcpp::SHT_REL);
 
 
  typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc Reltype;
  typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc Reltype;
  typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc_write
  typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc_write
    Reltype_write;
    Reltype_write;
  const Arm_address invalid_address = static_cast<Arm_address>(0) - 1;
  const Arm_address invalid_address = static_cast<Arm_address>(0) - 1;
 
 
  const Arm_relobj<big_endian>* object =
  const Arm_relobj<big_endian>* object =
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
  const unsigned int local_count = object->local_symbol_count();
  const unsigned int local_count = object->local_symbol_count();
 
 
  Reltype reloc(preloc_in);
  Reltype reloc(preloc_in);
  Reltype_write reloc_write(preloc_out);
  Reltype_write reloc_write(preloc_out);
 
 
  elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info();
  elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info();
  const unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
  const unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
  const unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
  const unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
 
 
  const Arm_reloc_property* arp =
  const Arm_reloc_property* arp =
    arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
    arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
  gold_assert(arp != NULL);
  gold_assert(arp != NULL);
 
 
  // Get the new symbol index.
  // Get the new symbol index.
  // We only use RELOC_SPECIAL strategy in local relocations.
  // We only use RELOC_SPECIAL strategy in local relocations.
  gold_assert(r_sym < local_count);
  gold_assert(r_sym < local_count);
 
 
  // We are adjusting a section symbol.  We need to find
  // We are adjusting a section symbol.  We need to find
  // the symbol table index of the section symbol for
  // the symbol table index of the section symbol for
  // the output section corresponding to input section
  // the output section corresponding to input section
  // in which this symbol is defined.
  // in which this symbol is defined.
  bool is_ordinary;
  bool is_ordinary;
  unsigned int shndx = object->local_symbol_input_shndx(r_sym, &is_ordinary);
  unsigned int shndx = object->local_symbol_input_shndx(r_sym, &is_ordinary);
  gold_assert(is_ordinary);
  gold_assert(is_ordinary);
  Output_section* os = object->output_section(shndx);
  Output_section* os = object->output_section(shndx);
  gold_assert(os != NULL);
  gold_assert(os != NULL);
  gold_assert(os->needs_symtab_index());
  gold_assert(os->needs_symtab_index());
  unsigned int new_symndx = os->symtab_index();
  unsigned int new_symndx = os->symtab_index();
 
 
  // Get the new offset--the location in the output section where
  // Get the new offset--the location in the output section where
  // this relocation should be applied.
  // this relocation should be applied.
 
 
  Arm_address offset = reloc.get_r_offset();
  Arm_address offset = reloc.get_r_offset();
  Arm_address new_offset;
  Arm_address new_offset;
  if (offset_in_output_section != invalid_address)
  if (offset_in_output_section != invalid_address)
    new_offset = offset + offset_in_output_section;
    new_offset = offset + offset_in_output_section;
  else
  else
    {
    {
      section_offset_type sot_offset =
      section_offset_type sot_offset =
          convert_types<section_offset_type, Arm_address>(offset);
          convert_types<section_offset_type, Arm_address>(offset);
      section_offset_type new_sot_offset =
      section_offset_type new_sot_offset =
          output_section->output_offset(object, relinfo->data_shndx,
          output_section->output_offset(object, relinfo->data_shndx,
                                        sot_offset);
                                        sot_offset);
      gold_assert(new_sot_offset != -1);
      gold_assert(new_sot_offset != -1);
      new_offset = new_sot_offset;
      new_offset = new_sot_offset;
    }
    }
 
 
  // In an object file, r_offset is an offset within the section.
  // In an object file, r_offset is an offset within the section.
  // In an executable or dynamic object, generated by
  // In an executable or dynamic object, generated by
  // --emit-relocs, r_offset is an absolute address.
  // --emit-relocs, r_offset is an absolute address.
  if (!parameters->options().relocatable())
  if (!parameters->options().relocatable())
    {
    {
      new_offset += view_address;
      new_offset += view_address;
      if (offset_in_output_section != invalid_address)
      if (offset_in_output_section != invalid_address)
        new_offset -= offset_in_output_section;
        new_offset -= offset_in_output_section;
    }
    }
 
 
  reloc_write.put_r_offset(new_offset);
  reloc_write.put_r_offset(new_offset);
  reloc_write.put_r_info(elfcpp::elf_r_info<32>(new_symndx, r_type));
  reloc_write.put_r_info(elfcpp::elf_r_info<32>(new_symndx, r_type));
 
 
  // Handle the reloc addend.
  // Handle the reloc addend.
  // The relocation uses a section symbol in the input file.
  // The relocation uses a section symbol in the input file.
  // We are adjusting it to use a section symbol in the output
  // We are adjusting it to use a section symbol in the output
  // file.  The input section symbol refers to some address in
  // file.  The input section symbol refers to some address in
  // the input section.  We need the relocation in the output
  // the input section.  We need the relocation in the output
  // file to refer to that same address.  This adjustment to
  // file to refer to that same address.  This adjustment to
  // the addend is the same calculation we use for a simple
  // the addend is the same calculation we use for a simple
  // absolute relocation for the input section symbol.
  // absolute relocation for the input section symbol.
 
 
  const Symbol_value<32>* psymval = object->local_symbol(r_sym);
  const Symbol_value<32>* psymval = object->local_symbol(r_sym);
 
 
  // Handle THUMB bit.
  // Handle THUMB bit.
  Symbol_value<32> symval;
  Symbol_value<32> symval;
  Arm_address thumb_bit =
  Arm_address thumb_bit =
     object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
     object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
  if (thumb_bit != 0
  if (thumb_bit != 0
      && arp->uses_thumb_bit()
      && arp->uses_thumb_bit()
      && ((psymval->value(object, 0) & 1) != 0))
      && ((psymval->value(object, 0) & 1) != 0))
    {
    {
      Arm_address stripped_value =
      Arm_address stripped_value =
        psymval->value(object, 0) & ~static_cast<Arm_address>(1);
        psymval->value(object, 0) & ~static_cast<Arm_address>(1);
      symval.set_output_value(stripped_value);
      symval.set_output_value(stripped_value);
      psymval = &symval;
      psymval = &symval;
    }
    }
 
 
  unsigned char* paddend = view + offset;
  unsigned char* paddend = view + offset;
  typename Arm_relocate_functions<big_endian>::Status reloc_status =
  typename Arm_relocate_functions<big_endian>::Status reloc_status =
        Arm_relocate_functions<big_endian>::STATUS_OKAY;
        Arm_relocate_functions<big_endian>::STATUS_OKAY;
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_ABS8:
    case elfcpp::R_ARM_ABS8:
      reloc_status = Arm_relocate_functions<big_endian>::abs8(paddend, object,
      reloc_status = Arm_relocate_functions<big_endian>::abs8(paddend, object,
                                                              psymval);
                                                              psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS12:
    case elfcpp::R_ARM_ABS12:
      reloc_status = Arm_relocate_functions<big_endian>::abs12(paddend, object,
      reloc_status = Arm_relocate_functions<big_endian>::abs12(paddend, object,
                                                               psymval);
                                                               psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_ABS16:
    case elfcpp::R_ARM_ABS16:
      reloc_status = Arm_relocate_functions<big_endian>::abs16(paddend, object,
      reloc_status = Arm_relocate_functions<big_endian>::abs16(paddend, object,
                                                               psymval);
                                                               psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_ABS5:
    case elfcpp::R_ARM_THM_ABS5:
      reloc_status = Arm_relocate_functions<big_endian>::thm_abs5(paddend,
      reloc_status = Arm_relocate_functions<big_endian>::thm_abs5(paddend,
                                                                  object,
                                                                  object,
                                                                  psymval);
                                                                  psymval);
      break;
      break;
 
 
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVW_ABS_NC:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_PREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL_NC:
    case elfcpp::R_ARM_MOVW_BREL:
    case elfcpp::R_ARM_MOVW_BREL:
      reloc_status = Arm_relocate_functions<big_endian>::movw(
      reloc_status = Arm_relocate_functions<big_endian>::movw(
          paddend, object, psymval, 0, thumb_bit, arp->checks_overflow());
          paddend, object, psymval, 0, thumb_bit, arp->checks_overflow());
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVW_ABS_NC:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_PREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL_NC:
    case elfcpp::R_ARM_THM_MOVW_BREL:
    case elfcpp::R_ARM_THM_MOVW_BREL:
      reloc_status = Arm_relocate_functions<big_endian>::thm_movw(
      reloc_status = Arm_relocate_functions<big_endian>::thm_movw(
          paddend, object, psymval, 0, thumb_bit, arp->checks_overflow());
          paddend, object, psymval, 0, thumb_bit, arp->checks_overflow());
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thumb_branch_common(
        Arm_relocate_functions<big_endian>::thumb_branch_common(
            r_type, relinfo, paddend, NULL, object, 0, psymval, 0, thumb_bit,
            r_type, relinfo, paddend, NULL, object, 0, psymval, 0, thumb_bit,
            false);
            false);
      break;
      break;
 
 
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_XPC25:
    case elfcpp::R_ARM_XPC25:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::arm_branch_common(
        Arm_relocate_functions<big_endian>::arm_branch_common(
            r_type, relinfo, paddend, NULL, object, 0, psymval, 0, thumb_bit,
            r_type, relinfo, paddend, NULL, object, 0, psymval, 0, thumb_bit,
            false);
            false);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_jump19(paddend, object,
        Arm_relocate_functions<big_endian>::thm_jump19(paddend, object,
                                                       psymval, 0, thumb_bit);
                                                       psymval, 0, thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP6:
    case elfcpp::R_ARM_THM_JUMP6:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_jump6(paddend, object, psymval,
        Arm_relocate_functions<big_endian>::thm_jump6(paddend, object, psymval,
                                                      0);
                                                      0);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP8:
    case elfcpp::R_ARM_THM_JUMP8:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_jump8(paddend, object, psymval,
        Arm_relocate_functions<big_endian>::thm_jump8(paddend, object, psymval,
                                                      0);
                                                      0);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_JUMP11:
    case elfcpp::R_ARM_THM_JUMP11:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_jump11(paddend, object, psymval,
        Arm_relocate_functions<big_endian>::thm_jump11(paddend, object, psymval,
                                                       0);
                                                       0);
      break;
      break;
 
 
    case elfcpp::R_ARM_PREL31:
    case elfcpp::R_ARM_PREL31:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::prel31(paddend, object, psymval, 0,
        Arm_relocate_functions<big_endian>::prel31(paddend, object, psymval, 0,
                                                   thumb_bit);
                                                   thumb_bit);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_PC8:
    case elfcpp::R_ARM_THM_PC8:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_pc8(paddend, object, psymval,
        Arm_relocate_functions<big_endian>::thm_pc8(paddend, object, psymval,
                                                    0);
                                                    0);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_PC12:
    case elfcpp::R_ARM_THM_PC12:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_pc12(paddend, object, psymval,
        Arm_relocate_functions<big_endian>::thm_pc12(paddend, object, psymval,
                                                     0);
                                                     0);
      break;
      break;
 
 
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
    case elfcpp::R_ARM_THM_ALU_PREL_11_0:
      reloc_status =
      reloc_status =
        Arm_relocate_functions<big_endian>::thm_alu11(paddend, object, psymval,
        Arm_relocate_functions<big_endian>::thm_alu11(paddend, object, psymval,
                                                      0, thumb_bit);
                                                      0, thumb_bit);
      break;
      break;
 
 
    // These relocation truncate relocation results so we cannot handle them
    // These relocation truncate relocation results so we cannot handle them
    // in a relocatable link.
    // in a relocatable link.
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_THM_MOVT_ABS:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_PREL:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_PREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_THM_MOVT_BREL:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0_NC:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G0:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1_NC:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G1:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_PC_G2:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0_NC:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G0:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1_NC:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G1:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_ALU_SB_G2:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G0:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G1:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_PC_G2:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G0:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G1:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDR_SB_G2:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G0:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G1:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_PC_G2:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G0:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G1:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDRS_SB_G2:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G0:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G1:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_PC_G2:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G0:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G1:
    case elfcpp::R_ARM_LDC_SB_G2:
    case elfcpp::R_ARM_LDC_SB_G2:
      gold_error(_("cannot handle %s in a relocatable link"),
      gold_error(_("cannot handle %s in a relocatable link"),
                 arp->name().c_str());
                 arp->name().c_str());
      break;
      break;
 
 
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
 
 
  // Report any errors.
  // Report any errors.
  switch (reloc_status)
  switch (reloc_status)
    {
    {
    case Arm_relocate_functions<big_endian>::STATUS_OKAY:
    case Arm_relocate_functions<big_endian>::STATUS_OKAY:
      break;
      break;
    case Arm_relocate_functions<big_endian>::STATUS_OVERFLOW:
    case Arm_relocate_functions<big_endian>::STATUS_OVERFLOW:
      gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
      gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
                             _("relocation overflow in %s"),
                             _("relocation overflow in %s"),
                             arp->name().c_str());
                             arp->name().c_str());
      break;
      break;
    case Arm_relocate_functions<big_endian>::STATUS_BAD_RELOC:
    case Arm_relocate_functions<big_endian>::STATUS_BAD_RELOC:
      gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
      gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
        _("unexpected opcode while processing relocation %s"),
        _("unexpected opcode while processing relocation %s"),
        arp->name().c_str());
        arp->name().c_str());
      break;
      break;
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
}
}
 
 
// Return the value to use for a dynamic symbol which requires special
// Return the value to use for a dynamic symbol which requires special
// treatment.  This is how we support equality comparisons of function
// treatment.  This is how we support equality comparisons of function
// pointers across shared library boundaries, as described in the
// pointers across shared library boundaries, as described in the
// processor specific ABI supplement.
// processor specific ABI supplement.
 
 
template<bool big_endian>
template<bool big_endian>
uint64_t
uint64_t
Target_arm<big_endian>::do_dynsym_value(const Symbol* gsym) const
Target_arm<big_endian>::do_dynsym_value(const Symbol* gsym) const
{
{
  gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
  gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
  return this->plt_section()->address() + gsym->plt_offset();
  return this->plt_section()->address() + gsym->plt_offset();
}
}
 
 
// Map platform-specific relocs to real relocs
// Map platform-specific relocs to real relocs
//
//
template<bool big_endian>
template<bool big_endian>
unsigned int
unsigned int
Target_arm<big_endian>::get_real_reloc_type(unsigned int r_type)
Target_arm<big_endian>::get_real_reloc_type(unsigned int r_type)
{
{
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_TARGET1:
    case elfcpp::R_ARM_TARGET1:
      // This is either R_ARM_ABS32 or R_ARM_REL32;
      // This is either R_ARM_ABS32 or R_ARM_REL32;
      return elfcpp::R_ARM_ABS32;
      return elfcpp::R_ARM_ABS32;
 
 
    case elfcpp::R_ARM_TARGET2:
    case elfcpp::R_ARM_TARGET2:
      // This can be any reloc type but usually is R_ARM_GOT_PREL
      // This can be any reloc type but usually is R_ARM_GOT_PREL
      return elfcpp::R_ARM_GOT_PREL;
      return elfcpp::R_ARM_GOT_PREL;
 
 
    default:
    default:
      return r_type;
      return r_type;
    }
    }
}
}
 
 
// Whether if two EABI versions V1 and V2 are compatible.
// Whether if two EABI versions V1 and V2 are compatible.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Target_arm<big_endian>::are_eabi_versions_compatible(
Target_arm<big_endian>::are_eabi_versions_compatible(
    elfcpp::Elf_Word v1,
    elfcpp::Elf_Word v1,
    elfcpp::Elf_Word v2)
    elfcpp::Elf_Word v2)
{
{
  // v4 and v5 are the same spec before and after it was released,
  // v4 and v5 are the same spec before and after it was released,
  // so allow mixing them.
  // so allow mixing them.
  if ((v1 == elfcpp::EF_ARM_EABI_UNKNOWN || v2 == elfcpp::EF_ARM_EABI_UNKNOWN)
  if ((v1 == elfcpp::EF_ARM_EABI_UNKNOWN || v2 == elfcpp::EF_ARM_EABI_UNKNOWN)
      || (v1 == elfcpp::EF_ARM_EABI_VER4 && v2 == elfcpp::EF_ARM_EABI_VER5)
      || (v1 == elfcpp::EF_ARM_EABI_VER4 && v2 == elfcpp::EF_ARM_EABI_VER5)
      || (v1 == elfcpp::EF_ARM_EABI_VER5 && v2 == elfcpp::EF_ARM_EABI_VER4))
      || (v1 == elfcpp::EF_ARM_EABI_VER5 && v2 == elfcpp::EF_ARM_EABI_VER4))
    return true;
    return true;
 
 
  return v1 == v2;
  return v1 == v2;
}
}
 
 
// Combine FLAGS from an input object called NAME and the processor-specific
// Combine FLAGS from an input object called NAME and the processor-specific
// flags in the ELF header of the output.  Much of this is adapted from the
// flags in the ELF header of the output.  Much of this is adapted from the
// processor-specific flags merging code in elf32_arm_merge_private_bfd_data
// processor-specific flags merging code in elf32_arm_merge_private_bfd_data
// in bfd/elf32-arm.c.
// in bfd/elf32-arm.c.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::merge_processor_specific_flags(
Target_arm<big_endian>::merge_processor_specific_flags(
    const std::string& name,
    const std::string& name,
    elfcpp::Elf_Word flags)
    elfcpp::Elf_Word flags)
{
{
  if (this->are_processor_specific_flags_set())
  if (this->are_processor_specific_flags_set())
    {
    {
      elfcpp::Elf_Word out_flags = this->processor_specific_flags();
      elfcpp::Elf_Word out_flags = this->processor_specific_flags();
 
 
      // Nothing to merge if flags equal to those in output.
      // Nothing to merge if flags equal to those in output.
      if (flags == out_flags)
      if (flags == out_flags)
        return;
        return;
 
 
      // Complain about various flag mismatches.
      // Complain about various flag mismatches.
      elfcpp::Elf_Word version1 = elfcpp::arm_eabi_version(flags);
      elfcpp::Elf_Word version1 = elfcpp::arm_eabi_version(flags);
      elfcpp::Elf_Word version2 = elfcpp::arm_eabi_version(out_flags);
      elfcpp::Elf_Word version2 = elfcpp::arm_eabi_version(out_flags);
      if (!this->are_eabi_versions_compatible(version1, version2)
      if (!this->are_eabi_versions_compatible(version1, version2)
          && parameters->options().warn_mismatch())
          && parameters->options().warn_mismatch())
        gold_error(_("Source object %s has EABI version %d but output has "
        gold_error(_("Source object %s has EABI version %d but output has "
                     "EABI version %d."),
                     "EABI version %d."),
                   name.c_str(),
                   name.c_str(),
                   (flags & elfcpp::EF_ARM_EABIMASK) >> 24,
                   (flags & elfcpp::EF_ARM_EABIMASK) >> 24,
                   (out_flags & elfcpp::EF_ARM_EABIMASK) >> 24);
                   (out_flags & elfcpp::EF_ARM_EABIMASK) >> 24);
    }
    }
  else
  else
    {
    {
      // If the input is the default architecture and had the default
      // If the input is the default architecture and had the default
      // flags then do not bother setting the flags for the output
      // flags then do not bother setting the flags for the output
      // architecture, instead allow future merges to do this.  If no
      // architecture, instead allow future merges to do this.  If no
      // future merges ever set these flags then they will retain their
      // future merges ever set these flags then they will retain their
      // uninitialised values, which surprise surprise, correspond
      // uninitialised values, which surprise surprise, correspond
      // to the default values.
      // to the default values.
      if (flags == 0)
      if (flags == 0)
        return;
        return;
 
 
      // This is the first time, just copy the flags.
      // This is the first time, just copy the flags.
      // We only copy the EABI version for now.
      // We only copy the EABI version for now.
      this->set_processor_specific_flags(flags & elfcpp::EF_ARM_EABIMASK);
      this->set_processor_specific_flags(flags & elfcpp::EF_ARM_EABIMASK);
    }
    }
}
}
 
 
// Adjust ELF file header.
// Adjust ELF file header.
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::do_adjust_elf_header(
Target_arm<big_endian>::do_adjust_elf_header(
    unsigned char* view,
    unsigned char* view,
    int len) const
    int len) const
{
{
  gold_assert(len == elfcpp::Elf_sizes<32>::ehdr_size);
  gold_assert(len == elfcpp::Elf_sizes<32>::ehdr_size);
 
 
  elfcpp::Ehdr<32, big_endian> ehdr(view);
  elfcpp::Ehdr<32, big_endian> ehdr(view);
  unsigned char e_ident[elfcpp::EI_NIDENT];
  unsigned char e_ident[elfcpp::EI_NIDENT];
  memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
  memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
 
 
  if (elfcpp::arm_eabi_version(this->processor_specific_flags())
  if (elfcpp::arm_eabi_version(this->processor_specific_flags())
      == elfcpp::EF_ARM_EABI_UNKNOWN)
      == elfcpp::EF_ARM_EABI_UNKNOWN)
    e_ident[elfcpp::EI_OSABI] = elfcpp::ELFOSABI_ARM;
    e_ident[elfcpp::EI_OSABI] = elfcpp::ELFOSABI_ARM;
  else
  else
    e_ident[elfcpp::EI_OSABI] = 0;
    e_ident[elfcpp::EI_OSABI] = 0;
  e_ident[elfcpp::EI_ABIVERSION] = 0;
  e_ident[elfcpp::EI_ABIVERSION] = 0;
 
 
  // FIXME: Do EF_ARM_BE8 adjustment.
  // FIXME: Do EF_ARM_BE8 adjustment.
 
 
  elfcpp::Ehdr_write<32, big_endian> oehdr(view);
  elfcpp::Ehdr_write<32, big_endian> oehdr(view);
  oehdr.put_e_ident(e_ident);
  oehdr.put_e_ident(e_ident);
}
}
 
 
// do_make_elf_object to override the same function in the base class.
// do_make_elf_object to override the same function in the base class.
// We need to use a target-specific sub-class of
// We need to use a target-specific sub-class of
// Sized_relobj_file<32, big_endian> to store ARM specific information.
// Sized_relobj_file<32, big_endian> to store ARM specific information.
// Hence we need to have our own ELF object creation.
// Hence we need to have our own ELF object creation.
 
 
template<bool big_endian>
template<bool big_endian>
Object*
Object*
Target_arm<big_endian>::do_make_elf_object(
Target_arm<big_endian>::do_make_elf_object(
    const std::string& name,
    const std::string& name,
    Input_file* input_file,
    Input_file* input_file,
    off_t offset, const elfcpp::Ehdr<32, big_endian>& ehdr)
    off_t offset, const elfcpp::Ehdr<32, big_endian>& ehdr)
{
{
  int et = ehdr.get_e_type();
  int et = ehdr.get_e_type();
  // ET_EXEC files are valid input for --just-symbols/-R,
  // ET_EXEC files are valid input for --just-symbols/-R,
  // and we treat them as relocatable objects.
  // and we treat them as relocatable objects.
  if (et == elfcpp::ET_REL
  if (et == elfcpp::ET_REL
      || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
      || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
    {
    {
      Arm_relobj<big_endian>* obj =
      Arm_relobj<big_endian>* obj =
        new Arm_relobj<big_endian>(name, input_file, offset, ehdr);
        new Arm_relobj<big_endian>(name, input_file, offset, ehdr);
      obj->setup();
      obj->setup();
      return obj;
      return obj;
    }
    }
  else if (et == elfcpp::ET_DYN)
  else if (et == elfcpp::ET_DYN)
    {
    {
      Sized_dynobj<32, big_endian>* obj =
      Sized_dynobj<32, big_endian>* obj =
        new Arm_dynobj<big_endian>(name, input_file, offset, ehdr);
        new Arm_dynobj<big_endian>(name, input_file, offset, ehdr);
      obj->setup();
      obj->setup();
      return obj;
      return obj;
    }
    }
  else
  else
    {
    {
      gold_error(_("%s: unsupported ELF file type %d"),
      gold_error(_("%s: unsupported ELF file type %d"),
                 name.c_str(), et);
                 name.c_str(), et);
      return NULL;
      return NULL;
    }
    }
}
}
 
 
// Read the architecture from the Tag_also_compatible_with attribute, if any.
// Read the architecture from the Tag_also_compatible_with attribute, if any.
// Returns -1 if no architecture could be read.
// Returns -1 if no architecture could be read.
// This is adapted from get_secondary_compatible_arch() in bfd/elf32-arm.c.
// This is adapted from get_secondary_compatible_arch() in bfd/elf32-arm.c.
 
 
template<bool big_endian>
template<bool big_endian>
int
int
Target_arm<big_endian>::get_secondary_compatible_arch(
Target_arm<big_endian>::get_secondary_compatible_arch(
    const Attributes_section_data* pasd)
    const Attributes_section_data* pasd)
{
{
  const Object_attribute* known_attributes =
  const Object_attribute* known_attributes =
    pasd->known_attributes(Object_attribute::OBJ_ATTR_PROC);
    pasd->known_attributes(Object_attribute::OBJ_ATTR_PROC);
 
 
  // Note: the tag and its argument below are uleb128 values, though
  // Note: the tag and its argument below are uleb128 values, though
  // currently-defined values fit in one byte for each.
  // currently-defined values fit in one byte for each.
  const std::string& sv =
  const std::string& sv =
    known_attributes[elfcpp::Tag_also_compatible_with].string_value();
    known_attributes[elfcpp::Tag_also_compatible_with].string_value();
  if (sv.size() == 2
  if (sv.size() == 2
      && sv.data()[0] == elfcpp::Tag_CPU_arch
      && sv.data()[0] == elfcpp::Tag_CPU_arch
      && (sv.data()[1] & 128) != 128)
      && (sv.data()[1] & 128) != 128)
   return sv.data()[1];
   return sv.data()[1];
 
 
  // This tag is "safely ignorable", so don't complain if it looks funny.
  // This tag is "safely ignorable", so don't complain if it looks funny.
  return -1;
  return -1;
}
}
 
 
// Set, or unset, the architecture of the Tag_also_compatible_with attribute.
// Set, or unset, the architecture of the Tag_also_compatible_with attribute.
// The tag is removed if ARCH is -1.
// The tag is removed if ARCH is -1.
// This is adapted from set_secondary_compatible_arch() in bfd/elf32-arm.c.
// This is adapted from set_secondary_compatible_arch() in bfd/elf32-arm.c.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::set_secondary_compatible_arch(
Target_arm<big_endian>::set_secondary_compatible_arch(
    Attributes_section_data* pasd,
    Attributes_section_data* pasd,
    int arch)
    int arch)
{
{
  Object_attribute* known_attributes =
  Object_attribute* known_attributes =
    pasd->known_attributes(Object_attribute::OBJ_ATTR_PROC);
    pasd->known_attributes(Object_attribute::OBJ_ATTR_PROC);
 
 
  if (arch == -1)
  if (arch == -1)
    {
    {
      known_attributes[elfcpp::Tag_also_compatible_with].set_string_value("");
      known_attributes[elfcpp::Tag_also_compatible_with].set_string_value("");
      return;
      return;
    }
    }
 
 
  // Note: the tag and its argument below are uleb128 values, though
  // Note: the tag and its argument below are uleb128 values, though
  // currently-defined values fit in one byte for each.
  // currently-defined values fit in one byte for each.
  char sv[3];
  char sv[3];
  sv[0] = elfcpp::Tag_CPU_arch;
  sv[0] = elfcpp::Tag_CPU_arch;
  gold_assert(arch != 0);
  gold_assert(arch != 0);
  sv[1] = arch;
  sv[1] = arch;
  sv[2] = '\0';
  sv[2] = '\0';
 
 
  known_attributes[elfcpp::Tag_also_compatible_with].set_string_value(sv);
  known_attributes[elfcpp::Tag_also_compatible_with].set_string_value(sv);
}
}
 
 
// Combine two values for Tag_CPU_arch, taking secondary compatibility tags
// Combine two values for Tag_CPU_arch, taking secondary compatibility tags
// into account.
// into account.
// This is adapted from tag_cpu_arch_combine() in bfd/elf32-arm.c.
// This is adapted from tag_cpu_arch_combine() in bfd/elf32-arm.c.
 
 
template<bool big_endian>
template<bool big_endian>
int
int
Target_arm<big_endian>::tag_cpu_arch_combine(
Target_arm<big_endian>::tag_cpu_arch_combine(
    const char* name,
    const char* name,
    int oldtag,
    int oldtag,
    int* secondary_compat_out,
    int* secondary_compat_out,
    int newtag,
    int newtag,
    int secondary_compat)
    int secondary_compat)
{
{
#define T(X) elfcpp::TAG_CPU_ARCH_##X
#define T(X) elfcpp::TAG_CPU_ARCH_##X
  static const int v6t2[] =
  static const int v6t2[] =
    {
    {
      T(V6T2),   // PRE_V4.
      T(V6T2),   // PRE_V4.
      T(V6T2),   // V4.
      T(V6T2),   // V4.
      T(V6T2),   // V4T.
      T(V6T2),   // V4T.
      T(V6T2),   // V5T.
      T(V6T2),   // V5T.
      T(V6T2),   // V5TE.
      T(V6T2),   // V5TE.
      T(V6T2),   // V5TEJ.
      T(V6T2),   // V5TEJ.
      T(V6T2),   // V6.
      T(V6T2),   // V6.
      T(V7),     // V6KZ.
      T(V7),     // V6KZ.
      T(V6T2)    // V6T2.
      T(V6T2)    // V6T2.
    };
    };
  static const int v6k[] =
  static const int v6k[] =
    {
    {
      T(V6K),    // PRE_V4.
      T(V6K),    // PRE_V4.
      T(V6K),    // V4.
      T(V6K),    // V4.
      T(V6K),    // V4T.
      T(V6K),    // V4T.
      T(V6K),    // V5T.
      T(V6K),    // V5T.
      T(V6K),    // V5TE.
      T(V6K),    // V5TE.
      T(V6K),    // V5TEJ.
      T(V6K),    // V5TEJ.
      T(V6K),    // V6.
      T(V6K),    // V6.
      T(V6KZ),   // V6KZ.
      T(V6KZ),   // V6KZ.
      T(V7),     // V6T2.
      T(V7),     // V6T2.
      T(V6K)     // V6K.
      T(V6K)     // V6K.
    };
    };
  static const int v7[] =
  static const int v7[] =
    {
    {
      T(V7),     // PRE_V4.
      T(V7),     // PRE_V4.
      T(V7),     // V4.
      T(V7),     // V4.
      T(V7),     // V4T.
      T(V7),     // V4T.
      T(V7),     // V5T.
      T(V7),     // V5T.
      T(V7),     // V5TE.
      T(V7),     // V5TE.
      T(V7),     // V5TEJ.
      T(V7),     // V5TEJ.
      T(V7),     // V6.
      T(V7),     // V6.
      T(V7),     // V6KZ.
      T(V7),     // V6KZ.
      T(V7),     // V6T2.
      T(V7),     // V6T2.
      T(V7),     // V6K.
      T(V7),     // V6K.
      T(V7)      // V7.
      T(V7)      // V7.
    };
    };
  static const int v6_m[] =
  static const int v6_m[] =
    {
    {
      -1,        // PRE_V4.
      -1,        // PRE_V4.
      -1,        // V4.
      -1,        // V4.
      T(V6K),    // V4T.
      T(V6K),    // V4T.
      T(V6K),    // V5T.
      T(V6K),    // V5T.
      T(V6K),    // V5TE.
      T(V6K),    // V5TE.
      T(V6K),    // V5TEJ.
      T(V6K),    // V5TEJ.
      T(V6K),    // V6.
      T(V6K),    // V6.
      T(V6KZ),   // V6KZ.
      T(V6KZ),   // V6KZ.
      T(V7),     // V6T2.
      T(V7),     // V6T2.
      T(V6K),    // V6K.
      T(V6K),    // V6K.
      T(V7),     // V7.
      T(V7),     // V7.
      T(V6_M)    // V6_M.
      T(V6_M)    // V6_M.
    };
    };
  static const int v6s_m[] =
  static const int v6s_m[] =
    {
    {
      -1,        // PRE_V4.
      -1,        // PRE_V4.
      -1,        // V4.
      -1,        // V4.
      T(V6K),    // V4T.
      T(V6K),    // V4T.
      T(V6K),    // V5T.
      T(V6K),    // V5T.
      T(V6K),    // V5TE.
      T(V6K),    // V5TE.
      T(V6K),    // V5TEJ.
      T(V6K),    // V5TEJ.
      T(V6K),    // V6.
      T(V6K),    // V6.
      T(V6KZ),   // V6KZ.
      T(V6KZ),   // V6KZ.
      T(V7),     // V6T2.
      T(V7),     // V6T2.
      T(V6K),    // V6K.
      T(V6K),    // V6K.
      T(V7),     // V7.
      T(V7),     // V7.
      T(V6S_M),  // V6_M.
      T(V6S_M),  // V6_M.
      T(V6S_M)   // V6S_M.
      T(V6S_M)   // V6S_M.
    };
    };
  static const int v7e_m[] =
  static const int v7e_m[] =
    {
    {
      -1,       // PRE_V4.
      -1,       // PRE_V4.
      -1,       // V4.
      -1,       // V4.
      T(V7E_M), // V4T.
      T(V7E_M), // V4T.
      T(V7E_M), // V5T.
      T(V7E_M), // V5T.
      T(V7E_M), // V5TE.
      T(V7E_M), // V5TE.
      T(V7E_M), // V5TEJ.
      T(V7E_M), // V5TEJ.
      T(V7E_M), // V6.
      T(V7E_M), // V6.
      T(V7E_M), // V6KZ.
      T(V7E_M), // V6KZ.
      T(V7E_M), // V6T2.
      T(V7E_M), // V6T2.
      T(V7E_M), // V6K.
      T(V7E_M), // V6K.
      T(V7E_M), // V7.
      T(V7E_M), // V7.
      T(V7E_M), // V6_M.
      T(V7E_M), // V6_M.
      T(V7E_M), // V6S_M.
      T(V7E_M), // V6S_M.
      T(V7E_M)  // V7E_M.
      T(V7E_M)  // V7E_M.
    };
    };
  static const int v4t_plus_v6_m[] =
  static const int v4t_plus_v6_m[] =
    {
    {
      -1,               // PRE_V4.
      -1,               // PRE_V4.
      -1,               // V4.
      -1,               // V4.
      T(V4T),           // V4T.
      T(V4T),           // V4T.
      T(V5T),           // V5T.
      T(V5T),           // V5T.
      T(V5TE),          // V5TE.
      T(V5TE),          // V5TE.
      T(V5TEJ),         // V5TEJ.
      T(V5TEJ),         // V5TEJ.
      T(V6),            // V6.
      T(V6),            // V6.
      T(V6KZ),          // V6KZ.
      T(V6KZ),          // V6KZ.
      T(V6T2),          // V6T2.
      T(V6T2),          // V6T2.
      T(V6K),           // V6K.
      T(V6K),           // V6K.
      T(V7),            // V7.
      T(V7),            // V7.
      T(V6_M),          // V6_M.
      T(V6_M),          // V6_M.
      T(V6S_M),         // V6S_M.
      T(V6S_M),         // V6S_M.
      T(V7E_M),         // V7E_M.
      T(V7E_M),         // V7E_M.
      T(V4T_PLUS_V6_M)  // V4T plus V6_M.
      T(V4T_PLUS_V6_M)  // V4T plus V6_M.
    };
    };
  static const int* comb[] =
  static const int* comb[] =
    {
    {
      v6t2,
      v6t2,
      v6k,
      v6k,
      v7,
      v7,
      v6_m,
      v6_m,
      v6s_m,
      v6s_m,
      v7e_m,
      v7e_m,
      // Pseudo-architecture.
      // Pseudo-architecture.
      v4t_plus_v6_m
      v4t_plus_v6_m
    };
    };
 
 
  // Check we've not got a higher architecture than we know about.
  // Check we've not got a higher architecture than we know about.
 
 
  if (oldtag > elfcpp::MAX_TAG_CPU_ARCH || newtag > elfcpp::MAX_TAG_CPU_ARCH)
  if (oldtag > elfcpp::MAX_TAG_CPU_ARCH || newtag > elfcpp::MAX_TAG_CPU_ARCH)
    {
    {
      gold_error(_("%s: unknown CPU architecture"), name);
      gold_error(_("%s: unknown CPU architecture"), name);
      return -1;
      return -1;
    }
    }
 
 
  // Override old tag if we have a Tag_also_compatible_with on the output.
  // Override old tag if we have a Tag_also_compatible_with on the output.
 
 
  if ((oldtag == T(V6_M) && *secondary_compat_out == T(V4T))
  if ((oldtag == T(V6_M) && *secondary_compat_out == T(V4T))
      || (oldtag == T(V4T) && *secondary_compat_out == T(V6_M)))
      || (oldtag == T(V4T) && *secondary_compat_out == T(V6_M)))
    oldtag = T(V4T_PLUS_V6_M);
    oldtag = T(V4T_PLUS_V6_M);
 
 
  // And override the new tag if we have a Tag_also_compatible_with on the
  // And override the new tag if we have a Tag_also_compatible_with on the
  // input.
  // input.
 
 
  if ((newtag == T(V6_M) && secondary_compat == T(V4T))
  if ((newtag == T(V6_M) && secondary_compat == T(V4T))
      || (newtag == T(V4T) && secondary_compat == T(V6_M)))
      || (newtag == T(V4T) && secondary_compat == T(V6_M)))
    newtag = T(V4T_PLUS_V6_M);
    newtag = T(V4T_PLUS_V6_M);
 
 
  // Architectures before V6KZ add features monotonically.
  // Architectures before V6KZ add features monotonically.
  int tagh = std::max(oldtag, newtag);
  int tagh = std::max(oldtag, newtag);
  if (tagh <= elfcpp::TAG_CPU_ARCH_V6KZ)
  if (tagh <= elfcpp::TAG_CPU_ARCH_V6KZ)
    return tagh;
    return tagh;
 
 
  int tagl = std::min(oldtag, newtag);
  int tagl = std::min(oldtag, newtag);
  int result = comb[tagh - T(V6T2)][tagl];
  int result = comb[tagh - T(V6T2)][tagl];
 
 
  // Use Tag_CPU_arch == V4T and Tag_also_compatible_with (Tag_CPU_arch V6_M)
  // Use Tag_CPU_arch == V4T and Tag_also_compatible_with (Tag_CPU_arch V6_M)
  // as the canonical version.
  // as the canonical version.
  if (result == T(V4T_PLUS_V6_M))
  if (result == T(V4T_PLUS_V6_M))
    {
    {
      result = T(V4T);
      result = T(V4T);
      *secondary_compat_out = T(V6_M);
      *secondary_compat_out = T(V6_M);
    }
    }
  else
  else
    *secondary_compat_out = -1;
    *secondary_compat_out = -1;
 
 
  if (result == -1)
  if (result == -1)
    {
    {
      gold_error(_("%s: conflicting CPU architectures %d/%d"),
      gold_error(_("%s: conflicting CPU architectures %d/%d"),
                 name, oldtag, newtag);
                 name, oldtag, newtag);
      return -1;
      return -1;
    }
    }
 
 
  return result;
  return result;
#undef T
#undef T
}
}
 
 
// Helper to print AEABI enum tag value.
// Helper to print AEABI enum tag value.
 
 
template<bool big_endian>
template<bool big_endian>
std::string
std::string
Target_arm<big_endian>::aeabi_enum_name(unsigned int value)
Target_arm<big_endian>::aeabi_enum_name(unsigned int value)
{
{
  static const char* aeabi_enum_names[] =
  static const char* aeabi_enum_names[] =
    { "", "variable-size", "32-bit", "" };
    { "", "variable-size", "32-bit", "" };
  const size_t aeabi_enum_names_size =
  const size_t aeabi_enum_names_size =
    sizeof(aeabi_enum_names) / sizeof(aeabi_enum_names[0]);
    sizeof(aeabi_enum_names) / sizeof(aeabi_enum_names[0]);
 
 
  if (value < aeabi_enum_names_size)
  if (value < aeabi_enum_names_size)
    return std::string(aeabi_enum_names[value]);
    return std::string(aeabi_enum_names[value]);
  else
  else
    {
    {
      char buffer[100];
      char buffer[100];
      sprintf(buffer, "<unknown value %u>", value);
      sprintf(buffer, "<unknown value %u>", value);
      return std::string(buffer);
      return std::string(buffer);
    }
    }
}
}
 
 
// Return the string value to store in TAG_CPU_name.
// Return the string value to store in TAG_CPU_name.
 
 
template<bool big_endian>
template<bool big_endian>
std::string
std::string
Target_arm<big_endian>::tag_cpu_name_value(unsigned int value)
Target_arm<big_endian>::tag_cpu_name_value(unsigned int value)
{
{
  static const char* name_table[] = {
  static const char* name_table[] = {
    // These aren't real CPU names, but we can't guess
    // These aren't real CPU names, but we can't guess
    // that from the architecture version alone.
    // that from the architecture version alone.
   "Pre v4",
   "Pre v4",
   "ARM v4",
   "ARM v4",
   "ARM v4T",
   "ARM v4T",
   "ARM v5T",
   "ARM v5T",
   "ARM v5TE",
   "ARM v5TE",
   "ARM v5TEJ",
   "ARM v5TEJ",
   "ARM v6",
   "ARM v6",
   "ARM v6KZ",
   "ARM v6KZ",
   "ARM v6T2",
   "ARM v6T2",
   "ARM v6K",
   "ARM v6K",
   "ARM v7",
   "ARM v7",
   "ARM v6-M",
   "ARM v6-M",
   "ARM v6S-M",
   "ARM v6S-M",
   "ARM v7E-M"
   "ARM v7E-M"
 };
 };
 const size_t name_table_size = sizeof(name_table) / sizeof(name_table[0]);
 const size_t name_table_size = sizeof(name_table) / sizeof(name_table[0]);
 
 
  if (value < name_table_size)
  if (value < name_table_size)
    return std::string(name_table[value]);
    return std::string(name_table[value]);
  else
  else
    {
    {
      char buffer[100];
      char buffer[100];
      sprintf(buffer, "<unknown CPU value %u>", value);
      sprintf(buffer, "<unknown CPU value %u>", value);
      return std::string(buffer);
      return std::string(buffer);
    }
    }
}
}
 
 
// Merge object attributes from input file called NAME with those of the
// Merge object attributes from input file called NAME with those of the
// output.  The input object attributes are in the object pointed by PASD.
// output.  The input object attributes are in the object pointed by PASD.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::merge_object_attributes(
Target_arm<big_endian>::merge_object_attributes(
    const char* name,
    const char* name,
    const Attributes_section_data* pasd)
    const Attributes_section_data* pasd)
{
{
  // Return if there is no attributes section data.
  // Return if there is no attributes section data.
  if (pasd == NULL)
  if (pasd == NULL)
    return;
    return;
 
 
  // If output has no object attributes, just copy.
  // If output has no object attributes, just copy.
  const int vendor = Object_attribute::OBJ_ATTR_PROC;
  const int vendor = Object_attribute::OBJ_ATTR_PROC;
  if (this->attributes_section_data_ == NULL)
  if (this->attributes_section_data_ == NULL)
    {
    {
      this->attributes_section_data_ = new Attributes_section_data(*pasd);
      this->attributes_section_data_ = new Attributes_section_data(*pasd);
      Object_attribute* out_attr =
      Object_attribute* out_attr =
        this->attributes_section_data_->known_attributes(vendor);
        this->attributes_section_data_->known_attributes(vendor);
 
 
      // We do not output objects with Tag_MPextension_use_legacy - we move
      // We do not output objects with Tag_MPextension_use_legacy - we move
      //  the attribute's value to Tag_MPextension_use.  */
      //  the attribute's value to Tag_MPextension_use.  */
      if (out_attr[elfcpp::Tag_MPextension_use_legacy].int_value() != 0)
      if (out_attr[elfcpp::Tag_MPextension_use_legacy].int_value() != 0)
        {
        {
          if (out_attr[elfcpp::Tag_MPextension_use].int_value() != 0
          if (out_attr[elfcpp::Tag_MPextension_use].int_value() != 0
              && out_attr[elfcpp::Tag_MPextension_use_legacy].int_value()
              && out_attr[elfcpp::Tag_MPextension_use_legacy].int_value()
                != out_attr[elfcpp::Tag_MPextension_use].int_value())
                != out_attr[elfcpp::Tag_MPextension_use].int_value())
            {
            {
              gold_error(_("%s has both the current and legacy "
              gold_error(_("%s has both the current and legacy "
                           "Tag_MPextension_use attributes"),
                           "Tag_MPextension_use attributes"),
                         name);
                         name);
            }
            }
 
 
          out_attr[elfcpp::Tag_MPextension_use] =
          out_attr[elfcpp::Tag_MPextension_use] =
            out_attr[elfcpp::Tag_MPextension_use_legacy];
            out_attr[elfcpp::Tag_MPextension_use_legacy];
          out_attr[elfcpp::Tag_MPextension_use_legacy].set_type(0);
          out_attr[elfcpp::Tag_MPextension_use_legacy].set_type(0);
          out_attr[elfcpp::Tag_MPextension_use_legacy].set_int_value(0);
          out_attr[elfcpp::Tag_MPextension_use_legacy].set_int_value(0);
        }
        }
 
 
      return;
      return;
    }
    }
 
 
  const Object_attribute* in_attr = pasd->known_attributes(vendor);
  const Object_attribute* in_attr = pasd->known_attributes(vendor);
  Object_attribute* out_attr =
  Object_attribute* out_attr =
    this->attributes_section_data_->known_attributes(vendor);
    this->attributes_section_data_->known_attributes(vendor);
 
 
  // This needs to happen before Tag_ABI_FP_number_model is merged.  */
  // This needs to happen before Tag_ABI_FP_number_model is merged.  */
  if (in_attr[elfcpp::Tag_ABI_VFP_args].int_value()
  if (in_attr[elfcpp::Tag_ABI_VFP_args].int_value()
      != out_attr[elfcpp::Tag_ABI_VFP_args].int_value())
      != out_attr[elfcpp::Tag_ABI_VFP_args].int_value())
    {
    {
      // Ignore mismatches if the object doesn't use floating point.  */
      // Ignore mismatches if the object doesn't use floating point.  */
      if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value() == 0)
      if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value() == 0)
        out_attr[elfcpp::Tag_ABI_VFP_args].set_int_value(
        out_attr[elfcpp::Tag_ABI_VFP_args].set_int_value(
            in_attr[elfcpp::Tag_ABI_VFP_args].int_value());
            in_attr[elfcpp::Tag_ABI_VFP_args].int_value());
      else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() != 0
      else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() != 0
               && parameters->options().warn_mismatch())
               && parameters->options().warn_mismatch())
        gold_error(_("%s uses VFP register arguments, output does not"),
        gold_error(_("%s uses VFP register arguments, output does not"),
                   name);
                   name);
    }
    }
 
 
  for (int i = 4; i < Vendor_object_attributes::NUM_KNOWN_ATTRIBUTES; ++i)
  for (int i = 4; i < Vendor_object_attributes::NUM_KNOWN_ATTRIBUTES; ++i)
    {
    {
      // Merge this attribute with existing attributes.
      // Merge this attribute with existing attributes.
      switch (i)
      switch (i)
        {
        {
        case elfcpp::Tag_CPU_raw_name:
        case elfcpp::Tag_CPU_raw_name:
        case elfcpp::Tag_CPU_name:
        case elfcpp::Tag_CPU_name:
          // These are merged after Tag_CPU_arch.
          // These are merged after Tag_CPU_arch.
          break;
          break;
 
 
        case elfcpp::Tag_ABI_optimization_goals:
        case elfcpp::Tag_ABI_optimization_goals:
        case elfcpp::Tag_ABI_FP_optimization_goals:
        case elfcpp::Tag_ABI_FP_optimization_goals:
          // Use the first value seen.
          // Use the first value seen.
          break;
          break;
 
 
        case elfcpp::Tag_CPU_arch:
        case elfcpp::Tag_CPU_arch:
          {
          {
            unsigned int saved_out_attr = out_attr->int_value();
            unsigned int saved_out_attr = out_attr->int_value();
            // Merge Tag_CPU_arch and Tag_also_compatible_with.
            // Merge Tag_CPU_arch and Tag_also_compatible_with.
            int secondary_compat =
            int secondary_compat =
              this->get_secondary_compatible_arch(pasd);
              this->get_secondary_compatible_arch(pasd);
            int secondary_compat_out =
            int secondary_compat_out =
              this->get_secondary_compatible_arch(
              this->get_secondary_compatible_arch(
                  this->attributes_section_data_);
                  this->attributes_section_data_);
            out_attr[i].set_int_value(
            out_attr[i].set_int_value(
                tag_cpu_arch_combine(name, out_attr[i].int_value(),
                tag_cpu_arch_combine(name, out_attr[i].int_value(),
                                     &secondary_compat_out,
                                     &secondary_compat_out,
                                     in_attr[i].int_value(),
                                     in_attr[i].int_value(),
                                     secondary_compat));
                                     secondary_compat));
            this->set_secondary_compatible_arch(this->attributes_section_data_,
            this->set_secondary_compatible_arch(this->attributes_section_data_,
                                                secondary_compat_out);
                                                secondary_compat_out);
 
 
            // Merge Tag_CPU_name and Tag_CPU_raw_name.
            // Merge Tag_CPU_name and Tag_CPU_raw_name.
            if (out_attr[i].int_value() == saved_out_attr)
            if (out_attr[i].int_value() == saved_out_attr)
              ; // Leave the names alone.
              ; // Leave the names alone.
            else if (out_attr[i].int_value() == in_attr[i].int_value())
            else if (out_attr[i].int_value() == in_attr[i].int_value())
              {
              {
                // The output architecture has been changed to match the
                // The output architecture has been changed to match the
                // input architecture.  Use the input names.
                // input architecture.  Use the input names.
                out_attr[elfcpp::Tag_CPU_name].set_string_value(
                out_attr[elfcpp::Tag_CPU_name].set_string_value(
                    in_attr[elfcpp::Tag_CPU_name].string_value());
                    in_attr[elfcpp::Tag_CPU_name].string_value());
                out_attr[elfcpp::Tag_CPU_raw_name].set_string_value(
                out_attr[elfcpp::Tag_CPU_raw_name].set_string_value(
                    in_attr[elfcpp::Tag_CPU_raw_name].string_value());
                    in_attr[elfcpp::Tag_CPU_raw_name].string_value());
              }
              }
            else
            else
              {
              {
                out_attr[elfcpp::Tag_CPU_name].set_string_value("");
                out_attr[elfcpp::Tag_CPU_name].set_string_value("");
                out_attr[elfcpp::Tag_CPU_raw_name].set_string_value("");
                out_attr[elfcpp::Tag_CPU_raw_name].set_string_value("");
              }
              }
 
 
            // If we still don't have a value for Tag_CPU_name,
            // If we still don't have a value for Tag_CPU_name,
            // make one up now.  Tag_CPU_raw_name remains blank.
            // make one up now.  Tag_CPU_raw_name remains blank.
            if (out_attr[elfcpp::Tag_CPU_name].string_value() == "")
            if (out_attr[elfcpp::Tag_CPU_name].string_value() == "")
              {
              {
                const std::string cpu_name =
                const std::string cpu_name =
                  this->tag_cpu_name_value(out_attr[i].int_value());
                  this->tag_cpu_name_value(out_attr[i].int_value());
                // FIXME:  If we see an unknown CPU, this will be set
                // FIXME:  If we see an unknown CPU, this will be set
                // to "<unknown CPU n>", where n is the attribute value.
                // to "<unknown CPU n>", where n is the attribute value.
                // This is different from BFD, which leaves the name alone.
                // This is different from BFD, which leaves the name alone.
                out_attr[elfcpp::Tag_CPU_name].set_string_value(cpu_name);
                out_attr[elfcpp::Tag_CPU_name].set_string_value(cpu_name);
              }
              }
          }
          }
          break;
          break;
 
 
        case elfcpp::Tag_ARM_ISA_use:
        case elfcpp::Tag_ARM_ISA_use:
        case elfcpp::Tag_THUMB_ISA_use:
        case elfcpp::Tag_THUMB_ISA_use:
        case elfcpp::Tag_WMMX_arch:
        case elfcpp::Tag_WMMX_arch:
        case elfcpp::Tag_Advanced_SIMD_arch:
        case elfcpp::Tag_Advanced_SIMD_arch:
          // ??? Do Advanced_SIMD (NEON) and WMMX conflict?
          // ??? Do Advanced_SIMD (NEON) and WMMX conflict?
        case elfcpp::Tag_ABI_FP_rounding:
        case elfcpp::Tag_ABI_FP_rounding:
        case elfcpp::Tag_ABI_FP_exceptions:
        case elfcpp::Tag_ABI_FP_exceptions:
        case elfcpp::Tag_ABI_FP_user_exceptions:
        case elfcpp::Tag_ABI_FP_user_exceptions:
        case elfcpp::Tag_ABI_FP_number_model:
        case elfcpp::Tag_ABI_FP_number_model:
        case elfcpp::Tag_VFP_HP_extension:
        case elfcpp::Tag_VFP_HP_extension:
        case elfcpp::Tag_CPU_unaligned_access:
        case elfcpp::Tag_CPU_unaligned_access:
        case elfcpp::Tag_T2EE_use:
        case elfcpp::Tag_T2EE_use:
        case elfcpp::Tag_Virtualization_use:
        case elfcpp::Tag_Virtualization_use:
        case elfcpp::Tag_MPextension_use:
        case elfcpp::Tag_MPextension_use:
          // Use the largest value specified.
          // Use the largest value specified.
          if (in_attr[i].int_value() > out_attr[i].int_value())
          if (in_attr[i].int_value() > out_attr[i].int_value())
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
 
 
        case elfcpp::Tag_ABI_align8_preserved:
        case elfcpp::Tag_ABI_align8_preserved:
        case elfcpp::Tag_ABI_PCS_RO_data:
        case elfcpp::Tag_ABI_PCS_RO_data:
          // Use the smallest value specified.
          // Use the smallest value specified.
          if (in_attr[i].int_value() < out_attr[i].int_value())
          if (in_attr[i].int_value() < out_attr[i].int_value())
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
 
 
        case elfcpp::Tag_ABI_align8_needed:
        case elfcpp::Tag_ABI_align8_needed:
          if ((in_attr[i].int_value() > 0 || out_attr[i].int_value() > 0)
          if ((in_attr[i].int_value() > 0 || out_attr[i].int_value() > 0)
              && (in_attr[elfcpp::Tag_ABI_align8_preserved].int_value() == 0
              && (in_attr[elfcpp::Tag_ABI_align8_preserved].int_value() == 0
                  || (out_attr[elfcpp::Tag_ABI_align8_preserved].int_value()
                  || (out_attr[elfcpp::Tag_ABI_align8_preserved].int_value()
                      == 0)))
                      == 0)))
            {
            {
              // This error message should be enabled once all non-conforming
              // This error message should be enabled once all non-conforming
              // binaries in the toolchain have had the attributes set
              // binaries in the toolchain have had the attributes set
              // properly.
              // properly.
              // gold_error(_("output 8-byte data alignment conflicts with %s"),
              // gold_error(_("output 8-byte data alignment conflicts with %s"),
              //            name);
              //            name);
            }
            }
          // Fall through.
          // Fall through.
        case elfcpp::Tag_ABI_FP_denormal:
        case elfcpp::Tag_ABI_FP_denormal:
        case elfcpp::Tag_ABI_PCS_GOT_use:
        case elfcpp::Tag_ABI_PCS_GOT_use:
          {
          {
            // These tags have 0 = don't care, 1 = strong requirement,
            // These tags have 0 = don't care, 1 = strong requirement,
            // 2 = weak requirement.
            // 2 = weak requirement.
            static const int order_021[3] = {0, 2, 1};
            static const int order_021[3] = {0, 2, 1};
 
 
            // Use the "greatest" from the sequence 0, 2, 1, or the largest
            // Use the "greatest" from the sequence 0, 2, 1, or the largest
            // value if greater than 2 (for future-proofing).
            // value if greater than 2 (for future-proofing).
            if ((in_attr[i].int_value() > 2
            if ((in_attr[i].int_value() > 2
                 && in_attr[i].int_value() > out_attr[i].int_value())
                 && in_attr[i].int_value() > out_attr[i].int_value())
                || (in_attr[i].int_value() <= 2
                || (in_attr[i].int_value() <= 2
                    && out_attr[i].int_value() <= 2
                    && out_attr[i].int_value() <= 2
                    && (order_021[in_attr[i].int_value()]
                    && (order_021[in_attr[i].int_value()]
                        > order_021[out_attr[i].int_value()])))
                        > order_021[out_attr[i].int_value()])))
              out_attr[i].set_int_value(in_attr[i].int_value());
              out_attr[i].set_int_value(in_attr[i].int_value());
          }
          }
          break;
          break;
 
 
        case elfcpp::Tag_CPU_arch_profile:
        case elfcpp::Tag_CPU_arch_profile:
          if (out_attr[i].int_value() != in_attr[i].int_value())
          if (out_attr[i].int_value() != in_attr[i].int_value())
            {
            {
              // 0 will merge with anything.
              // 0 will merge with anything.
              // 'A' and 'S' merge to 'A'.
              // 'A' and 'S' merge to 'A'.
              // 'R' and 'S' merge to 'R'.
              // 'R' and 'S' merge to 'R'.
              // 'M' and 'A|R|S' is an error.
              // 'M' and 'A|R|S' is an error.
              if (out_attr[i].int_value() == 0
              if (out_attr[i].int_value() == 0
                  || (out_attr[i].int_value() == 'S'
                  || (out_attr[i].int_value() == 'S'
                      && (in_attr[i].int_value() == 'A'
                      && (in_attr[i].int_value() == 'A'
                          || in_attr[i].int_value() == 'R')))
                          || in_attr[i].int_value() == 'R')))
                out_attr[i].set_int_value(in_attr[i].int_value());
                out_attr[i].set_int_value(in_attr[i].int_value());
              else if (in_attr[i].int_value() == 0
              else if (in_attr[i].int_value() == 0
                       || (in_attr[i].int_value() == 'S'
                       || (in_attr[i].int_value() == 'S'
                           && (out_attr[i].int_value() == 'A'
                           && (out_attr[i].int_value() == 'A'
                               || out_attr[i].int_value() == 'R')))
                               || out_attr[i].int_value() == 'R')))
                ; // Do nothing.
                ; // Do nothing.
              else if (parameters->options().warn_mismatch())
              else if (parameters->options().warn_mismatch())
                {
                {
                  gold_error
                  gold_error
                    (_("conflicting architecture profiles %c/%c"),
                    (_("conflicting architecture profiles %c/%c"),
                     in_attr[i].int_value() ? in_attr[i].int_value() : '0',
                     in_attr[i].int_value() ? in_attr[i].int_value() : '0',
                     out_attr[i].int_value() ? out_attr[i].int_value() : '0');
                     out_attr[i].int_value() ? out_attr[i].int_value() : '0');
                }
                }
            }
            }
          break;
          break;
        case elfcpp::Tag_VFP_arch:
        case elfcpp::Tag_VFP_arch:
            {
            {
              static const struct
              static const struct
              {
              {
                  int ver;
                  int ver;
                  int regs;
                  int regs;
              } vfp_versions[7] =
              } vfp_versions[7] =
                {
                {
                  {0, 0},
                  {0, 0},
                  {1, 16},
                  {1, 16},
                  {2, 16},
                  {2, 16},
                  {3, 32},
                  {3, 32},
                  {3, 16},
                  {3, 16},
                  {4, 32},
                  {4, 32},
                  {4, 16}
                  {4, 16}
                };
                };
 
 
              // Values greater than 6 aren't defined, so just pick the
              // Values greater than 6 aren't defined, so just pick the
              // biggest.
              // biggest.
              if (in_attr[i].int_value() > 6
              if (in_attr[i].int_value() > 6
                  && in_attr[i].int_value() > out_attr[i].int_value())
                  && in_attr[i].int_value() > out_attr[i].int_value())
                {
                {
                  *out_attr = *in_attr;
                  *out_attr = *in_attr;
                  break;
                  break;
                }
                }
              // The output uses the superset of input features
              // The output uses the superset of input features
              // (ISA version) and registers.
              // (ISA version) and registers.
              int ver = std::max(vfp_versions[in_attr[i].int_value()].ver,
              int ver = std::max(vfp_versions[in_attr[i].int_value()].ver,
                                 vfp_versions[out_attr[i].int_value()].ver);
                                 vfp_versions[out_attr[i].int_value()].ver);
              int regs = std::max(vfp_versions[in_attr[i].int_value()].regs,
              int regs = std::max(vfp_versions[in_attr[i].int_value()].regs,
                                  vfp_versions[out_attr[i].int_value()].regs);
                                  vfp_versions[out_attr[i].int_value()].regs);
              // This assumes all possible supersets are also a valid
              // This assumes all possible supersets are also a valid
              // options.
              // options.
              int newval;
              int newval;
              for (newval = 6; newval > 0; newval--)
              for (newval = 6; newval > 0; newval--)
                {
                {
                  if (regs == vfp_versions[newval].regs
                  if (regs == vfp_versions[newval].regs
                      && ver == vfp_versions[newval].ver)
                      && ver == vfp_versions[newval].ver)
                    break;
                    break;
                }
                }
              out_attr[i].set_int_value(newval);
              out_attr[i].set_int_value(newval);
            }
            }
          break;
          break;
        case elfcpp::Tag_PCS_config:
        case elfcpp::Tag_PCS_config:
          if (out_attr[i].int_value() == 0)
          if (out_attr[i].int_value() == 0)
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          else if (in_attr[i].int_value() != 0
          else if (in_attr[i].int_value() != 0
                   && out_attr[i].int_value() != 0
                   && out_attr[i].int_value() != 0
                   && parameters->options().warn_mismatch())
                   && parameters->options().warn_mismatch())
            {
            {
              // It's sometimes ok to mix different configs, so this is only
              // It's sometimes ok to mix different configs, so this is only
              // a warning.
              // a warning.
              gold_warning(_("%s: conflicting platform configuration"), name);
              gold_warning(_("%s: conflicting platform configuration"), name);
            }
            }
          break;
          break;
        case elfcpp::Tag_ABI_PCS_R9_use:
        case elfcpp::Tag_ABI_PCS_R9_use:
          if (in_attr[i].int_value() != out_attr[i].int_value()
          if (in_attr[i].int_value() != out_attr[i].int_value()
              && out_attr[i].int_value() != elfcpp::AEABI_R9_unused
              && out_attr[i].int_value() != elfcpp::AEABI_R9_unused
              && in_attr[i].int_value() != elfcpp::AEABI_R9_unused
              && in_attr[i].int_value() != elfcpp::AEABI_R9_unused
              && parameters->options().warn_mismatch())
              && parameters->options().warn_mismatch())
            {
            {
              gold_error(_("%s: conflicting use of R9"), name);
              gold_error(_("%s: conflicting use of R9"), name);
            }
            }
          if (out_attr[i].int_value() == elfcpp::AEABI_R9_unused)
          if (out_attr[i].int_value() == elfcpp::AEABI_R9_unused)
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
        case elfcpp::Tag_ABI_PCS_RW_data:
        case elfcpp::Tag_ABI_PCS_RW_data:
          if (in_attr[i].int_value() == elfcpp::AEABI_PCS_RW_data_SBrel
          if (in_attr[i].int_value() == elfcpp::AEABI_PCS_RW_data_SBrel
              && (in_attr[elfcpp::Tag_ABI_PCS_R9_use].int_value()
              && (in_attr[elfcpp::Tag_ABI_PCS_R9_use].int_value()
                  != elfcpp::AEABI_R9_SB)
                  != elfcpp::AEABI_R9_SB)
              && (out_attr[elfcpp::Tag_ABI_PCS_R9_use].int_value()
              && (out_attr[elfcpp::Tag_ABI_PCS_R9_use].int_value()
                  != elfcpp::AEABI_R9_unused)
                  != elfcpp::AEABI_R9_unused)
              && parameters->options().warn_mismatch())
              && parameters->options().warn_mismatch())
            {
            {
              gold_error(_("%s: SB relative addressing conflicts with use "
              gold_error(_("%s: SB relative addressing conflicts with use "
                           "of R9"),
                           "of R9"),
                           name);
                           name);
            }
            }
          // Use the smallest value specified.
          // Use the smallest value specified.
          if (in_attr[i].int_value() < out_attr[i].int_value())
          if (in_attr[i].int_value() < out_attr[i].int_value())
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
        case elfcpp::Tag_ABI_PCS_wchar_t:
        case elfcpp::Tag_ABI_PCS_wchar_t:
          if (out_attr[i].int_value()
          if (out_attr[i].int_value()
              && in_attr[i].int_value()
              && in_attr[i].int_value()
              && out_attr[i].int_value() != in_attr[i].int_value()
              && out_attr[i].int_value() != in_attr[i].int_value()
              && parameters->options().warn_mismatch()
              && parameters->options().warn_mismatch()
              && parameters->options().wchar_size_warning())
              && parameters->options().wchar_size_warning())
            {
            {
              gold_warning(_("%s uses %u-byte wchar_t yet the output is to "
              gold_warning(_("%s uses %u-byte wchar_t yet the output is to "
                             "use %u-byte wchar_t; use of wchar_t values "
                             "use %u-byte wchar_t; use of wchar_t values "
                             "across objects may fail"),
                             "across objects may fail"),
                           name, in_attr[i].int_value(),
                           name, in_attr[i].int_value(),
                           out_attr[i].int_value());
                           out_attr[i].int_value());
            }
            }
          else if (in_attr[i].int_value() && !out_attr[i].int_value())
          else if (in_attr[i].int_value() && !out_attr[i].int_value())
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
        case elfcpp::Tag_ABI_enum_size:
        case elfcpp::Tag_ABI_enum_size:
          if (in_attr[i].int_value() != elfcpp::AEABI_enum_unused)
          if (in_attr[i].int_value() != elfcpp::AEABI_enum_unused)
            {
            {
              if (out_attr[i].int_value() == elfcpp::AEABI_enum_unused
              if (out_attr[i].int_value() == elfcpp::AEABI_enum_unused
                  || out_attr[i].int_value() == elfcpp::AEABI_enum_forced_wide)
                  || out_attr[i].int_value() == elfcpp::AEABI_enum_forced_wide)
                {
                {
                  // The existing object is compatible with anything.
                  // The existing object is compatible with anything.
                  // Use whatever requirements the new object has.
                  // Use whatever requirements the new object has.
                  out_attr[i].set_int_value(in_attr[i].int_value());
                  out_attr[i].set_int_value(in_attr[i].int_value());
                }
                }
              else if (in_attr[i].int_value() != elfcpp::AEABI_enum_forced_wide
              else if (in_attr[i].int_value() != elfcpp::AEABI_enum_forced_wide
                       && out_attr[i].int_value() != in_attr[i].int_value()
                       && out_attr[i].int_value() != in_attr[i].int_value()
                       && parameters->options().warn_mismatch()
                       && parameters->options().warn_mismatch()
                       && parameters->options().enum_size_warning())
                       && parameters->options().enum_size_warning())
                {
                {
                  unsigned int in_value = in_attr[i].int_value();
                  unsigned int in_value = in_attr[i].int_value();
                  unsigned int out_value = out_attr[i].int_value();
                  unsigned int out_value = out_attr[i].int_value();
                  gold_warning(_("%s uses %s enums yet the output is to use "
                  gold_warning(_("%s uses %s enums yet the output is to use "
                                 "%s enums; use of enum values across objects "
                                 "%s enums; use of enum values across objects "
                                 "may fail"),
                                 "may fail"),
                               name,
                               name,
                               this->aeabi_enum_name(in_value).c_str(),
                               this->aeabi_enum_name(in_value).c_str(),
                               this->aeabi_enum_name(out_value).c_str());
                               this->aeabi_enum_name(out_value).c_str());
                }
                }
            }
            }
          break;
          break;
        case elfcpp::Tag_ABI_VFP_args:
        case elfcpp::Tag_ABI_VFP_args:
          // Already done.
          // Already done.
          break;
          break;
        case elfcpp::Tag_ABI_WMMX_args:
        case elfcpp::Tag_ABI_WMMX_args:
          if (in_attr[i].int_value() != out_attr[i].int_value()
          if (in_attr[i].int_value() != out_attr[i].int_value()
              && parameters->options().warn_mismatch())
              && parameters->options().warn_mismatch())
            {
            {
              gold_error(_("%s uses iWMMXt register arguments, output does "
              gold_error(_("%s uses iWMMXt register arguments, output does "
                           "not"),
                           "not"),
                         name);
                         name);
            }
            }
          break;
          break;
        case Object_attribute::Tag_compatibility:
        case Object_attribute::Tag_compatibility:
          // Merged in target-independent code.
          // Merged in target-independent code.
          break;
          break;
        case elfcpp::Tag_ABI_HardFP_use:
        case elfcpp::Tag_ABI_HardFP_use:
          // 1 (SP) and 2 (DP) conflict, so combine to 3 (SP & DP).
          // 1 (SP) and 2 (DP) conflict, so combine to 3 (SP & DP).
          if ((in_attr[i].int_value() == 1 && out_attr[i].int_value() == 2)
          if ((in_attr[i].int_value() == 1 && out_attr[i].int_value() == 2)
              || (in_attr[i].int_value() == 2 && out_attr[i].int_value() == 1))
              || (in_attr[i].int_value() == 2 && out_attr[i].int_value() == 1))
            out_attr[i].set_int_value(3);
            out_attr[i].set_int_value(3);
          else if (in_attr[i].int_value() > out_attr[i].int_value())
          else if (in_attr[i].int_value() > out_attr[i].int_value())
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
        case elfcpp::Tag_ABI_FP_16bit_format:
        case elfcpp::Tag_ABI_FP_16bit_format:
          if (in_attr[i].int_value() != 0 && out_attr[i].int_value() != 0)
          if (in_attr[i].int_value() != 0 && out_attr[i].int_value() != 0)
            {
            {
              if (in_attr[i].int_value() != out_attr[i].int_value()
              if (in_attr[i].int_value() != out_attr[i].int_value()
                  && parameters->options().warn_mismatch())
                  && parameters->options().warn_mismatch())
                gold_error(_("fp16 format mismatch between %s and output"),
                gold_error(_("fp16 format mismatch between %s and output"),
                           name);
                           name);
            }
            }
          if (in_attr[i].int_value() != 0)
          if (in_attr[i].int_value() != 0)
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
          break;
          break;
 
 
        case elfcpp::Tag_DIV_use:
        case elfcpp::Tag_DIV_use:
          // This tag is set to zero if we can use UDIV and SDIV in Thumb
          // This tag is set to zero if we can use UDIV and SDIV in Thumb
          // mode on a v7-M or v7-R CPU; to one if we can not use UDIV or
          // mode on a v7-M or v7-R CPU; to one if we can not use UDIV or
          // SDIV at all; and to two if we can use UDIV or SDIV on a v7-A
          // SDIV at all; and to two if we can use UDIV or SDIV on a v7-A
          // CPU.  We will merge as follows: If the input attribute's value
          // CPU.  We will merge as follows: If the input attribute's value
          // is one then the output attribute's value remains unchanged.  If
          // is one then the output attribute's value remains unchanged.  If
          // the input attribute's value is zero or two then if the output
          // the input attribute's value is zero or two then if the output
          // attribute's value is one the output value is set to the input
          // attribute's value is one the output value is set to the input
          // value, otherwise the output value must be the same as the
          // value, otherwise the output value must be the same as the
          // inputs.  */ 
          // inputs.  */ 
          if (in_attr[i].int_value() != 1 && out_attr[i].int_value() != 1)
          if (in_attr[i].int_value() != 1 && out_attr[i].int_value() != 1)
            {
            {
              if (in_attr[i].int_value() != out_attr[i].int_value())
              if (in_attr[i].int_value() != out_attr[i].int_value())
                {
                {
                  gold_error(_("DIV usage mismatch between %s and output"),
                  gold_error(_("DIV usage mismatch between %s and output"),
                             name);
                             name);
                }
                }
            }
            }
 
 
          if (in_attr[i].int_value() != 1)
          if (in_attr[i].int_value() != 1)
            out_attr[i].set_int_value(in_attr[i].int_value());
            out_attr[i].set_int_value(in_attr[i].int_value());
 
 
          break;
          break;
 
 
        case elfcpp::Tag_MPextension_use_legacy:
        case elfcpp::Tag_MPextension_use_legacy:
          // We don't output objects with Tag_MPextension_use_legacy - we
          // We don't output objects with Tag_MPextension_use_legacy - we
          // move the value to Tag_MPextension_use.
          // move the value to Tag_MPextension_use.
          if (in_attr[i].int_value() != 0
          if (in_attr[i].int_value() != 0
              && in_attr[elfcpp::Tag_MPextension_use].int_value() != 0)
              && in_attr[elfcpp::Tag_MPextension_use].int_value() != 0)
            {
            {
              if (in_attr[elfcpp::Tag_MPextension_use].int_value()
              if (in_attr[elfcpp::Tag_MPextension_use].int_value()
                  != in_attr[i].int_value())
                  != in_attr[i].int_value())
                {
                {
                  gold_error(_("%s has has both the current and legacy "
                  gold_error(_("%s has has both the current and legacy "
                               "Tag_MPextension_use attributes"),
                               "Tag_MPextension_use attributes"),
                             name);
                             name);
                }
                }
            }
            }
 
 
          if (in_attr[i].int_value()
          if (in_attr[i].int_value()
              > out_attr[elfcpp::Tag_MPextension_use].int_value())
              > out_attr[elfcpp::Tag_MPextension_use].int_value())
            out_attr[elfcpp::Tag_MPextension_use] = in_attr[i];
            out_attr[elfcpp::Tag_MPextension_use] = in_attr[i];
 
 
          break;
          break;
 
 
        case elfcpp::Tag_nodefaults:
        case elfcpp::Tag_nodefaults:
          // This tag is set if it exists, but the value is unused (and is
          // This tag is set if it exists, but the value is unused (and is
          // typically zero).  We don't actually need to do anything here -
          // typically zero).  We don't actually need to do anything here -
          // the merge happens automatically when the type flags are merged
          // the merge happens automatically when the type flags are merged
          // below.
          // below.
          break;
          break;
        case elfcpp::Tag_also_compatible_with:
        case elfcpp::Tag_also_compatible_with:
          // Already done in Tag_CPU_arch.
          // Already done in Tag_CPU_arch.
          break;
          break;
        case elfcpp::Tag_conformance:
        case elfcpp::Tag_conformance:
          // Keep the attribute if it matches.  Throw it away otherwise.
          // Keep the attribute if it matches.  Throw it away otherwise.
          // No attribute means no claim to conform.
          // No attribute means no claim to conform.
          if (in_attr[i].string_value() != out_attr[i].string_value())
          if (in_attr[i].string_value() != out_attr[i].string_value())
            out_attr[i].set_string_value("");
            out_attr[i].set_string_value("");
          break;
          break;
 
 
        default:
        default:
          {
          {
            const char* err_object = NULL;
            const char* err_object = NULL;
 
 
            // The "known_obj_attributes" table does contain some undefined
            // The "known_obj_attributes" table does contain some undefined
            // attributes.  Ensure that there are unused.
            // attributes.  Ensure that there are unused.
            if (out_attr[i].int_value() != 0
            if (out_attr[i].int_value() != 0
                || out_attr[i].string_value() != "")
                || out_attr[i].string_value() != "")
              err_object = "output";
              err_object = "output";
            else if (in_attr[i].int_value() != 0
            else if (in_attr[i].int_value() != 0
                     || in_attr[i].string_value() != "")
                     || in_attr[i].string_value() != "")
              err_object = name;
              err_object = name;
 
 
            if (err_object != NULL
            if (err_object != NULL
                && parameters->options().warn_mismatch())
                && parameters->options().warn_mismatch())
              {
              {
                // Attribute numbers >=64 (mod 128) can be safely ignored.
                // Attribute numbers >=64 (mod 128) can be safely ignored.
                if ((i & 127) < 64)
                if ((i & 127) < 64)
                  gold_error(_("%s: unknown mandatory EABI object attribute "
                  gold_error(_("%s: unknown mandatory EABI object attribute "
                               "%d"),
                               "%d"),
                             err_object, i);
                             err_object, i);
                else
                else
                  gold_warning(_("%s: unknown EABI object attribute %d"),
                  gold_warning(_("%s: unknown EABI object attribute %d"),
                               err_object, i);
                               err_object, i);
              }
              }
 
 
            // Only pass on attributes that match in both inputs.
            // Only pass on attributes that match in both inputs.
            if (!in_attr[i].matches(out_attr[i]))
            if (!in_attr[i].matches(out_attr[i]))
              {
              {
                out_attr[i].set_int_value(0);
                out_attr[i].set_int_value(0);
                out_attr[i].set_string_value("");
                out_attr[i].set_string_value("");
              }
              }
          }
          }
        }
        }
 
 
      // If out_attr was copied from in_attr then it won't have a type yet.
      // If out_attr was copied from in_attr then it won't have a type yet.
      if (in_attr[i].type() && !out_attr[i].type())
      if (in_attr[i].type() && !out_attr[i].type())
        out_attr[i].set_type(in_attr[i].type());
        out_attr[i].set_type(in_attr[i].type());
    }
    }
 
 
  // Merge Tag_compatibility attributes and any common GNU ones.
  // Merge Tag_compatibility attributes and any common GNU ones.
  this->attributes_section_data_->merge(name, pasd);
  this->attributes_section_data_->merge(name, pasd);
 
 
  // Check for any attributes not known on ARM.
  // Check for any attributes not known on ARM.
  typedef Vendor_object_attributes::Other_attributes Other_attributes;
  typedef Vendor_object_attributes::Other_attributes Other_attributes;
  const Other_attributes* in_other_attributes = pasd->other_attributes(vendor);
  const Other_attributes* in_other_attributes = pasd->other_attributes(vendor);
  Other_attributes::const_iterator in_iter = in_other_attributes->begin();
  Other_attributes::const_iterator in_iter = in_other_attributes->begin();
  Other_attributes* out_other_attributes =
  Other_attributes* out_other_attributes =
    this->attributes_section_data_->other_attributes(vendor);
    this->attributes_section_data_->other_attributes(vendor);
  Other_attributes::iterator out_iter = out_other_attributes->begin();
  Other_attributes::iterator out_iter = out_other_attributes->begin();
 
 
  while (in_iter != in_other_attributes->end()
  while (in_iter != in_other_attributes->end()
         || out_iter != out_other_attributes->end())
         || out_iter != out_other_attributes->end())
    {
    {
      const char* err_object = NULL;
      const char* err_object = NULL;
      int err_tag = 0;
      int err_tag = 0;
 
 
      // The tags for each list are in numerical order.
      // The tags for each list are in numerical order.
      // If the tags are equal, then merge.
      // If the tags are equal, then merge.
      if (out_iter != out_other_attributes->end()
      if (out_iter != out_other_attributes->end()
          && (in_iter == in_other_attributes->end()
          && (in_iter == in_other_attributes->end()
              || in_iter->first > out_iter->first))
              || in_iter->first > out_iter->first))
        {
        {
          // This attribute only exists in output.  We can't merge, and we
          // This attribute only exists in output.  We can't merge, and we
          // don't know what the tag means, so delete it.
          // don't know what the tag means, so delete it.
          err_object = "output";
          err_object = "output";
          err_tag = out_iter->first;
          err_tag = out_iter->first;
          int saved_tag = out_iter->first;
          int saved_tag = out_iter->first;
          delete out_iter->second;
          delete out_iter->second;
          out_other_attributes->erase(out_iter);
          out_other_attributes->erase(out_iter);
          out_iter = out_other_attributes->upper_bound(saved_tag);
          out_iter = out_other_attributes->upper_bound(saved_tag);
        }
        }
      else if (in_iter != in_other_attributes->end()
      else if (in_iter != in_other_attributes->end()
               && (out_iter != out_other_attributes->end()
               && (out_iter != out_other_attributes->end()
                   || in_iter->first < out_iter->first))
                   || in_iter->first < out_iter->first))
        {
        {
          // This attribute only exists in input. We can't merge, and we
          // This attribute only exists in input. We can't merge, and we
          // don't know what the tag means, so ignore it.
          // don't know what the tag means, so ignore it.
          err_object = name;
          err_object = name;
          err_tag = in_iter->first;
          err_tag = in_iter->first;
          ++in_iter;
          ++in_iter;
        }
        }
      else // The tags are equal.
      else // The tags are equal.
        {
        {
          // As present, all attributes in the list are unknown, and
          // As present, all attributes in the list are unknown, and
          // therefore can't be merged meaningfully.
          // therefore can't be merged meaningfully.
          err_object = "output";
          err_object = "output";
          err_tag = out_iter->first;
          err_tag = out_iter->first;
 
 
          //  Only pass on attributes that match in both inputs.
          //  Only pass on attributes that match in both inputs.
          if (!in_iter->second->matches(*(out_iter->second)))
          if (!in_iter->second->matches(*(out_iter->second)))
            {
            {
              // No match.  Delete the attribute.
              // No match.  Delete the attribute.
              int saved_tag = out_iter->first;
              int saved_tag = out_iter->first;
              delete out_iter->second;
              delete out_iter->second;
              out_other_attributes->erase(out_iter);
              out_other_attributes->erase(out_iter);
              out_iter = out_other_attributes->upper_bound(saved_tag);
              out_iter = out_other_attributes->upper_bound(saved_tag);
            }
            }
          else
          else
            {
            {
              // Matched.  Keep the attribute and move to the next.
              // Matched.  Keep the attribute and move to the next.
              ++out_iter;
              ++out_iter;
              ++in_iter;
              ++in_iter;
            }
            }
        }
        }
 
 
      if (err_object && parameters->options().warn_mismatch())
      if (err_object && parameters->options().warn_mismatch())
        {
        {
          // Attribute numbers >=64 (mod 128) can be safely ignored.  */
          // Attribute numbers >=64 (mod 128) can be safely ignored.  */
          if ((err_tag & 127) < 64)
          if ((err_tag & 127) < 64)
            {
            {
              gold_error(_("%s: unknown mandatory EABI object attribute %d"),
              gold_error(_("%s: unknown mandatory EABI object attribute %d"),
                         err_object, err_tag);
                         err_object, err_tag);
            }
            }
          else
          else
            {
            {
              gold_warning(_("%s: unknown EABI object attribute %d"),
              gold_warning(_("%s: unknown EABI object attribute %d"),
                           err_object, err_tag);
                           err_object, err_tag);
            }
            }
        }
        }
    }
    }
}
}
 
 
// Stub-generation methods for Target_arm.
// Stub-generation methods for Target_arm.
 
 
// Make a new Arm_input_section object.
// Make a new Arm_input_section object.
 
 
template<bool big_endian>
template<bool big_endian>
Arm_input_section<big_endian>*
Arm_input_section<big_endian>*
Target_arm<big_endian>::new_arm_input_section(
Target_arm<big_endian>::new_arm_input_section(
    Relobj* relobj,
    Relobj* relobj,
    unsigned int shndx)
    unsigned int shndx)
{
{
  Section_id sid(relobj, shndx);
  Section_id sid(relobj, shndx);
 
 
  Arm_input_section<big_endian>* arm_input_section =
  Arm_input_section<big_endian>* arm_input_section =
    new Arm_input_section<big_endian>(relobj, shndx);
    new Arm_input_section<big_endian>(relobj, shndx);
  arm_input_section->init();
  arm_input_section->init();
 
 
  // Register new Arm_input_section in map for look-up.
  // Register new Arm_input_section in map for look-up.
  std::pair<typename Arm_input_section_map::iterator, bool> ins =
  std::pair<typename Arm_input_section_map::iterator, bool> ins =
    this->arm_input_section_map_.insert(std::make_pair(sid, arm_input_section));
    this->arm_input_section_map_.insert(std::make_pair(sid, arm_input_section));
 
 
  // Make sure that it we have not created another Arm_input_section
  // Make sure that it we have not created another Arm_input_section
  // for this input section already.
  // for this input section already.
  gold_assert(ins.second);
  gold_assert(ins.second);
 
 
  return arm_input_section;
  return arm_input_section;
}
}
 
 
// Find the Arm_input_section object corresponding to the SHNDX-th input
// Find the Arm_input_section object corresponding to the SHNDX-th input
// section of RELOBJ.
// section of RELOBJ.
 
 
template<bool big_endian>
template<bool big_endian>
Arm_input_section<big_endian>*
Arm_input_section<big_endian>*
Target_arm<big_endian>::find_arm_input_section(
Target_arm<big_endian>::find_arm_input_section(
    Relobj* relobj,
    Relobj* relobj,
    unsigned int shndx) const
    unsigned int shndx) const
{
{
  Section_id sid(relobj, shndx);
  Section_id sid(relobj, shndx);
  typename Arm_input_section_map::const_iterator p =
  typename Arm_input_section_map::const_iterator p =
    this->arm_input_section_map_.find(sid);
    this->arm_input_section_map_.find(sid);
  return (p != this->arm_input_section_map_.end()) ? p->second : NULL;
  return (p != this->arm_input_section_map_.end()) ? p->second : NULL;
}
}
 
 
// Make a new stub table.
// Make a new stub table.
 
 
template<bool big_endian>
template<bool big_endian>
Stub_table<big_endian>*
Stub_table<big_endian>*
Target_arm<big_endian>::new_stub_table(Arm_input_section<big_endian>* owner)
Target_arm<big_endian>::new_stub_table(Arm_input_section<big_endian>* owner)
{
{
  Stub_table<big_endian>* stub_table =
  Stub_table<big_endian>* stub_table =
    new Stub_table<big_endian>(owner);
    new Stub_table<big_endian>(owner);
  this->stub_tables_.push_back(stub_table);
  this->stub_tables_.push_back(stub_table);
 
 
  stub_table->set_address(owner->address() + owner->data_size());
  stub_table->set_address(owner->address() + owner->data_size());
  stub_table->set_file_offset(owner->offset() + owner->data_size());
  stub_table->set_file_offset(owner->offset() + owner->data_size());
  stub_table->finalize_data_size();
  stub_table->finalize_data_size();
 
 
  return stub_table;
  return stub_table;
}
}
 
 
// Scan a relocation for stub generation.
// Scan a relocation for stub generation.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::scan_reloc_for_stub(
Target_arm<big_endian>::scan_reloc_for_stub(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned int r_type,
    unsigned int r_type,
    const Sized_symbol<32>* gsym,
    const Sized_symbol<32>* gsym,
    unsigned int r_sym,
    unsigned int r_sym,
    const Symbol_value<32>* psymval,
    const Symbol_value<32>* psymval,
    elfcpp::Elf_types<32>::Elf_Swxword addend,
    elfcpp::Elf_types<32>::Elf_Swxword addend,
    Arm_address address)
    Arm_address address)
{
{
  typedef typename Target_arm<big_endian>::Relocate Relocate;
  typedef typename Target_arm<big_endian>::Relocate Relocate;
 
 
  const Arm_relobj<big_endian>* arm_relobj =
  const Arm_relobj<big_endian>* arm_relobj =
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
 
 
  bool target_is_thumb;
  bool target_is_thumb;
  Symbol_value<32> symval;
  Symbol_value<32> symval;
  if (gsym != NULL)
  if (gsym != NULL)
    {
    {
      // This is a global symbol.  Determine if we use PLT and if the
      // This is a global symbol.  Determine if we use PLT and if the
      // final target is THUMB.
      // final target is THUMB.
      if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
      if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
        {
        {
          // This uses a PLT, change the symbol value.
          // This uses a PLT, change the symbol value.
          symval.set_output_value(this->plt_section()->address()
          symval.set_output_value(this->plt_section()->address()
                                  + gsym->plt_offset());
                                  + gsym->plt_offset());
          psymval = &symval;
          psymval = &symval;
          target_is_thumb = false;
          target_is_thumb = false;
        }
        }
      else if (gsym->is_undefined())
      else if (gsym->is_undefined())
        // There is no need to generate a stub symbol is undefined.
        // There is no need to generate a stub symbol is undefined.
        return;
        return;
      else
      else
        {
        {
          target_is_thumb =
          target_is_thumb =
            ((gsym->type() == elfcpp::STT_ARM_TFUNC)
            ((gsym->type() == elfcpp::STT_ARM_TFUNC)
             || (gsym->type() == elfcpp::STT_FUNC
             || (gsym->type() == elfcpp::STT_FUNC
                 && !gsym->is_undefined()
                 && !gsym->is_undefined()
                 && ((psymval->value(arm_relobj, 0) & 1) != 0)));
                 && ((psymval->value(arm_relobj, 0) & 1) != 0)));
        }
        }
    }
    }
  else
  else
    {
    {
      // This is a local symbol.  Determine if the final target is THUMB.
      // This is a local symbol.  Determine if the final target is THUMB.
      target_is_thumb = arm_relobj->local_symbol_is_thumb_function(r_sym);
      target_is_thumb = arm_relobj->local_symbol_is_thumb_function(r_sym);
    }
    }
 
 
  // Strip LSB if this points to a THUMB target.
  // Strip LSB if this points to a THUMB target.
  const Arm_reloc_property* reloc_property =
  const Arm_reloc_property* reloc_property =
    arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
    arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
  gold_assert(reloc_property != NULL);
  gold_assert(reloc_property != NULL);
  if (target_is_thumb
  if (target_is_thumb
      && reloc_property->uses_thumb_bit()
      && reloc_property->uses_thumb_bit()
      && ((psymval->value(arm_relobj, 0) & 1) != 0))
      && ((psymval->value(arm_relobj, 0) & 1) != 0))
    {
    {
      Arm_address stripped_value =
      Arm_address stripped_value =
        psymval->value(arm_relobj, 0) & ~static_cast<Arm_address>(1);
        psymval->value(arm_relobj, 0) & ~static_cast<Arm_address>(1);
      symval.set_output_value(stripped_value);
      symval.set_output_value(stripped_value);
      psymval = &symval;
      psymval = &symval;
    }
    }
 
 
  // Get the symbol value.
  // Get the symbol value.
  Symbol_value<32>::Value value = psymval->value(arm_relobj, 0);
  Symbol_value<32>::Value value = psymval->value(arm_relobj, 0);
 
 
  // Owing to pipelining, the PC relative branches below actually skip
  // Owing to pipelining, the PC relative branches below actually skip
  // two instructions when the branch offset is 0.
  // two instructions when the branch offset is 0.
  Arm_address destination;
  Arm_address destination;
  switch (r_type)
  switch (r_type)
    {
    {
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_CALL:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_JUMP24:
    case elfcpp::R_ARM_PLT32:
    case elfcpp::R_ARM_PLT32:
      // ARM branches.
      // ARM branches.
      destination = value + addend + 8;
      destination = value + addend + 8;
      break;
      break;
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_CALL:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_XPC22:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP24:
    case elfcpp::R_ARM_THM_JUMP19:
    case elfcpp::R_ARM_THM_JUMP19:
      // THUMB branches.
      // THUMB branches.
      destination = value + addend + 4;
      destination = value + addend + 4;
      break;
      break;
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
 
 
  Reloc_stub* stub = NULL;
  Reloc_stub* stub = NULL;
  Stub_type stub_type =
  Stub_type stub_type =
    Reloc_stub::stub_type_for_reloc(r_type, address, destination,
    Reloc_stub::stub_type_for_reloc(r_type, address, destination,
                                    target_is_thumb);
                                    target_is_thumb);
  if (stub_type != arm_stub_none)
  if (stub_type != arm_stub_none)
    {
    {
      // Try looking up an existing stub from a stub table.
      // Try looking up an existing stub from a stub table.
      Stub_table<big_endian>* stub_table =
      Stub_table<big_endian>* stub_table =
        arm_relobj->stub_table(relinfo->data_shndx);
        arm_relobj->stub_table(relinfo->data_shndx);
      gold_assert(stub_table != NULL);
      gold_assert(stub_table != NULL);
 
 
      // Locate stub by destination.
      // Locate stub by destination.
      Reloc_stub::Key stub_key(stub_type, gsym, arm_relobj, r_sym, addend);
      Reloc_stub::Key stub_key(stub_type, gsym, arm_relobj, r_sym, addend);
 
 
      // Create a stub if there is not one already
      // Create a stub if there is not one already
      stub = stub_table->find_reloc_stub(stub_key);
      stub = stub_table->find_reloc_stub(stub_key);
      if (stub == NULL)
      if (stub == NULL)
        {
        {
          // create a new stub and add it to stub table.
          // create a new stub and add it to stub table.
          stub = this->stub_factory().make_reloc_stub(stub_type);
          stub = this->stub_factory().make_reloc_stub(stub_type);
          stub_table->add_reloc_stub(stub, stub_key);
          stub_table->add_reloc_stub(stub, stub_key);
        }
        }
 
 
      // Record the destination address.
      // Record the destination address.
      stub->set_destination_address(destination
      stub->set_destination_address(destination
                                    | (target_is_thumb ? 1 : 0));
                                    | (target_is_thumb ? 1 : 0));
    }
    }
 
 
  // For Cortex-A8, we need to record a relocation at 4K page boundary.
  // For Cortex-A8, we need to record a relocation at 4K page boundary.
  if (this->fix_cortex_a8_
  if (this->fix_cortex_a8_
      && (r_type == elfcpp::R_ARM_THM_JUMP24
      && (r_type == elfcpp::R_ARM_THM_JUMP24
          || r_type == elfcpp::R_ARM_THM_JUMP19
          || r_type == elfcpp::R_ARM_THM_JUMP19
          || r_type == elfcpp::R_ARM_THM_CALL
          || r_type == elfcpp::R_ARM_THM_CALL
          || r_type == elfcpp::R_ARM_THM_XPC22)
          || r_type == elfcpp::R_ARM_THM_XPC22)
      && (address & 0xfffU) == 0xffeU)
      && (address & 0xfffU) == 0xffeU)
    {
    {
      // Found a candidate.  Note we haven't checked the destination is
      // Found a candidate.  Note we haven't checked the destination is
      // within 4K here: if we do so (and don't create a record) we can't
      // within 4K here: if we do so (and don't create a record) we can't
      // tell that a branch should have been relocated when scanning later.
      // tell that a branch should have been relocated when scanning later.
      this->cortex_a8_relocs_info_[address] =
      this->cortex_a8_relocs_info_[address] =
        new Cortex_a8_reloc(stub, r_type,
        new Cortex_a8_reloc(stub, r_type,
                            destination | (target_is_thumb ? 1 : 0));
                            destination | (target_is_thumb ? 1 : 0));
    }
    }
}
}
 
 
// This function scans a relocation sections for stub generation.
// This function scans a relocation sections for stub generation.
// The template parameter Relocate must be a class type which provides
// The template parameter Relocate must be a class type which provides
// a single function, relocate(), which implements the machine
// a single function, relocate(), which implements the machine
// specific part of a relocation.
// specific part of a relocation.
 
 
// BIG_ENDIAN is the endianness of the data.  SH_TYPE is the section type:
// BIG_ENDIAN is the endianness of the data.  SH_TYPE is the section type:
// SHT_REL or SHT_RELA.
// SHT_REL or SHT_RELA.
 
 
// PRELOCS points to the relocation data.  RELOC_COUNT is the number
// PRELOCS points to the relocation data.  RELOC_COUNT is the number
// of relocs.  OUTPUT_SECTION is the output section.
// of relocs.  OUTPUT_SECTION is the output section.
// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be
// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be
// mapped to output offsets.
// mapped to output offsets.
 
 
// VIEW is the section data, VIEW_ADDRESS is its memory address, and
// VIEW is the section data, VIEW_ADDRESS is its memory address, and
// VIEW_SIZE is the size.  These refer to the input section, unless
// VIEW_SIZE is the size.  These refer to the input section, unless
// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to
// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to
// the output section.
// the output section.
 
 
template<bool big_endian>
template<bool big_endian>
template<int sh_type>
template<int sh_type>
void inline
void inline
Target_arm<big_endian>::scan_reloc_section_for_stubs(
Target_arm<big_endian>::scan_reloc_section_for_stubs(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    const unsigned char* prelocs,
    const unsigned char* prelocs,
    size_t reloc_count,
    size_t reloc_count,
    Output_section* output_section,
    Output_section* output_section,
    bool needs_special_offset_handling,
    bool needs_special_offset_handling,
    const unsigned char* view,
    const unsigned char* view,
    elfcpp::Elf_types<32>::Elf_Addr view_address,
    elfcpp::Elf_types<32>::Elf_Addr view_address,
    section_size_type)
    section_size_type)
{
{
  typedef typename Reloc_types<sh_type, 32, big_endian>::Reloc Reltype;
  typedef typename Reloc_types<sh_type, 32, big_endian>::Reloc Reltype;
  const int reloc_size =
  const int reloc_size =
    Reloc_types<sh_type, 32, big_endian>::reloc_size;
    Reloc_types<sh_type, 32, big_endian>::reloc_size;
 
 
  Arm_relobj<big_endian>* arm_object =
  Arm_relobj<big_endian>* arm_object =
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
    Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
  unsigned int local_count = arm_object->local_symbol_count();
  unsigned int local_count = arm_object->local_symbol_count();
 
 
  Comdat_behavior comdat_behavior = CB_UNDETERMINED;
  Comdat_behavior comdat_behavior = CB_UNDETERMINED;
 
 
  for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
  for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
    {
    {
      Reltype reloc(prelocs);
      Reltype reloc(prelocs);
 
 
      typename elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info();
      typename elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info();
      unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
      unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
      unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
      unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
 
 
      r_type = this->get_real_reloc_type(r_type);
      r_type = this->get_real_reloc_type(r_type);
 
 
      // Only a few relocation types need stubs.
      // Only a few relocation types need stubs.
      if ((r_type != elfcpp::R_ARM_CALL)
      if ((r_type != elfcpp::R_ARM_CALL)
         && (r_type != elfcpp::R_ARM_JUMP24)
         && (r_type != elfcpp::R_ARM_JUMP24)
         && (r_type != elfcpp::R_ARM_PLT32)
         && (r_type != elfcpp::R_ARM_PLT32)
         && (r_type != elfcpp::R_ARM_THM_CALL)
         && (r_type != elfcpp::R_ARM_THM_CALL)
         && (r_type != elfcpp::R_ARM_THM_XPC22)
         && (r_type != elfcpp::R_ARM_THM_XPC22)
         && (r_type != elfcpp::R_ARM_THM_JUMP24)
         && (r_type != elfcpp::R_ARM_THM_JUMP24)
         && (r_type != elfcpp::R_ARM_THM_JUMP19)
         && (r_type != elfcpp::R_ARM_THM_JUMP19)
         && (r_type != elfcpp::R_ARM_V4BX))
         && (r_type != elfcpp::R_ARM_V4BX))
        continue;
        continue;
 
 
      section_offset_type offset =
      section_offset_type offset =
        convert_to_section_size_type(reloc.get_r_offset());
        convert_to_section_size_type(reloc.get_r_offset());
 
 
      if (needs_special_offset_handling)
      if (needs_special_offset_handling)
        {
        {
          offset = output_section->output_offset(relinfo->object,
          offset = output_section->output_offset(relinfo->object,
                                                 relinfo->data_shndx,
                                                 relinfo->data_shndx,
                                                 offset);
                                                 offset);
          if (offset == -1)
          if (offset == -1)
            continue;
            continue;
        }
        }
 
 
      // Create a v4bx stub if --fix-v4bx-interworking is used.
      // Create a v4bx stub if --fix-v4bx-interworking is used.
      if (r_type == elfcpp::R_ARM_V4BX)
      if (r_type == elfcpp::R_ARM_V4BX)
        {
        {
          if (this->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING)
          if (this->fix_v4bx() == General_options::FIX_V4BX_INTERWORKING)
            {
            {
              // Get the BX instruction.
              // Get the BX instruction.
              typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
              typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
              const Valtype* wv =
              const Valtype* wv =
                reinterpret_cast<const Valtype*>(view + offset);
                reinterpret_cast<const Valtype*>(view + offset);
              elfcpp::Elf_types<32>::Elf_Swxword insn =
              elfcpp::Elf_types<32>::Elf_Swxword insn =
                elfcpp::Swap<32, big_endian>::readval(wv);
                elfcpp::Swap<32, big_endian>::readval(wv);
              const uint32_t reg = (insn & 0xf);
              const uint32_t reg = (insn & 0xf);
 
 
              if (reg < 0xf)
              if (reg < 0xf)
                {
                {
                  // Try looking up an existing stub from a stub table.
                  // Try looking up an existing stub from a stub table.
                  Stub_table<big_endian>* stub_table =
                  Stub_table<big_endian>* stub_table =
                    arm_object->stub_table(relinfo->data_shndx);
                    arm_object->stub_table(relinfo->data_shndx);
                  gold_assert(stub_table != NULL);
                  gold_assert(stub_table != NULL);
 
 
                  if (stub_table->find_arm_v4bx_stub(reg) == NULL)
                  if (stub_table->find_arm_v4bx_stub(reg) == NULL)
                    {
                    {
                      // create a new stub and add it to stub table.
                      // create a new stub and add it to stub table.
                      Arm_v4bx_stub* stub =
                      Arm_v4bx_stub* stub =
                        this->stub_factory().make_arm_v4bx_stub(reg);
                        this->stub_factory().make_arm_v4bx_stub(reg);
                      gold_assert(stub != NULL);
                      gold_assert(stub != NULL);
                      stub_table->add_arm_v4bx_stub(stub);
                      stub_table->add_arm_v4bx_stub(stub);
                    }
                    }
                }
                }
            }
            }
          continue;
          continue;
        }
        }
 
 
      // Get the addend.
      // Get the addend.
      Stub_addend_reader<sh_type, big_endian> stub_addend_reader;
      Stub_addend_reader<sh_type, big_endian> stub_addend_reader;
      elfcpp::Elf_types<32>::Elf_Swxword addend =
      elfcpp::Elf_types<32>::Elf_Swxword addend =
        stub_addend_reader(r_type, view + offset, reloc);
        stub_addend_reader(r_type, view + offset, reloc);
 
 
      const Sized_symbol<32>* sym;
      const Sized_symbol<32>* sym;
 
 
      Symbol_value<32> symval;
      Symbol_value<32> symval;
      const Symbol_value<32> *psymval;
      const Symbol_value<32> *psymval;
      bool is_defined_in_discarded_section;
      bool is_defined_in_discarded_section;
      unsigned int shndx;
      unsigned int shndx;
      if (r_sym < local_count)
      if (r_sym < local_count)
        {
        {
          sym = NULL;
          sym = NULL;
          psymval = arm_object->local_symbol(r_sym);
          psymval = arm_object->local_symbol(r_sym);
 
 
          // If the local symbol belongs to a section we are discarding,
          // If the local symbol belongs to a section we are discarding,
          // and that section is a debug section, try to find the
          // and that section is a debug section, try to find the
          // corresponding kept section and map this symbol to its
          // corresponding kept section and map this symbol to its
          // counterpart in the kept section.  The symbol must not 
          // counterpart in the kept section.  The symbol must not 
          // correspond to a section we are folding.
          // correspond to a section we are folding.
          bool is_ordinary;
          bool is_ordinary;
          shndx = psymval->input_shndx(&is_ordinary);
          shndx = psymval->input_shndx(&is_ordinary);
          is_defined_in_discarded_section =
          is_defined_in_discarded_section =
            (is_ordinary
            (is_ordinary
             && shndx != elfcpp::SHN_UNDEF
             && shndx != elfcpp::SHN_UNDEF
             && !arm_object->is_section_included(shndx)
             && !arm_object->is_section_included(shndx)
             && !relinfo->symtab->is_section_folded(arm_object, shndx));
             && !relinfo->symtab->is_section_folded(arm_object, shndx));
 
 
          // We need to compute the would-be final value of this local
          // We need to compute the would-be final value of this local
          // symbol.
          // symbol.
          if (!is_defined_in_discarded_section)
          if (!is_defined_in_discarded_section)
            {
            {
              typedef Sized_relobj_file<32, big_endian> ObjType;
              typedef Sized_relobj_file<32, big_endian> ObjType;
              typename ObjType::Compute_final_local_value_status status =
              typename ObjType::Compute_final_local_value_status status =
                arm_object->compute_final_local_value(r_sym, psymval, &symval,
                arm_object->compute_final_local_value(r_sym, psymval, &symval,
                                                      relinfo->symtab);
                                                      relinfo->symtab);
              if (status == ObjType::CFLV_OK)
              if (status == ObjType::CFLV_OK)
                {
                {
                  // Currently we cannot handle a branch to a target in
                  // Currently we cannot handle a branch to a target in
                  // a merged section.  If this is the case, issue an error
                  // a merged section.  If this is the case, issue an error
                  // and also free the merge symbol value.
                  // and also free the merge symbol value.
                  if (!symval.has_output_value())
                  if (!symval.has_output_value())
                    {
                    {
                      const std::string& section_name =
                      const std::string& section_name =
                        arm_object->section_name(shndx);
                        arm_object->section_name(shndx);
                      arm_object->error(_("cannot handle branch to local %u "
                      arm_object->error(_("cannot handle branch to local %u "
                                          "in a merged section %s"),
                                          "in a merged section %s"),
                                        r_sym, section_name.c_str());
                                        r_sym, section_name.c_str());
                    }
                    }
                  psymval = &symval;
                  psymval = &symval;
                }
                }
              else
              else
                {
                {
                  // We cannot determine the final value.
                  // We cannot determine the final value.
                  continue;
                  continue;
                }
                }
            }
            }
        }
        }
      else
      else
        {
        {
          const Symbol* gsym;
          const Symbol* gsym;
          gsym = arm_object->global_symbol(r_sym);
          gsym = arm_object->global_symbol(r_sym);
          gold_assert(gsym != NULL);
          gold_assert(gsym != NULL);
          if (gsym->is_forwarder())
          if (gsym->is_forwarder())
            gsym = relinfo->symtab->resolve_forwards(gsym);
            gsym = relinfo->symtab->resolve_forwards(gsym);
 
 
          sym = static_cast<const Sized_symbol<32>*>(gsym);
          sym = static_cast<const Sized_symbol<32>*>(gsym);
          if (sym->has_symtab_index() && sym->symtab_index() != -1U)
          if (sym->has_symtab_index() && sym->symtab_index() != -1U)
            symval.set_output_symtab_index(sym->symtab_index());
            symval.set_output_symtab_index(sym->symtab_index());
          else
          else
            symval.set_no_output_symtab_entry();
            symval.set_no_output_symtab_entry();
 
 
          // We need to compute the would-be final value of this global
          // We need to compute the would-be final value of this global
          // symbol.
          // symbol.
          const Symbol_table* symtab = relinfo->symtab;
          const Symbol_table* symtab = relinfo->symtab;
          const Sized_symbol<32>* sized_symbol =
          const Sized_symbol<32>* sized_symbol =
            symtab->get_sized_symbol<32>(gsym);
            symtab->get_sized_symbol<32>(gsym);
          Symbol_table::Compute_final_value_status status;
          Symbol_table::Compute_final_value_status status;
          Arm_address value =
          Arm_address value =
            symtab->compute_final_value<32>(sized_symbol, &status);
            symtab->compute_final_value<32>(sized_symbol, &status);
 
 
          // Skip this if the symbol has not output section.
          // Skip this if the symbol has not output section.
          if (status == Symbol_table::CFVS_NO_OUTPUT_SECTION)
          if (status == Symbol_table::CFVS_NO_OUTPUT_SECTION)
            continue;
            continue;
          symval.set_output_value(value);
          symval.set_output_value(value);
 
 
          if (gsym->type() == elfcpp::STT_TLS)
          if (gsym->type() == elfcpp::STT_TLS)
            symval.set_is_tls_symbol();
            symval.set_is_tls_symbol();
          else if (gsym->type() == elfcpp::STT_GNU_IFUNC)
          else if (gsym->type() == elfcpp::STT_GNU_IFUNC)
            symval.set_is_ifunc_symbol();
            symval.set_is_ifunc_symbol();
          psymval = &symval;
          psymval = &symval;
 
 
          is_defined_in_discarded_section =
          is_defined_in_discarded_section =
            (gsym->is_defined_in_discarded_section()
            (gsym->is_defined_in_discarded_section()
             && gsym->is_undefined());
             && gsym->is_undefined());
          shndx = 0;
          shndx = 0;
        }
        }
 
 
      Symbol_value<32> symval2;
      Symbol_value<32> symval2;
      if (is_defined_in_discarded_section)
      if (is_defined_in_discarded_section)
        {
        {
          if (comdat_behavior == CB_UNDETERMINED)
          if (comdat_behavior == CB_UNDETERMINED)
            {
            {
              std::string name = arm_object->section_name(relinfo->data_shndx);
              std::string name = arm_object->section_name(relinfo->data_shndx);
              comdat_behavior = get_comdat_behavior(name.c_str());
              comdat_behavior = get_comdat_behavior(name.c_str());
            }
            }
          if (comdat_behavior == CB_PRETEND)
          if (comdat_behavior == CB_PRETEND)
            {
            {
              // FIXME: This case does not work for global symbols.
              // FIXME: This case does not work for global symbols.
              // We have no place to store the original section index.
              // We have no place to store the original section index.
              // Fortunately this does not matter for comdat sections,
              // Fortunately this does not matter for comdat sections,
              // only for sections explicitly discarded by a linker
              // only for sections explicitly discarded by a linker
              // script.
              // script.
              bool found;
              bool found;
              typename elfcpp::Elf_types<32>::Elf_Addr value =
              typename elfcpp::Elf_types<32>::Elf_Addr value =
                arm_object->map_to_kept_section(shndx, &found);
                arm_object->map_to_kept_section(shndx, &found);
              if (found)
              if (found)
                symval2.set_output_value(value + psymval->input_value());
                symval2.set_output_value(value + psymval->input_value());
              else
              else
                symval2.set_output_value(0);
                symval2.set_output_value(0);
            }
            }
          else
          else
            {
            {
              if (comdat_behavior == CB_WARNING)
              if (comdat_behavior == CB_WARNING)
                gold_warning_at_location(relinfo, i, offset,
                gold_warning_at_location(relinfo, i, offset,
                                         _("relocation refers to discarded "
                                         _("relocation refers to discarded "
                                           "section"));
                                           "section"));
              symval2.set_output_value(0);
              symval2.set_output_value(0);
            }
            }
          symval2.set_no_output_symtab_entry();
          symval2.set_no_output_symtab_entry();
          psymval = &symval2;
          psymval = &symval2;
        }
        }
 
 
      // If symbol is a section symbol, we don't know the actual type of
      // If symbol is a section symbol, we don't know the actual type of
      // destination.  Give up.
      // destination.  Give up.
      if (psymval->is_section_symbol())
      if (psymval->is_section_symbol())
        continue;
        continue;
 
 
      this->scan_reloc_for_stub(relinfo, r_type, sym, r_sym, psymval,
      this->scan_reloc_for_stub(relinfo, r_type, sym, r_sym, psymval,
                                addend, view_address + offset);
                                addend, view_address + offset);
    }
    }
}
}
 
 
// Scan an input section for stub generation.
// Scan an input section for stub generation.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::scan_section_for_stubs(
Target_arm<big_endian>::scan_section_for_stubs(
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    unsigned int sh_type,
    unsigned int sh_type,
    const unsigned char* prelocs,
    const unsigned char* prelocs,
    size_t reloc_count,
    size_t reloc_count,
    Output_section* output_section,
    Output_section* output_section,
    bool needs_special_offset_handling,
    bool needs_special_offset_handling,
    const unsigned char* view,
    const unsigned char* view,
    Arm_address view_address,
    Arm_address view_address,
    section_size_type view_size)
    section_size_type view_size)
{
{
  if (sh_type == elfcpp::SHT_REL)
  if (sh_type == elfcpp::SHT_REL)
    this->scan_reloc_section_for_stubs<elfcpp::SHT_REL>(
    this->scan_reloc_section_for_stubs<elfcpp::SHT_REL>(
        relinfo,
        relinfo,
        prelocs,
        prelocs,
        reloc_count,
        reloc_count,
        output_section,
        output_section,
        needs_special_offset_handling,
        needs_special_offset_handling,
        view,
        view,
        view_address,
        view_address,
        view_size);
        view_size);
  else if (sh_type == elfcpp::SHT_RELA)
  else if (sh_type == elfcpp::SHT_RELA)
    // We do not support RELA type relocations yet.  This is provided for
    // We do not support RELA type relocations yet.  This is provided for
    // completeness.
    // completeness.
    this->scan_reloc_section_for_stubs<elfcpp::SHT_RELA>(
    this->scan_reloc_section_for_stubs<elfcpp::SHT_RELA>(
        relinfo,
        relinfo,
        prelocs,
        prelocs,
        reloc_count,
        reloc_count,
        output_section,
        output_section,
        needs_special_offset_handling,
        needs_special_offset_handling,
        view,
        view,
        view_address,
        view_address,
        view_size);
        view_size);
  else
  else
    gold_unreachable();
    gold_unreachable();
}
}
 
 
// Group input sections for stub generation.
// Group input sections for stub generation.
//
//
// We group input sections in an output section so that the total size,
// We group input sections in an output section so that the total size,
// including any padding space due to alignment is smaller than GROUP_SIZE
// including any padding space due to alignment is smaller than GROUP_SIZE
// unless the only input section in group is bigger than GROUP_SIZE already.
// unless the only input section in group is bigger than GROUP_SIZE already.
// Then an ARM stub table is created to follow the last input section
// Then an ARM stub table is created to follow the last input section
// in group.  For each group an ARM stub table is created an is placed
// in group.  For each group an ARM stub table is created an is placed
// after the last group.  If STUB_ALWAYS_AFTER_BRANCH is false, we further
// after the last group.  If STUB_ALWAYS_AFTER_BRANCH is false, we further
// extend the group after the stub table.
// extend the group after the stub table.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::group_sections(
Target_arm<big_endian>::group_sections(
    Layout* layout,
    Layout* layout,
    section_size_type group_size,
    section_size_type group_size,
    bool stubs_always_after_branch,
    bool stubs_always_after_branch,
    const Task* task)
    const Task* task)
{
{
  // Group input sections and insert stub table
  // Group input sections and insert stub table
  Layout::Section_list section_list;
  Layout::Section_list section_list;
  layout->get_allocated_sections(&section_list);
  layout->get_allocated_sections(&section_list);
  for (Layout::Section_list::const_iterator p = section_list.begin();
  for (Layout::Section_list::const_iterator p = section_list.begin();
       p != section_list.end();
       p != section_list.end();
       ++p)
       ++p)
    {
    {
      Arm_output_section<big_endian>* output_section =
      Arm_output_section<big_endian>* output_section =
        Arm_output_section<big_endian>::as_arm_output_section(*p);
        Arm_output_section<big_endian>::as_arm_output_section(*p);
      output_section->group_sections(group_size, stubs_always_after_branch,
      output_section->group_sections(group_size, stubs_always_after_branch,
                                     this, task);
                                     this, task);
    }
    }
}
}
 
 
// Relaxation hook.  This is where we do stub generation.
// Relaxation hook.  This is where we do stub generation.
 
 
template<bool big_endian>
template<bool big_endian>
bool
bool
Target_arm<big_endian>::do_relax(
Target_arm<big_endian>::do_relax(
    int pass,
    int pass,
    const Input_objects* input_objects,
    const Input_objects* input_objects,
    Symbol_table* symtab,
    Symbol_table* symtab,
    Layout* layout,
    Layout* layout,
    const Task* task)
    const Task* task)
{
{
  // No need to generate stubs if this is a relocatable link.
  // No need to generate stubs if this is a relocatable link.
  gold_assert(!parameters->options().relocatable());
  gold_assert(!parameters->options().relocatable());
 
 
  // If this is the first pass, we need to group input sections into
  // If this is the first pass, we need to group input sections into
  // stub groups.
  // stub groups.
  bool done_exidx_fixup = false;
  bool done_exidx_fixup = false;
  typedef typename Stub_table_list::iterator Stub_table_iterator;
  typedef typename Stub_table_list::iterator Stub_table_iterator;
  if (pass == 1)
  if (pass == 1)
    {
    {
      // Determine the stub group size.  The group size is the absolute
      // Determine the stub group size.  The group size is the absolute
      // value of the parameter --stub-group-size.  If --stub-group-size
      // value of the parameter --stub-group-size.  If --stub-group-size
      // is passed a negative value, we restrict stubs to be always after
      // is passed a negative value, we restrict stubs to be always after
      // the stubbed branches.
      // the stubbed branches.
      int32_t stub_group_size_param =
      int32_t stub_group_size_param =
        parameters->options().stub_group_size();
        parameters->options().stub_group_size();
      bool stubs_always_after_branch = stub_group_size_param < 0;
      bool stubs_always_after_branch = stub_group_size_param < 0;
      section_size_type stub_group_size = abs(stub_group_size_param);
      section_size_type stub_group_size = abs(stub_group_size_param);
 
 
      if (stub_group_size == 1)
      if (stub_group_size == 1)
        {
        {
          // Default value.
          // Default value.
          // Thumb branch range is +-4MB has to be used as the default
          // Thumb branch range is +-4MB has to be used as the default
          // maximum size (a given section can contain both ARM and Thumb
          // maximum size (a given section can contain both ARM and Thumb
          // code, so the worst case has to be taken into account).  If we are
          // 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,
          // fixing cortex-a8 errata, the branch range has to be even smaller,
          // since wide conditional branch has a range of +-1MB only.
          // since wide conditional branch has a range of +-1MB only.
          //
          //
          // This value is 48K less than that, which allows for 4096
          // This value is 48K less than that, which allows for 4096
          // 12-byte stubs.  If we exceed that, then we will fail to link.
          // 12-byte stubs.  If we exceed that, then we will fail to link.
          // The user will have to relink with an explicit group size
          // The user will have to relink with an explicit group size
          // option.
          // option.
            stub_group_size = 4145152;
            stub_group_size = 4145152;
        }
        }
 
 
      // The Cortex-A8 erratum fix depends on stubs not being in the same 4K
      // The Cortex-A8 erratum fix depends on stubs not being in the same 4K
      // page as the first half of a 32-bit branch straddling two 4K pages.
      // page as the first half of a 32-bit branch straddling two 4K pages.
      // This is a crude way of enforcing that.  In addition, long conditional
      // This is a crude way of enforcing that.  In addition, long conditional
      // branches of THUMB-2 have a range of +-1M.  If we are fixing cortex-A8
      // branches of THUMB-2 have a range of +-1M.  If we are fixing cortex-A8
      // erratum, limit the group size to  (1M - 12k) to avoid unreachable
      // erratum, limit the group size to  (1M - 12k) to avoid unreachable
      // cortex-A8 stubs from long conditional branches.
      // cortex-A8 stubs from long conditional branches.
      if (this->fix_cortex_a8_)
      if (this->fix_cortex_a8_)
        {
        {
          stubs_always_after_branch = true;
          stubs_always_after_branch = true;
          const section_size_type cortex_a8_group_size = 1024 * (1024 - 12);
          const section_size_type cortex_a8_group_size = 1024 * (1024 - 12);
          stub_group_size = std::max(stub_group_size, cortex_a8_group_size);
          stub_group_size = std::max(stub_group_size, cortex_a8_group_size);
        }
        }
 
 
      group_sections(layout, stub_group_size, stubs_always_after_branch, task);
      group_sections(layout, stub_group_size, stubs_always_after_branch, task);
 
 
      // Also fix .ARM.exidx section coverage.
      // Also fix .ARM.exidx section coverage.
      Arm_output_section<big_endian>* exidx_output_section = NULL;
      Arm_output_section<big_endian>* exidx_output_section = NULL;
      for (Layout::Section_list::const_iterator p =
      for (Layout::Section_list::const_iterator p =
             layout->section_list().begin();
             layout->section_list().begin();
           p != layout->section_list().end();
           p != layout->section_list().end();
           ++p)
           ++p)
        if ((*p)->type() == elfcpp::SHT_ARM_EXIDX)
        if ((*p)->type() == elfcpp::SHT_ARM_EXIDX)
          {
          {
            if (exidx_output_section == NULL)
            if (exidx_output_section == NULL)
              exidx_output_section =
              exidx_output_section =
                Arm_output_section<big_endian>::as_arm_output_section(*p);
                Arm_output_section<big_endian>::as_arm_output_section(*p);
            else
            else
              // We cannot handle this now.
              // We cannot handle this now.
              gold_error(_("multiple SHT_ARM_EXIDX sections %s and %s in a "
              gold_error(_("multiple SHT_ARM_EXIDX sections %s and %s in a "
                           "non-relocatable link"),
                           "non-relocatable link"),
                          exidx_output_section->name(),
                          exidx_output_section->name(),
                          (*p)->name());
                          (*p)->name());
          }
          }
 
 
      if (exidx_output_section != NULL)
      if (exidx_output_section != NULL)
        {
        {
          this->fix_exidx_coverage(layout, input_objects, exidx_output_section,
          this->fix_exidx_coverage(layout, input_objects, exidx_output_section,
                                   symtab, task);
                                   symtab, task);
          done_exidx_fixup = true;
          done_exidx_fixup = true;
        }
        }
    }
    }
  else
  else
    {
    {
      // If this is not the first pass, addresses and file offsets have
      // If this is not the first pass, addresses and file offsets have
      // been reset at this point, set them here.
      // been reset at this point, set them here.
      for (Stub_table_iterator sp = this->stub_tables_.begin();
      for (Stub_table_iterator sp = this->stub_tables_.begin();
           sp != this->stub_tables_.end();
           sp != this->stub_tables_.end();
           ++sp)
           ++sp)
        {
        {
          Arm_input_section<big_endian>* owner = (*sp)->owner();
          Arm_input_section<big_endian>* owner = (*sp)->owner();
          off_t off = align_address(owner->original_size(),
          off_t off = align_address(owner->original_size(),
                                    (*sp)->addralign());
                                    (*sp)->addralign());
          (*sp)->set_address_and_file_offset(owner->address() + off,
          (*sp)->set_address_and_file_offset(owner->address() + off,
                                             owner->offset() + off);
                                             owner->offset() + off);
        }
        }
    }
    }
 
 
  // The Cortex-A8 stubs are sensitive to layout of code sections.  At the
  // The Cortex-A8 stubs are sensitive to layout of code sections.  At the
  // beginning of each relaxation pass, just blow away all the stubs.
  // beginning of each relaxation pass, just blow away all the stubs.
  // Alternatively, we could selectively remove only the stubs and reloc
  // Alternatively, we could selectively remove only the stubs and reloc
  // information for code sections that have moved since the last pass.
  // information for code sections that have moved since the last pass.
  // That would require more book-keeping.
  // That would require more book-keeping.
  if (this->fix_cortex_a8_)
  if (this->fix_cortex_a8_)
    {
    {
      // Clear all Cortex-A8 reloc information.
      // Clear all Cortex-A8 reloc information.
      for (typename Cortex_a8_relocs_info::const_iterator p =
      for (typename Cortex_a8_relocs_info::const_iterator p =
             this->cortex_a8_relocs_info_.begin();
             this->cortex_a8_relocs_info_.begin();
           p != this->cortex_a8_relocs_info_.end();
           p != this->cortex_a8_relocs_info_.end();
           ++p)
           ++p)
        delete p->second;
        delete p->second;
      this->cortex_a8_relocs_info_.clear();
      this->cortex_a8_relocs_info_.clear();
 
 
      // Remove all Cortex-A8 stubs.
      // Remove all Cortex-A8 stubs.
      for (Stub_table_iterator sp = this->stub_tables_.begin();
      for (Stub_table_iterator sp = this->stub_tables_.begin();
           sp != this->stub_tables_.end();
           sp != this->stub_tables_.end();
           ++sp)
           ++sp)
        (*sp)->remove_all_cortex_a8_stubs();
        (*sp)->remove_all_cortex_a8_stubs();
    }
    }
 
 
  // Scan relocs for relocation stubs
  // Scan relocs for relocation stubs
  for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
  for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
       op != input_objects->relobj_end();
       op != input_objects->relobj_end();
       ++op)
       ++op)
    {
    {
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(*op);
        Arm_relobj<big_endian>::as_arm_relobj(*op);
      // Lock the object so we can read from it.  This is only called
      // Lock the object so we can read from it.  This is only called
      // single-threaded from Layout::finalize, so it is OK to lock.
      // single-threaded from Layout::finalize, so it is OK to lock.
      Task_lock_obj<Object> tl(task, arm_relobj);
      Task_lock_obj<Object> tl(task, arm_relobj);
      arm_relobj->scan_sections_for_stubs(this, symtab, layout);
      arm_relobj->scan_sections_for_stubs(this, symtab, layout);
    }
    }
 
 
  // Check all stub tables to see if any of them have their data sizes
  // Check all stub tables to see if any of them have their data sizes
  // or addresses alignments changed.  These are the only things that
  // or addresses alignments changed.  These are the only things that
  // matter.
  // matter.
  bool any_stub_table_changed = false;
  bool any_stub_table_changed = false;
  Unordered_set<const Output_section*> sections_needing_adjustment;
  Unordered_set<const Output_section*> sections_needing_adjustment;
  for (Stub_table_iterator sp = this->stub_tables_.begin();
  for (Stub_table_iterator sp = this->stub_tables_.begin();
       (sp != this->stub_tables_.end()) && !any_stub_table_changed;
       (sp != this->stub_tables_.end()) && !any_stub_table_changed;
       ++sp)
       ++sp)
    {
    {
      if ((*sp)->update_data_size_and_addralign())
      if ((*sp)->update_data_size_and_addralign())
        {
        {
          // Update data size of stub table owner.
          // Update data size of stub table owner.
          Arm_input_section<big_endian>* owner = (*sp)->owner();
          Arm_input_section<big_endian>* owner = (*sp)->owner();
          uint64_t address = owner->address();
          uint64_t address = owner->address();
          off_t offset = owner->offset();
          off_t offset = owner->offset();
          owner->reset_address_and_file_offset();
          owner->reset_address_and_file_offset();
          owner->set_address_and_file_offset(address, offset);
          owner->set_address_and_file_offset(address, offset);
 
 
          sections_needing_adjustment.insert(owner->output_section());
          sections_needing_adjustment.insert(owner->output_section());
          any_stub_table_changed = true;
          any_stub_table_changed = true;
        }
        }
    }
    }
 
 
  // Output_section_data::output_section() returns a const pointer but we
  // Output_section_data::output_section() returns a const pointer but we
  // need to update output sections, so we record all output sections needing
  // need to update output sections, so we record all output sections needing
  // update above and scan the sections here to find out what sections need
  // update above and scan the sections here to find out what sections need
  // to be updated.
  // to be updated.
  for (Layout::Section_list::const_iterator p = layout->section_list().begin();
  for (Layout::Section_list::const_iterator p = layout->section_list().begin();
      p != layout->section_list().end();
      p != layout->section_list().end();
      ++p)
      ++p)
    {
    {
      if (sections_needing_adjustment.find(*p)
      if (sections_needing_adjustment.find(*p)
          != sections_needing_adjustment.end())
          != sections_needing_adjustment.end())
        (*p)->set_section_offsets_need_adjustment();
        (*p)->set_section_offsets_need_adjustment();
    }
    }
 
 
  // Stop relaxation if no EXIDX fix-up and no stub table change.
  // Stop relaxation if no EXIDX fix-up and no stub table change.
  bool continue_relaxation = done_exidx_fixup || any_stub_table_changed;
  bool continue_relaxation = done_exidx_fixup || any_stub_table_changed;
 
 
  // Finalize the stubs in the last relaxation pass.
  // Finalize the stubs in the last relaxation pass.
  if (!continue_relaxation)
  if (!continue_relaxation)
    {
    {
      for (Stub_table_iterator sp = this->stub_tables_.begin();
      for (Stub_table_iterator sp = this->stub_tables_.begin();
           (sp != this->stub_tables_.end()) && !any_stub_table_changed;
           (sp != this->stub_tables_.end()) && !any_stub_table_changed;
            ++sp)
            ++sp)
        (*sp)->finalize_stubs();
        (*sp)->finalize_stubs();
 
 
      // Update output local symbol counts of objects if necessary.
      // Update output local symbol counts of objects if necessary.
      for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
      for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
           op != input_objects->relobj_end();
           op != input_objects->relobj_end();
           ++op)
           ++op)
        {
        {
          Arm_relobj<big_endian>* arm_relobj =
          Arm_relobj<big_endian>* arm_relobj =
            Arm_relobj<big_endian>::as_arm_relobj(*op);
            Arm_relobj<big_endian>::as_arm_relobj(*op);
 
 
          // Update output local symbol counts.  We need to discard local
          // Update output local symbol counts.  We need to discard local
          // symbols defined in parts of input sections that are discarded by
          // symbols defined in parts of input sections that are discarded by
          // relaxation.
          // relaxation.
          if (arm_relobj->output_local_symbol_count_needs_update())
          if (arm_relobj->output_local_symbol_count_needs_update())
            {
            {
              // We need to lock the object's file to update it.
              // We need to lock the object's file to update it.
              Task_lock_obj<Object> tl(task, arm_relobj);
              Task_lock_obj<Object> tl(task, arm_relobj);
              arm_relobj->update_output_local_symbol_count();
              arm_relobj->update_output_local_symbol_count();
            }
            }
        }
        }
    }
    }
 
 
  return continue_relaxation;
  return continue_relaxation;
}
}
 
 
// Relocate a stub.
// Relocate a stub.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::relocate_stub(
Target_arm<big_endian>::relocate_stub(
    Stub* stub,
    Stub* stub,
    const Relocate_info<32, big_endian>* relinfo,
    const Relocate_info<32, big_endian>* relinfo,
    Output_section* output_section,
    Output_section* output_section,
    unsigned char* view,
    unsigned char* view,
    Arm_address address,
    Arm_address address,
    section_size_type view_size)
    section_size_type view_size)
{
{
  Relocate relocate;
  Relocate relocate;
  const Stub_template* stub_template = stub->stub_template();
  const Stub_template* stub_template = stub->stub_template();
  for (size_t i = 0; i < stub_template->reloc_count(); i++)
  for (size_t i = 0; i < stub_template->reloc_count(); i++)
    {
    {
      size_t reloc_insn_index = stub_template->reloc_insn_index(i);
      size_t reloc_insn_index = stub_template->reloc_insn_index(i);
      const Insn_template* insn = &stub_template->insns()[reloc_insn_index];
      const Insn_template* insn = &stub_template->insns()[reloc_insn_index];
 
 
      unsigned int r_type = insn->r_type();
      unsigned int r_type = insn->r_type();
      section_size_type reloc_offset = stub_template->reloc_offset(i);
      section_size_type reloc_offset = stub_template->reloc_offset(i);
      section_size_type reloc_size = insn->size();
      section_size_type reloc_size = insn->size();
      gold_assert(reloc_offset + reloc_size <= view_size);
      gold_assert(reloc_offset + reloc_size <= view_size);
 
 
      // This is the address of the stub destination.
      // This is the address of the stub destination.
      Arm_address target = stub->reloc_target(i) + insn->reloc_addend();
      Arm_address target = stub->reloc_target(i) + insn->reloc_addend();
      Symbol_value<32> symval;
      Symbol_value<32> symval;
      symval.set_output_value(target);
      symval.set_output_value(target);
 
 
      // Synthesize a fake reloc just in case.  We don't have a symbol so
      // Synthesize a fake reloc just in case.  We don't have a symbol so
      // we use 0.
      // we use 0.
      unsigned char reloc_buffer[elfcpp::Elf_sizes<32>::rel_size];
      unsigned char reloc_buffer[elfcpp::Elf_sizes<32>::rel_size];
      memset(reloc_buffer, 0, sizeof(reloc_buffer));
      memset(reloc_buffer, 0, sizeof(reloc_buffer));
      elfcpp::Rel_write<32, big_endian> reloc_write(reloc_buffer);
      elfcpp::Rel_write<32, big_endian> reloc_write(reloc_buffer);
      reloc_write.put_r_offset(reloc_offset);
      reloc_write.put_r_offset(reloc_offset);
      reloc_write.put_r_info(elfcpp::elf_r_info<32>(0, r_type));
      reloc_write.put_r_info(elfcpp::elf_r_info<32>(0, r_type));
      elfcpp::Rel<32, big_endian> rel(reloc_buffer);
      elfcpp::Rel<32, big_endian> rel(reloc_buffer);
 
 
      relocate.relocate(relinfo, this, output_section,
      relocate.relocate(relinfo, this, output_section,
                        this->fake_relnum_for_stubs, rel, r_type,
                        this->fake_relnum_for_stubs, rel, r_type,
                        NULL, &symval, view + reloc_offset,
                        NULL, &symval, view + reloc_offset,
                        address + reloc_offset, reloc_size);
                        address + reloc_offset, reloc_size);
    }
    }
}
}
 
 
// Determine whether an object attribute tag takes an integer, a
// Determine whether an object attribute tag takes an integer, a
// string or both.
// string or both.
 
 
template<bool big_endian>
template<bool big_endian>
int
int
Target_arm<big_endian>::do_attribute_arg_type(int tag) const
Target_arm<big_endian>::do_attribute_arg_type(int tag) const
{
{
  if (tag == Object_attribute::Tag_compatibility)
  if (tag == Object_attribute::Tag_compatibility)
    return (Object_attribute::ATTR_TYPE_FLAG_INT_VAL
    return (Object_attribute::ATTR_TYPE_FLAG_INT_VAL
            | Object_attribute::ATTR_TYPE_FLAG_STR_VAL);
            | Object_attribute::ATTR_TYPE_FLAG_STR_VAL);
  else if (tag == elfcpp::Tag_nodefaults)
  else if (tag == elfcpp::Tag_nodefaults)
    return (Object_attribute::ATTR_TYPE_FLAG_INT_VAL
    return (Object_attribute::ATTR_TYPE_FLAG_INT_VAL
            | Object_attribute::ATTR_TYPE_FLAG_NO_DEFAULT);
            | Object_attribute::ATTR_TYPE_FLAG_NO_DEFAULT);
  else if (tag == elfcpp::Tag_CPU_raw_name || tag == elfcpp::Tag_CPU_name)
  else if (tag == elfcpp::Tag_CPU_raw_name || tag == elfcpp::Tag_CPU_name)
    return Object_attribute::ATTR_TYPE_FLAG_STR_VAL;
    return Object_attribute::ATTR_TYPE_FLAG_STR_VAL;
  else if (tag < 32)
  else if (tag < 32)
    return Object_attribute::ATTR_TYPE_FLAG_INT_VAL;
    return Object_attribute::ATTR_TYPE_FLAG_INT_VAL;
  else
  else
    return ((tag & 1) != 0
    return ((tag & 1) != 0
            ? Object_attribute::ATTR_TYPE_FLAG_STR_VAL
            ? Object_attribute::ATTR_TYPE_FLAG_STR_VAL
            : Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
            : Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
}
}
 
 
// Reorder attributes.
// Reorder attributes.
//
//
// The ABI defines that Tag_conformance should be emitted first, and that
// The ABI defines that Tag_conformance should be emitted first, and that
// Tag_nodefaults should be second (if either is defined).  This sets those
// Tag_nodefaults should be second (if either is defined).  This sets those
// two positions, and bumps up the position of all the remaining tags to
// two positions, and bumps up the position of all the remaining tags to
// compensate.
// compensate.
 
 
template<bool big_endian>
template<bool big_endian>
int
int
Target_arm<big_endian>::do_attributes_order(int num) const
Target_arm<big_endian>::do_attributes_order(int num) const
{
{
  // Reorder the known object attributes in output.  We want to move
  // Reorder the known object attributes in output.  We want to move
  // Tag_conformance to position 4 and Tag_conformance to position 5
  // Tag_conformance to position 4 and Tag_conformance to position 5
  // and shift everything between 4 .. Tag_conformance - 1 to make room.
  // and shift everything between 4 .. Tag_conformance - 1 to make room.
  if (num == 4)
  if (num == 4)
    return elfcpp::Tag_conformance;
    return elfcpp::Tag_conformance;
  if (num == 5)
  if (num == 5)
    return elfcpp::Tag_nodefaults;
    return elfcpp::Tag_nodefaults;
  if ((num - 2) < elfcpp::Tag_nodefaults)
  if ((num - 2) < elfcpp::Tag_nodefaults)
    return num - 2;
    return num - 2;
  if ((num - 1) < elfcpp::Tag_conformance)
  if ((num - 1) < elfcpp::Tag_conformance)
    return num - 1;
    return num - 1;
  return num;
  return num;
}
}
 
 
// Scan a span of THUMB code for Cortex-A8 erratum.
// Scan a span of THUMB code for Cortex-A8 erratum.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::scan_span_for_cortex_a8_erratum(
Target_arm<big_endian>::scan_span_for_cortex_a8_erratum(
    Arm_relobj<big_endian>* arm_relobj,
    Arm_relobj<big_endian>* arm_relobj,
    unsigned int shndx,
    unsigned int shndx,
    section_size_type span_start,
    section_size_type span_start,
    section_size_type span_end,
    section_size_type span_end,
    const unsigned char* view,
    const unsigned char* view,
    Arm_address address)
    Arm_address address)
{
{
  // Scan for 32-bit Thumb-2 branches which span two 4K regions, where:
  // Scan for 32-bit Thumb-2 branches which span two 4K regions, where:
  //
  //
  // The opcode is BLX.W, BL.W, B.W, Bcc.W
  // The opcode is BLX.W, BL.W, B.W, Bcc.W
  // The branch target is in the same 4KB region as the
  // The branch target is in the same 4KB region as the
  // first half of the branch.
  // first half of the branch.
  // The instruction before the branch is a 32-bit
  // The instruction before the branch is a 32-bit
  // length non-branch instruction.
  // length non-branch instruction.
  section_size_type i = span_start;
  section_size_type i = span_start;
  bool last_was_32bit = false;
  bool last_was_32bit = false;
  bool last_was_branch = false;
  bool last_was_branch = false;
  while (i < span_end)
  while (i < span_end)
    {
    {
      typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
      typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
      const Valtype* wv = reinterpret_cast<const Valtype*>(view + i);
      const Valtype* wv = reinterpret_cast<const Valtype*>(view + i);
      uint32_t insn = elfcpp::Swap<16, big_endian>::readval(wv);
      uint32_t insn = elfcpp::Swap<16, big_endian>::readval(wv);
      bool is_blx = false, is_b = false;
      bool is_blx = false, is_b = false;
      bool is_bl = false, is_bcc = false;
      bool is_bl = false, is_bcc = false;
 
 
      bool insn_32bit = (insn & 0xe000) == 0xe000 && (insn & 0x1800) != 0x0000;
      bool insn_32bit = (insn & 0xe000) == 0xe000 && (insn & 0x1800) != 0x0000;
      if (insn_32bit)
      if (insn_32bit)
        {
        {
          // Load the rest of the insn (in manual-friendly order).
          // Load the rest of the insn (in manual-friendly order).
          insn = (insn << 16) | elfcpp::Swap<16, big_endian>::readval(wv + 1);
          insn = (insn << 16) | elfcpp::Swap<16, big_endian>::readval(wv + 1);
 
 
          // Encoding T4: B<c>.W.
          // Encoding T4: B<c>.W.
          is_b = (insn & 0xf800d000U) == 0xf0009000U;
          is_b = (insn & 0xf800d000U) == 0xf0009000U;
          // Encoding T1: BL<c>.W.
          // Encoding T1: BL<c>.W.
          is_bl = (insn & 0xf800d000U) == 0xf000d000U;
          is_bl = (insn & 0xf800d000U) == 0xf000d000U;
          // Encoding T2: BLX<c>.W.
          // Encoding T2: BLX<c>.W.
          is_blx = (insn & 0xf800d000U) == 0xf000c000U;
          is_blx = (insn & 0xf800d000U) == 0xf000c000U;
          // Encoding T3: B<c>.W (not permitted in IT block).
          // Encoding T3: B<c>.W (not permitted in IT block).
          is_bcc = ((insn & 0xf800d000U) == 0xf0008000U
          is_bcc = ((insn & 0xf800d000U) == 0xf0008000U
                    && (insn & 0x07f00000U) != 0x03800000U);
                    && (insn & 0x07f00000U) != 0x03800000U);
        }
        }
 
 
      bool is_32bit_branch = is_b || is_bl || is_blx || is_bcc;
      bool is_32bit_branch = is_b || is_bl || is_blx || is_bcc;
 
 
      // If this instruction is a 32-bit THUMB branch that crosses a 4K
      // If this instruction is a 32-bit THUMB branch that crosses a 4K
      // page boundary and it follows 32-bit non-branch instruction,
      // page boundary and it follows 32-bit non-branch instruction,
      // we need to work around.
      // we need to work around.
      if (is_32bit_branch
      if (is_32bit_branch
          && ((address + i) & 0xfffU) == 0xffeU
          && ((address + i) & 0xfffU) == 0xffeU
          && last_was_32bit
          && last_was_32bit
          && !last_was_branch)
          && !last_was_branch)
        {
        {
          // Check to see if there is a relocation stub for this branch.
          // Check to see if there is a relocation stub for this branch.
          bool force_target_arm = false;
          bool force_target_arm = false;
          bool force_target_thumb = false;
          bool force_target_thumb = false;
          const Cortex_a8_reloc* cortex_a8_reloc = NULL;
          const Cortex_a8_reloc* cortex_a8_reloc = NULL;
          Cortex_a8_relocs_info::const_iterator p =
          Cortex_a8_relocs_info::const_iterator p =
            this->cortex_a8_relocs_info_.find(address + i);
            this->cortex_a8_relocs_info_.find(address + i);
 
 
          if (p != this->cortex_a8_relocs_info_.end())
          if (p != this->cortex_a8_relocs_info_.end())
            {
            {
              cortex_a8_reloc = p->second;
              cortex_a8_reloc = p->second;
              bool target_is_thumb = (cortex_a8_reloc->destination() & 1) != 0;
              bool target_is_thumb = (cortex_a8_reloc->destination() & 1) != 0;
 
 
              if (cortex_a8_reloc->r_type() == elfcpp::R_ARM_THM_CALL
              if (cortex_a8_reloc->r_type() == elfcpp::R_ARM_THM_CALL
                  && !target_is_thumb)
                  && !target_is_thumb)
                force_target_arm = true;
                force_target_arm = true;
              else if (cortex_a8_reloc->r_type() == elfcpp::R_ARM_THM_CALL
              else if (cortex_a8_reloc->r_type() == elfcpp::R_ARM_THM_CALL
                       && target_is_thumb)
                       && target_is_thumb)
                force_target_thumb = true;
                force_target_thumb = true;
            }
            }
 
 
          off_t offset;
          off_t offset;
          Stub_type stub_type = arm_stub_none;
          Stub_type stub_type = arm_stub_none;
 
 
          // Check if we have an offending branch instruction.
          // Check if we have an offending branch instruction.
          uint16_t upper_insn = (insn >> 16) & 0xffffU;
          uint16_t upper_insn = (insn >> 16) & 0xffffU;
          uint16_t lower_insn = insn & 0xffffU;
          uint16_t lower_insn = insn & 0xffffU;
          typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
          typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
 
 
          if (cortex_a8_reloc != NULL
          if (cortex_a8_reloc != NULL
              && cortex_a8_reloc->reloc_stub() != NULL)
              && cortex_a8_reloc->reloc_stub() != NULL)
            // We've already made a stub for this instruction, e.g.
            // We've already made a stub for this instruction, e.g.
            // it's a long branch or a Thumb->ARM stub.  Assume that
            // it's a long branch or a Thumb->ARM stub.  Assume that
            // stub will suffice to work around the A8 erratum (see
            // stub will suffice to work around the A8 erratum (see
            // setting of always_after_branch above).
            // setting of always_after_branch above).
            ;
            ;
          else if (is_bcc)
          else if (is_bcc)
            {
            {
              offset = RelocFuncs::thumb32_cond_branch_offset(upper_insn,
              offset = RelocFuncs::thumb32_cond_branch_offset(upper_insn,
                                                              lower_insn);
                                                              lower_insn);
              stub_type = arm_stub_a8_veneer_b_cond;
              stub_type = arm_stub_a8_veneer_b_cond;
            }
            }
          else if (is_b || is_bl || is_blx)
          else if (is_b || is_bl || is_blx)
            {
            {
              offset = RelocFuncs::thumb32_branch_offset(upper_insn,
              offset = RelocFuncs::thumb32_branch_offset(upper_insn,
                                                         lower_insn);
                                                         lower_insn);
              if (is_blx)
              if (is_blx)
                offset &= ~3;
                offset &= ~3;
 
 
              stub_type = (is_blx
              stub_type = (is_blx
                           ? arm_stub_a8_veneer_blx
                           ? arm_stub_a8_veneer_blx
                           : (is_bl
                           : (is_bl
                              ? arm_stub_a8_veneer_bl
                              ? arm_stub_a8_veneer_bl
                              : arm_stub_a8_veneer_b));
                              : arm_stub_a8_veneer_b));
            }
            }
 
 
          if (stub_type != arm_stub_none)
          if (stub_type != arm_stub_none)
            {
            {
              Arm_address pc_for_insn = address + i + 4;
              Arm_address pc_for_insn = address + i + 4;
 
 
              // The original instruction is a BL, but the target is
              // The original instruction is a BL, but the target is
              // an ARM instruction.  If we were not making a stub,
              // an ARM instruction.  If we were not making a stub,
              // the BL would have been converted to a BLX.  Use the
              // the BL would have been converted to a BLX.  Use the
              // BLX stub instead in that case.
              // BLX stub instead in that case.
              if (this->may_use_v5t_interworking() && force_target_arm
              if (this->may_use_v5t_interworking() && force_target_arm
                  && stub_type == arm_stub_a8_veneer_bl)
                  && stub_type == arm_stub_a8_veneer_bl)
                {
                {
                  stub_type = arm_stub_a8_veneer_blx;
                  stub_type = arm_stub_a8_veneer_blx;
                  is_blx = true;
                  is_blx = true;
                  is_bl = false;
                  is_bl = false;
                }
                }
              // Conversely, if the original instruction was
              // Conversely, if the original instruction was
              // BLX but the target is Thumb mode, use the BL stub.
              // BLX but the target is Thumb mode, use the BL stub.
              else if (force_target_thumb
              else if (force_target_thumb
                       && stub_type == arm_stub_a8_veneer_blx)
                       && stub_type == arm_stub_a8_veneer_blx)
                {
                {
                  stub_type = arm_stub_a8_veneer_bl;
                  stub_type = arm_stub_a8_veneer_bl;
                  is_blx = false;
                  is_blx = false;
                  is_bl = true;
                  is_bl = true;
                }
                }
 
 
              if (is_blx)
              if (is_blx)
                pc_for_insn &= ~3;
                pc_for_insn &= ~3;
 
 
              // If we found a relocation, use the proper destination,
              // If we found a relocation, use the proper destination,
              // not the offset in the (unrelocated) instruction.
              // not the offset in the (unrelocated) instruction.
              // Note this is always done if we switched the stub type above.
              // Note this is always done if we switched the stub type above.
              if (cortex_a8_reloc != NULL)
              if (cortex_a8_reloc != NULL)
                offset = (off_t) (cortex_a8_reloc->destination() - pc_for_insn);
                offset = (off_t) (cortex_a8_reloc->destination() - pc_for_insn);
 
 
              Arm_address target = (pc_for_insn + offset) | (is_blx ? 0 : 1);
              Arm_address target = (pc_for_insn + offset) | (is_blx ? 0 : 1);
 
 
              // Add a new stub if destination address in in the same page.
              // Add a new stub if destination address in in the same page.
              if (((address + i) & ~0xfffU) == (target & ~0xfffU))
              if (((address + i) & ~0xfffU) == (target & ~0xfffU))
                {
                {
                  Cortex_a8_stub* stub =
                  Cortex_a8_stub* stub =
                    this->stub_factory_.make_cortex_a8_stub(stub_type,
                    this->stub_factory_.make_cortex_a8_stub(stub_type,
                                                            arm_relobj, shndx,
                                                            arm_relobj, shndx,
                                                            address + i,
                                                            address + i,
                                                            target, insn);
                                                            target, insn);
                  Stub_table<big_endian>* stub_table =
                  Stub_table<big_endian>* stub_table =
                    arm_relobj->stub_table(shndx);
                    arm_relobj->stub_table(shndx);
                  gold_assert(stub_table != NULL);
                  gold_assert(stub_table != NULL);
                  stub_table->add_cortex_a8_stub(address + i, stub);
                  stub_table->add_cortex_a8_stub(address + i, stub);
                }
                }
            }
            }
        }
        }
 
 
      i += insn_32bit ? 4 : 2;
      i += insn_32bit ? 4 : 2;
      last_was_32bit = insn_32bit;
      last_was_32bit = insn_32bit;
      last_was_branch = is_32bit_branch;
      last_was_branch = is_32bit_branch;
    }
    }
}
}
 
 
// Apply the Cortex-A8 workaround.
// Apply the Cortex-A8 workaround.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::apply_cortex_a8_workaround(
Target_arm<big_endian>::apply_cortex_a8_workaround(
    const Cortex_a8_stub* stub,
    const Cortex_a8_stub* stub,
    Arm_address stub_address,
    Arm_address stub_address,
    unsigned char* insn_view,
    unsigned char* insn_view,
    Arm_address insn_address)
    Arm_address insn_address)
{
{
  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
  Valtype* wv = reinterpret_cast<Valtype*>(insn_view);
  Valtype* wv = reinterpret_cast<Valtype*>(insn_view);
  Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
  Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
  Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
  Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
  off_t branch_offset = stub_address - (insn_address + 4);
  off_t branch_offset = stub_address - (insn_address + 4);
 
 
  typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
  typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
  switch (stub->stub_template()->type())
  switch (stub->stub_template()->type())
    {
    {
    case arm_stub_a8_veneer_b_cond:
    case arm_stub_a8_veneer_b_cond:
      // For a conditional branch, we re-write it to be an unconditional
      // For a conditional branch, we re-write it to be an unconditional
      // branch to the stub.  We use the THUMB-2 encoding here.
      // branch to the stub.  We use the THUMB-2 encoding here.
      upper_insn = 0xf000U;
      upper_insn = 0xf000U;
      lower_insn = 0xb800U;
      lower_insn = 0xb800U;
      // Fall through
      // Fall through
    case arm_stub_a8_veneer_b:
    case arm_stub_a8_veneer_b:
    case arm_stub_a8_veneer_bl:
    case arm_stub_a8_veneer_bl:
    case arm_stub_a8_veneer_blx:
    case arm_stub_a8_veneer_blx:
      if ((lower_insn & 0x5000U) == 0x4000U)
      if ((lower_insn & 0x5000U) == 0x4000U)
        // For a BLX instruction, make sure that the relocation is
        // For a BLX instruction, make sure that the relocation is
        // rounded up to a word boundary.  This follows the semantics of
        // rounded up to a word boundary.  This follows the semantics of
        // the instruction which specifies that bit 1 of the target
        // the instruction which specifies that bit 1 of the target
        // address will come from bit 1 of the base address.
        // address will come from bit 1 of the base address.
        branch_offset = (branch_offset + 2) & ~3;
        branch_offset = (branch_offset + 2) & ~3;
 
 
      // Put BRANCH_OFFSET back into the insn.
      // Put BRANCH_OFFSET back into the insn.
      gold_assert(!utils::has_overflow<25>(branch_offset));
      gold_assert(!Bits<25>::has_overflow32(branch_offset));
      upper_insn = RelocFuncs::thumb32_branch_upper(upper_insn, branch_offset);
      upper_insn = RelocFuncs::thumb32_branch_upper(upper_insn, branch_offset);
      lower_insn = RelocFuncs::thumb32_branch_lower(lower_insn, branch_offset);
      lower_insn = RelocFuncs::thumb32_branch_lower(lower_insn, branch_offset);
      break;
      break;
 
 
    default:
    default:
      gold_unreachable();
      gold_unreachable();
    }
    }
 
 
  // Put the relocated value back in the object file:
  // Put the relocated value back in the object file:
  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
}
}
 
 
template<bool big_endian>
template<bool big_endian>
class Target_selector_arm : public Target_selector
class Target_selector_arm : public Target_selector
{
{
 public:
 public:
  Target_selector_arm()
  Target_selector_arm()
    : Target_selector(elfcpp::EM_ARM, 32, big_endian,
    : Target_selector(elfcpp::EM_ARM, 32, big_endian,
                      (big_endian ? "elf32-bigarm" : "elf32-littlearm"),
                      (big_endian ? "elf32-bigarm" : "elf32-littlearm"),
                      (big_endian ? "armelfb" : "armelf"))
                      (big_endian ? "armelfb" : "armelf"))
  { }
  { }
 
 
  Target*
  Target*
  do_instantiate_target()
  do_instantiate_target()
  { return new Target_arm<big_endian>(); }
  { return new Target_arm<big_endian>(); }
};
};
 
 
// Fix .ARM.exidx section coverage.
// Fix .ARM.exidx section coverage.
 
 
template<bool big_endian>
template<bool big_endian>
void
void
Target_arm<big_endian>::fix_exidx_coverage(
Target_arm<big_endian>::fix_exidx_coverage(
    Layout* layout,
    Layout* layout,
    const Input_objects* input_objects,
    const Input_objects* input_objects,
    Arm_output_section<big_endian>* exidx_section,
    Arm_output_section<big_endian>* exidx_section,
    Symbol_table* symtab,
    Symbol_table* symtab,
    const Task* task)
    const Task* task)
{
{
  // We need to look at all the input sections in output in ascending
  // We need to look at all the input sections in output in ascending
  // order of of output address.  We do that by building a sorted list
  // order of of output address.  We do that by building a sorted list
  // of output sections by addresses.  Then we looks at the output sections
  // of output sections by addresses.  Then we looks at the output sections
  // in order.  The input sections in an output section are already sorted
  // in order.  The input sections in an output section are already sorted
  // by addresses within the output section.
  // by addresses within the output section.
 
 
  typedef std::set<Output_section*, output_section_address_less_than>
  typedef std::set<Output_section*, output_section_address_less_than>
      Sorted_output_section_list;
      Sorted_output_section_list;
  Sorted_output_section_list sorted_output_sections;
  Sorted_output_section_list sorted_output_sections;
 
 
  // Find out all the output sections of input sections pointed by
  // Find out all the output sections of input sections pointed by
  // EXIDX input sections.
  // EXIDX input sections.
  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
       p != input_objects->relobj_end();
       p != input_objects->relobj_end();
       ++p)
       ++p)
    {
    {
      Arm_relobj<big_endian>* arm_relobj =
      Arm_relobj<big_endian>* arm_relobj =
        Arm_relobj<big_endian>::as_arm_relobj(*p);
        Arm_relobj<big_endian>::as_arm_relobj(*p);
      std::vector<unsigned int> shndx_list;
      std::vector<unsigned int> shndx_list;
      arm_relobj->get_exidx_shndx_list(&shndx_list);
      arm_relobj->get_exidx_shndx_list(&shndx_list);
      for (size_t i = 0; i < shndx_list.size(); ++i)
      for (size_t i = 0; i < shndx_list.size(); ++i)
        {
        {
          const Arm_exidx_input_section* exidx_input_section =
          const Arm_exidx_input_section* exidx_input_section =
            arm_relobj->exidx_input_section_by_shndx(shndx_list[i]);
            arm_relobj->exidx_input_section_by_shndx(shndx_list[i]);
          gold_assert(exidx_input_section != NULL);
          gold_assert(exidx_input_section != NULL);
          if (!exidx_input_section->has_errors())
          if (!exidx_input_section->has_errors())
            {
            {
              unsigned int text_shndx = exidx_input_section->link();
              unsigned int text_shndx = exidx_input_section->link();
              Output_section* os = arm_relobj->output_section(text_shndx);
              Output_section* os = arm_relobj->output_section(text_shndx);
              if (os != NULL && (os->flags() & elfcpp::SHF_ALLOC) != 0)
              if (os != NULL && (os->flags() & elfcpp::SHF_ALLOC) != 0)
                sorted_output_sections.insert(os);
                sorted_output_sections.insert(os);
            }
            }
        }
        }
    }
    }
 
 
  // Go over the output sections in ascending order of output addresses.
  // Go over the output sections in ascending order of output addresses.
  typedef typename Arm_output_section<big_endian>::Text_section_list
  typedef typename Arm_output_section<big_endian>::Text_section_list
      Text_section_list;
      Text_section_list;
  Text_section_list sorted_text_sections;
  Text_section_list sorted_text_sections;
  for (typename Sorted_output_section_list::iterator p =
  for (typename Sorted_output_section_list::iterator p =
        sorted_output_sections.begin();
        sorted_output_sections.begin();
      p != sorted_output_sections.end();
      p != sorted_output_sections.end();
      ++p)
      ++p)
    {
    {
      Arm_output_section<big_endian>* arm_output_section =
      Arm_output_section<big_endian>* arm_output_section =
        Arm_output_section<big_endian>::as_arm_output_section(*p);
        Arm_output_section<big_endian>::as_arm_output_section(*p);
      arm_output_section->append_text_sections_to_list(&sorted_text_sections);
      arm_output_section->append_text_sections_to_list(&sorted_text_sections);
    }
    }
 
 
  exidx_section->fix_exidx_coverage(layout, sorted_text_sections, symtab,
  exidx_section->fix_exidx_coverage(layout, sorted_text_sections, symtab,
                                    merge_exidx_entries(), task);
                                    merge_exidx_entries(), task);
}
}
 
 
Target_selector_arm<false> target_selector_arm;
Target_selector_arm<false> target_selector_arm;
Target_selector_arm<true> target_selector_armbe;
Target_selector_arm<true> target_selector_armbe;
 
 
} // End anonymous namespace.
} // End anonymous namespace.
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.