| // arm.cc -- arm target support for gold. |
| |
| // Copyright (C) 2009-2021 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); |
| } |
| |
| // 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_(), |
| target1_reloc_(elfcpp::R_ARM_ABS32), |
| // This can be any reloc type but usually is R_ARM_GOT_PREL. |
| target2_reloc_(elfcpp::R_ARM_GOT_PREL) |
| { } |
| |
| // 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 |
| unsigned int |
| get_real_reloc_type(unsigned int r_type) const; |
| |
| // |
| // 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(); |
| if (parameters->options().user_set_target1_rel()) |
| { |
| // FIXME: This is not strictly compatible with ld, which allows both |
| // --target1-abs and --target-rel to be given. |
| if (parameters->options().user_set_target1_abs()) |
| gold_error(_("Cannot use both --target1-abs and --target1-rel.")); |
| else |
| this->target1_reloc_ = elfcpp::R_ARM_REL32; |
| } |
| // We don't need to handle --target1-abs because target1_reloc_ is set |
| // to elfcpp::R_ARM_ABS32 in the member initializer list. |
| |
| if (parameters->options().user_set_target2()) |
| { |
| const char* target2 = parameters->options().target2(); |
| if (strcmp(target2, "rel") == 0) |
| this->target2_reloc_ = elfcpp::R_ARM_REL32; |
| else if (strcmp(target2, "abs") == 0) |
| this->target2_reloc_ = elfcpp::R_ARM_ABS32; |
| else if (strcmp(target2, "got-rel") == 0) |
| this->target2_reloc_ = elfcpp::R_ARM_GOT_PREL; |
| else |
| gold_unreachable(); |
| } |
| } |
| |
| // 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); |
| |
| // Helper to print AEABI enum tag value. |
| static std::string |
| aeabi_enum_name(unsigned int); |
| |
| // Return string value for TAG_CPU_name. |
| static std::string |
| tag_cpu_name_value(unsigned int); |
| |
| // Query attributes object to see if integer divide instructions may be |
| // present in an object. |
| static bool |
| attributes_accept_div(int arch, int profile, |
| const Object_attribute* div_attr); |
| |
| // Query attributes object to see if integer divide instructions are |
| // forbidden to be in the object. This is not the inverse of |
| // attributes_accept_div. |
| static bool |
| attributes_forbid_div(const Object_attribute* div_attr); |
| |
| // Merge object attributes from input object and those in the output. |
| void |
| merge_object_attributes(const char*, const Attributes_section_data*); |
| |
| // Helper to get an AEABI object attribute |
| Object_attribute* |
| get_aeabi_object_attribute(int tag) const |
| { |
| Attributes_section_data* pasd = this->attributes_section_data_; |
| gold_assert(pasd != NULL); |
| Object_attribute* attr = |
| pasd->get_attribute(Object_attribute::OBJ_ATTR_PROC, tag); |
| gold_assert(attr != NULL); |
| return attr; |
| } |
| |
| // |
| // Methods to support stub-generations. |
| // |
| |
| // Group input sections for stub generation. |
| void |
| group_sections(Layout*, section_size_type, bool, const Task*); |
| |
| // Scan a relocation for stub generation. |
| void |
| scan_reloc_for_stub(const Relocate_info<32, big_endian>*, unsigned int, |
| const Sized_symbol<32>*, unsigned int, |
| const Symbol_value<32>*, |
| elfcpp::Elf_types<32>::Elf_Swxword, Arm_address); |
| |
| // Scan a relocation section for stub. |
| template<int sh_type> |
| void |
| scan_reloc_section_for_stubs( |
| const Relocate_info<32, big_endian>* relinfo, |
| const unsigned char* prelocs, |
| size_t reloc_count, |
| Output_section* output_section, |
| bool needs_special_offset_handling, |
| const unsigned char* view, |
| elfcpp::Elf_types<32>::Elf_Addr view_address, |
| section_size_type); |
| |
| // Fix .ARM.exidx section coverage. |
| void |
| fix_exidx_coverage(Layout*, const Input_objects*, |
| Arm_output_section<big_endian>*, Symbol_table*, |
| const Task*); |
| |
| // Functors for STL set. |
| struct output_section_address_less_than |
| { |
| bool |
| operator()(const Output_section* s1, const Output_section* s2) const |
| { return s1->address() < s2->address(); } |
| }; |
| |
| // Information about this specific target which we pass to the |
| // general Target structure. |
| static const Target::Target_info arm_info; |
| |
| // The types of GOT entries needed for this platform. |
| // These values are exposed to the ABI in an incremental link. |
| // Do not renumber existing values without changing the version |
| // number of the .gnu_incremental_inputs section. |
| enum Got_type |
| { |
| GOT_TYPE_STANDARD = 0, // GOT entry for a regular symbol |
| GOT_TYPE_TLS_NOFFSET = 1, // GOT entry for negative TLS offset |
| GOT_TYPE_TLS_OFFSET = 2, // GOT entry for positive TLS offset |
| GOT_TYPE_TLS_PAIR = 3, // GOT entry for TLS module/offset pair |
| GOT_TYPE_TLS_DESC = 4 // GOT entry for TLS_DESC pair |
| }; |
| |
| typedef typename std::vector<Stub_table<big_endian>*> Stub_table_list; |
| |
| // Map input section to Arm_input_section. |
| typedef Unordered_map<Section_id, |
| Arm_input_section<big_endian>*, |
| Section_id_hash> |
| Arm_input_section_map; |
| |
| // Map output addresses to relocs for Cortex-A8 erratum. |
| typedef Unordered_map<Arm_address, const Cortex_a8_reloc*> |
| Cortex_a8_relocs_info; |
| |
| // The GOT section. |
| Arm_output_data_got<big_endian>* got_; |
| // The PLT section. |
| Output_data_plt_arm<big_endian>* plt_; |
| // The GOT PLT section. |
| Output_data_space* got_plt_; |
| // The GOT section for IRELATIVE relocations. |
| Output_data_space* got_irelative_; |
| // The dynamic reloc section. |
| Reloc_section* rel_dyn_; |
| // The section to use for IRELATIVE relocs. |
| Reloc_section* rel_irelative_; |
| // Relocs saved to avoid a COPY reloc. |
| Copy_relocs<elfcpp::SHT_REL, 32, big_endian> copy_relocs_; |
| // Offset of the GOT entry for the TLS module index. |
| unsigned int got_mod_index_offset_; |
| // True if the _TLS_MODULE_BASE_ symbol has been defined. |
| bool tls_base_symbol_defined_; |
| // Vector of Stub_tables created. |
| Stub_table_list stub_tables_; |
| // Stub factory. |
| const Stub_factory &stub_factory_; |
| // Whether we force PIC branch veneers. |
| bool should_force_pic_veneer_; |
| // Map for locating Arm_input_sections. |
| Arm_input_section_map arm_input_section_map_; |
| // Attributes section data in output. |
| Attributes_section_data* attributes_section_data_; |
| // Whether we want to fix code for Cortex-A8 erratum. |
| bool fix_cortex_a8_; |
| // Map addresses to relocs for Cortex-A8 erratum. |
| Cortex_a8_relocs_info cortex_a8_relocs_info_; |
| // What R_ARM_TARGET1 maps to. It can be R_ARM_REL32 or R_ARM_ABS32. |
| unsigned int target1_reloc_; |
| // What R_ARM_TARGET2 maps to. It should be one of R_ARM_REL32, R_ARM_ABS32 |
| // and R_ARM_GOT_PREL. |
| unsigned int target2_reloc_; |
| }; |
| |
| template<bool big_endian> |
| const Target::Target_info Target_arm<big_endian>::arm_info = |
| { |
| 32, // size |
| big_endian, // is_big_endian |
| elfcpp::EM_ARM, // machine_code |
| false, // has_make_symbol |
| false, // has_resolve |
| false, // has_code_fill |
| true, // is_default_stack_executable |
| false, // can_icf_inline_merge_sections |
| '\0', // wrap_char |
| "/usr/lib/libc.so.1", // dynamic_linker |
| 0x8000, // default_text_segment_address |
| 0x1000, // abi_pagesize (overridable by -z max-page-size) |
| 0x1000, // common_pagesize (overridable by -z common-page-size) |
| false, // isolate_execinstr |
| 0, // rosegment_gap |
| elfcpp::SHN_UNDEF, // small_common_shndx |
| elfcpp::SHN_UNDEF, // large_common_shndx |
| 0, // small_common_section_flags |
| 0, // large_common_section_flags |
| ".ARM.attributes", // attributes_section |
| "aeabi", // attributes_vendor |
| "_start", // entry_symbol_name |
| 32, // hash_entry_size |
| elfcpp::SHT_PROGBITS, // unwind_section_type |
| }; |
| |
| // Arm relocate functions class |
| // |
| |
| template<bool big_endian> |
| class Arm_relocate_functions : public Relocate_functions<32, big_endian> |
| { |
| public: |
| typedef enum |
| { |
| STATUS_OKAY, // No error during relocation. |
| STATUS_OVERFLOW, // Relocation overflow. |
| STATUS_BAD_RELOC // Relocation cannot be applied. |
| } Status; |
| |
| private: |
| typedef Relocate_functions<32, big_endian> Base; |
| typedef Arm_relocate_functions<big_endian> This; |
| |
| // Encoding of imm16 argument for movt and movw ARM instructions |
| // from ARM ARM: |
| // |
| // imm16 := imm4 | imm12 |
| // |
| // f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 |
| // +-------+---------------+-------+-------+-----------------------+ |
| // | | |imm4 | |imm12 | |
| // +-------+---------------+-------+-------+-----------------------+ |
| |
| // Extract the relocation addend from VAL based on the ARM |
| // instruction encoding described above. |
| static inline typename elfcpp::Swap<32, big_endian>::Valtype |
| extract_arm_movw_movt_addend( |
| typename elfcpp::Swap<32, big_endian>::Valtype val) |
| { |
| // According to the Elf ABI for ARM Architecture the immediate |
| // field is sign-extended to form the addend. |
| return Bits<16>::sign_extend32(((val >> 4) & 0xf000) | (val & 0xfff)); |
| } |
| |
| // Insert X into VAL based on the ARM instruction encoding described |
| // above. |
| static inline typename elfcpp::Swap<32, big_endian>::Valtype |
| insert_val_arm_movw_movt( |
| typename elfcpp::Swap<32, big_endian>::Valtype val, |
| typename elfcpp::Swap<32, big_endian>::Valtype x) |
| { |
| val &= 0xfff0f000; |
| val |= x & 0x0fff; |
| val |= (x & 0xf000) << 4; |
| return val; |
| } |
| |
| // Encoding of imm16 argument for movt and movw Thumb2 instructions |
| // from ARM ARM: |
| // |
| // imm16 := imm4 | i | imm3 | imm8 |
| // |
| // f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 |
| // +---------+-+-----------+-------++-+-----+-------+---------------+ |
| // | |i| |imm4 || |imm3 | |imm8 | |
| // +---------+-+-----------+-------++-+-----+-------+---------------+ |
| |
| // Extract the relocation addend from VAL based on the Thumb2 |
| // instruction encoding described above. |
| static inline typename elfcpp::Swap<32, big_endian>::Valtype |
| extract_thumb_movw_movt_addend( |
| typename elfcpp::Swap<32, big_endian>::Valtype val) |
| { |
| // According to the Elf ABI for ARM Architecture the immediate |
| // field is sign-extended to form the addend. |
| return Bits<16>::sign_extend32(((val >> 4) & 0xf000) |
| | ((val >> 15) & 0x0800) |
| | ((val >> 4) & 0x0700) |
| | (val & 0x00ff)); |
| } |
| |
| // Insert X into VAL based on the Thumb2 instruction encoding |
| // described above. |
| static inline typename elfcpp::Swap<32, big_endian>::Valtype |
| insert_val_thumb_movw_movt( |
| typename elfcpp::Swap<32, big_endian>::Valtype val, |
| typename elfcpp::Swap<32, big_endian>::Valtype x) |
| { |
| val &= 0xfbf08f00; |
| val |= (x & 0xf000) << 4; |
| val |= (x & 0x0800) << 15; |
| val |= (x & 0x0700) << 4; |
| val |= (x & 0x00ff); |
| return val; |
| } |
| |
| // Calculate the smallest constant Kn for the specified residual. |
| // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32) |
| static uint32_t |
| calc_grp_kn(typename elfcpp::Swap<32, big_endian>::Valtype residual) |
| { |
| int32_t msb; |
| |
| if (residual == 0) |
| return 0; |
| // Determine the most significant bit in the residual and |
| // align the resulting value to a 2-bit boundary. |
| for (msb = 30; (msb >= 0) && !(residual & (3 << msb)); msb -= 2) |
| ; |
| // The desired shift is now (msb - 6), or zero, whichever |
| // is the greater. |
| return (((msb - 6) < 0) ? 0 : (msb - 6)); |
| } |
| |
| // Calculate the final residual for the specified group index. |
| // If the passed group index is less than zero, the method will return |
| // the value of the specified residual without any change. |
| // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32) |
| static typename elfcpp::Swap<32, big_endian>::Valtype |
| calc_grp_residual(typename elfcpp::Swap<32, big_endian>::Valtype residual, |
| const int group) |
| { |
| for (int n = 0; n <= group; n++)<
|