// arm.cc -- arm target support for gold.
// Copyright (C) 2009-2023 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 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);
}
|