blob: c47b00224cc7c0c15c28ae69a9d2893684e58caa [file] [log] [blame]
// arm.cc -- arm target support for gold.
// Copyright (C) 2009-2016 Free Software Foundation, Inc.
// Written by Doug Kwan <dougkwan@google.com> based on the i386 code
// by Ian Lance Taylor <iant@google.com>.
// This file also contains borrowed and adapted code from
// bfd/elf32-arm.c.
// This file is part of gold.
// 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
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "gold.h"
#include <cstring>
#include <limits>
#include <cstdio>
#include <string>
#include <algorithm>
#include <map>
#include <utility>
#include <set>
#include "elfcpp.h"
#include "parameters.h"
#include "reloc.h"
#include "arm.h"
#include "object.h"
#include "symtab.h"
#include "layout.h"
#include "output.h"
#include "copy-relocs.h"
#include "target.h"
#include "target-reloc.h"
#include "target-select.h"
#include "tls.h"
#include "defstd.h"
#include "gc.h"
#include "attributes.h"
#include "arm-reloc-property.h"
#include "nacl.h"
namespace
{
using namespace gold;
template<bool big_endian>
class Output_data_plt_arm;
template<bool big_endian>
class Output_data_plt_arm_short;
template<bool big_endian>
class Output_data_plt_arm_long;
template<bool big_endian>
class Stub_table;
template<bool big_endian>
class Arm_input_section;
class Arm_exidx_cantunwind;
class Arm_exidx_merged_section;
class Arm_exidx_fixup;
template<bool big_endian>
class Arm_output_section;
class Arm_exidx_input_section;
template<bool big_endian>
class Arm_relobj;
template<bool big_endian>
class Arm_relocate_functions;
template<bool big_endian>
class Arm_output_data_got;
template<bool big_endian>
class Target_arm;
// For convenience.
typedef elfcpp::Elf_types<32>::Elf_Addr Arm_address;
// 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_BWD_BRANCH_OFFSET = ((-((1 << 23) << 2)) + 8);
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 THM2_MAX_FWD_BRANCH_OFFSET = (((1 << 24) - 2) + 4);
const int32_t THM2_MAX_BWD_BRANCH_OFFSET = (-(1 << 24) + 4);
// Thread Control Block size.
const size_t ARM_TCB_SIZE = 8;
// The arm target class.
//
// This is a very simple port of gold for ARM-EABI. It is intended for
// supporting Android only for the time being.
//
// TODOs:
// - Implement all static relocation types documented in arm-reloc.def.
// - Make PLTs more flexible for different architecture features like
// Thumb-2 and BE8.
// There are probably a lot more.
// 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
// 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
// locked. The pointer is below initialized in the
// Target::do_select_as_default_target() hook so that we do not spend time
// building the table if we are not linking ARM objects.
//
// 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
// way we can avoid initialization when the linker starts.
Arm_reloc_property_table* arm_reloc_property_table = NULL;
// Instruction template class. This class is similar to the insn_sequence
// struct in bfd/elf32-arm.c.
class Insn_template
{
public:
// Types of instruction templates.
enum Type
{
THUMB16_TYPE = 1,
// THUMB16_SPECIAL_TYPE is used by sub-classes of Stub for instruction
// templates with class-specific semantics. Currently this is used
// only by the Cortex_a8_stub class for handling condition codes in
// conditional branches.
THUMB16_SPECIAL_TYPE,
THUMB32_TYPE,
ARM_TYPE,
DATA_TYPE
};
// Factory methods to create instruction templates in different formats.
static const Insn_template
thumb16_insn(uint32_t data)
{ return Insn_template(data, THUMB16_TYPE, elfcpp::R_ARM_NONE, 0); }
// A Thumb conditional branch, in which the proper condition is inserted
// when we build the stub.
static const Insn_template
thumb16_bcond_insn(uint32_t data)
{ return Insn_template(data, THUMB16_SPECIAL_TYPE, elfcpp::R_ARM_NONE, 1); }
static const Insn_template
thumb32_insn(uint32_t data)
{ return Insn_template(data, THUMB32_TYPE, elfcpp::R_ARM_NONE, 0); }
static const Insn_template
thumb32_b_insn(uint32_t data, int reloc_addend)
{
return Insn_template(data, THUMB32_TYPE, elfcpp::R_ARM_THM_JUMP24,
reloc_addend);
}
static const Insn_template
arm_insn(uint32_t data)
{ return Insn_template(data, ARM_TYPE, elfcpp::R_ARM_NONE, 0); }
static const Insn_template
arm_rel_insn(unsigned data, int reloc_addend)
{ return Insn_template(data, ARM_TYPE, elfcpp::R_ARM_JUMP24, reloc_addend); }
static const Insn_template
data_word(unsigned data, unsigned int r_type, int reloc_addend)
{ return Insn_template(data, DATA_TYPE, r_type, reloc_addend); }
// Accessors. This class is used for read-only objects so no modifiers
// are provided.
uint32_t
data() const
{ return this->data_; }
// Return the instruction sequence type of this.
Type
type() const
{ return this->type_; }
// Return the ARM relocation type of this.
unsigned int
r_type() const
{ return this->r_type_; }
int32_t
reloc_addend() const
{ return this->reloc_addend_; }
// Return size of instruction template in bytes.
size_t
size() const;
// Return byte-alignment of instruction template.
unsigned
alignment() const;
private:
// We make the constructor private to ensure that only the factory
// methods are used.
inline
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)
{ }
// Instruction specific data. This is used to store information like
// some of the instruction bits.
uint32_t data_;
// Instruction template type.
Type type_;
// Relocation type if there is a relocation or R_ARM_NONE otherwise.
unsigned int r_type_;
// Relocation addend.
int32_t reloc_addend_;
};
// Macro for generating code to stub types. One entry per long/short
// branch stub
#define DEF_STUBS \
DEF_STUB(long_branch_any_any) \
DEF_STUB(long_branch_v4t_arm_thumb) \
DEF_STUB(long_branch_thumb_only) \
DEF_STUB(long_branch_v4t_thumb_thumb) \
DEF_STUB(long_branch_v4t_thumb_arm) \
DEF_STUB(short_branch_v4t_thumb_arm) \
DEF_STUB(long_branch_any_arm_pic) \
DEF_STUB(long_branch_any_thumb_pic) \
DEF_STUB(long_branch_v4t_thumb_thumb_pic) \
DEF_STUB(long_branch_v4t_arm_thumb_pic) \
DEF_STUB(long_branch_v4t_thumb_arm_pic) \
DEF_STUB(long_branch_thumb_only_pic) \
DEF_STUB(a8_veneer_b_cond) \
DEF_STUB(a8_veneer_b) \
DEF_STUB(a8_veneer_bl) \
DEF_STUB(a8_veneer_blx) \
DEF_STUB(v4_veneer_bx)
// Stub types.
#define DEF_STUB(x) arm_stub_##x,
typedef enum
{
arm_stub_none,
DEF_STUBS
// First reloc stub type.
arm_stub_reloc_first = arm_stub_long_branch_any_any,
// Last reloc stub type.
arm_stub_reloc_last = arm_stub_long_branch_thumb_only_pic,
// First Cortex-A8 stub type.
arm_stub_cortex_a8_first = arm_stub_a8_veneer_b_cond,
// Last Cortex-A8 stub type.
arm_stub_cortex_a8_last = arm_stub_a8_veneer_blx,
// Last stub type.
arm_stub_type_last = arm_stub_v4_veneer_bx
} Stub_type;
#undef DEF_STUB
// Stub template class. Templates are meant to be read-only objects.
// A stub template for a stub type contains all read-only attributes
// common to all stubs of the same type.
class Stub_template
{
public:
Stub_template(Stub_type, const Insn_template*, size_t);
~Stub_template()
{ }
// Return stub type.
Stub_type
type() const
{ return this->type_; }
// Return an array of instruction templates.
const Insn_template*
insns() const
{ return this->insns_; }
// Return size of template in number of instructions.
size_t
insn_count() const
{ return this->insn_count_; }
// Return size of template in bytes.
size_t
size() const
{ return this->size_; }
// Return alignment of the stub template.
unsigned
alignment() const
{ return this->alignment_; }
// Return whether entry point is in thumb mode.
bool
entry_in_thumb_mode() const
{ return this->entry_in_thumb_mode_; }
// Return number of relocations in this template.
size_t
reloc_count() const
{ return this->relocs_.size(); }
// Return index of the I-th instruction with relocation.
size_t
reloc_insn_index(size_t i) const
{
gold_assert(i < this->relocs_.size());
return this->relocs_[i].first;
}
// Return the offset of the I-th instruction with relocation from the
// beginning of the stub.
section_size_type
reloc_offset(size_t i) const
{
gold_assert(i < this->relocs_.size());
return this->relocs_[i].second;
}
private:
// This contains information about an instruction template with a relocation
// and its offset from start of stub.
typedef std::pair<size_t, section_size_type> Reloc;
// A Stub_template may not be copied. We want to share templates as much
// as possible.
Stub_template(const Stub_template&);
Stub_template& operator=(const Stub_template&);
// Stub type.
Stub_type type_;
// Points to an array of Insn_templates.
const Insn_template* insns_;
// Number of Insn_templates in insns_[].
size_t insn_count_;
// Size of templated instructions in bytes.
size_t size_;
// Alignment of templated instructions.
unsigned alignment_;
// Flag to indicate if entry is in thumb mode.
bool entry_in_thumb_mode_;
// 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
// them here for speed.
std::vector<Reloc> relocs_;
};
//
// A class for code stubs. This is a base class for different type of
// stubs used in the ARM target.
//
class Stub
{
private:
static const section_offset_type invalid_offset =
static_cast<section_offset_type>(-1);
public:
Stub(const Stub_template* stub_template)
: stub_template_(stub_template), offset_(invalid_offset)
{ }
virtual
~Stub()
{ }
// Return the stub template.
const Stub_template*
stub_template() const
{ return this->stub_template_; }
// Return offset of code stub from beginning of its containing stub table.
section_offset_type
offset() const
{
gold_assert(this->offset_ != invalid_offset);
return this->offset_;
}
// Set offset of code stub from beginning of its containing stub table.
void
set_offset(section_offset_type offset)
{ this->offset_ = offset; }
// Return the relocation target address of the i-th relocation in the
// stub. This must be defined in a child class.
Arm_address
reloc_target(size_t i)
{ return this->do_reloc_target(i); }
// Write a stub at output VIEW. BIG_ENDIAN select how a stub is written.
void
write(unsigned char* view, section_size_type view_size, bool big_endian)
{ this->do_write(view, view_size, big_endian); }
// Return the instruction for THUMB16_SPECIAL_TYPE instruction template
// for the i-th instruction.
uint16_t
thumb16_special(size_t i)
{ return this->do_thumb16_special(i); }
protected:
// This must be defined in the child class.
virtual Arm_address
do_reloc_target(size_t) = 0;
// This may be overridden in the child class.
virtual void
do_write(unsigned char* view, section_size_type view_size, bool big_endian)
{
if (big_endian)
this->do_fixed_endian_write<true>(view, view_size);
else
this->do_fixed_endian_write<false>(view, view_size);
}
// This must be overridden if a child class uses the THUMB16_SPECIAL_TYPE
// instruction template.
virtual uint16_t
do_thumb16_special(size_t)
{ gold_unreachable(); }
private:
// A template to implement do_write.
template<bool big_endian>
void inline
do_fixed_endian_write(unsigned char*, section_size_type);
// Its template.
const Stub_template* stub_template_;
// Offset within the section of containing this stub.
section_offset_type offset_;
};
// Reloc stub class. These are stubs we use to fix up relocation because
// of limited branch ranges.
class Reloc_stub : public Stub
{
public:
static const unsigned int invalid_index = static_cast<unsigned int>(-1);
// We assume we never jump to this address.
static const Arm_address invalid_address = static_cast<Arm_address>(-1);
// Return destination address.
Arm_address
destination_address() const
{
gold_assert(this->destination_address_ != this->invalid_address);
return this->destination_address_;
}
// Set destination address.
void
set_destination_address(Arm_address address)
{
gold_assert(address != this->invalid_address);
this->destination_address_ = address;
}
// Reset destination address.
void
reset_destination_address()
{ this->destination_address_ = this->invalid_address; }
// 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,
// the branch target is a thumb instruction. TARGET is used for look
// up ARM-specific linker settings.
static Stub_type
stub_type_for_reloc(unsigned int r_type, Arm_address branch_address,
Arm_address branch_target, bool target_is_thumb);
// 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
// use a Symbol object for a global symbol and a object-index pair for
// a local symbol.
class Key
{
public:
// 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
// and R_SYM must not be invalid_index.
Key(Stub_type stub_type, const Symbol* symbol, const Relobj* relobj,
unsigned int r_sym, int32_t addend)
: stub_type_(stub_type), addend_(addend)
{
if (symbol != NULL)
{
this->r_sym_ = Reloc_stub::invalid_index;
this->u_.symbol = symbol;
}
else
{
gold_assert(relobj != NULL && r_sym != invalid_index);
this->r_sym_ = r_sym;
this->u_.relobj = relobj;
}
}
~Key()
{ }
// Accessors: Keys are meant to be read-only object so no modifiers are
// provided.
// Return stub type.
Stub_type
stub_type() const
{ return this->stub_type_; }
// Return the local symbol index or invalid_index.
unsigned int
r_sym() const
{ return this->r_sym_; }
// Return the symbol if there is one.
const Symbol*
symbol() const
{ return this->r_sym_ == invalid_index ? this->u_.symbol : NULL; }
// Return the relobj if there is one.
const Relobj*
relobj() const
{ return this->r_sym_ != invalid_index ? this->u_.relobj : NULL; }
// Whether this equals to another key k.
bool
eq(const Key& k) const
{
return ((this->stub_type_ == k.stub_type_)
&& (this->r_sym_ == k.r_sym_)
&& ((this->r_sym_ != Reloc_stub::invalid_index)
? (this->u_.relobj == k.u_.relobj)
: (this->u_.symbol == k.u_.symbol))
&& (this->addend_ == k.addend_));
}
// Return a hash value.
size_t
hash_value() const
{
return (this->stub_type_
^ this->r_sym_
^ gold::string_hash<char>(
(this->r_sym_ != Reloc_stub::invalid_index)
? this->u_.relobj->name().c_str()
: this->u_.symbol->name())
^ this->addend_);
}
// Functors for STL associative containers.
struct hash
{
size_t
operator()(const Key& k) const
{ return k.hash_value(); }
};
struct equal_to
{
bool
operator()(const Key& k1, const Key& k2) const
{ return k1.eq(k2); }
};
// Name of key. This is mainly for debugging.
std::string
name() const ATTRIBUTE_UNUSED;
private:
// Stub type.
Stub_type stub_type_;
// If this is a local symbol, this is the index in the defining object.
// Otherwise, it is invalid_index for a global symbol.
unsigned int r_sym_;
// 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
// independent Symbol and Relobj classes instead of Sized_symbol<32> and
// Arm_relobj, in order to avoid making the stub class a template
// as most of the stub machinery is endianness-neutral. However, it
// may require a bit of casting done by users of this class.
union
{
const Symbol* symbol;
const Relobj* relobj;
} u_;
// Addend associated with a reloc.
int32_t addend_;
};
protected:
// Reloc_stubs are created via a stub factory. So these are protected.
Reloc_stub(const Stub_template* stub_template)
: Stub(stub_template), destination_address_(invalid_address)
{ }
~Reloc_stub()
{ }
friend class Stub_factory;
// Return the relocation target address of the i-th relocation in the
// stub.
Arm_address
do_reloc_target(size_t i)
{
// All reloc stub have only one relocation.
gold_assert(i == 0);
return this->destination_address_;
}
private:
// Address of destination.
Arm_address destination_address_;
};
// Cortex-A8 stub class. We need a Cortex-A8 stub to redirect any 32-bit
// THUMB branch that meets the following conditions:
//
// 1. The branch straddles across a page boundary. i.e. lower 12-bit of
// branch address is 0xffe.
// 2. The branch target address is in the same page as the first word of the
// 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
// and its target at least. We also need to store the original branch
// instruction bits for the condition code in a conditional branch. The
// condition code is used in a special instruction template. We also want
// to identify input sections needing Cortex-A8 workaround quickly. We store
// extra information about object and section index of the code section
// containing a branch being fixed up. The information is used to mark
// the code section when we finalize the Cortex-A8 stubs.
//
class Cortex_a8_stub : public Stub
{
public:
~Cortex_a8_stub()
{ }
// Return the object of the code section containing the branch being fixed
// up.
Relobj*
relobj() const
{ return this->relobj_; }
// Return the section index of the code section containing the branch being
// fixed up.
unsigned int
shndx() const
{ return this->shndx_; }
// 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
// instruction.
Arm_address
source_address() const
{ return this->source_address_; }
// 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
// instruction address.
Arm_address
destination_address() const
{ return this->destination_address_; }
// Return the instruction being fixed up.
uint32_t
original_insn() const
{ return this->original_insn_; }
protected:
// Cortex_a8_stubs are created via a stub factory. So these are protected.
Cortex_a8_stub(const Stub_template* stub_template, Relobj* relobj,
unsigned int shndx, Arm_address source_address,
Arm_address destination_address, uint32_t original_insn)
: Stub(stub_template), relobj_(relobj), shndx_(shndx),
source_address_(source_address | 1U),
destination_address_(destination_address),
original_insn_(original_insn)
{ }
friend class Stub_factory;
// Return the relocation target address of the i-th relocation in the
// stub.
Arm_address
do_reloc_target(size_t i)
{
if (this->stub_template()->type() == arm_stub_a8_veneer_b_cond)
{
// The conditional branch veneer has two relocations.
gold_assert(i < 2);
return i == 0 ? this->source_address_ + 4 : this->destination_address_;
}
else
{
// All other Cortex-A8 stubs have only one relocation.
gold_assert(i == 0);
return this->destination_address_;
}
}
// Return an instruction for the THUMB16_SPECIAL_TYPE instruction template.
uint16_t
do_thumb16_special(size_t);
private:
// Object of the code section containing the branch being fixed up.
Relobj* relobj_;
// Section index of the code section containing the branch begin fixed up.
unsigned int shndx_;
// Source address of original branch.
Arm_address source_address_;
// Destination address of the original branch.
Arm_address destination_address_;
// Original branch instruction. This is needed for copying the condition
// code from a condition branch to its stub.
uint32_t original_insn_;
};
// ARMv4 BX Rx branch relocation stub class.
class Arm_v4bx_stub : public Stub
{
public:
~Arm_v4bx_stub()
{ }
// Return the associated register.
uint32_t
reg() const
{ return this->reg_; }
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)
: Stub(stub_template), reg_(reg)
{ }
friend class Stub_factory;
// Return the relocation target address of the i-th relocation in the
// stub.
Arm_address
do_reloc_target(size_t)
{ gold_unreachable(); }
// This may be overridden in the child class.
virtual void
do_write(unsigned char* view, section_size_type view_size, bool big_endian)
{
if (big_endian)
this->do_fixed_endian_v4bx_write<true>(view, view_size);
else
this->do_fixed_endian_v4bx_write<false>(view, view_size);
}
private:
// A template to implement do_write.
template<bool big_endian>
void inline
do_fixed_endian_v4bx_write(unsigned char* view, section_size_type)
{
const Insn_template* insns = this->stub_template()->insns();
elfcpp::Swap<32, big_endian>::writeval(view,
(insns[0].data()
+ (this->reg_ << 16)));
view += insns[0].size();
elfcpp::Swap<32, big_endian>::writeval(view,
(insns[1].data() + this->reg_));
view += insns[1].size();
elfcpp::Swap<32, big_endian>::writeval(view,
(insns[2].data() + this->reg_));
}
// A register index (r0-r14), which is associated with the stub.
uint32_t reg_;
};
// Stub factory class.
class Stub_factory
{
public:
// Return the unique instance of this class.
static const Stub_factory&
get_instance()
{
static Stub_factory singleton;
return singleton;
}
// Make a relocation stub.
Reloc_stub*
make_reloc_stub(Stub_type stub_type) const
{
gold_assert(stub_type >= arm_stub_reloc_first
&& stub_type <= arm_stub_reloc_last);
return new Reloc_stub(this->stub_templates_[stub_type]);
}
// Make a Cortex-A8 stub.
Cortex_a8_stub*
make_cortex_a8_stub(Stub_type stub_type, Relobj* relobj, unsigned int shndx,
Arm_address source, Arm_address destination,
uint32_t original_insn) const
{
gold_assert(stub_type >= arm_stub_cortex_a8_first
&& stub_type <= arm_stub_cortex_a8_last);
return new Cortex_a8_stub(this->stub_templates_[stub_type], relobj, shndx,
source, destination, original_insn);
}
// Make an ARM V4BX relocation stub.
// This method creates a stub from the arm_stub_v4_veneer_bx template only.
Arm_v4bx_stub*
make_arm_v4bx_stub(uint32_t reg) const
{
gold_assert(reg < 0xf);
return new Arm_v4bx_stub(this->stub_templates_[arm_stub_v4_veneer_bx],
reg);
}
private:
// Constructor and destructor are protected since we only return a single
// instance created in Stub_factory::get_instance().
Stub_factory();
// A Stub_factory may not be copied since it is a singleton.
Stub_factory(const Stub_factory&);
Stub_factory& operator=(Stub_factory&);
// Stub templates. These are initialized in the constructor.
const Stub_template* stub_templates_[arm_stub_type_last+1];
};
// A class to hold stubs for the ARM target.
template<bool big_endian>
class Stub_table : public Output_data
{
public:
Stub_table(Arm_input_section<big_endian>* owner)
: Output_data(), owner_(owner), reloc_stubs_(), reloc_stubs_size_(0),
reloc_stubs_addralign_(1), cortex_a8_stubs_(), arm_v4bx_stubs_(0xf),
prev_data_size_(0), prev_addralign_(1)
{ }
~Stub_table()
{ }
// Owner of this stub table.
Arm_input_section<big_endian>*
owner() const
{ return this->owner_; }
// Whether this stub table is empty.
bool
empty() const
{
return (this->reloc_stubs_.empty()
&& this->cortex_a8_stubs_.empty()
&& this->arm_v4bx_stubs_.empty());
}
// Return the current data size.
off_t
current_data_size() const
{ return this->current_data_size_for_child(); }
// Add a STUB using KEY. The caller is responsible for avoiding addition
// if a STUB with the same key has already been added.
void
add_reloc_stub(Reloc_stub* stub, const Reloc_stub::Key& key)
{
const Stub_template* stub_template = stub->stub_template();
gold_assert(stub_template->type() == key.stub_type());
this->reloc_stubs_[key] = stub;
// Assign stub offset early. We can do this because we never remove
// reloc stubs and they are in the beginning of the stub table.
uint64_t align = stub_template->alignment();
this->reloc_stubs_size_ = align_address(this->reloc_stubs_size_, align);
stub->set_offset(this->reloc_stubs_size_);
this->reloc_stubs_size_ += stub_template->size();
this->reloc_stubs_addralign_ =
std::max(this->reloc_stubs_addralign_, align);
}
// Add a Cortex-A8 STUB that fixes up a THUMB branch at ADDRESS.
// The caller is responsible for avoiding addition if a STUB with the same
// address has already been added.
void
add_cortex_a8_stub(Arm_address address, Cortex_a8_stub* stub)
{
std::pair<Arm_address, Cortex_a8_stub*> value(address, stub);
this->cortex_a8_stubs_.insert(value);
}
// Add an ARM V4BX relocation stub. A register index will be retrieved
// from the stub.
void
add_arm_v4bx_stub(Arm_v4bx_stub* stub)
{
gold_assert(stub != NULL && this->arm_v4bx_stubs_[stub->reg()] == NULL);
this->arm_v4bx_stubs_[stub->reg()] = stub;
}
// Remove all Cortex-A8 stubs.
void
remove_all_cortex_a8_stubs();
// Look up a relocation stub using KEY. Return NULL if there is none.
Reloc_stub*
find_reloc_stub(const Reloc_stub::Key& key) const
{
typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.find(key);
return (p != this->reloc_stubs_.end()) ? p->second : NULL;
}
// Look up an arm v4bx relocation stub using the register index.
// Return NULL if there is none.
Arm_v4bx_stub*
find_arm_v4bx_stub(const uint32_t reg) const
{
gold_assert(reg < 0xf);
return this->arm_v4bx_stubs_[reg];
}
// Relocate stubs in this stub table.
void
relocate_stubs(const Relocate_info<32, big_endian>*,
Target_arm<big_endian>*, Output_section*,
unsigned char*, Arm_address, section_size_type);
// 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
// previous relaxation pass.
bool
update_data_size_and_addralign();
// Finalize stubs. Set the offsets of all stubs and mark input sections
// needing the Cortex-A8 workaround.
void
finalize_stubs();
// Apply Cortex-A8 workaround to an address range.
void
apply_cortex_a8_workaround_to_address_range(Target_arm<big_endian>*,
unsigned char*, Arm_address,
section_size_type);
protected:
// Write out section contents.
void
do_write(Output_file*);
// Return the required alignment.
uint64_t
do_addralign() const
{ return this->prev_addralign_; }
// Reset address and file offset.
void
do_reset_address_and_file_offset()
{ this->set_current_data_size_for_child(this->prev_data_size_); }
// Set final data size.
void
set_final_data_size()
{ this->set_data_size(this->current_data_size()); }
private:
// Relocate one stub.
void
relocate_stub(Stub*, const Relocate_info<32, big_endian>*,
Target_arm<big_endian>*, Output_section*,
unsigned char*, Arm_address, section_size_type);
// Unordered map of relocation stubs.
typedef
Unordered_map<Reloc_stub::Key, Reloc_stub*, Reloc_stub::Key::hash,
Reloc_stub::Key::equal_to>
Reloc_stub_map;
// List of Cortex-A8 stubs ordered by addresses of branches being
// fixed up in output.
typedef std::map<Arm_address, Cortex_a8_stub*> Cortex_a8_stub_list;
// List of Arm V4BX relocation stubs ordered by associated registers.
typedef std::vector<Arm_v4bx_stub*> Arm_v4bx_stub_list;
// Owner of this stub table.
Arm_input_section<big_endian>* owner_;
// The relocation stubs.
Reloc_stub_map reloc_stubs_;
// Size of reloc stubs.
off_t reloc_stubs_size_;
// Maximum address alignment of reloc stubs.
uint64_t reloc_stubs_addralign_;
// The cortex_a8_stubs.
Cortex_a8_stub_list cortex_a8_stubs_;
// The Arm V4BX relocation stubs.
Arm_v4bx_stub_list arm_v4bx_stubs_;
// data size of this in the previous pass.
off_t prev_data_size_;
// address alignment of this in the previous pass.
uint64_t prev_addralign_;
};
// 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.
class Arm_exidx_cantunwind : public Output_section_data
{
public:
Arm_exidx_cantunwind(Relobj* relobj, unsigned int shndx)
: Output_section_data(8, 4, true), relobj_(relobj), shndx_(shndx)
{ }
// Return the object containing the section pointed by this.
Relobj*
relobj() const
{ return this->relobj_; }
// Return the section index of the section pointed by this.
unsigned int
shndx() const
{ return this->shndx_; }
protected:
void
do_write(Output_file* of)
{
if (parameters->target().is_big_endian())
this->do_fixed_endian_write<true>(of);
else
this->do_fixed_endian_write<false>(of);
}
// Write to a map file.
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, _("** ARM cantunwind")); }
private:
// Implement do_write for a given endianness.
template<bool big_endian>
void inline
do_fixed_endian_write(Output_file*);
// The object containing the section pointed by this.
Relobj* relobj_;
// The section index of the section pointed by this.
unsigned int shndx_;
};
// During EXIDX coverage fix-up, we compact an EXIDX section. The
// Offset map is used to map input section offset within the EXIDX section
// to the output offset from the start of this EXIDX section.
typedef std::map<section_offset_type, section_offset_type>
Arm_exidx_section_offset_map;
// Arm_exidx_merged_section class. This represents an EXIDX input section
// with some of its entries merged.
class Arm_exidx_merged_section : public Output_relaxed_input_section
{
public:
// Constructor for Arm_exidx_merged_section.
// EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
// SECTION_OFFSET_MAP points to a section offset map describing how
// parts of the input section are mapped to output. DELETED_BYTES is
// the number of bytes deleted from the EXIDX input section.
Arm_exidx_merged_section(
const Arm_exidx_input_section& exidx_input_section,
const Arm_exidx_section_offset_map& section_offset_map,
uint32_t deleted_bytes);
// Build output contents.
void
build_contents(const unsigned char*, section_size_type);
// Return the original EXIDX input section.
const Arm_exidx_input_section&
exidx_input_section() const
{ return this->exidx_input_section_; }
// Return the section offset map.
const Arm_exidx_section_offset_map&
section_offset_map() const
{ return this->section_offset_map_; }
protected:
// Write merged section into file OF.
void
do_write(Output_file* of);
bool
do_output_offset(const Relobj*, unsigned int, section_offset_type,
section_offset_type*) const;
private:
// Original EXIDX input section.
const Arm_exidx_input_section& exidx_input_section_;
// Section offset map.
const Arm_exidx_section_offset_map& section_offset_map_;
// Merged section contents. We need to keep build the merged section
// and save it here to avoid accessing the original EXIDX section when
// we cannot lock the sections' object.
unsigned char* section_contents_;
};
// A class to wrap an ordinary input section containing executable code.
template<bool big_endian>
class Arm_input_section : public Output_relaxed_input_section
{
public:
Arm_input_section(Relobj* relobj, unsigned int shndx)
: Output_relaxed_input_section(relobj, shndx, 1),
original_addralign_(1), original_size_(0), stub_table_(NULL),
original_contents_(NULL)
{ }
~Arm_input_section()
{ delete[] this->original_contents_; }
// Initialize.
void
init();
// Whether this is a stub table owner.
bool
is_stub_table_owner() const
{ return this->stub_table_ != NULL && this->stub_table_->owner() == this; }
// Return the stub table.
Stub_table<big_endian>*
stub_table() const
{ return this->stub_table_; }
// Set the stub_table.
void
set_stub_table(Stub_table<big_endian>* stub_table)
{ this->stub_table_ = stub_table; }
// 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.
static Arm_input_section<big_endian>*
as_arm_input_section(Output_relaxed_input_section* poris)
{ return static_cast<Arm_input_section<big_endian>*>(poris); }
// Return the original size of the section.
uint32_t
original_size() const
{ return this->original_size_; }
protected:
// Write data to output file.
void
do_write(Output_file*);
// Return required alignment of this.
uint64_t
do_addralign() const
{
if (this->is_stub_table_owner())
return std::max(this->stub_table_->addralign(),
static_cast<uint64_t>(this->original_addralign_));
else
return this->original_addralign_;
}
// Finalize data size.
void
set_final_data_size();
// Reset address and file offset.
void
do_reset_address_and_file_offset();
// Output offset.
bool
do_output_offset(const Relobj* object, unsigned int shndx,
section_offset_type offset,
section_offset_type* poutput) const
{
if ((object == this->relobj())
&& (shndx == this->shndx())
&& (offset >= 0)
&& (offset <=
convert_types<section_offset_type, uint32_t>(this->original_size_)))
{
*poutput = offset;
return true;
}
else
return false;
}
private:
// Copying is not allowed.
Arm_input_section(const Arm_input_section&);
Arm_input_section& operator=(const Arm_input_section&);
// Address alignment of the original input section.
uint32_t original_addralign_;
// Section size of the original input section.
uint32_t original_size_;
// Stub table.
Stub_table<big_endian>* stub_table_;
// 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
// the contents.
unsigned char* original_contents_;
};
// Arm_exidx_fixup class. This is used to define a number of methods
// and keep states for fixing up EXIDX coverage.
class Arm_exidx_fixup
{
public:
Arm_exidx_fixup(Output_section* exidx_output_section,
bool merge_exidx_entries = true)
: exidx_output_section_(exidx_output_section), last_unwind_type_(UT_NONE),
last_inlined_entry_(0), last_input_section_(NULL),
section_offset_map_(NULL), first_output_text_section_(NULL),
merge_exidx_entries_(merge_exidx_entries)
{ }
~Arm_exidx_fixup()
{ delete this->section_offset_map_; }
// Process an EXIDX section for entry merging. SECTION_CONTENTS points
// 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
// 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
// responsible for releasing it.
template<bool big_endian>
uint32_t
process_exidx_section(const Arm_exidx_input_section* exidx_input_section,
const unsigned char* section_contents,
section_size_type section_size,
Arm_exidx_section_offset_map** psection_offset_map);
// Append an EXIDX_CANTUNWIND entry pointing at the end of the last
// input section, if there is not one already.
void
add_exidx_cantunwind_as_needed();
// Return the output section for the text section which is linked to the
// first exidx input in output.
Output_section*
first_output_text_section() const
{ return this->first_output_text_section_; }
private:
// Copying is not allowed.
Arm_exidx_fixup(const Arm_exidx_fixup&);
Arm_exidx_fixup& operator=(const Arm_exidx_fixup&);
// Type of EXIDX unwind entry.
enum Unwind_type
{
// No type.
UT_NONE,
// EXIDX_CANTUNWIND.
UT_EXIDX_CANTUNWIND,
// Inlined entry.
UT_INLINED_ENTRY,
// Normal entry.
UT_NORMAL_ENTRY,
};
// Process an EXIDX entry. We only care about the second word of the
// entry. Return true if the entry can be deleted.
bool
process_exidx_entry(uint32_t second_word);
// 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
// reference point, DELETED_BYTES is the number of deleted by in the
// section so far. If DELETE_ENTRY is true, the reference point and
// all offsets after the previous reference point are discarded.
void
update_offset_map(section_offset_type input_offset,
section_size_type deleted_bytes, bool delete_entry);
// EXIDX output section.
Output_section* exidx_output_section_;
// Unwind type of the last EXIDX entry processed.
Unwind_type last_unwind_type_;
// Last seen inlined EXIDX entry.
uint32_t last_inlined_entry_;
// Last processed EXIDX input section.
const Arm_exidx_input_section* last_input_section_;
// Section offset map created in process_exidx_section.
Arm_exidx_section_offset_map* section_offset_map_;
// Output section for the text section which is linked to the first exidx
// input in output.
Output_section* first_output_text_section_;
bool merge_exidx_entries_;
};
// Arm output section class. This is defined mainly to add a number of
// stub generation methods.
template<bool big_endian>
class Arm_output_section : public Output_section
{
public:
typedef std::vector<std::pair<Relobj*, unsigned int> > Text_section_list;
// We need to force SHF_LINK_ORDER in a SHT_ARM_EXIDX section.
Arm_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags)
: Output_section(name, type,
(type == elfcpp::SHT_ARM_EXIDX
? flags | elfcpp::SHF_LINK_ORDER
: flags))
{
if (type == elfcpp::SHT_ARM_EXIDX)
this->set_always_keeps_input_sections();
}
~Arm_output_section()
{ }
// Group input sections for stub generation.
void
group_sections(section_size_type, bool, Target_arm<big_endian>*, const Task*);
// 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.
static Arm_output_section<big_endian>*
as_arm_output_section(Output_section* os)
{ return static_cast<Arm_output_section<big_endian>*>(os); }
// Append all input text sections in this into LIST.
void
append_text_sections_to_list(Text_section_list* list);
// Fix EXIDX coverage of this EXIDX output section. SORTED_TEXT_SECTION
// is a list of text input sections sorted in ascending order of their
// output addresses.
void
fix_exidx_coverage(Layout* layout,
const Text_section_list& sorted_text_section,
Symbol_table* symtab,
bool merge_exidx_entries,
const Task* task);
// Link an EXIDX section into its corresponding text section.
void
set_exidx_section_link();
private:
// For convenience.
typedef Output_section::Input_section Input_section;
typedef Output_section::Input_section_list Input_section_list;
// Create a stub group.
void create_stub_group(Input_section_list::const_iterator,
Input_section_list::const_iterator,
Input_section_list::const_iterator,
Target_arm<big_endian>*,
std::vector<Output_relaxed_input_section*>*,
const Task* task);
};
// Arm_exidx_input_section class. This represents an EXIDX input section.
class Arm_exidx_input_section
{
public:
static const section_offset_type invalid_offset =
static_cast<section_offset_type>(-1);
Arm_exidx_input_section(Relobj* relobj, unsigned int shndx,
unsigned int link, uint32_t size,
uint32_t addralign, uint32_t text_size)
: relobj_(relobj), shndx_(shndx), link_(link), size_(size),
addralign_(addralign), text_size_(text_size), has_errors_(false)
{ }
~Arm_exidx_input_section()
{ }
// Accessors: This is a read-only class.
// Return the object containing this EXIDX input section.
Relobj*
relobj() const
{ return this->relobj_; }
// Return the section index of this EXIDX input section.
unsigned int
shndx() const
{ return this->shndx_; }
// Return the section index of linked text section in the same object.
unsigned int
link() const
{ return this->link_; }
// Return size of the EXIDX input section.
uint32_t
size() const
{ return this->size_; }
// Return address alignment of EXIDX input section.
uint32_t
addralign() const
{ return this->addralign_; }
// Return size of the associated text input section.
uint32_t
text_size() const
{ return this->text_size_; }
// Whether there are any errors in the EXIDX input section.
bool
has_errors() const
{ return this->has_errors_; }
// Set has-errors flag.
void
set_has_errors()
{ this->has_errors_ = true; }
private:
// Object containing this.
Relobj* relobj_;
// Section index of this.
unsigned int shndx_;
// text section linked to this in the same object.
unsigned int link_;
// Size of this. For ARM 32-bit is sufficient.
uint32_t size_;
// Address alignment of this. For ARM 32-bit is sufficient.
uint32_t addralign_;
// Size of associated text section.
uint32_t text_size_;
// Whether this has any errors.
bool has_errors_;
};
// Arm_relobj class.
template<bool big_endian>
class Arm_relobj : public Sized_relobj_file<32, big_endian>
{
public:
static const Arm_address invalid_address = static_cast<Arm_address>(-1);
Arm_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<32, big_endian>& ehdr)
: Sized_relobj_file<32, big_endian>(name, input_file, offset, ehdr),
stub_tables_(), local_symbol_is_thumb_function_(),
attributes_section_data_(NULL), mapping_symbols_info_(),
section_has_cortex_a8_workaround_(NULL), exidx_section_map_(),
output_local_symbol_count_needs_update_(false),
merge_flags_and_attributes_(true)
{ }
~Arm_relobj()
{ delete this->attributes_section_data_; }
// Return the stub table of the SHNDX-th section if there is one.
Stub_table<big_endian>*
stub_table(unsigned int shndx) const
{
gold_assert(shndx < this->stub_tables_.size());
return this->stub_tables_[shndx];
}
// Set STUB_TABLE to be the stub_table of the SHNDX-th section.
void
set_stub_table(unsigned int shndx, Stub_table<big_endian>* stub_table)
{
gold_assert(shndx < this->stub_tables_.size());
this->stub_tables_[shndx] = stub_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.
bool
local_symbol_is_thumb_function(unsigned int r_sym) const
{
gold_assert(r_sym < this->local_symbol_is_thumb_function_.size());
return this->local_symbol_is_thumb_function_[r_sym];
}
// Scan all relocation sections for stub generation.
void
scan_sections_for_stubs(Target_arm<big_endian>*, const Symbol_table*,
const Layout*);
// Convert regular input section with index SHNDX to a relaxed section.
void
convert_input_section_to_relaxed_section(unsigned shndx)
{
// The stubs have relocations and we need to process them after writing
// out the stubs. So relocation now must follow section write.
this->set_section_offset(shndx, -1ULL);
this->set_relocs_must_follow_section_writes();
}
// Downcast a base pointer to an Arm_relobj pointer. This is
// not type-safe but we only use Arm_relobj not the base class.
static Arm_relobj<big_endian>*
as_arm_relobj(Relobj* relobj)
{ return static_cast<Arm_relobj<big_endian>*>(relobj); }
// Processor-specific flags in ELF file header. This is valid only after
// reading symbols.
elfcpp::Elf_Word
processor_specific_flags() const
{ return this->processor_specific_flags_; }
// Attribute section data This is the contents of the .ARM.attribute section
// if there is one.
const Attributes_section_data*
attributes_section_data() const
{ return this->attributes_section_data_; }
// Mapping symbol location.
typedef std::pair<unsigned int, Arm_address> Mapping_symbol_position;
// Functor for STL container.
struct Mapping_symbol_position_less
{
bool
operator()(const Mapping_symbol_position& p1,
const Mapping_symbol_position& p2) const
{
return (p1.first < p2.first
|| (p1.first == p2.first && p1.second < p2.second));
}
};
// We only care about the first character of a mapping symbol, so
// we only store that instead of the whole symbol name.
typedef std::map<Mapping_symbol_position, char,
Mapping_symbol_position_less> Mapping_symbols_info;
// Whether a section contains any Cortex-A8 workaround.
bool
section_has_cortex_a8_workaround(unsigned int shndx) const
{
return (this->section_has_cortex_a8_workaround_ != NULL
&& (*this->section_has_cortex_a8_workaround_)[shndx]);
}
// Mark a section that has Cortex-A8 workaround.
void
mark_section_for_cortex_a8_workaround(unsigned int shndx)
{
if (this->section_has_cortex_a8_workaround_ == NULL)
this->section_has_cortex_a8_workaround_ =
new std::vector<bool>(this->shnum(), false);
(*this->section_has_cortex_a8_workaround_)[shndx] = true;
}
// Return the EXIDX section of an text section with index SHNDX or NULL
// if the text section has no associated EXIDX section.
const Arm_exidx_input_section*
exidx_input_section_by_link(unsigned int shndx) const
{
Exidx_section_map::const_iterator p = this->exidx_section_map_.find(shndx);
return ((p != this->exidx_section_map_.end()
&& p->second->link() == shndx)
? p->second
: NULL);
}
// Return the EXIDX section with index SHNDX or NULL if there is none.
const Arm_exidx_input_section*
exidx_input_section_by_shndx(unsigned shndx) const
{
Exidx_section_map::const_iterator p = this->exidx_section_map_.find(shndx);
return ((p != this->exidx_section_map_.end()
&& p->second->shndx() == shndx)
? p->second
: NULL);
}
// Whether output local symbol count needs updating.
bool
output_local_symbol_count_needs_update() const
{ return this->output_local_symbol_count_needs_update_; }
// Set output_local_symbol_count_needs_update flag to be true.
void
set_output_local_symbol_count_needs_update()
{ this->output_local_symbol_count_needs_update_ = true; }
// Update output local symbol count at the end of relaxation.
void
update_output_local_symbol_count();
// Whether we want to merge processor-specific flags and attributes.
bool
merge_flags_and_attributes() const
{ return this->merge_flags_and_attributes_; }
// Export list of EXIDX section indices.
void
get_exidx_shndx_list(std::vector<unsigned int>* list) const
{
list->clear();
for (Exidx_section_map::const_iterator p = this->exidx_section_map_.begin();
p != this->exidx_section_map_.end();
++p)
{
if (p->second->shndx() == p->first)
list->push_back(p->first);
}
// Sort list to make result independent of implementation of map.
std::sort(list->begin(), list->end());
}
protected:
// Post constructor setup.
void
do_setup()
{
// Call parent's setup method.
Sized_relobj_file<32, big_endian>::do_setup();
// Initialize look-up tables.
Stub_table_list empty_stub_table_list(this->shnum(), NULL);
this->stub_tables_.swap(empty_stub_table_list);
}
// Count the local symbols.
void
do_count_local_symbols(Stringpool_template<char>*,
Stringpool_template<char>*);
void
do_relocate_sections(
const Symbol_table* symtab, const Layout* layout,
const unsigned char* pshdrs, Output_file* of,
typename Sized_relobj_file<32, big_endian>::Views* pivews);
// Read the symbol information.
void
do_read_symbols(Read_symbols_data* sd);
// Process relocs for garbage collection.
void
do_gc_process_relocs(Symbol_table*, Layout*, Read_relocs_data*);
private:
// Whether a section needs to be scanned for relocation stubs.
bool
section_needs_reloc_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
const Relobj::Output_sections&,
const Symbol_table*, const unsigned char*);
// Whether a section is a scannable text section.
bool
section_is_scannable(const elfcpp::Shdr<32, big_endian>&, unsigned int,
const Output_section*, const Symbol_table*);
// Whether a section needs to be scanned for the Cortex-A8 erratum.
bool
section_needs_cortex_a8_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
unsigned int, Output_section*,
const Symbol_table*);
// Scan a section for the Cortex-A8 erratum.
void
scan_section_for_cortex_a8_erratum(const elfcpp::Shdr<32, big_endian>&,
unsigned int, Output_section*,
Target_arm<big_endian>*);
// Find the linked text section of an EXIDX section by looking at the
// first relocation of the EXIDX section. PSHDR points to the section
// headers of a relocation section and PSYMS points to the local symbols.
// PSHNDX points to a location storing the text section index if found.
// Return whether we can find the linked section.
bool
find_linked_text_section(const unsigned char* pshdr,
const unsigned char* psyms, unsigned int* pshndx);
//
// Make a new Arm_exidx_input_section object for EXIDX section with
// index SHNDX and section header SHDR. TEXT_SHNDX is the section
// index of the linked text section.
void
make_exidx_input_section(unsigned int shndx,
const elfcpp::Shdr<32, big_endian>& shdr,
unsigned int text_shndx,
const elfcpp::Shdr<32, big_endian>& text_shdr);
// Return the output address of either a plain input section or a
// relaxed input section. SHNDX is the section index.
Arm_address
simple_input_section_output_address(unsigned int, Output_section*);
typedef std::vector<Stub_table<big_endian>*> Stub_table_list;
typedef Unordered_map<unsigned int, const Arm_exidx_input_section*>
Exidx_section_map;
// List of stub tables.
Stub_table_list stub_tables_;
// 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.
std::vector<bool> local_symbol_is_thumb_function_;
// processor-specific flags in ELF file header.
elfcpp::Elf_Word processor_specific_flags_;
// Object attributes if there is an .ARM.attributes section or NULL.
Attributes_section_data* attributes_section_data_;
// Mapping symbols information.
Mapping_symbols_info mapping_symbols_info_;
// Bitmap to indicate sections with Cortex-A8 workaround or NULL.
std::vector<bool>* section_has_cortex_a8_workaround_;
// Map a text section to its associated .ARM.exidx section, if there is one.
Exidx_section_map exidx_section_map_;
// Whether output local symbol count needs updating.
bool output_local_symbol_count_needs_update_;
// Whether we merge processor flags and attributes of this object to
// output.
bool merge_flags_and_attributes_;
};
// Arm_dynobj class.
template<bool big_endian>
class Arm_dynobj : public Sized_dynobj<32, big_endian>
{
public:
Arm_dynobj(const std::string& name, Input_file* input_file, off_t offset,
const elfcpp::Ehdr<32, big_endian>& ehdr)
: Sized_dynobj<32, big_endian>(name, input_file, offset, ehdr),
processor_specific_flags_(0), attributes_section_data_(NULL)
{ }
~Arm_dynobj()
{ delete this->attributes_section_data_; }
// Downcast a base pointer to an Arm_relobj pointer. This is
// not type-safe but we only use Arm_relobj not the base class.
static Arm_dynobj<big_endian>*
as_arm_dynobj(Dynobj* dynobj)
{ return static_cast<Arm_dynobj<big_endian>*>(dynobj); }
// Processor-specific flags in ELF file header. This is valid only after
// reading symbols.
elfcpp::Elf_Word
processor_specific_flags() const
{ return this->processor_specific_flags_; }
// Attributes section data.
const Attributes_section_data*
attributes_section_data() const
{ return this->attributes_section_data_; }
protected:
// Read the symbol information.
void
do_read_symbols(Read_symbols_data* sd);
private:
// processor-specific flags in ELF file header.
elfcpp::Elf_Word processor_specific_flags_;
// Object attributes if there is an .ARM.attributes section or NULL.
Attributes_section_data* attributes_section_data_;
};
// Functor to read reloc addends during stub generation.
template<int sh_type, bool big_endian>
struct Stub_addend_reader
{
// 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
// view or from a Reloc object.
elfcpp::Elf_types<32>::Elf_Swxword
operator()(
unsigned int /* r_type */,
const unsigned char* /* view */,
const typename Reloc_types<sh_type,
32, big_endian>::Reloc& /* reloc */) const;
};
// Specialized Stub_addend_reader for SHT_REL type relocation sections.
template<bool big_endian>
struct Stub_addend_reader<elfcpp::SHT_REL, big_endian>
{
elfcpp::Elf_types<32>::Elf_Swxword
operator()(
unsigned int,
const unsigned char*,
const typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc&) const;
};
// Specialized Stub_addend_reader for RELA type relocation sections.
// 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
// make it easier to add support for RELA relocation sections in the future.
template<bool big_endian>
struct Stub_addend_reader<elfcpp::SHT_RELA, big_endian>
{
elfcpp::Elf_types<32>::Elf_Swxword
operator()(
unsigned int,
const unsigned char*,
const typename Reloc_types<elfcpp::SHT_RELA, 32,
big_endian>::Reloc& reloc) const
{ return reloc.get_r_addend(); }
};
// Cortex_a8_reloc class. We keep record of relocation that may need
// the Cortex-A8 erratum workaround.
class Cortex_a8_reloc
{
public:
Cortex_a8_reloc(Reloc_stub* reloc_stub, unsigned r_type,
Arm_address destination)
: reloc_stub_(reloc_stub), r_type_(r_type), destination_(destination)
{ }
~Cortex_a8_reloc()
{ }
// Accessors: This is a read-only class.
// Return the relocation stub associated with this relocation if there is
// one.
const Reloc_stub*
reloc_stub() const
{ return this->reloc_stub_; }
// Return the relocation type.
unsigned int
r_type() const
{ return this->r_type_; }
// Return the destination address of the relocation. LSB stores the THUMB
// bit.
Arm_address
destination() const
{ return this->destination_; }
private:
// Associated relocation stub if there is one, or NULL.
const Reloc_stub* reloc_stub_;
// Relocation type.
unsigned int r_type_;
// Destination address of this relocation. LSB is used to distinguish
// ARM/THUMB mode.
Arm_address destination_;
};
// Arm_output_data_got class. We derive this from Output_data_got to add
// extra methods to handle TLS relocations in a static link.
template<bool big_endian>
class Arm_output_data_got : public Output_data_got<32, big_endian>
{
public:
Arm_output_data_got(Symbol_table* symtab, Layout* layout)
: Output_data_got<32, big_endian>(), symbol_table_(symtab), layout_(layout)
{ }
// Add a static entry for the GOT entry at OFFSET. GSYM is a global
// symbol and R_TYPE is the code of a dynamic relocation that needs to be
// applied in a static link.
void
add_static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
{ this->static_relocs_.push_back(Static_reloc(got_offset, r_type, gsym)); }
// Add a static reloc for the GOT entry at OFFSET. RELOBJ is an object
// defining a local symbol with INDEX. R_TYPE is the code of a dynamic
// relocation that needs to be applied in a static link.
void
add_static_reloc(unsigned int got_offset, unsigned int r_type,
Sized_relobj_file<32, big_endian>* relobj,
unsigned int index)
{
this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj,
index));
}
// Add a GOT pair for R_ARM_TLS_GD32. The creates a pair of GOT entries.
// The first one is initialized to be 1, which is the module index for
// the main executable and the second one 0. A reloc of the type
// R_ARM_TLS_DTPOFF32 will be created for the second GOT entry and will
// be applied by gold. GSYM is a global symbol.
void
add_tls_gd32_with_static_reloc(unsigned int got_type, Symbol* gsym);
// Same as the above but for a local symbol in OBJECT with INDEX.
void
add_tls_gd32_with_static_reloc(unsigned int got_type,
Sized_relobj_file<32, big_endian>* object,
unsigned int index);
protected:
// Write out the GOT table.
void
do_write(Output_file*);
private:
// This class represent dynamic relocations that need to be applied by
// gold because we are using TLS relocations in a static link.
class Static_reloc
{
public:
Static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
: got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true)
{ this->u_.global.symbol = gsym; }
Static_reloc(unsigned int got_offset, unsigned int r_type,
Sized_relobj_file<32, big_endian>* relobj, unsigned int index)
: got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false)
{
this->u_.local.relobj = relobj;
this->u_.local.index = index;
}
// Return the GOT offset.
unsigned int
got_offset() const
{ return this->got_offset_; }
// Relocation type.
unsigned int
r_type() const
{ return this->r_type_; }
// Whether the symbol is global or not.
bool
symbol_is_global() const
{ return this->symbol_is_global_; }
// For a relocation against a global symbol, the global symbol.
Symbol*
symbol() const
{
gold_assert(this->symbol_is_global_);
return this->u_.global.symbol;
}
// For a relocation against a local symbol, the defining object.
Sized_relobj_file<32, big_endian>*
relobj() const
{
gold_assert(!this->symbol_is_global_);
return this->u_.local.relobj;
}
// For a relocation against a local symbol, the local symbol index.
unsigned int
index() const
{
gold_assert(!this->symbol_is_global_);
return this->u_.local.index;
}
private:
// GOT offset of the entry to which this relocation is applied.
unsigned int got_offset_;
// Type of relocation.
unsigned int r_type_;
// Whether this relocation is against a global symbol.
bool symbol_is_global_;
// A global or local symbol.
union
{
struct
{
// For a global symbol, the symbol itself.
Symbol* symbol;
} global;
struct
{
// For a local symbol, the object defining object.
Sized_relobj_file<32, big_endian>* relobj;
// For a local symbol, the symbol index.
unsigned int index;
} local;
} u_;
};
// Symbol table of the output object.
Symbol_table* symbol_table_;
// Layout of the output object.
Layout* layout_;
// Static relocs to be applied to the GOT.
std::vector<Static_reloc> static_relocs_;
};
// The ARM target has many relocation types with odd-sizes or noncontiguous
// bits. The default handling of relocatable relocation cannot process these
// relocations. So we have to extend the default code.
template<bool big_endian, typename Classify_reloc>
class Arm_scan_relocatable_relocs :
public Default_scan_relocatable_relocs<Classify_reloc>
{
public:
// Return the strategy to use for a local symbol which is a section
// symbol, given the relocation type.
inline Relocatable_relocs::Reloc_strategy
local_section_strategy(unsigned int r_type, Relobj*)
{
if (Classify_reloc::sh_type == elfcpp::SHT_RELA)
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA;
else
{
if (r_type == elfcpp::R_ARM_TARGET1
|| r_type == elfcpp::R_ARM_TARGET2)
{
const Target_arm<big_endian>* arm_target =
Target_arm<big_endian>::default_target();
r_type = arm_target->get_real_reloc_type(r_type);
}
switch(r_type)
{
// Relocations that write nothing. These exclude R_ARM_TARGET1
// and R_ARM_TARGET2.
case elfcpp::R_ARM_NONE:
case elfcpp::R_ARM_V4BX:
case elfcpp::R_ARM_TLS_GOTDESC:
case elfcpp::R_ARM_TLS_CALL:
case elfcpp::R_ARM_TLS_DESCSEQ:
case elfcpp::R_ARM_THM_TLS_CALL:
case elfcpp::R_ARM_GOTRELAX:
case elfcpp::R_ARM_GNU_VTENTRY:
case elfcpp::R_ARM_GNU_VTINHERIT:
case elfcpp::R_ARM_THM_TLS_DESCSEQ16:
case elfcpp::R_ARM_THM_TLS_DESCSEQ32:
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0;
// These should have been converted to something else above.
case elfcpp::R_ARM_TARGET1:
case elfcpp::R_ARM_TARGET2:
gold_unreachable();
// Relocations that write full 32 bits and
// have alignment of 1.
case elfcpp::R_ARM_ABS32:
case elfcpp::R_ARM_REL32:
case elfcpp::R_ARM_SBREL32:
case elfcpp::R_ARM_GOTOFF32:
case elfcpp::R_ARM_BASE_PREL:
case elfcpp::R_ARM_GOT_BREL:
case elfcpp::R_ARM_BASE_ABS:
case elfcpp::R_ARM_ABS32_NOI:
case elfcpp::R_ARM_REL32_NOI:
case elfcpp::R_ARM_PLT32_ABS:
case elfcpp::R_ARM_GOT_ABS:
case elfcpp::R_ARM_GOT_PREL:
case elfcpp::R_ARM_TLS_GD32:
case elfcpp::R_ARM_TLS_LDM32:
case elfcpp::R_ARM_TLS_LDO32:
case elfcpp::R_ARM_TLS_IE32:
case elfcpp::R_ARM_TLS_LE32:
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4_UNALIGNED;
default:
// For all other static relocations, return RELOC_SPECIAL.
return Relocatable_relocs::RELOC_SPECIAL;
}
}
}
};
template<bool big_endian>
class Target_arm : public Sized_target<32, big_endian>
{
public:
typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
Reloc_section;
// 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);
Target_arm(const Target::Target_info* info = &arm_info)
: Sized_target<32, big_endian>(info),
got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
rel_dyn_(NULL), rel_irelative_(NULL), copy_relocs_(elfcpp::R_ARM_COPY),
got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
stub_tables_(), stub_factory_(Stub_factory::get_instance()),
should_force_pic_veneer_(false),
arm_input_section_map_(), attributes_section_data_(NULL),
fix_cortex_a8_(false), cortex_a8_relocs_info_()
{ }
// Whether we force PCI branch veneers.
bool
should_force_pic_veneer() const
{ return this->should_force_pic_veneer_; }
// Set PIC veneer flag.
void
set_should_force_pic_veneer(bool value)
{ this->should_force_pic_veneer_ = value; }
// Whether we use THUMB-2 instructions.
bool
using_thumb2() const
{
Object_attribute* attr =
this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
int arch = attr->int_value();
return arch == elfcpp::TAG_CPU_ARCH_V6T2 || arch >= elfcpp::TAG_CPU_ARCH_V7;
}
// Whether we use THUMB/THUMB-2 instructions only.
bool
using_thumb_only() const
{
Object_attribute* attr =
this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
if (attr->int_value() == elfcpp::TAG_CPU_ARCH_V6_M
|| attr->int_value() == elfcpp::TAG_CPU_ARCH_V6S_M)
return true;
if (attr->int_value() != elfcpp::TAG_CPU_ARCH_V7
&& attr->int_value() != elfcpp::TAG_CPU_ARCH_V7E_M)
return false;
attr = this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
return attr->int_value() == 'M';
}
// Whether we have an NOP instruction. If not, use mov r0, r0 instead.
bool
may_use_arm_nop() const
{
Object_attribute* attr =
this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
int arch = attr->int_value();
return (arch == elfcpp::TAG_CPU_ARCH_V6T2
|| arch == elfcpp::TAG_CPU_ARCH_V6K
|| arch == elfcpp::TAG_CPU_ARCH_V7
|| arch == elfcpp::TAG_CPU_ARCH_V7E_M);
}
// Whether we have THUMB-2 NOP.W instruction.
bool
may_use_thumb2_nop() const
{
Object_attribute* attr =
this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
int arch = attr->int_value();
return (arch == elfcpp::TAG_CPU_ARCH_V6T2
|| arch == elfcpp::TAG_CPU_ARCH_V7
|| arch == elfcpp::TAG_CPU_ARCH_V7E_M);
}
// Whether we have v4T interworking instructions available.
bool
may_use_v4t_interworking() const
{
Object_attribute* attr =
this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
int arch = attr->int_value();
return (arch != elfcpp::TAG_CPU_ARCH_PRE_V4
&& arch != elfcpp::TAG_CPU_ARCH_V4);
}
// Whether we have v5T interworking instructions available.
bool
may_use_v5t_interworking() const
{
Object_attribute* attr =
this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
int arch = attr->int_value();
if (parameters->options().fix_arm1176())
return (arch == elfcpp::TAG_CPU_ARCH_V6T2
|| arch == elfcpp::TAG_CPU_ARCH_V7
|| arch == elfcpp::TAG_CPU_ARCH_V6_M
|| arch == elfcpp::TAG_CPU_ARCH_V6S_M
|| arch == elfcpp::TAG_CPU_ARCH_V7E_M);
else
return (arch != elfcpp::TAG_CPU_ARCH_PRE_V4
&& arch != elfcpp::TAG_CPU_ARCH_V4
&& arch != elfcpp::TAG_CPU_ARCH_V4T);
}
// Process the relocations to determine unreferenced sections for
// garbage collection.
void
gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<32, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols);
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<32, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols);
// Finalize the sections.
void
do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
// Return the value to use for a dynamic symbol which requires special
// treatment.
uint64_t
do_dynsym_value(const Symbol*) const;
// Return the plt address for globals. Since we have irelative plt entries,
// address calculation is not as straightforward as plt_address + plt_offset.
uint64_t
do_plt_address_for_global(const Symbol* gsym) const
{ return this->plt_section()->address_for_global(gsym); }
// Return the plt address for locals. Since we have irelative plt entries,
// address calculation is not as straightforward as plt_address + plt_offset.
uint64_t
do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
{ return this->plt_section()->address_for_local(relobj, symndx); }
// Relocate a section.
void
relocate_section(const Relocate_info<32, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
Arm_address view_address,
section_size_type view_size,
const Reloc_symbol_changes*);
// Scan the relocs during a relocatable link.
void
scan_relocatable_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<32, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols,
Relocatable_relocs*);
// Scan the relocs for --emit-relocs.
void
emit_relocs_scan(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<32, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_syms,
Relocatable_relocs* rr);
// Emit relocations for a section.
void
relocate_relocs(const Relocate_info<32, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<32>::Elf_Off
offset_in_output_section,
unsigned char* view,
Arm_address view_address,
section_size_type view_size,
unsigned char* reloc_view,
section_size_type reloc_view_size);
// Perform target-specific processing in a relocatable link. This is
// only used if we use the relocation strategy RELOC_SPECIAL.
void
relocate_special_relocatable(const Relocate_info<32, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* preloc_in,
size_t relnum,
Output_section* output_section,
typename elfcpp::Elf_types<32>::Elf_Off
offset_in_output_section,
unsigned char* view,
typename elfcpp::Elf_types<32>::Elf_Addr
view_address,
section_size_type view_size,
unsigned char* preloc_out);
// Return whether SYM is defined by the ABI.
bool
do_is_defined_by_abi(const Symbol* sym) const
{ return strcmp(sym->name(), "__tls_get_addr") == 0; }
// Return whether there is a GOT section.
bool
has_got_section() const
{ return this->got_ != NULL; }
// Return the size of the GOT section.
section_size_type
got_size() const
{
gold_assert(this->got_ != NULL);
return this->got_->data_size();
}
// Return the number of entries in the GOT.
unsigned int
got_entry_count() const
{
if (!this->has_got_section())
return 0;
return this->got_size() / 4;
}
// Return the number of entries in the PLT.
unsigned int
plt_entry_count() const;
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset() const;
// Return the size of each PLT entry.
unsigned int
plt_entry_size() const;
// Get the section to use for IRELATIVE relocations, create it if necessary.
Reloc_section*
rel_irelative_section(Layout*);
// Map platform-specific reloc types
static unsigned int
get_real_reloc_type(unsigned int r_type);
//
// Methods to support stub-generations.
//
// Return the stub factory
const Stub_factory&
stub_factory() const
{ return this->stub_factory_; }
// Make a new Arm_input_section object.
Arm_input_section<big_endian>*
new_arm_input_section(Relobj*, unsigned int);
// Find the Arm_input_section object corresponding to the SHNDX-th input
// section of RELOBJ.
Arm_input_section<big_endian>*
find_arm_input_section(Relobj* relobj, unsigned int shndx) const;
// Make a new Stub_table
Stub_table<big_endian>*
new_stub_table(Arm_input_section<big_endian>*);
// Scan a section for stub generation.
void
scan_section_for_stubs(const Relocate_info<32, big_endian>*, unsigned int,
const unsigned char*, size_t, Output_section*,
bool, const unsigned char*, Arm_address,
section_size_type);
// Relocate a stub.
void
relocate_stub(Stub*, const Relocate_info<32, big_endian>*,
Output_section*, unsigned char*, Arm_address,
section_size_type);
// Get the default ARM target.
static Target_arm<big_endian>*
default_target()
{
gold_assert(parameters->target().machine_code() == elfcpp::EM_ARM
&& parameters->target().is_big_endian() == big_endian);
return static_cast<Target_arm<big_endian>*>(
parameters->sized_target<32, big_endian>());
}
// Whether NAME belongs to a mapping symbol.
static bool
is_mapping_symbol_name(const char* name)
{
return (name
&& name[0] == '$'
&& (name[1] == 'a' || name[1] == 't' || name[1] == 'd')
&& (name[2] == '\0' || name[2] == '.'));
}
// Whether we work around the Cortex-A8 erratum.
bool
fix_cortex_a8() const
{ return this->fix_cortex_a8_; }
// Whether we merge exidx entries in debuginfo.
bool
merge_exidx_entries() const
{ return parameters->options().merge_exidx_entries(); }
// Whether we fix R_ARM_V4BX relocation.
// 0 - do not fix
// 1 - replace with MOV instruction (armv4 target)
// 2 - make interworking veneer (>= armv4t targets only)
General_options::Fix_v4bx
fix_v4bx() const
{ return parameters->options().fix_v4bx(); }
// Scan a span of THUMB code section for Cortex-A8 erratum.
void
scan_span_for_cortex_a8_erratum(Arm_relobj<big_endian>*, unsigned int,
section_size_type, section_size_type,
const unsigned char*, Arm_address);
// Apply Cortex-A8 workaround to a branch.
void
apply_cortex_a8_workaround(const Cortex_a8_stub*, Arm_address,
unsigned char*, Arm_address);
protected:
// Make the PLT-generator object.
Output_data_plt_arm<big_endian>*
make_data_plt(Layout* layout,
Arm_output_data_got<big_endian>* got,
Output_data_space* got_plt,
Output_data_space* got_irelative)
{ return this->do_make_data_plt(layout, got, got_plt, got_irelative); }
// Make an ELF object.
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<32, big_endian>& ehdr);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<32, !big_endian>&)
{ gold_unreachable(); }
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<64, false>&)
{ gold_unreachable(); }
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<64, true>&)
{ gold_unreachable(); }
// Make an output section.
Output_section*
do_make_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags)
{ return new Arm_output_section<big_endian>(name, type, flags); }
void
do_adjust_elf_header(unsigned char* view, int len);
// We only need to generate stubs, and hence perform relaxation if we are
// not doing relocatable linking.
bool
do_may_relax() const
{ return !parameters->options().relocatable(); }
bool
do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
// Determine whether an object attribute tag takes an integer, a
// string or both.
int
do_attribute_arg_type(int tag) const;
// Reorder tags during output.
int
do_attributes_order(int num) const;
// This is called when the target is selected as the default.
void
do_select_as_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
// as the default.
gold_assert(arm_reloc_property_table == NULL);
arm_reloc_property_table = new Arm_reloc_property_table();
}
// Virtual function which is set to return true by a target if
// it can use relocation types to determine if a function's
// pointer is taken.
virtual bool
do_can_check_for_function_pointers() const
{ return true; }
// Whether a section called SECTION_NAME may have function pointers to
// sections not eligible for safe ICF folding.
virtual bool
do_section_may_have_icf_unsafe_pointers(const char* section_name) const
{
return (!is_prefix_of(".ARM.exidx", section_name)
&& !is_prefix_of(".ARM.extab", section_name)
&& Target::do_section_may_have_icf_unsafe_pointers(section_name));
}
virtual void
do_define_standard_symbols(Symbol_table*, Layout*);
virtual Output_data_plt_arm<big_endian>*
do_make_data_plt(Layout* layout,
Arm_output_data_got<big_endian>* got,
Output_data_space* got_plt,
Output_data_space* got_irelative)
{
gold_assert(got_plt != NULL && got_irelative != NULL);
if (parameters->options().long_plt())
return new Output_data_plt_arm_long<big_endian>(
layout, got, got_plt, got_irelative);
else
return new Output_data_plt_arm_short<big_endian>(
layout, got, got_plt, got_irelative);
}
private:
// The class which scans relocations.
class Scan
{
public:
Scan()
: issued_non_pic_error_(false)
{ }
static inline int
get_reference_flags(unsigned int r_type);
inline void
local(Symbol_table* symtab, Layout* layout, Target_arm* target,
Sized_relobj_file<32, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
const elfcpp::Sym<32, big_endian>& lsym,
bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_arm* target,
Sized_relobj_file<32, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
Symbol* gsym);
inline bool
local_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
Sized_relobj_file<32, big_endian>* ,
unsigned int ,
Output_section* ,
const elfcpp::Rel<32, big_endian>& ,
unsigned int ,
const elfcpp::Sym<32, big_endian>&);
inline bool
global_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
Sized_relobj_file<32, big_endian>* ,
unsigned int ,
Output_section* ,
const elfcpp::Rel<32, big_endian>& ,
unsigned int , Symbol*);
private:
static void
unsupported_reloc_local(Sized_relobj_file<32, big_endian>*,
unsigned int r_type);
static void
unsupported_reloc_global(Sized_relobj_file<32, big_endian>*,
unsigned int r_type, Symbol*);
void
check_non_pic(Relobj*, unsigned int r_type);
// Almost identical to Symbol::needs_plt_entry except that it also
// handles STT_ARM_TFUNC.
static bool
symbol_needs_plt_entry(const Symbol* sym)
{
// An undefined symbol from an executable does not need a PLT entry.
if (sym->is_undefined() && !parameters->options().shared())
return false;
if (sym->type() == elfcpp::STT_GNU_IFUNC)
return true;
return (!parameters->doing_static_link()
&& (sym->type() == elfcpp::STT_FUNC
|| sym->type() == elfcpp::STT_ARM_TFUNC)
&& (sym->is_from_dynobj()
|| sym->is_undefined()
|| sym->is_preemptible()));
}
inline bool
possible_function_pointer_reloc(unsigned int r_type);
// Whether a plt entry is needed for ifunc.
bool
reloc_needs_plt_for_ifunc(Sized_relobj_file<32, big_endian>*,
unsigned int r_type);
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
// The class which implements relocation.
class Relocate
{
public:
Relocate()
{ }
~Relocate()
{ }
// Return whether the static relocation needs to be applied.
inline bool
should_apply_static_reloc(const Sized_symbol<32>* gsym,
unsigned int r_type,
bool is_32bit,
Output_section* output_section);
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
relocate(const Relocate_info<32, big_endian>*, unsigned int,
Target_arm*, Output_section*, size_t, const unsigned char*,
const Sized_symbol<32>*, const Symbol_value<32>*,
unsigned char*, Arm_address, section_size_type);
// Return whether we want to pass flag NON_PIC_REF for this
// reloc. This means the relocation type accesses a symbol not via
// GOT or PLT.
static inline bool
reloc_is_non_pic(unsigned int r_type)
{
switch (r_type)
{
// These relocation types reference GOT or PLT entries explicitly.
case elfcpp::R_ARM_GOT_BREL:
case elfcpp::R_ARM_GOT_ABS:
case elfcpp::R_ARM_GOT_PREL:
case elfcpp::R_ARM_GOT_BREL12:
case elfcpp::R_ARM_PLT32_ABS:
case elfcpp::R_ARM_TLS_GD32:
case elfcpp::R_ARM_TLS_LDM32:
case elfcpp::R_ARM_TLS_IE32:
case elfcpp::R_ARM_TLS_IE12GP:
// These relocate types may use PLT entries.
case elfcpp::R_ARM_CALL:
case elfcpp::R_ARM_THM_CALL:
case elfcpp::R_ARM_JUMP24:
case elfcpp::R_ARM_THM_JUMP24:
case elfcpp::R_ARM_THM_JUMP19:
case elfcpp::R_ARM_PLT32:
case elfcpp::R_ARM_THM_XPC22:
case elfcpp::R_ARM_PREL31:
case elfcpp::R_ARM_SBREL31:
return false;
default:
return true;
}
}
private:
// Do a TLS relocation.
inline typename Arm_relocate_functions<big_endian>::Status
relocate_tls(const Relocate_info<32, big_endian>*, Target_arm<big_endian>*,
size_t, const elfcpp::Rel<32, big_endian>&, unsigned int,
const Sized_symbol<32>*, const Symbol_value<32>*,
unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
section_size_type);
};
// A class for inquiring about properties of a relocation,
// used while scanning relocs during a relocatable link and
// garbage collection.
class Classify_reloc :
public gold::Default_classify_reloc<elfcpp::SHT_REL, 32, big_endian>
{
public:
typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc
Reltype;
// Return the explicit addend of the relocation (return 0 for SHT_REL).
static typename elfcpp::Elf_types<32>::Elf_Swxword
get_r_addend(const Reltype*)
{ return 0; }
// Return the size of the addend of the relocation (only used for SHT_REL).
static unsigned int
get_size_for_reloc(unsigned int, Relobj*);
};
// Adjust TLS relocation type based on the options and whether this
// is a local symbol.
static tls::Tls_optimization
optimize_tls_reloc(bool is_final, int r_type);
// Get the GOT section, creating it if necessary.
Arm_output_data_got<big_endian>*
got_section(Symbol_table*, Layout*);
// Get the GOT PLT section.
Output_data_space*
got_plt_section() const
{
gold_assert(this->got_plt_ != NULL);
return this->got_plt_;
}
// Create the PLT section.
void
make_plt_section(Symbol_table* symtab, Layout* layout);
// Create a PLT entry for a global symbol.
void
make_plt_entry(Symbol_table*, Layout*, Symbol*);
// Create a PLT entry for a local STT_GNU_IFUNC symbol.
void
make_local_ifunc_plt_entry(Symbol_table*, Layout*,
Sized_relobj_file<32, big_endian>* relobj,
unsigned int local_sym_index);
// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
void
define_tls_base_symbol(Symbol_table*, Layout*);
// Create a GOT entry for the TLS module index.
unsigned int
got_mod_index_entry(Symbol_table* symtab, Layout* layout,
Sized_relobj_file<32, big_endian>* object);
// Get the PLT section.
const Output_data_plt_arm<big_endian>*
plt_section() const
{
gold_assert(this->plt_ != NULL);
return this->plt_;
}
// Get the dynamic reloc section, creating it if necessary.
Reloc_section*
rel_dyn_section(Layout*);
// Get the section to use for TLS_DESC relocations.
Reloc_section*
rel_tls_desc_section(Layout*) const;
// Return true if the symbol may need a COPY relocation.
// References from an executable object to non-function symbols
// defined in a dynamic object may need a COPY relocation.
bool
may_need_copy_reloc(Symbol* gsym)
{
return (gsym->type() != elfcpp::STT_ARM_TFUNC
&& gsym->may_need_copy_reloc());
}
// Add a potential copy relocation.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
Sized_relobj_file<32, big_endian>* object,
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rel<32, big_endian>& reloc)
{
unsigned int r_type = elfcpp::elf_r_type<32>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<32>(sym),
object, shndx, output_section,
r_type, reloc.get_r_offset(), 0,
this->rel_dyn_section(layout));
}
// Whether two EABI versions are compatible.
static bool
are_eabi_versions_compatible(elfcpp::Elf_Word v1, elfcpp::Elf_Word v2);
// Merge processor-specific flags from input object and those in the ELF
// header of the output.
void
merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word);
// Get the secondary compatible architecture.
static int
get_secondary_compatible_arch(const Attributes_section_data*);
// Set the secondary compatible architecture.
static void
set_secondary_compatible_arch(Attributes_section_data*, int);
static int
tag_cpu_arch_combine(const char*, int, int*, int, int);