diff --git a/elfcpp/aarch64.h b/elfcpp/aarch64.h index 4d1898f..52ac3ea 100644 --- a/elfcpp/aarch64.h +++ b/elfcpp/aarch64.h @@ -46,7 +46,7 @@ enum { // Null relocation codes R_AARCH64_NONE = 0, // None - withdrawn = 256, // Treat as R_AARCH64_NONE + R_AARCH64_withdrawn = 256, // Treat as R_AARCH64_NONE // Static relocations R_AARCH64_ABS64 = 257, // S + A diff --git a/gold/Makefile.am b/gold/Makefile.am index 17ba4b4..df99f23 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -106,6 +106,7 @@ CCFILES = \ HFILES = \ arm-reloc-property.h \ + aarch64-reloc-property.h \ archive.h \ attributes.h \ binary.h \ @@ -158,18 +159,18 @@ HFILES = \ YFILES = \ yyscript.y -DEFFILES = arm-reloc.def +DEFFILES = arm-reloc.def aarch64-reloc.def EXTRA_DIST = yyscript.c yyscript.h TARGETSOURCES = \ i386.cc x86_64.cc sparc.cc powerpc.cc arm.cc arm-reloc-property.cc tilegx.cc \ - mips.cc aarch64.cc + mips.cc aarch64.cc aarch64-reloc-property.cc ALL_TARGETOBJS = \ i386.$(OBJEXT) x86_64.$(OBJEXT) sparc.$(OBJEXT) powerpc.$(OBJEXT) \ arm.$(OBJEXT) arm-reloc-property.$(OBJEXT) tilegx.$(OBJEXT) \ - mips.$(OBJEXT) aarch64.$(OBJEXT) + mips.$(OBJEXT) aarch64.$(OBJEXT) aarch64-reloc-property.$(OBJEXT) libgold_a_SOURCES = $(CCFILES) $(HFILES) $(YFILES) $(DEFFILES) libgold_a_LIBADD = $(LIBOBJS) diff --git a/gold/aarch64-reloc-property.cc b/gold/aarch64-reloc-property.cc new file mode 100644 index 0000000..beaed10 --- /dev/null +++ b/gold/aarch64-reloc-property.cc @@ -0,0 +1,164 @@ +// aarch64-reloc-property.cc -- AArch64 relocation properties -*- C++ -*- + +// Copyright (C) 2014 Free Software Foundation, Inc. +// Written by Han Shen and Jing Yu . + +// 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 "aarch64-reloc-property.h" +#include "aarch64.h" + +#include "symtab.h" + +#include + +namespace gold +{ + +template +bool +rvalue_checkup(int64_t x) +{ + // We save the extra_alignment_requirement bits on [31:16] of U. + // "extra_alignment_requirement" could be 0, 1, 3, 7 and 15. + unsigned short extra_alignment_requirement = (U & 0xFFFF0000) >> 16; + // [15:0] of U indicates the upper bound check. + int64_t u = U & 0x0000FFFF; + if (u == 0) + { + // No requirement to check overflow. + gold_assert(L == 0); + return (x & extra_alignment_requirement) == 0; + } + + // Check both overflow and alignment if needed. + int64_t low_bound = -(L == 0 ? 0 : ((int64_t)1 << L)); + int64_t up_bound = ((int64_t)1 << u); + return ((low_bound <= x && x < up_bound) + && ((x & extra_alignment_requirement) == 0)); +} + +template<> +bool +rvalue_checkup<0, 0>(int64_t) { return true; } + +template +uint64_t +rvalue_bit_select(uint64_t x) +{ + if (U == 63) return x >> L; + return (x & (((uint64_t)1 << (U+1)) - 1)) >> L; +} + +template<> +uint64_t +rvalue_bit_select<0, 0>(uint64_t x) { return x; } + +AArch64_reloc_property::AArch64_reloc_property( + unsigned int code, + const char* name, + Reloc_type rtype, + Reloc_class rclass, + bool is_implemented, + int group_index, + int reference_flags, + Reloc_inst reloc_inst, + rvalue_checkup_func_p rvalue_checkup_func, + rvalue_bit_select_func rvalue_bit_select) + : code_(code), name_(name), reloc_type_(rtype), reloc_class_(rclass), + group_index_(group_index), + is_implemented_(is_implemented), + reference_flags_(reference_flags), + reloc_inst_(reloc_inst), + rvalue_checkup_func_(rvalue_checkup_func), + rvalue_bit_select_func_(rvalue_bit_select) +{} + +AArch64_reloc_property_table::AArch64_reloc_property_table() +{ + const bool Y(true), N(false); + for (unsigned int i = 0; i < Property_table_size; ++i) + table_[i] = NULL; + +#define RL_CHECK_ALIGN2 (1 << 16) +#define RL_CHECK_ALIGN4 (3 << 16) +#define RL_CHECK_ALIGN8 (7 << 16) +#define RL_CHECK_ALIGN16 (15 << 16) + +#undef ARD +#define ARD(rname, type, class, is_implemented, group_index, LB, UB, BSL, BSH, RFLAGS, inst) \ + do \ + { \ + int tidx = code_to_array_index(elfcpp::R_AARCH64_##rname); \ + AArch64_reloc_property * p = new AArch64_reloc_property( \ + elfcpp::R_AARCH64_##rname, "R_AARCH64_" #rname, \ + AArch64_reloc_property::RT_##type, \ + AArch64_reloc_property::RC_##class, \ + is_implemented, \ + group_index, \ + (RFLAGS), \ + AArch64_reloc_property::INST_##inst, \ + rvalue_checkup, \ + rvalue_bit_select); \ + table_[tidx] = p; \ + } \ + while (0); +#include"aarch64-reloc.def" +#undef ARD +} + +// Return a string describing a relocation code that fails to get a +// relocation property in get_implemented_static_reloc_property(). + +std::string +AArch64_reloc_property_table::reloc_name_in_error_message(unsigned int code) +{ + gold_assert(code < Property_table_size); + + const AArch64_reloc_property* arp = this->table_[code]; + + if (arp == NULL) + { + char buffer[100]; + sprintf(buffer, _("invalid reloc %u"), code); + return std::string(buffer); + } + + // gold only implements static relocation codes. + AArch64_reloc_property::Reloc_type reloc_type = arp->reloc_type(); + gold_assert(reloc_type == AArch64_reloc_property::RT_STATIC + || !arp->is_implemented()); + + const char* prefix = NULL; + switch (reloc_type) + { + case AArch64_reloc_property::RT_STATIC: + prefix = arp->is_implemented() ? _("reloc ") : _("unimplemented reloc "); + break; + case AArch64_reloc_property::RT_DYNAMIC: + prefix = _("dynamic reloc "); + break; + default: + gold_unreachable(); + } + return std::string(prefix) + arp->name(); +} + +} diff --git a/gold/aarch64-reloc-property.h b/gold/aarch64-reloc-property.h new file mode 100644 index 0000000..d8d1301 --- /dev/null +++ b/gold/aarch64-reloc-property.h @@ -0,0 +1,245 @@ +// aarch64-reloc-property.h -- AArch64 relocation properties -*- C++ -*- + +// Copyright (C) 2014 Free Software Foundation, Inc. +// Written by Han Shen and Jing Yu . + +// 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. + +#ifndef GOLD_AARCH64_RELOC_PROPERTY_H +#define GOLD_AARCH64_RELOC_PROPERTY_H + +#include +#include + +#include"aarch64.h" + +namespace gold +{ +// The AArch64_reloc_property class is to store information about a particular +// relocation code. + +class AArch64_reloc_property +{ + public: + // Types of relocation codes. + enum Reloc_type { + RT_NONE, // No relocation type. + RT_STATIC, // Relocations processed by static linkers. + RT_DYNAMIC, // Relocations processed by dynamic linkers. + }; + + // Classes of relocation codes. + enum Reloc_class { + RC_NONE, // No relocation class. + RC_DATA, // Data relocation. + RC_AARCH64, // Static AArch64 relocations + RC_CFLOW, // Control flow + RC_TLS, // Thread local storage + RC_DYNAMIC, // Dynamic relocation + }; + + // Instructions that are associated with relocations. + enum Reloc_inst { + INST_DATA = 0, + INST_MOVW = 1, // movz, movk, movn + INST_LD = 2, // ld literal + INST_ADR = 3, // adr + INST_ADRP = 4, // adrp + INST_ADD = 5, // add + INST_LDST = 6, // ld/st + INST_TBZNZ = 7, // tbz/tbnz + INST_CONDB = 8, // B.cond + INST_B = 9, // b [25:0] + INST_CALL = 10, // bl [25:0] + INST_NUM = 11, // total number of entries in the table + }; + + // Types of bases of relative addressing relocation codes. + // enum Relative_address_base { + // RAB_NONE, // Relocation is not relative addressing + // }; + + typedef bool (*rvalue_checkup_func_p)(int64_t); + typedef uint64_t (*rvalue_bit_select_func)(uint64_t); + + // Relocation code represented by this. + unsigned int + code() const + { return this->code_; } + + // Name of the relocation code. + const std::string& + name() const + { return this->name_; } + + // Type of relocation code. + Reloc_type + reloc_type() const + { return this->reloc_type_; } + + // Class of relocation code. + Reloc_class + reloc_class() const + { return this->reloc_class_; } + + // Whether this code is implemented in gold. + bool + is_implemented() const + { return this->is_implemented_; } + + // If code is a group relocation code, return the group number, otherwise -1. + int + group_index() const + { return this->group_index_; } + + // Return alignment of relocation. + size_t + align() const + { return this->align_; } + + int + reference_flags() const + { return this->reference_flags_; } + + // Instruction associated with this relocation. + Reloc_inst + reloc_inst() const + { return this->reloc_inst_; } + + // Check overflow of x + bool checkup_x_value(int64_t x) const + { return this->rvalue_checkup_func_(x); } + + // Return portions of x as is defined in aarch64-reloc.def. + uint64_t select_x_value(uint64_t x) const + { return this->rvalue_bit_select_func_(x); } + + protected: + // These are protected. We only allow AArch64_reloc_property_table to + // manage AArch64_reloc_property. + AArch64_reloc_property(unsigned int code, const char* name, Reloc_type rtype, + Reloc_class rclass, + bool is_implemented, + int group_index, + int reference_flags, + Reloc_inst reloc_inst, + rvalue_checkup_func_p rvalue_checkup_func, + rvalue_bit_select_func rvalue_bit_select); + + friend class AArch64_reloc_property_table; + + private: + // Copying is not allowed. + AArch64_reloc_property(const AArch64_reloc_property&); + AArch64_reloc_property& operator=(const AArch64_reloc_property&); + + // Relocation code. + const unsigned int code_; + // Relocation name. + const std::string name_; + // Type of relocation. + Reloc_type reloc_type_; + // Class of relocation. + Reloc_class reloc_class_; + // Group index (0, 1, or 2) if this is a group relocation or -1 otherwise. + int group_index_; + // Size of relocation. + size_t size_; + // Alignment of relocation. + size_t align_; + // Relative address base. + // Relative_address_base relative_address_base_; + // Whether this is deprecated. + bool is_deprecated_ : 1; + // Whether this is implemented in gold. + bool is_implemented_ : 1; + // Whether this checks overflow. + bool checks_overflow_ : 1; + const int reference_flags_; + // Instruction associated with relocation. + Reloc_inst reloc_inst_; + rvalue_checkup_func_p rvalue_checkup_func_; + rvalue_bit_select_func rvalue_bit_select_func_; +}; + +class AArch64_reloc_property_table +{ + public: + AArch64_reloc_property_table(); + + const AArch64_reloc_property* + get_reloc_property(unsigned int code) const + { + unsigned int idx = code_to_array_index(code); + return this->table_[idx]; + } + + // Like get_reloc_property but only return non-NULL if relocation code is + // static and implemented. + const AArch64_reloc_property* + get_implemented_static_reloc_property(unsigned int code) const + { + unsigned int idx = code_to_array_index(code); + const AArch64_reloc_property* arp = this->table_[idx]; + return ((arp != NULL + && (arp->reloc_type() == AArch64_reloc_property::RT_STATIC) + && arp->is_implemented()) + ? arp + : NULL); + } + + // Return a string describing the relocation code that is not + // an implemented static reloc code. + std::string + reloc_name_in_error_message(unsigned int code); + + private: + // Copying is not allowed. + AArch64_reloc_property_table(const AArch64_reloc_property_table&); + AArch64_reloc_property_table& operator=(const AArch64_reloc_property_table&); + + // Map aarch64 rtypes into range(0,300) as following + // 256 ~ 313 -> 0 ~ 57 + // 512 ~ 573 -> 128 ~ 189 + int + code_to_array_index(unsigned int code) const + { + if (code == 0) return 0; + if (!((code >= elfcpp::R_AARCH64_ABS64 && + code <= elfcpp::R_AARCH64_LD64_GOTPAGE_LO15) + || (code >= elfcpp::R_AARCH64_TLSGD_ADR_PREL21 && + code <= elfcpp::R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC))) + { + gold_error(_("Invalid/unrecognized reloc reloc %d."), code); + } + unsigned int rv = -1; + if (code & (1 << 9)) + rv = 128 + code - 512; // 512 - 573 + else if (code & (1 << 8)) + rv = code - 256; // 256 - 313 + gold_assert(rv <= Property_table_size); + return rv; + } + + static const unsigned int Property_table_size = 300; + AArch64_reloc_property* table_[Property_table_size]; +}; // End of class AArch64_reloc_property_table + +} // End namespace gold. + +#endif // !defined(GOLD_AARCH64_RELOC_PROPERTY_H) diff --git a/gold/aarch64-reloc.def b/gold/aarch64-reloc.def new file mode 100644 index 0000000..b279c69 --- /dev/null +++ b/gold/aarch64-reloc.def @@ -0,0 +1,68 @@ +// aarch64-reloc.def -- AArch64 relocation definitions. + +// Copyright (C) 2014 Free Software Foundation, Inc. +// Written by Han Shen and Jing Yu . + +// 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. +// +// +// +// Insn modified by relocation, see enum Reloc_inst -------------------------------------------------------------------------+ +// Symbol reference type -----------------------------------------------------------------------------+ | +// Portion off X to retrieve -------------------------------------------------------------------+ | | +// Checking function, see Note(A)---------------------------------------+ | | | +// Group index---------------------------------------------------+ | | | | +// Implemented----------------------------------------------+ | | | | | +// Class-------------------------------------+ | | | | | | +// Type----------------------------+ | | | | | | | +// Name | | | | | | | | +// | | | | | | | | | +ARD(ABS64 , STATIC , DATA , Y, -1, 0,0 , 0,0 , Symbol::ABSOLUTE_REF , DATA ) +ARD(ABS32 , STATIC , DATA , Y, -1, 31,32 , 0,0 , Symbol::ABSOLUTE_REF , DATA ) +ARD(ABS16 , STATIC , DATA , Y, -1, 15,16 , 0,0 , Symbol::ABSOLUTE_REF , DATA ) +ARD(PREL64 , STATIC , DATA , Y, -1, 0,0 , 0,0 , Symbol::RELATIVE_REF , DATA ) +ARD(PREL32 , STATIC , DATA , Y, -1, 31,32 , 0,0 , Symbol::RELATIVE_REF , DATA ) +ARD(PREL16 , STATIC , DATA , Y, -1, 15,16 , 0,0 , Symbol::RELATIVE_REF , DATA ) +// Above is from Table 4-6, Data relocations, 257-262. + +ARD(ADR_PREL_PG_HI21 , STATIC , AARCH64 , Y, -1, 32,32 , 12,32 , Symbol::RELATIVE_REF , ADRP ) +ARD(ADR_PREL_PG_HI21_NC , STATIC , AARCH64 , Y, -1, 0,0 , 12,32 , Symbol::RELATIVE_REF , ADRP ) +ARD(LDST8_ABS_LO12_NC , STATIC , AARCH64 , Y, -1, 0,0 , 0,11 , Symbol::ABSOLUTE_REF , LDST ) +ARD(LDST16_ABS_LO12_NC , STATIC , AARCH64 , Y, -1, 0,RL_CHECK_ALIGN2 , 1,11 , Symbol::ABSOLUTE_REF , LDST ) +ARD(LDST32_ABS_LO12_NC , STATIC , AARCH64 , Y, -1, 0,RL_CHECK_ALIGN4 , 2,11 , Symbol::ABSOLUTE_REF , LDST ) +ARD(LDST64_ABS_LO12_NC , STATIC , AARCH64 , Y, -1, 0,RL_CHECK_ALIGN8 , 3,11 , Symbol::ABSOLUTE_REF , LDST ) +ARD(LDST128_ABS_LO12_NC , STATIC , AARCH64 , Y, -1, 0,RL_CHECK_ALIGN16 , 4,11 , Symbol::ABSOLUTE_REF , LDST ) +ARD(ADD_ABS_LO12_NC , STATIC , AARCH64 , Y, -1, 0,0 , 0,11 , Symbol::ABSOLUTE_REF , ADD ) +ARD(ADR_GOT_PAGE , STATIC , AARCH64 , Y, -1, 32,32 , 12,32 , Symbol::RELATIVE_REF , ADRP ) +ARD(LD64_GOT_LO12_NC , STATIC , AARCH64 , Y, -1, 0,RL_CHECK_ALIGN8 , 3,11 , Symbol::ABSOLUTE_REF , LDST ) +ARD(TSTBR14 , STATIC , CFLOW , N, -1, 15,15 , 2,15 , Symbol::ABSOLUTE_REF , TBZNZ ) +ARD(CONDBR19 , STATIC , CFLOW , N, -1, 20,20 , 2,20 , Symbol::ABSOLUTE_REF , CONDB ) +ARD(CALL26 , STATIC , CFLOW , Y, -1, 27,27 , 2,27 , (Symbol::FUNCTION_CALL|Symbol::RELATIVE_REF) , CALL ) +ARD(JUMP26 , STATIC , CFLOW , Y, -1, 27,27 , 2,27 , (Symbol::FUNCTION_CALL|Symbol::RELATIVE_REF) , B ) +// Above is from Table 4-10, Relocations for control-fl + +ARD(TLSIE_MOVW_GOTTPREL_G1 , STATIC , AARCH64 , N, -1, 0,0 , 16,31 , Symbol::ABSOLUTE_REF , MOVW ) +ARD(TLSIE_MOVW_GOTTPREL_G0_NC , STATIC , AARCH64 , N, -1, 0,0 , 0,15 , Symbol::ABSOLUTE_REF , MOVW ) +ARD(TLSIE_ADR_GOTTPREL_PAGE21 , STATIC , AARCH64 , Y, -1, 32,32 , 12,32 , Symbol::ABSOLUTE_REF , ADRP ) +ARD(TLSIE_LD64_GOTTPREL_LO12_NC , STATIC , AARCH64 , N, -1, 32,32 , 12,32 , Symbol::ABSOLUTE_REF , LDST ) +ARD(TLSIE_LD_GOTTPREL_PREL19 , STATIC , AARCH64 , N, -1, 20,20 , 2,20 , Symbol::ABSOLUTE_REF , LD ) +// Above is from Table 4-17, Initial Exec TLS relocatios, 539-543. + +// Note - +// A - Checking X, (L,U), if L == 0 && U == 0, no check. Otherwise, L!=0, check that -2^L<=X<2^U. +// Also an extra alignment check could be embeded into U. diff --git a/gold/aarch64.cc b/gold/aarch64.cc index 17fe031..7d4baaf 100644 --- a/gold/aarch64.cc +++ b/gold/aarch64.cc @@ -1,7 +1,7 @@ // aarch64.cc -- aarch64 target support for gold. // Copyright (C) 2014 Free Software Foundation, Inc. -// Written by Jing Yu . +// Written by Jing Yu and Han Shen . // This file is part of gold. @@ -42,6 +42,7 @@ #include "nacl.h" #include "gc.h" #include "icf.h" +#include "aarch64-reloc-property.h" // The first three .got.plt entries are reserved. const int32_t AARCH64_GOTPLT_RESERVE_COUNT = 3; @@ -60,6 +61,9 @@ class Output_data_plt_aarch64_standard; template class Target_aarch64; +template +class AArch64_relocate_functions; + // Output_data_got_aarch64 class. template @@ -68,7 +72,8 @@ class Output_data_got_aarch64 : public Output_data_got public: typedef typename elfcpp::Elf_types::Elf_Addr Valtype; Output_data_got_aarch64(Symbol_table* symtab, Layout* layout) - : Output_data_got(), layout_(layout) + : Output_data_got(), + symbol_table_(symtab), layout_(layout) { } protected: @@ -84,11 +89,15 @@ class Output_data_got_aarch64 : public Output_data_got } private: + // Symbol table of the output object. + Symbol_table* symbol_table_; // A pointer to the Layout class, so that we can find the .dynamic // section when we write out the GOT section. Layout* layout_; }; +AArch64_reloc_property_table* aarch64_reloc_property_table = NULL; + // The aarch64 target class. // See the ABI at // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf @@ -184,6 +193,15 @@ class Target_aarch64 : public Sized_target unsigned char* reloc_view, section_size_type reloc_view_size); + // Return the PLT section. + uint64_t + do_plt_address_for_global(const Symbol* gsym) const + { return this->plt_section()->address_for_global(gsym); } + + uint64_t + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const + { return this->plt_section()->address_for_local(relobj, symndx); } + // Return the number of entries in the PLT. unsigned int plt_entry_count() const; @@ -196,6 +214,27 @@ class Target_aarch64 : public Sized_target unsigned int plt_entry_size() const; + protected: + void + do_select_as_default_target() + { + gold_assert(aarch64_reloc_property_table == NULL); + aarch64_reloc_property_table = new AArch64_reloc_property_table(); + } + + virtual Output_data_plt_aarch64* + do_make_data_plt(Layout* layout, Output_data_space* got_plt) + { + return new Output_data_plt_aarch64_standard(layout, + got_plt); + } + + Output_data_plt_aarch64* + make_data_plt(Layout* layout, Output_data_space* got_plt) + { + return this->do_make_data_plt(layout, got_plt); + } + private: // The class which scans relocations. class Scan @@ -205,9 +244,6 @@ class Target_aarch64 : public Sized_target : issued_non_pic_error_(false) { } - static inline int - get_reference_flags(unsigned int r_type); - inline void local(Symbol_table* symtab, Layout* layout, Target_aarch64* target, Sized_relobj_file* object, @@ -227,23 +263,23 @@ class Target_aarch64 : public Sized_target inline bool local_reloc_may_be_function_pointer(Symbol_table* , Layout* , - Target_aarch64* , - Sized_relobj_file* , - unsigned int , - Output_section* , - const elfcpp::Rela& , - unsigned int r_type, - const elfcpp::Sym&); + Target_aarch64* , + Sized_relobj_file* , + unsigned int , + Output_section* , + const elfcpp::Rela& , + unsigned int r_type, + const elfcpp::Sym&); inline bool global_reloc_may_be_function_pointer(Symbol_table* , Layout* , - Target_aarch64* , - Sized_relobj_file* , - unsigned int , - Output_section* , - const elfcpp::Rela& , - unsigned int r_type, - Symbol* gsym); + Target_aarch64* , + Sized_relobj_file* , + unsigned int , + Output_section* , + const elfcpp::Rela& , + unsigned int r_type, + Symbol* gsym); private: static void @@ -489,7 +525,7 @@ const Target::Target_info Target_aarch64<32, true>::aarch64_info = template Output_data_got_aarch64* Target_aarch64::got_section(Symbol_table* symtab, - Layout* layout) + Layout* layout) { if (this->got_ == NULL) { @@ -515,10 +551,10 @@ Target_aarch64::got_section(Symbol_table* symtab, // Generate .got section. this->got_ = new Output_data_got_aarch64(symtab, - layout); + layout); layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, - (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_, got_order, true); + (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), + this->got_, got_order, true); // The first word of GOT is reserved for the address of .dynamic. // We put 0 here now. The value will be replaced later in // Output_data_got_aarch64::do_write. @@ -528,32 +564,32 @@ Target_aarch64::got_section(Symbol_table* symtab, // _GLOBAL_OFFSET_TABLE_ value points to the start of the .got section, // even if there is a .got.plt section. this->global_offset_table_ = - symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, - Symbol_table::PREDEFINED, - this->got_, - 0, 0, elfcpp::STT_OBJECT, - elfcpp::STB_LOCAL, - elfcpp::STV_HIDDEN, 0, - false, false); + symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, + Symbol_table::PREDEFINED, + this->got_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); // Generate .got.plt section. this->got_plt_ = new Output_data_space(size / 8, "** GOT PLT"); layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, - (elfcpp::SHF_ALLOC - | elfcpp::SHF_WRITE), - this->got_plt_, got_plt_order, - is_got_plt_relro); + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_plt_, got_plt_order, + is_got_plt_relro); // The first three entries are reserved. - this->got_plt_->set_current_data_size - (AARCH64_GOTPLT_RESERVE_COUNT * (size / 8)); + this->got_plt_->set_current_data_size( + AARCH64_GOTPLT_RESERVE_COUNT * (size / 8)); if (!is_got_plt_relro) - { - // Those bytes can go into the relro segment. - layout->increase_relro - (AARCH64_GOTPLT_RESERVE_COUNT * (size / 8)); - } + { + // Those bytes can go into the relro segment. + layout->increase_relro( + AARCH64_GOTPLT_RESERVE_COUNT * (size / 8)); + } } return this->got_; @@ -590,8 +626,8 @@ class Output_data_plt_aarch64 : public Output_section_data typedef typename elfcpp::Elf_types::Elf_Addr Address; Output_data_plt_aarch64(Layout* layout, - uint64_t addralign, - Output_data_space* got_plt) + uint64_t addralign, + Output_data_space* got_plt) : Output_section_data(addralign), got_plt_(got_plt), count_(0) @@ -610,6 +646,11 @@ class Output_data_plt_aarch64 : public Output_section_data rela_plt() { return this->rel_; } + // Return whether we created a section for IRELATIVE relocations. + bool + has_irelative_section() const + { return this->irelative_rel_ != NULL; } + // Return the number of PLT entries. unsigned int entry_count() const @@ -625,6 +666,14 @@ class Output_data_plt_aarch64 : public Output_section_data get_plt_entry_size() const { return this->do_get_plt_entry_size(); } + // Return the PLT address to use for a global symbol. + uint64_t + address_for_global(const Symbol*); + + // Return the PLT address to use for a local symbol. + uint64_t + address_for_local(const Relobj*, unsigned int symndx); + protected: // Fill in the first PLT entry. void @@ -682,6 +731,9 @@ class Output_data_plt_aarch64 : public Output_section_data // The reloc section. Reloc_section* rel_; + // The IRELATIVE relocs, if necessary. These must follow the + // regular PLT relocations. + Reloc_section* irelative_rel_; // The .got section. Output_data_got_aarch64* got_; // The .got.plt section. @@ -717,16 +769,67 @@ void Output_data_plt_aarch64::add_entry(Symbol* gsym) { gold_assert(!gsym->has_plt_offset()); - //TODO + + gsym->set_plt_offset((this->count_) * this->get_plt_entry_size() + + this->first_plt_entry_offset()); + + ++this->count_; + + section_offset_type got_offset = this->got_plt_->current_data_size(); + + // Every PLT entry needs a GOT entry which points back to the PLT + // entry (this will be changed by the dynamic linker, normally + // lazily when the function is called). + this->got_plt_->set_current_data_size(got_offset + size / 8); + + // Every PLT entry needs a reloc. + gsym->set_needs_dynsym_entry(); + this->rel_->add_global(gsym, elfcpp::R_AARCH64_JUMP_SLOT, + this->got_plt_, got_offset, 0); + + // Note that we don't need to save the symbol. The contents of the + // PLT are independent of which symbols are used. The symbols only + // appear in the relocations. +} + +// Return the PLT address to use for a global symbol. + +template +uint64_t +Output_data_plt_aarch64::address_for_global( + const Symbol* gsym) +{ + uint64_t offset = 0; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + offset = (this->first_plt_entry_offset() + + this->count_ * this->get_plt_entry_size()); + return this->address() + offset + gsym->plt_offset(); +} + +// Return the PLT address to use for a local symbol. These are always +// IRELATIVE relocs. + +template +uint64_t +Output_data_plt_aarch64::address_for_local( + const Relobj* object, + unsigned int r_sym) +{ + return (this->address() + + this->first_plt_entry_offset() + + this->count_ * this->get_plt_entry_size() + + object->local_plt_offset(r_sym)); } // Set the final size. + template void Output_data_plt_aarch64::set_final_data_size() { this->set_data_size(this->first_plt_entry_offset() - + this->count * this->get_plt_entry_size()); + + this->count_ * this->get_plt_entry_size()); } template @@ -737,8 +840,8 @@ class Output_data_plt_aarch64_standard : typedef typename elfcpp::Elf_types::Elf_Addr Address; Output_data_plt_aarch64_standard(Layout* layout, Output_data_space* got_plt) : Output_data_plt_aarch64(layout, - size == 32 ? 4 : 8, - got_plt) + size == 32 ? 4 : 8, + got_plt) { } protected: @@ -754,15 +857,15 @@ class Output_data_plt_aarch64_standard : virtual void do_fill_first_plt_entry(unsigned char* pov, - Address got_address, - Address plt_address); + Address got_address, + Address plt_address); virtual void do_fill_plt_entry(unsigned char* pov, - Address got_address, - Address plt_address, - unsigned int got_offset, - unsigned int plt_offset); + Address got_address, + Address plt_address, + unsigned int got_offset, + unsigned int plt_offset); private: // The size of the first plt entry size. @@ -885,8 +988,8 @@ template void Output_data_plt_aarch64_standard::do_fill_first_plt_entry( unsigned char* pov, - Address /* got_address */, - Address /* plt_address */) + Address got_address, + Address plt_address) { // PLT0 of the small PLT looks like this in ELF64 - // stp x16, x30, [sp, #-16]! Save the reloc and lr on stack. @@ -899,22 +1002,62 @@ Output_data_plt_aarch64_standard::do_fill_first_plt_entry( // PLT0 will be slightly different in ELF32 due to different got entry // size. memcpy(pov, this->first_plt_entry, this->first_plt_entry_size); - // TODO + Address gotplt_2nd_ent = got_address + (size / 8) * 2; + + // Fill in the top 21 bits for this: ADRP x16, PLT_GOT + 8 * 2. + // ADRP: (PG(S+A)-PG(P)) >> 12) & 0x1fffff. + // FIXME: This only works for 64bit + AArch64_relocate_functions::adrp(pov + 4, + gotplt_2nd_ent, plt_address + 4); + + // Fill in R_AARCH64_LDST8_LO12 + elfcpp::Swap<32, big_endian>::writeval( + pov + 8, + ((this->first_plt_entry[2] & 0xffc003ff) + | ((gotplt_2nd_ent & 0xff8) << 7))); + + // Fill in R_AARCH64_ADD_ABS_LO12 + elfcpp::Swap<32, big_endian>::writeval( + pov + 12, + ((this->first_plt_entry[3] & 0xffc003ff) + | ((gotplt_2nd_ent & 0xfff) << 10))); } // Subsequent entries in the PLT for an executable. +// FIXME: This only works for 64bit template void Output_data_plt_aarch64_standard::do_fill_plt_entry( unsigned char* pov, - Address /* got_address*/, - Address /* plt_address */, - unsigned int /* got_offset */, - unsigned int /* plt_offset */) + Address got_address, + Address plt_address, + unsigned int got_offset, + unsigned int plt_offset) { memcpy(pov, this->plt_entry, this->plt_entry_size); - //TODO + + Address gotplt_entry_address = got_address + got_offset; + Address plt_entry_address = plt_address + plt_offset; + + // Fill in R_AARCH64_PCREL_ADR_HI21 + AArch64_relocate_functions::adrp( + pov, + gotplt_entry_address, + plt_entry_address); + + // Fill in R_AARCH64_LDST64_ABS_LO12 + elfcpp::Swap<32, big_endian>::writeval( + pov + 4, + ((this->plt_entry[1] & 0xffc003ff) + | ((gotplt_entry_address & 0xff8) << 7))); + + // Fill in R_AARCH64_ADD_ABS_LO12 + elfcpp::Swap<32, big_endian>::writeval( + pov + 8, + ((this->plt_entry[2] & 0xffc003ff) + | ((gotplt_entry_address & 0xfff) <<10))); + } // Write out the PLT. This uses the hand-coded instructions above, @@ -939,8 +1082,6 @@ Output_data_plt_aarch64::do_write(Output_file* of) // The base address of the .plt section. typename elfcpp::Elf_types::Elf_Addr plt_address = this->address(); - // The base address of the .got section. - typename elfcpp::Elf_types::Elf_Addr got_base = this->got_->address(); // The base address of the PLT portion of the .got section. typename elfcpp::Elf_types::Elf_Addr got_address = this->got_plt_->address(); @@ -966,11 +1107,10 @@ Output_data_plt_aarch64::do_write(Output_file* of) { // Set and adjust the PLT entry itself. this->fill_plt_entry(pov, got_address, plt_address, - got_offset, plt_offset); + got_offset, plt_offset); - // Set the entry in the GOT. - elfcpp::Swap::writeval(got_pov, - plt_address + plt_offset); + // Set the entry in the GOT, which points to plt0. + elfcpp::Swap::writeval(got_pov, plt_address); } gold_assert(static_cast(pov - oview) == oview_size); @@ -980,6 +1120,341 @@ Output_data_plt_aarch64::do_write(Output_file* of) of->write_output_view(got_file_offset, got_size, got_view); } +// Telling how to update the immediate field of an instruction. +struct AArch64_howto +{ + // The immediate field mask. + elfcpp::Elf_Xword dst_mask; + + // The offset to apply relocation immediate + int doffset; + + // The second part offset, if the immediate field has two parts. + // -1 if the immediate field has only one part. + int doffset2; +}; + +static const AArch64_howto aarch64_howto[AArch64_reloc_property::INST_NUM] = +{ + {0, -1, -1}, // DATA + {0x1fffe0, 5, -1}, // MOVW [20:5]-imm16 + {0xffffe0, 5, -1}, // LD [23:5]-imm19 + {0x60ffffe0, 29, 5}, // ADR [30:29]-immlo [23:5]-immhi + {0x60ffffe0, 29, 5}, // ADRP [30:29]-immlo [23:5]-immhi + {0x3ffc00, 10, -1}, // ADD [21:10]-imm12 + {0x3ffc00, 10, -1}, // LDST [21:10]-imm12 + {0x7ffe0, 5, -1}, // TBZNZ [18:5]-imm14 + {0xffffe0, 5, -1}, // CONDB [23:5]-imm19 + {0x3ffffff, 0, -1}, // B [25:0]-imm26 + {0x3ffffff, 0, -1}, // CALL [25:0]-imm26 +}; + +// AArch64 relocate function class + +template +class AArch64_relocate_functions +{ + public: + typedef enum + { + STATUS_OKAY, // No error during relocation. + STATUS_OVERFLOW, // Relocation overflow. + STATUS_BAD_RELOC, // Relocation cannot be applied. + } Status; + + private: + typedef AArch64_relocate_functions This; + + // Return the page address of the address. + // Page(address) = address & ~0xFFF + + static inline typename elfcpp::Swap::Valtype + Page(typename elfcpp::Elf_types::Elf_Addr address) + { + return (address & + (~(typename elfcpp::Elf_types::Elf_Addr)0xFFF)); + } + + // Update instruction (pointed by view) with selected bits (immed). + // val = (val & ~dst_mask) | (immed << doffset) + + template + static inline void + update_view(unsigned char* view, + typename elfcpp::Swap::Valtype immed, + elfcpp::Elf_Xword doffset, + elfcpp::Elf_Xword dst_mask) + { + typedef typename elfcpp::Swap::Valtype Valtype; + Valtype* wv = reinterpret_cast(view); + Valtype val = elfcpp::Swap::readval(wv); + + // Clear immediate fields. + val &= ~dst_mask; + elfcpp::Swap::writeval(wv, + static_cast(val | (immed << doffset))); + } + + // Update two parts of an instruction (pointed by view) with selected + // bits (immed1 and immed2). + // val = (val & ~dst_mask) | (immed1 << doffset1) | (immed2 << doffset2) + + template + static inline void + update_view_two_parts( + unsigned char* view, + typename elfcpp::Swap::Valtype immed1, + typename elfcpp::Swap::Valtype immed2, + elfcpp::Elf_Xword doffset1, + elfcpp::Elf_Xword doffset2, + elfcpp::Elf_Xword dst_mask) + { + typedef typename elfcpp::Swap::Valtype Valtype; + Valtype* wv = reinterpret_cast(view); + Valtype val = elfcpp::Swap::readval(wv); + val &= ~dst_mask; + elfcpp::Swap::writeval(wv, + static_cast(val | (immed1 << doffset1) | + (immed2 << doffset2))); + } + + // Update adr or adrp instruction with [32:12] of X. + // In adr and adrp: [30:29] immlo [23:5] immhi + + static inline void + update_adr(unsigned char* view, + typename elfcpp::Swap::Valtype x, + const AArch64_reloc_property* /* reloc_property */) + { + elfcpp::Elf_Xword dst_mask = (0x3 << 29) | (0x7ffff << 5); + typename elfcpp::Swap<32, big_endian>::Valtype immed = + (x >> 12) & 0x1fffff; + This::template update_view_two_parts<32>( + view, + immed & 0x3, + (immed & 0x1ffffc) >> 2, + 29, + 5, + dst_mask); + } + + public: + + // Do a simple rela relocation at unaligned addresses. + + template + static inline typename This::Status + rela_ua(unsigned char* view, + const Sized_relobj_file* object, + const Symbol_value* psymval, + typename elfcpp::Swap::Valtype addend, + const AArch64_reloc_property* reloc_property) + { + typedef typename elfcpp::Swap_unaligned::Valtype + Valtype; + typename elfcpp::Elf_types::Elf_Addr x = + psymval->value(object, addend); + elfcpp::Swap_unaligned::writeval(view, + static_cast(x)); + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + + // Do a simple pc-relative relocation at unaligned addresses. + + template + static inline typename This::Status + pcrela_ua(unsigned char* view, + const Sized_relobj_file* object, + const Symbol_value* psymval, + typename elfcpp::Swap::Valtype addend, + typename elfcpp::Elf_types::Elf_Addr address, + const AArch64_reloc_property* reloc_property) + { + typedef typename elfcpp::Swap_unaligned::Valtype + Valtype; + typename elfcpp::Elf_types::Elf_Addr x = + psymval->value(object, addend) - address; + elfcpp::Swap_unaligned::writeval(view, + static_cast(x)); + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + + // Do a simple rela relocation at aligned addresses. + + template + static inline typename This::Status + rela( + unsigned char* view, + const Sized_relobj_file* object, + const Symbol_value* psymval, + typename elfcpp::Swap::Valtype addend, + const AArch64_reloc_property* reloc_property) + { + typedef typename elfcpp::Swap::Valtype + Valtype; + Valtype* wv = reinterpret_cast(view); + typename elfcpp::Elf_types::Elf_Addr x = + psymval->value(object, addend); + elfcpp::Swap::writeval(wv, + static_cast(x)); + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + + // Do relocate. Update selected bits in text. + // new_val = (val & ~dst_mask) | (immed << doffset) + + template + static inline typename This::Status + rela_general(unsigned char* view, + const Sized_relobj_file* object, + const Symbol_value* psymval, + typename elfcpp::Swap::Valtype addend, + const AArch64_reloc_property* reloc_property) + { + // Calculate relocation. + typename elfcpp::Elf_types::Elf_Addr x = + psymval->value(object, addend); + + // Select bits from X. + typename elfcpp::Elf_types::Elf_Addr immed = + reloc_property->select_x_value(x); + + // Update view. + const AArch64_reloc_property::Reloc_inst inst = + reloc_property->reloc_inst(); + // If it is a data relocation or instruction has 2 parts of immediate + // fields, you should not call rela_general. + gold_assert(aarch64_howto[inst].doffset2 == -1 && + aarch64_howto[inst].doffset != -1); + This::template update_view(view, immed, + aarch64_howto[inst].doffset, + aarch64_howto[inst].dst_mask); + + // Do check overflow or alignment if needed. + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + + // Do relocate. Update selected bits in text. + // new val = (val & ~dst_mask) | (immed << doffset) + + template + static inline typename This::Status + rela_general( + unsigned char* view, + typename elfcpp::Swap::Valtype s, + typename elfcpp::Swap::Valtype addend, + const AArch64_reloc_property* reloc_property) + { + // Calculate relocation. + typename elfcpp::Elf_types::Elf_Addr x = s + addend; + + // Select bits from X. + typename elfcpp::Elf_types::Elf_Addr immed = + reloc_property->select_x_value(x); + + // Update view. + const AArch64_reloc_property::Reloc_inst inst = + reloc_property->reloc_inst(); + // If it is a data relocation or instruction has 2 parts of immediate + // fields, you should not call rela_general. + gold_assert(aarch64_howto[inst].doffset2 == -1 && + aarch64_howto[inst].doffset != -1); + This::template update_view(view, immed, + aarch64_howto[inst].doffset, + aarch64_howto[inst].dst_mask); + + // Do check overflow or alignment if needed. + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + + // Do address relative relocate. Update selected bits in text. + // new val = (val & ~dst_mask) | (immed << doffset) + + template + static inline typename This::Status + pcrela_general( + unsigned char* view, + const Sized_relobj_file* object, + const Symbol_value* psymval, + typename elfcpp::Swap::Valtype addend, + typename elfcpp::Elf_types::Elf_Addr address, + const AArch64_reloc_property* reloc_property) + { + // Calculate relocation. + typename elfcpp::Elf_types::Elf_Addr x = + psymval->value(object, addend) - address; + + // Select bits from X. + typename elfcpp::Elf_types::Elf_Addr immed = + reloc_property->select_x_value(x); + + // Update view. + const AArch64_reloc_property::Reloc_inst inst = + reloc_property->reloc_inst(); + // If it is a data relocation or instruction has 2 parts of immediate + // fields, you should not call pcrela_general. + gold_assert(aarch64_howto[inst].doffset2 == -1 && + aarch64_howto[inst].doffset != -1); + This::template update_view(view, immed, + aarch64_howto[inst].doffset, + aarch64_howto[inst].dst_mask); + + // Do check overflow or alignment if needed. + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + + // Calculate PG(S+A) - PG(address), update adrp instruction. + // R_AARCH64_ADR_PREL_PG_HI21 + + static inline typename This::Status + adrp( + unsigned char* view, + typename elfcpp::Elf_types::Elf_Addr sa, + typename elfcpp::Elf_types::Elf_Addr address) + { + typename elfcpp::Swap::Valtype x = + This::Page(sa) - This::Page(address); + update_adr(view, x, NULL); + return (size == 64 && Bits<32>::has_overflow(x) + ? This::STATUS_OVERFLOW + : This::STATUS_OKAY); + } + + // Calculate PG(S+A) - PG(address), update adrp instruction. + // R_AARCH64_ADR_PREL_PG_HI21 + + static inline typename This::Status + adrp(unsigned char* view, + const Sized_relobj_file* object, + const Symbol_value* psymval, + typename elfcpp::Elf_types::Elf_Addr addend, + typename elfcpp::Elf_types::Elf_Addr address, + const AArch64_reloc_property* reloc_property) + { + typename elfcpp::Elf_types::Elf_Addr sa = + psymval->value(object, addend); + typename elfcpp::Swap::Valtype x = + This::Page(sa) - This::Page(address); + update_adr(view, x, reloc_property); + return (reloc_property->checkup_x_value(x) + ? This::STATUS_OKAY + : This::STATUS_OVERFLOW); + } + +}; + // Return the number of entries in the PLT. template @@ -1016,29 +1491,12 @@ Target_aarch64::plt_entry_size() const template tls::Tls_optimization Target_aarch64::optimize_tls_reloc(bool /* is_final */, - int /* r_type */) + int /* r_type */) { //TODO return tls::TLSOPT_NONE; } -// Get the Reference_flags for a particular relocation. -template -int -Target_aarch64::Scan::get_reference_flags(unsigned int r_type) -{ - switch (r_type) - { - case elfcpp::R_AARCH64_NONE: - // No symbol reference. - return 0; - //TODO - default: - // Not expected. We will give an error later. - return 0; - } -} - // Returns true if this relocation type could be that of a function pointer. template @@ -1051,7 +1509,7 @@ Target_aarch64::Scan::possible_function_pointer_reloc( case elfcpp::R_AARCH64_ABS64: //TODO { - return true; + return true; } } return false; @@ -1078,7 +1536,7 @@ Target_aarch64::Scan::local_reloc_may_be_function_pointer( // not possible to distinguish pointer taken versus a call by looking at // the relocation types. return (parameters->options().shared() - || possible_function_pointer_reloc(r_type)); + || possible_function_pointer_reloc(r_type)); } // For safe ICF, scan a relocation for a global symbol to check if it @@ -1101,10 +1559,10 @@ Target_aarch64::Scan::global_reloc_may_be_function_pointer( // When building a shared library, do not fold symbols whose visibility // is hidden, internal or protected. return ((parameters->options().shared() - && (gsym->visibility() == elfcpp::STV_INTERNAL - || gsym->visibility() == elfcpp::STV_PROTECTED - || gsym->visibility() == elfcpp::STV_HIDDEN)) - || possible_function_pointer_reloc(r_type)); + && (gsym->visibility() == elfcpp::STV_INTERNAL + || gsym->visibility() == elfcpp::STV_PROTECTED + || gsym->visibility() == elfcpp::STV_HIDDEN)) + || possible_function_pointer_reloc(r_type)); } // Report an unsupported relocation against a local symbol. @@ -1125,7 +1583,7 @@ Target_aarch64::Scan::unsupported_reloc_local( template void Target_aarch64::Scan::check_non_pic(Relobj* object, - unsigned int r_type) + unsigned int r_type) { gold_assert(r_type != elfcpp::R_AARCH64_NONE); @@ -1157,7 +1615,7 @@ Target_aarch64::Scan::check_non_pic(Relobj* object, return; gold_assert(parameters->options().output_is_position_independent()); object->error(_("requires unsupported dynamic reloc; " - "recompile with -fPIC")); + "recompile with -fPIC")); this->issued_non_pic_error_ = true; return; } @@ -1170,7 +1628,7 @@ Target_aarch64::Scan::local( Symbol_table* /* symtab */, Layout* /* layout */, Target_aarch64* /* target */, - Sized_relobj_file* /* object */, + Sized_relobj_file* object, unsigned int /* data_shndx */, Output_section* /* output_section */, const elfcpp::Rela& /* reloc */, @@ -1183,10 +1641,35 @@ Target_aarch64::Scan::local( switch (r_type) { - case elfcpp::R_AARCH64_NONE: + case elfcpp::R_AARCH64_ABS64: + case elfcpp::R_AARCH64_ABS32: + case elfcpp::R_AARCH64_ABS16: + // If building a shared library or pie, we need to mark this as a dynmic + // reloction, so that the dynamic loader can relocate it. + // Not supported yet. + if (parameters->options().output_is_position_independent()) + { + gold_error(_("%s: unsupported ABS64 relocation type for pie or " + "shared library.\n"), + object->name().c_str()); + } + break; + + // Relocations to generate 19, 21 and 33-bit PC-relative address + case elfcpp::R_AARCH64_ADR_PREL_PG_HI21: // 275 + case elfcpp::R_AARCH64_LDST8_ABS_LO12_NC: // 278 + case elfcpp::R_AARCH64_LDST64_ABS_LO12_NC: // 286 + case elfcpp::R_AARCH64_ADD_ABS_LO12_NC: // 277 break; - //TODO + // Control flow, pc-relative. We don't need to do anything for a relative + // addressing relocation against a local symbol if it does not reference + // the GOT. + case elfcpp::R_AARCH64_CALL26: // 283 + break; + + default: + unsupported_reloc_local(object, r_type); } } @@ -1207,18 +1690,132 @@ Target_aarch64::Scan::unsupported_reloc_global( template inline void Target_aarch64::Scan::global( - Symbol_table* /* symtab */, - Layout* /* layout */, - Target_aarch64* /* target */, + Symbol_table* symtab, + Layout* layout, + Target_aarch64* target, Sized_relobj_file* /* object */, unsigned int /* data_shndx */, Output_section* /* output_section */, const elfcpp::Rela& /* reloc */, - unsigned int /* r_type */, - Symbol* /* gsym */) + unsigned int r_type, + Symbol* gsym) { - //TODO + switch (r_type) + { + case elfcpp::R_AARCH64_ABS64: + // This is used to fill the GOT absolute address. + if (gsym->needs_plt_entry()) + { + target->make_plt_entry(symtab, layout, gsym); + } + break; + + case elfcpp::R_AARCH64_ADR_PREL_PG_HI21: + case elfcpp::R_AARCH64_ADR_PREL_PG_HI21_NC: + case elfcpp::R_AARCH64_ADD_ABS_LO12_NC: + { + // Do nothing here. + break; + } + + case elfcpp::R_AARCH64_ADR_GOT_PAGE: + case elfcpp::R_AARCH64_LD64_GOT_LO12_NC: + { + // This pair of relocations is used to access a specific GOT entry. + // Note a GOT entry is an *address* to a symbol. + // The symbol requires a GOT entry + Output_data_got_aarch64* got = + target->got_section(symtab, layout); + if (gsym->final_value_is_known()) + { + got->add_global(gsym, GOT_TYPE_STANDARD); + } + else + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + if (gsym->is_from_dynobj() + || gsym->is_undefined() + || gsym->is_preemptible() + || (gsym->visibility() == elfcpp::STV_PROTECTED + && parameters->options().shared())) + got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, + rela_dyn, elfcpp::R_AARCH64_GLOB_DAT); + else + { + // Not implemented yet. + gold_assert(false); + } + } + break; + } + + case elfcpp::R_AARCH64_JUMP26: + case elfcpp::R_AARCH64_CALL26: + { + if (gsym->final_value_is_known()) + break; + + if (gsym->is_defined() && + !gsym->is_from_dynobj() && + !gsym->is_preemptible()) + break; + + // Make plt entry for function call. + const AArch64_reloc_property* arp = + aarch64_reloc_property_table->get_reloc_property(r_type); + gold_assert(arp != NULL); + target->make_plt_entry(symtab, layout, gsym); + break; + } + + default: + gold_error(_("%s: unsupported reloc type"), + aarch64_reloc_property_table-> + reloc_name_in_error_message(r_type).c_str()); + } return; +} // End of Scan::global + +// Create the PLT section. +template +void +Target_aarch64::make_plt_section( + Symbol_table* symtab, Layout* layout) +{ + if (this->plt_ == NULL) + { + // Create the GOT section first. + this->got_section(symtab, layout); + + this->plt_ = this->make_data_plt(layout, this->got_plt_); + + layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_EXECINSTR), + this->plt_, ORDER_PLT, false); + + // Make the sh_info field of .rela.plt point to .plt. + Output_section* rela_plt_os = this->plt_->rela_plt()->output_section(); + rela_plt_os->set_info_section(this->plt_->output_section()); + } +} + +// Create a PLT entry for a global symbol. + +template +void +Target_aarch64::make_plt_entry( + Symbol_table* symtab, + Layout* layout, + Symbol* gsym) +{ + if (gsym->has_plt_offset()) + return; + + if (this->plt_ == NULL) + this->make_plt_section(symtab, layout); + + this->plt_->add_entry(gsym); } template @@ -1241,11 +1838,12 @@ Target_aarch64::gc_process_relocs( return; } - gold::gc_process_relocs, - elfcpp::SHT_RELA, - typename Target_aarch64::Scan, - typename Target_aarch64::Relocatable_size_for_reloc>( + gold::gc_process_relocs< + size, big_endian, + Target_aarch64, + elfcpp::SHT_RELA, + typename Target_aarch64::Scan, + typename Target_aarch64::Relocatable_size_for_reloc>( symtab, layout, this, @@ -1282,10 +1880,7 @@ Target_aarch64::scan_relocs( object->name().c_str()); return; } - - gold::scan_relocs, - elfcpp::SHT_RELA, - typename Target_aarch64::Scan>( + gold::scan_relocs( symtab, layout, this, @@ -1304,11 +1899,73 @@ Target_aarch64::scan_relocs( template void Target_aarch64::do_finalize_sections( - Layout* /* layout */, + Layout* layout, const Input_objects*, - Symbol_table* /* symtab */) + Symbol_table* symtab) { - //TODO + const Reloc_section* rel_plt = (this->plt_ == NULL + ? NULL + : this->plt_->rela_plt()); + layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt, + this->rela_dyn_, true, false); + + // Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of + // the .got.plt section. + Symbol* sym = this->global_offset_table_; + if (sym != NULL) + { + uint64_t data_size = this->got_plt_->current_data_size(); + symtab->get_sized_symbol(sym)->set_symsize(data_size); + + // If the .got section is more than 0x8000 bytes, we add + // 0x8000 to the value of _GLOBAL_OFFSET_TABLE_, so that 16 + // bit relocations have a greater chance of working. + if (data_size >= 0x8000) + symtab->get_sized_symbol(sym)->set_value( + symtab->get_sized_symbol(sym)->value() + 0x8000); + } + + if (parameters->doing_static_link() + && (this->plt_ == NULL || !this->plt_->has_irelative_section())) + { + // If linking statically, make sure that the __rela_iplt symbols + // were defined if necessary, even if we didn't create a PLT. + static const Define_symbol_in_segment syms[] = + { + { + "__rela_iplt_start", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_W, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + true // only_if_ref + }, + { + "__rela_iplt_end", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_W, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + true // only_if_ref + } + }; + + symtab->define_symbols(layout, 2, syms, + layout->script_options()->saw_sections_clause()); + } + return; } @@ -1317,19 +1974,198 @@ Target_aarch64::do_finalize_sections( template inline bool Target_aarch64::Relocate::relocate( - const Relocate_info* /* relinfo */, - Target_aarch64* /* target */, + const Relocate_info* relinfo, + Target_aarch64* target, Output_section* , - size_t /* relnum */, - const elfcpp::Rela& /* rela */, - unsigned int /* r_type */, - const Sized_symbol* /* gsym */, - const Symbol_value* /* psymval */, - unsigned char* /* view */, - typename elfcpp::Elf_types::Elf_Addr /* address */, + size_t relnum, + const elfcpp::Rela& rela, + unsigned int r_type, + const Sized_symbol* gsym, + const Symbol_value* psymval, + unsigned char* view, + typename elfcpp::Elf_types::Elf_Addr address, section_size_type /* view_size */) { - //TODO + if (view == NULL) + return true; + + typedef AArch64_relocate_functions Reloc; + + const AArch64_reloc_property* reloc_property = + aarch64_reloc_property_table->get_reloc_property(r_type); + + if (reloc_property == NULL) + { + std::string reloc_name = + aarch64_reloc_property_table->reloc_name_in_error_message(r_type); + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("cannot relocate %s in object file"), + reloc_name.c_str()); + return true; + } + + const Sized_relobj_file* object = relinfo->object; + + // Pick the value to use for symbols defined in the PLT. + Symbol_value symval; + if (gsym != NULL + && gsym->use_plt_offset(reloc_property->reference_flags())) + { + symval.set_output_value(target->plt_address_for_global(gsym)); + psymval = &symval; + } + else if (gsym == NULL && psymval->is_ifunc_symbol()) + { + unsigned int r_sym = elfcpp::elf_r_sym(rela.get_r_info()); + if (object->local_has_plt_offset(r_sym)) + { + symval.set_output_value(target->plt_address_for_local(object, r_sym)); + psymval = &symval; + } + } + + const elfcpp::Elf_Xword addend = rela.get_r_addend(); + + // Get the GOT offset if needed. + // For aarch64, the GOT pointer points to the start of the GOT section. + bool have_got_offset = false; + int got_offset = 0; + int got_base = (target->got_ != NULL + ? (target->got_->current_data_size() >= 0x8000 + ? 0x8000 : 0) + : 0); + switch (r_type) + { + case elfcpp::R_AARCH64_MOVW_GOTOFF_G0: + case elfcpp::R_AARCH64_MOVW_GOTOFF_G0_NC: + case elfcpp::R_AARCH64_MOVW_GOTOFF_G1: + case elfcpp::R_AARCH64_MOVW_GOTOFF_G1_NC: + case elfcpp::R_AARCH64_MOVW_GOTOFF_G2: + case elfcpp::R_AARCH64_MOVW_GOTOFF_G2_NC: + case elfcpp::R_AARCH64_MOVW_GOTOFF_G3: + case elfcpp::R_AARCH64_GOTREL64: + case elfcpp::R_AARCH64_GOTREL32: + case elfcpp::R_AARCH64_GOT_LD_PREL19: + case elfcpp::R_AARCH64_LD64_GOTOFF_LO15: + case elfcpp::R_AARCH64_ADR_GOT_PAGE: + case elfcpp::R_AARCH64_LD64_GOT_LO12_NC: + case elfcpp::R_AARCH64_LD64_GOTPAGE_LO15: + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD)); + got_offset = gsym->got_offset(GOT_TYPE_STANDARD) - got_base; + } + else + { + unsigned int r_sym = elfcpp::elf_r_sym(rela.get_r_info()); + gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD)); + got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD) + - got_base); + } + have_got_offset = true; + break; + default: + break; + } + + typename Reloc::Status reloc_status = Reloc::STATUS_OKAY; + typename elfcpp::Elf_types::Elf_Addr value; + switch (r_type) + { + case elfcpp::R_AARCH64_NONE: + break; + + case elfcpp::R_AARCH64_ABS64: + reloc_status = Reloc::template rela_ua<64>( + view, object, psymval, addend, reloc_property); + break; + + case elfcpp::R_AARCH64_ABS32: + reloc_status = Reloc::template rela_ua<32>( + view, object, psymval, addend, reloc_property); + break; + + case elfcpp::R_AARCH64_ABS16: + reloc_status = Reloc::template rela_ua<16>( + view, object, psymval, addend, reloc_property); + break; + + case elfcpp::R_AARCH64_PREL64: + reloc_status = Reloc::template pcrela_ua<64>( + view, object, psymval, addend, address, reloc_property); + + case elfcpp::R_AARCH64_PREL32: + reloc_status = Reloc::template pcrela_ua<32>( + view, object, psymval, addend, address, reloc_property); + + case elfcpp::R_AARCH64_PREL16: + reloc_status = Reloc::template pcrela_ua<16>( + view, object, psymval, addend, address, reloc_property); + + case elfcpp::R_AARCH64_ADR_PREL_PG_HI21_NC: + case elfcpp::R_AARCH64_ADR_PREL_PG_HI21: + reloc_status = Reloc::adrp(view, object, psymval, addend, address, + reloc_property); + break; + + case elfcpp::R_AARCH64_LDST8_ABS_LO12_NC: + case elfcpp::R_AARCH64_LDST16_ABS_LO12_NC: + case elfcpp::R_AARCH64_LDST32_ABS_LO12_NC: + case elfcpp::R_AARCH64_LDST64_ABS_LO12_NC: + case elfcpp::R_AARCH64_LDST128_ABS_LO12_NC: + case elfcpp::R_AARCH64_ADD_ABS_LO12_NC: + reloc_status = Reloc::template rela_general<32>( + view, object, psymval, addend, reloc_property); + break; + + case elfcpp::R_AARCH64_CALL26: + case elfcpp::R_AARCH64_JUMP26: + reloc_status = Reloc::template pcrela_general<32>( + view, object, psymval, addend, address, reloc_property); + break; + + case elfcpp::R_AARCH64_ADR_GOT_PAGE: + gold_assert(have_got_offset); + value = target->got_->address() + got_base + got_offset; + reloc_status = Reloc::adrp(view, value + addend, address); + break; + + case elfcpp::R_AARCH64_LD64_GOT_LO12_NC: + gold_assert(have_got_offset); + value = target->got_->address() + got_base + got_offset; + reloc_status = Reloc::template rela_general<32>( + view, value, addend, reloc_property); + break; + + default: + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc aaa %u"), + r_type); + break; + } + + // Report any errors. + switch (reloc_status) + { + case Reloc::STATUS_OKAY: + break; + case Reloc::STATUS_OVERFLOW: + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("relocation overflow in %s"), + reloc_property->name().c_str()); + break; + case Reloc::STATUS_BAD_RELOC: + gold_error_at_location( + relinfo, + relnum, + rela.get_r_offset(), + _("unexpected opcode while processing relocation %s"), + reloc_property->name().c_str()); + break; + default: + gold_unreachable(); + } + return true; } @@ -1338,19 +2174,31 @@ Target_aarch64::Relocate::relocate( template void Target_aarch64::relocate_section( - const Relocate_info* /* relinfo */, + const Relocate_info* relinfo, unsigned int sh_type, - const unsigned char* /* prelocs */, - size_t /* reloc_count */, - Output_section* /* output_section */, - bool /*needs_special_offset_handling */, - unsigned char* /* view */, - typename elfcpp::Elf_types::Elf_Addr /* address */, - section_size_type /* view_size */, - const Reloc_symbol_changes* /* reloc_symbol_changes */) + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + unsigned char* view, + typename elfcpp::Elf_types::Elf_Addr address, + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { - //TODO gold_assert(sh_type == elfcpp::SHT_RELA); + typedef typename Target_aarch64::Relocate AArch64_relocate; + gold::relocate_section( + relinfo, + this, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + view, + address, + view_size, + reloc_symbol_changes); } // Return the size of a relocation while scanning during a relocatable @@ -1412,36 +2260,46 @@ Target_aarch64::relocate_relocs( gold_assert(sh_type == elfcpp::SHT_RELA); } - // The selector for aarch64 object files. template class Target_selector_aarch64 : public Target_selector { public: - Target_selector_aarch64() - : Target_selector(elfcpp::EM_AARCH64, size, big_endian, - (size == 64 - ? (big_endian ? "elf64-bigaarch64" - : "elf64-littleaarch64") - : (big_endian ? "elf32-bigaarch64" - : "elf32-littleaarch64")), - (size == 64 - ? (big_endian ? "aarch64_elf64_be_vec" - : "aarch64_elf64_le_vec") - : (big_endian ? "aarch64_elf32_be_vec" - : "aarch64_elf32_le_vec"))) - { } + Target_selector_aarch64(); virtual Target* do_instantiate_target() { return new Target_aarch64(); } }; +template<> +Target_selector_aarch64<32, true>::Target_selector_aarch64() + : Target_selector(elfcpp::EM_AARCH64, 32, true, + "elf32-bigaarch64", "aarch64_elf32_be_vec") +{ } + +template<> +Target_selector_aarch64<32, false>::Target_selector_aarch64() + : Target_selector(elfcpp::EM_AARCH64, 32, false, + "elf32-littleaarch64", "aarch64_elf32_le_vec") +{ } + +template<> +Target_selector_aarch64<64, true>::Target_selector_aarch64() + : Target_selector(elfcpp::EM_AARCH64, 64, true, + "elf64-bigaarch64", "aarch64_elf64_be_vec") +{ } + +template<> +Target_selector_aarch64<64, false>::Target_selector_aarch64() + : Target_selector(elfcpp::EM_AARCH64, 64, false, + "elf64-littleaarch64", "aarch64_elf64_le_vec") +{ } + Target_selector_aarch64<32, true> target_selector_aarch64elf32b; Target_selector_aarch64<32, false> target_selector_aarch64elf32; Target_selector_aarch64<64, true> target_selector_aarch64elfb; Target_selector_aarch64<64, false> target_selector_aarch64elf; - } // End anonymous namespace.