diff options
-rw-r--r-- | gold/ChangeLog | 53 | ||||
-rw-r--r-- | gold/aarch64.cc | 833 |
2 files changed, 710 insertions, 176 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 0ec1c27..97a03b4 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,56 @@ +2015-06-10 Han Shen <shenhan@google.com> + Patch for erratum-843419 (2 of 2 - fix erratum occurrences). + + Now fixing for 843419 is fully functional. + + The first part of the erratum fix CL is here - + https://sourceware.org/ml/binutils/2015-04/msg00229.html + + * aarch64.cc(global enum): New constants representing stub types. + (Stub_template): New POD struct. + (Stub_template_repertoire): New class. + (Stub_base): New class. + (Erratum_stub): New class. + (Reloc_stub): Refactored to be a subclass of Stub_base. + (Reloc_stub::Stub_type): Removed. + (Reloc_stub::offset): Moved to Stub_base. + (Reloc_stub::set_offset): Moved to Stub_base. + (Reloc_stub::destination_address): Moved to Stub_base. + (Reloc_stub::set_destination_address): Moved to Stub_base. + (Reloc_stub::reset_destination_address): Moved to Stub_base. + (Reloc_stub::stub_type): Renamed and moved to Stub_base. + (Reloc_stub::stub_size): Renamed and moved to Stub_base. + (Reloc_stub::stub_insns): Renamed and moved to Stub_base. + (Reloc_stub::write): Moved to Stub_base. + (Reloc_stub::invalid_offset): Moved to Stub_base. + (Reloc_stub::invalid_address): Moved to Stub_base. + (Reloc_stub::stub_type_): Renamed and moved to Stub_base. + (Reloc_stub::stub_insns_): Moved to Stub_base. + (Reloc_stub::offset_): Moved to Stub_base. + (Reloc_stub::destination_address_): Moved to Stub_base. + (Stub_table::The_aarch64_relobj): New typedef. + (Stub_table::The_erratum_stub): New typedef. + (Stub_table::The_erratum_stub_less): New typedef. + (Stub_table::The_erratum_stub_set): New typedef. + (Stub_table::The_erratum_stub_set_iter): New typedef. + (Stub_table::empty): Added emptiness testing for erratum stubs. + (Stub_table::add_erratum_stub): New method to add an erratum stub. + (Stub_table::find_erratum_stub): New method. + (Stub_table::find_erratum_stubs_for_input_section): New method. + (Stub_table::erratum_stub_address): New method. + (Stub_table::update_date_size_changed_p): Modified to handle erratum stubs. + (Stub_table::do_addralign): Modified to handle erratum stubs. + (Stub_table::erratum_stubs_): New member. + (Stub_table::erratum_stub_size_): New member. + (Stub_table::relocate_stubs): Modified to handle erratum stubs. + (Stub_table::do_write): Modified to handle erratum stubs. + (AArch64_relobj::The_erratum_stub): New typedef. + (AArch64_relobj::Erratum_stub_set_iter): New typedef. + (AArch64_relobj::fix_errata): New method. + (Target_aarch64::The_reloc_stub_type): Removed. + (Target_aarch64::The_erratum_stub): New typede. + (AArch64_relocate_functions::construct_b): New method. + 2015-06-08 Nick Clifton <nickc@redhat.com> * po/fr.po: New French Translation. diff --git a/gold/aarch64.cc b/gold/aarch64.cc index 2745776..130fcc2 100644 --- a/gold/aarch64.cc +++ b/gold/aarch64.cc @@ -24,6 +24,7 @@ #include <cstring> #include <map> +#include <set> #include "elfcpp.h" #include "dwarf.h" @@ -576,91 +577,177 @@ template<int size, bool big_endian> class AArch64_output_section; -// Reloc stub class. - template<int size, bool big_endian> -class Reloc_stub +class AArch64_relobj; + + +// Stub type enum constants. + +enum { - public: - typedef Reloc_stub<size, big_endian> This; - typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address; + ST_NONE = 0, - // Do not change the value of the enums, they are used to index into - // stub_insns array. - typedef enum - { - ST_NONE = 0, + // Using adrp/add pair, 4 insns (including alignment) without mem access, + // the fastest stub. This has a limited jump distance, which is tested by + // aarch64_valid_for_adrp_p. + ST_ADRP_BRANCH = 1, - // Using adrp/add pair, 4 insns (including alignment) without mem access, - // the fastest stub. This has a limited jump distance, which is tested by - // aarch64_valid_for_adrp_p. - ST_ADRP_BRANCH = 1, + // Using ldr-absolute-address/br-register, 4 insns with 1 mem access, + // unlimited in jump distance. + ST_LONG_BRANCH_ABS = 2, - // Using ldr-absolute-address/br-register, 4 insns with 1 mem access, - // unlimited in jump distance. - ST_LONG_BRANCH_ABS = 2, + // Using ldr/calculate-pcrel/jump, 8 insns (including alignment) with 1 + // mem access, slowest one. Only used in position independent executables. + ST_LONG_BRANCH_PCREL = 3, - // Using ldr/calculate-pcrel/jump, 8 insns (including alignment) with 1 mem - // access, slowest one. Only used in position independent executables. - ST_LONG_BRANCH_PCREL = 3, + // Stub for erratum 843419 handling. + ST_E_843419 = 4, - } Stub_type; + // Number of total stub types. + ST_NUMBER = 5 +}; - // Branch range. This is used to calculate the section group size, as well as - // determine whether a stub is needed. - static const int MAX_BRANCH_OFFSET = ((1 << 25) - 1) << 2; - static const int MIN_BRANCH_OFFSET = -((1 << 25) << 2); - // Constant used to determine if an offset fits in the adrp instruction - // encoding. - static const int MAX_ADRP_IMM = (1 << 20) - 1; - static const int MIN_ADRP_IMM = -(1 << 20); +// Struct that wraps insns for a particular stub. All stub templates are +// created/initialized as constants by Stub_template_repertoire. - static const int BYTES_PER_INSN = 4; - static const int STUB_ADDR_ALIGN = 4; +template<bool big_endian> +struct Stub_template +{ + const typename AArch64_insn_utilities<big_endian>::Insntype* insns; + const int insn_num; +}; - // Determine whether the offset fits in the jump/branch instruction. - static bool - aarch64_valid_branch_offset_p(int64_t offset) - { return offset >= MIN_BRANCH_OFFSET && offset <= MAX_BRANCH_OFFSET; } - // Determine whether the offset fits in the adrp immediate field. - static bool - aarch64_valid_for_adrp_p(AArch64_address location, AArch64_address dest) +// Simple singleton class that creates/initializes/stores all types of stub +// templates. + +template<bool big_endian> +class Stub_template_repertoire +{ +public: + typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype; + + // Single static method to get stub template for a given stub type. + static const Stub_template<big_endian>* + get_stub_template(int type) { - typedef AArch64_relocate_functions<size, big_endian> Reloc; - int64_t adrp_imm = (Reloc::Page(dest) - Reloc::Page(location)) >> 12; - return adrp_imm >= MIN_ADRP_IMM && adrp_imm <= MAX_ADRP_IMM; + static Stub_template_repertoire<big_endian> singleton; + return singleton.stub_templates_[type]; } - // Determine the stub type for a certain relocation or ST_NONE, if no stub is - // needed. - static Stub_type - stub_type_for_reloc(unsigned int r_type, AArch64_address address, - AArch64_address target); - - Reloc_stub(Stub_type stub_type) - : stub_type_(stub_type), offset_(invalid_offset), - destination_address_(invalid_address) +private: + // Constructor - creates/initializes all stub templates. + Stub_template_repertoire(); + ~Stub_template_repertoire() { } - ~Reloc_stub() - { } + // Disallowing copy ctor and copy assignment operator. + Stub_template_repertoire(Stub_template_repertoire&); + Stub_template_repertoire& operator=(Stub_template_repertoire&); - // Return offset of code stub from beginning of its containing stub table. - section_offset_type - offset() const + // Data that stores all insn templates. + const Stub_template<big_endian>* stub_templates_[ST_NUMBER]; +}; // End of "class Stub_template_repertoire". + + +// Constructor - creates/initilizes all stub templates. + +template<bool big_endian> +Stub_template_repertoire<big_endian>::Stub_template_repertoire() +{ + // Insn array definitions. + const static Insntype ST_NONE_INSNS[] = {}; + + const static Insntype ST_ADRP_BRANCH_INSNS[] = + { + 0x90000010, /* adrp ip0, X */ + /* ADR_PREL_PG_HI21(X) */ + 0x91000210, /* add ip0, ip0, :lo12:X */ + /* ADD_ABS_LO12_NC(X) */ + 0xd61f0200, /* br ip0 */ + 0x00000000, /* alignment padding */ + }; + + const static Insntype ST_LONG_BRANCH_ABS_INSNS[] = + { + 0x58000050, /* ldr ip0, 0x8 */ + 0xd61f0200, /* br ip0 */ + 0x00000000, /* address field */ + 0x00000000, /* address fields */ + }; + + const static Insntype ST_LONG_BRANCH_PCREL_INSNS[] = + { + 0x58000090, /* ldr ip0, 0x10 */ + 0x10000011, /* adr ip1, #0 */ + 0x8b110210, /* add ip0, ip0, ip1 */ + 0xd61f0200, /* br ip0 */ + 0x00000000, /* address field */ + 0x00000000, /* address field */ + 0x00000000, /* alignment padding */ + 0x00000000, /* alignment padding */ + }; + + const static Insntype ST_E_843419_INSNS[] = + { + 0x00000000, /* Placeholder for erratum insn. */ + 0x14000000, /* b <label> */ + }; + +#define install_insn_template(T) \ + const static Stub_template<big_endian> template_##T = { \ + T##_INSNS, sizeof(T##_INSNS) / sizeof(T##_INSNS[0]) }; \ + this->stub_templates_[T] = &template_##T + + install_insn_template(ST_NONE); + install_insn_template(ST_ADRP_BRANCH); + install_insn_template(ST_LONG_BRANCH_ABS); + install_insn_template(ST_LONG_BRANCH_PCREL); + install_insn_template(ST_E_843419); + +#undef install_insn_template +} + + +// Base class for stubs. + +template<int size, bool big_endian> +class Stub_base +{ +public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address; + typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype; + + static const AArch64_address invalid_address = + static_cast<AArch64_address>(-1); + + static const section_offset_type invalid_offset = + static_cast<section_offset_type>(-1); + + Stub_base(int type) + : destination_address_(invalid_address), + offset_(invalid_offset), + type_(type) + {} + + ~Stub_base() + {} + + // Get stub type. + int + type() const + { return this->type_; } + + // Get stub template that provides stub insn information. + const Stub_template<big_endian>* + stub_template() const { - gold_assert(this->offset_ != invalid_offset); - return this->offset_; + return Stub_template_repertoire<big_endian>:: + get_stub_template(this->type()); } - // Set offset of code stub from beginning of its containing stub table. - void - set_offset(section_offset_type offset) - { this->offset_ = offset; } - - // Return destination address. + // Get destination address. AArch64_address destination_address() const { @@ -681,38 +768,254 @@ class Reloc_stub reset_destination_address() { this->destination_address_ = this->invalid_address; } - // Return the stub type. - Stub_type - stub_type() const - { return stub_type_; } + // Get offset of code stub. For Reloc_stub, it is the offset from the + // beginning of its containing stub table; for Erratum_stub, it is the offset + // from the end of reloc_stubs. + section_offset_type + offset() const + { + gold_assert(this->offset_ != this->invalid_offset); + return this->offset_; + } - // Return the stub size. - uint32_t - stub_size() const - { return this->stub_insn_number() * BYTES_PER_INSN; } + // Set stub offset. + void + set_offset(section_offset_type offset) + { this->offset_ = offset; } - // Return the instruction number of this stub instance. - int - stub_insn_number() const - { return stub_insns_[this->stub_type_][0]; } + // Return the stub insn. + const Insntype* + insns() const + { return this->stub_template()->insns; } - // Note the first "insn" is the number of total insns in this array. - const uint32_t* - stub_insns() const - { return stub_insns_[this->stub_type_]; } + // Return num of stub insns. + unsigned int + insn_num() const + { return this->stub_template()->insn_num; } + + // Get size of the stub. + int + stub_size() const + { + return this->insn_num() * + AArch64_insn_utilities<big_endian>::BYTES_PER_INSN; + } // Write stub to output file. void write(unsigned char* view, section_size_type view_size) { this->do_write(view, view_size); } +protected: + // Abstract method to be implemented by sub-classes. + virtual void + do_write(unsigned char*, section_size_type) = 0; + +private: + // The last insn of a stub is a jump to destination insn. This field records + // the destination address. + AArch64_address destination_address_; + // The stub offset. Note this has difference interpretations between an + // Reloc_stub and an Erratum_stub. For Reloc_stub this is the offset from the + // beginning of the containing stub_table, whereas for Erratum_stub, this is + // the offset from the end of reloc_stubs. + section_offset_type offset_; + // Stub type. + const int type_; +}; // End of "Stub_base". + + +// Erratum stub class. An erratum stub differs from a reloc stub in that for +// each erratum occurrence, we generate an erratum stub. We never share erratum +// stubs, whereas for reloc stubs, different branches insns share a single reloc +// stub as long as the branch targets are the same. (More to the point, reloc +// stubs can be shared because they're used to reach a specific target, whereas +// erratum stubs branch back to the original control flow.) + +template<int size, bool big_endian> +class Erratum_stub : public Stub_base<size, big_endian> +{ +public: + typedef AArch64_relobj<size, big_endian> The_aarch64_relobj; + typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address; + typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype; + + static const int STUB_ADDR_ALIGN = 4; + + static const Insntype invalid_insn = static_cast<Insntype>(-1); + + Erratum_stub(The_aarch64_relobj* relobj, int type, + unsigned shndx, unsigned int sh_offset) + : Stub_base<size, big_endian>(type), relobj_(relobj), + shndx_(shndx), sh_offset_(sh_offset), + erratum_insn_(invalid_insn), + erratum_address_(this->invalid_address) + {} + + ~Erratum_stub() {} + + // Return the object that contains the erratum. + The_aarch64_relobj* + relobj() + { return this->relobj_; } + + // Get section index of the erratum. + unsigned int + shndx() const + { return this->shndx_; } + + // Get section offset of the erratum. + unsigned int + sh_offset() const + { return this->sh_offset_; } + + // Get the erratum insn. This is the insn located at erratum_insn_address. + Insntype + erratum_insn() const + { + gold_assert(this->erratum_insn_ != this->invalid_insn); + return this->erratum_insn_; + } + + // Set the insn that the erratum happens to. + void + set_erratum_insn(Insntype insn) + { this->erratum_insn_ = insn; } + + // Return the address where an erratum must be done. + AArch64_address + erratum_address() const + { + gold_assert(this->erratum_address_ != this->invalid_address); + return this->erratum_address_; + } + + // Set the address where an erratum must be done. + void + set_erratum_address(AArch64_address addr) + { this->erratum_address_ = addr; } + + // Comparator used to group Erratum_stubs in a set by (obj, shndx, + // sh_offset). We do not include 'type' in the calculation, becuase there is + // at most one stub type at (obj, shndx, sh_offset). + bool + operator<(const Erratum_stub<size, big_endian>& k) const + { + if (this == &k) + return false; + // We group stubs by relobj. + if (this->relobj_ != k.relobj_) + return this->relobj_ < k.relobj_; + // Then by section index. + if (this->shndx_ != k.shndx_) + return this->shndx_ < k.shndx_; + // Lastly by section offset. + return this->sh_offset_ < k.sh_offset_; + } + +protected: + virtual void + do_write(unsigned char*, section_size_type); + +private: + // The object that needs to be fixed. + The_aarch64_relobj* relobj_; + // The shndx in the object that needs to be fixed. + const unsigned int shndx_; + // The section offset in the obejct that needs to be fixed. + const unsigned int sh_offset_; + // The insn to be fixed. + Insntype erratum_insn_; + // The address of the above insn. + AArch64_address erratum_address_; +}; // End of "Erratum_stub". + + +// Comparator used in set definition. +template<int size, bool big_endian> +struct Erratum_stub_less +{ + bool + operator()(const Erratum_stub<size, big_endian>* s1, + const Erratum_stub<size, big_endian>* s2) const + { return *s1 < *s2; } +}; + +// Erratum_stub implementation for writing stub to output file. + +template<int size, bool big_endian> +void +Erratum_stub<size, big_endian>::do_write(unsigned char* view, section_size_type) +{ + typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype; + const Insntype* insns = this->insns(); + uint32_t num_insns = this->insn_num(); + Insntype* ip = reinterpret_cast<Insntype*>(view); + // For current implemnted erratum 843419, (and 835769 which is to be + // implemented soon), the first insn in the stub is always a copy of the + // problematic insn (in 843419, the mem access insn), followed by a jump-back. + elfcpp::Swap<32, big_endian>::writeval(ip, this->erratum_insn()); + for (uint32_t i = 1; i < num_insns; ++i) + elfcpp::Swap<32, big_endian>::writeval(ip + i, insns[i]); +} + + +// Reloc stub class. + +template<int size, bool big_endian> +class Reloc_stub : public Stub_base<size, big_endian> +{ + public: + typedef Reloc_stub<size, big_endian> This; + typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address; + + // Branch range. This is used to calculate the section group size, as well as + // determine whether a stub is needed. + static const int MAX_BRANCH_OFFSET = ((1 << 25) - 1) << 2; + static const int MIN_BRANCH_OFFSET = -((1 << 25) << 2); + + // Constant used to determine if an offset fits in the adrp instruction + // encoding. + static const int MAX_ADRP_IMM = (1 << 20) - 1; + static const int MIN_ADRP_IMM = -(1 << 20); + + static const int BYTES_PER_INSN = 4; + static const int STUB_ADDR_ALIGN = 4; + + // Determine whether the offset fits in the jump/branch instruction. + static bool + aarch64_valid_branch_offset_p(int64_t offset) + { return offset >= MIN_BRANCH_OFFSET && offset <= MAX_BRANCH_OFFSET; } + + // Determine whether the offset fits in the adrp immediate field. + static bool + aarch64_valid_for_adrp_p(AArch64_address location, AArch64_address dest) + { + typedef AArch64_relocate_functions<size, big_endian> Reloc; + int64_t adrp_imm = (Reloc::Page(dest) - Reloc::Page(location)) >> 12; + return adrp_imm >= MIN_ADRP_IMM && adrp_imm <= MAX_ADRP_IMM; + } + + // Determine the stub type for a certain relocation or ST_NONE, if no stub is + // needed. + static int + stub_type_for_reloc(unsigned int r_type, AArch64_address address, + AArch64_address target); + + Reloc_stub(int type) + : Stub_base<size, big_endian>(type) + { } + + ~Reloc_stub() + { } + // The key class used to index the stub instance in the stub table's stub map. class Key { public: - Key(Stub_type stub_type, const Symbol* symbol, const Relobj* relobj, + Key(int type, const Symbol* symbol, const Relobj* relobj, unsigned int r_sym, int32_t addend) - : stub_type_(stub_type), addend_(addend) + : type_(type), addend_(addend) { if (symbol != NULL) { @@ -731,9 +1034,9 @@ class Reloc_stub { } // Return stub type. - Stub_type - stub_type() const - { return this->stub_type_; } + int + type() const + { return this->type_; } // Return the local symbol index or invalid_index. unsigned int @@ -754,7 +1057,7 @@ class Reloc_stub bool eq(const Key& k) const { - return ((this->stub_type_ == k.stub_type_) + return ((this->type_ == k.type_) && (this->r_sym_ == k.r_sym_) && ((this->r_sym_ != Reloc_stub::invalid_index) ? (this->u_.relobj == k.u_.relobj) @@ -771,7 +1074,7 @@ class Reloc_stub ? this->u_.relobj->name().c_str() : this->u_.symbol->name()); // We only have 4 stub types. - size_t stub_type_hash_value = 0x03 & this->stub_type_; + size_t stub_type_hash_value = 0x03 & this->type_; return (name_hash_value ^ stub_type_hash_value ^ ((this->r_sym_ & 0x3fff) << 2) @@ -795,7 +1098,7 @@ class Reloc_stub private: // Stub type. - const Stub_type stub_type_; + const int 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_; @@ -820,17 +1123,7 @@ class Reloc_stub do_write(unsigned char*, section_size_type); private: - static const section_offset_type invalid_offset = - static_cast<section_offset_type>(-1); static const unsigned int invalid_index = static_cast<unsigned int>(-1); - static const AArch64_address invalid_address = - static_cast<AArch64_address>(-1); - - static const uint32_t stub_insns_[][10]; - - const Stub_type stub_type_; - section_offset_type offset_; - AArch64_address destination_address_; }; // End of Reloc_stub @@ -842,66 +1135,19 @@ Reloc_stub<size, big_endian>:: do_write(unsigned char* view, section_size_type) { typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype; - const uint32_t* insns = this->stub_insns(); - uint32_t num_insns = this->stub_insn_number(); + const uint32_t* insns = this->insns(); + uint32_t num_insns = this->insn_num(); Insntype* ip = reinterpret_cast<Insntype*>(view); - for (uint32_t i = 1; i <= num_insns; ++i) - elfcpp::Swap<32, big_endian>::writeval(ip + i - 1, insns[i]); + for (uint32_t i = 0; i < num_insns; ++i) + elfcpp::Swap<32, big_endian>::writeval(ip + i, insns[i]); } -// Stubs instructions definition. - -template<int size, bool big_endian> -const uint32_t -Reloc_stub<size, big_endian>::stub_insns_[][10] = - { - // The first element of each group is the num of the insns. - - // ST_NONE - {0, 0}, - - // ST_ADRP_BRANCH - { - 4, - 0x90000010, /* adrp ip0, X */ - /* ADR_PREL_PG_HI21(X) */ - 0x91000210, /* add ip0, ip0, :lo12:X */ - /* ADD_ABS_LO12_NC(X) */ - 0xd61f0200, /* br ip0 */ - 0x00000000, /* alignment padding */ - }, - - // ST_LONG_BRANCH_ABS - { - 4, - 0x58000050, /* ldr ip0, 0x8 */ - 0xd61f0200, /* br ip0 */ - 0x00000000, /* address field */ - 0x00000000, /* address fields */ - }, - - // ST_LONG_BRANCH_PCREL - { - 8, - 0x58000090, /* ldr ip0, 0x10 */ - 0x10000011, /* adr ip1, #0 */ - 0x8b110210, /* add ip0, ip0, ip1 */ - 0xd61f0200, /* br ip0 */ - 0x00000000, /* address field */ - 0x00000000, /* address field */ - 0x00000000, /* alignment padding */ - 0x00000000, /* alignment padding */ - } - }; - - // Determine the stub type for a certain relocation or ST_NONE, if no stub is // needed. template<int size, bool big_endian> -inline -typename Reloc_stub<size, big_endian>::Stub_type +inline int Reloc_stub<size, big_endian>::stub_type_for_reloc( unsigned int r_type, AArch64_address location, AArch64_address dest) { @@ -937,9 +1183,12 @@ class Stub_table : public Output_data public: typedef Target_aarch64<size, big_endian> The_target_aarch64; typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address; + typedef AArch64_relobj<size, big_endian> The_aarch64_relobj; typedef AArch64_input_section<size, big_endian> The_aarch64_input_section; typedef Reloc_stub<size, big_endian> The_reloc_stub; typedef typename The_reloc_stub::Key The_reloc_stub_key; + typedef Erratum_stub<size, big_endian> The_erratum_stub; + typedef Erratum_stub_less<size, big_endian> The_erratum_stub_less; typedef typename The_reloc_stub_key::hash The_reloc_stub_key_hash; typedef typename The_reloc_stub_key::equal_to The_reloc_stub_key_equal_to; typedef Stub_table<size, big_endian> The_stub_table; @@ -949,8 +1198,12 @@ class Stub_table : public Output_data typedef typename Reloc_stub_map::const_iterator Reloc_stub_map_const_iter; typedef Relocate_info<size, big_endian> The_relocate_info; + typedef std::set<The_erratum_stub*, The_erratum_stub_less> Erratum_stub_set; + typedef typename Erratum_stub_set::iterator Erratum_stub_set_iter; + Stub_table(The_aarch64_input_section* owner) - : Output_data(), owner_(owner), reloc_stubs_size_(0), prev_data_size_(0) + : Output_data(), owner_(owner), reloc_stubs_size_(0), + erratum_stubs_size_(0), prev_data_size_(0) { } ~Stub_table() @@ -963,7 +1216,7 @@ class Stub_table : public Output_data // Whether this stub table is empty. bool empty() const - { return reloc_stubs_.empty(); } + { return reloc_stubs_.empty() && erratum_stubs_.empty(); } // Return the current data size. off_t @@ -975,6 +1228,32 @@ class Stub_table : public Output_data void add_reloc_stub(The_reloc_stub* stub, const The_reloc_stub_key& key); + // Add an erratum stub into the erratum stub set. The set is ordered by + // (relobj, shndx, sh_offset). + void + add_erratum_stub(The_erratum_stub* stub); + + // Find if such erratum exists for any given (obj, shndx, sh_offset). + The_erratum_stub* + find_erratum_stub(The_aarch64_relobj* a64relobj, + unsigned int shndx, unsigned int sh_offset); + + // Find all the erratums for a given input section. The return value is a pair + // of iterators [begin, end). + std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter> + find_erratum_stubs_for_input_section(The_aarch64_relobj* a64relobj, + unsigned int shndx); + + // Compute the erratum stub address. + AArch64_address + erratum_stub_address(The_erratum_stub* stub) const + { + AArch64_address r = align_address(this->address() + this->reloc_stubs_size_, + The_erratum_stub::STUB_ADDR_ALIGN); + r += stub->offset(); + return r; + } + // Finalize stubs. No-op here, just for completeness. void finalize_stubs() @@ -1003,7 +1282,9 @@ class Stub_table : public Output_data update_data_size_changed_p() { // No addralign changed here. - off_t s = this->reloc_stubs_size_; + off_t s = align_address(this->reloc_stubs_size_, + The_erratum_stub::STUB_ADDR_ALIGN) + + this->erratum_stubs_size_; bool changed = (s != this->prev_data_size_); this->prev_data_size_ = s; return changed; @@ -1017,7 +1298,10 @@ class Stub_table : public Output_data // Return the required alignment. uint64_t do_addralign() const - { return The_reloc_stub::STUB_ADDR_ALIGN; } + { + return std::max(The_reloc_stub::STUB_ADDR_ALIGN, + The_erratum_stub::STUB_ADDR_ALIGN); + } // Reset address and file offset. void @@ -1045,13 +1329,79 @@ class Stub_table : public Output_data The_aarch64_input_section* owner_; // The relocation stubs. Reloc_stub_map reloc_stubs_; + // The erratum stubs. + Erratum_stub_set erratum_stubs_; // Size of reloc stubs. off_t reloc_stubs_size_; + // Size of erratum stubs. + off_t erratum_stubs_size_; // data size of this in the previous pass. off_t prev_data_size_; }; // End of Stub_table +// Add an erratum stub into the erratum stub set. The set is ordered by +// (relobj, shndx, sh_offset). + +template<int size, bool big_endian> +void +Stub_table<size, big_endian>::add_erratum_stub(The_erratum_stub* stub) +{ + std::pair<Erratum_stub_set_iter, bool> ret = + this->erratum_stubs_.insert(stub); + gold_assert(ret.second); + this->erratum_stubs_size_ = align_address( + this->erratum_stubs_size_, The_erratum_stub::STUB_ADDR_ALIGN); + stub->set_offset(this->erratum_stubs_size_); + this->erratum_stubs_size_ += stub->stub_size(); +} + + +// Find if such erratum exists for givein (obj, shndx, sh_offset). + +template<int size, bool big_endian> +Erratum_stub<size, big_endian>* +Stub_table<size, big_endian>::find_erratum_stub( + The_aarch64_relobj* a64relobj, unsigned int shndx, unsigned int sh_offset) +{ + // A dummy object used as key to search in the set. + The_erratum_stub key(a64relobj, ST_NONE, + shndx, sh_offset); + Erratum_stub_set_iter i = this->erratum_stubs_.find(&key); + if (i != this->erratum_stubs_.end()) + { + The_erratum_stub* stub(*i); + gold_assert(stub->erratum_insn() != 0); + return stub; + } + return NULL; +} + + +// Find all the errata for a given input section. The return value is a pair of +// iterators [begin, end). + +template<int size, bool big_endian> +std::pair<typename Stub_table<size, big_endian>::Erratum_stub_set_iter, + typename Stub_table<size, big_endian>::Erratum_stub_set_iter> +Stub_table<size, big_endian>::find_erratum_stubs_for_input_section( + The_aarch64_relobj* a64relobj, unsigned int shndx) +{ + typedef std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter> Result_pair; + Erratum_stub_set_iter start, end; + The_erratum_stub low_key(a64relobj, ST_NONE, shndx, 0); + start = this->erratum_stubs_.lower_bound(&low_key); + if (start == this->erratum_stubs_.end()) + return Result_pair(this->erratum_stubs_.end(), + this->erratum_stubs_.end()); + end = start; + while (end != this->erratum_stubs_.end() && + (*end)->relobj() == a64relobj && (*end)->shndx() == shndx) + ++end; + return Result_pair(start, end); +} + + // Add a STUB using KEY. The caller is responsible for avoiding addition // if a STUB with the same key has already been added. @@ -1060,7 +1410,7 @@ void Stub_table<size, big_endian>::add_reloc_stub( The_reloc_stub* stub, const The_reloc_stub_key& key) { - gold_assert(stub->stub_type() == key.stub_type()); + gold_assert(stub->type() == key.type()); this->reloc_stubs_[key] = stub; // Assign stub offset early. We can do this because we never remove @@ -1091,6 +1441,35 @@ relocate_stubs(const The_relocate_info* relinfo, p != this->reloc_stubs_.end(); ++p) relocate_stub(p->second, relinfo, target_aarch64, output_section, view, address, view_size); + + // Just for convenience. + const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN; + + // Now 'relocate' erratum stubs. + for(Erratum_stub_set_iter i = this->erratum_stubs_.begin(); + i != this->erratum_stubs_.end(); ++i) + { + AArch64_address stub_address = this->erratum_stub_address(*i); + // The address of "b" in the stub that is to be "relocated". + AArch64_address stub_b_insn_address; + // Branch offset that is to be filled in "b" insn. + int b_offset = 0; + switch ((*i)->type()) + { + case ST_E_843419: + // For the erratum, the 2nd insn is a b-insn to be patched + // (relocated). + stub_b_insn_address = stub_address + 1 * BPI; + b_offset = (*i)->destination_address() - stub_b_insn_address; + AArch64_relocate_functions<size, big_endian>::construct_b( + view + (stub_b_insn_address - this->address()), + ((unsigned int)(b_offset)) & 0xfffffff); + break; + default: + gold_unreachable(); + break; + } + } } @@ -1140,6 +1519,17 @@ Stub_table<size, big_endian>::do_write(Output_file* of) stub->write(oview + stub->offset(), stub->stub_size()); } + // Write erratum stubs. + unsigned int erratum_stub_start_offset = + align_address(this->reloc_stubs_size_, The_erratum_stub::STUB_ADDR_ALIGN); + for (typename Erratum_stub_set::iterator p = this->erratum_stubs_.begin(); + p != this->erratum_stubs_.end(); ++p) + { + The_erratum_stub* stub(*p); + stub->write(oview + erratum_stub_start_offset + stub->offset(), + stub->stub_size()); + } + of->write_output_view(this->offset(), oview_size, oview); } @@ -1155,6 +1545,8 @@ class AArch64_relobj : public Sized_relobj_file<size, big_endian> typedef AArch64_input_section<size, big_endian> The_aarch64_input_section; typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address; typedef Stub_table<size, big_endian> The_stub_table; + typedef Erratum_stub<size, big_endian> The_erratum_stub; + typedef typename The_stub_table::Erratum_stub_set_iter Erratum_stub_set_iter; typedef std::vector<The_stub_table*> Stub_table_list; static const AArch64_address invalid_address = static_cast<AArch64_address>(-1); @@ -1258,6 +1650,10 @@ class AArch64_relobj : public Sized_relobj_file<size, big_endian> Stringpool_template<char>*); private: + // Fix all errata in the object. + void + fix_errata(typename Sized_relobj_file<size, big_endian>::Views* pviews); + // Whether a section needs to be scanned for relocation stubs. bool section_needs_reloc_stub_scanning(const elfcpp::Shdr<size, big_endian>&, @@ -1353,6 +1749,48 @@ AArch64_relobj<size, big_endian>::do_count_local_symbols( } +// Fix all errata in the object. + +template<int size, bool big_endian> +void +AArch64_relobj<size, big_endian>::fix_errata( + typename Sized_relobj_file<size, big_endian>::Views* pviews) +{ + typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype; + unsigned int shnum = this->shnum(); + for (unsigned int i = 1; i < shnum; ++i) + { + The_stub_table* stub_table = this->stub_table(i); + if (!stub_table) + continue; + std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter> + ipair(stub_table->find_erratum_stubs_for_input_section(this, i)); + Erratum_stub_set_iter p = ipair.first, end = ipair.second; + while (p != end) + { + The_erratum_stub* stub = *p; + typename Sized_relobj_file<size, big_endian>::View_size& + pview((*pviews)[i]); + + // Double check data before fix. + Insntype* ip = + reinterpret_cast<Insntype*>(pview.view + stub->sh_offset()); + Insntype insn_to_fix = ip[0]; + gold_assert(insn_to_fix == stub->erratum_insn()); + gold_assert(pview.address + stub->sh_offset() + == stub->erratum_address()); + + AArch64_address stub_address = + stub_table->erratum_stub_address(stub); + unsigned int b_offset = stub_address - stub->erratum_address(); + AArch64_relocate_functions<size, big_endian>::construct_b( + pview.view + stub->sh_offset(), b_offset & 0xfffffff); + ++p; + } + } +} + + // Relocate sections. template<int size, bool big_endian> @@ -1370,6 +1808,9 @@ AArch64_relobj<size, big_endian>::do_relocate_sections( if (parameters->options().relocatable()) return; + if (parameters->options().fix_cortex_a53_843419()) + this->fix_errata(pviews); + Relocate_info<size, big_endian> relinfo; relinfo.symtab = symtab; relinfo.layout = layout; @@ -2088,7 +2529,7 @@ class Target_aarch64 : public Sized_target<size, big_endian> typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; typedef AArch64_relobj<size, big_endian> The_aarch64_relobj; typedef Reloc_stub<size, big_endian> The_reloc_stub; - typedef typename The_reloc_stub::Stub_type The_reloc_stub_type; + typedef Erratum_stub<size, big_endian> The_erratum_stub; typedef typename Reloc_stub<size, big_endian>::Key The_reloc_stub_key; typedef Stub_table<size, big_endian> The_stub_table; typedef std::vector<The_stub_table*> Stub_table_list; @@ -3028,9 +3469,9 @@ Target_aarch64<size, big_endian>::scan_reloc_for_stub( gold_unreachable(); } - typename The_reloc_stub::Stub_type stub_type = The_reloc_stub:: + int stub_type = The_reloc_stub:: stub_type_for_reloc(r_type, address, destination); - if (stub_type == The_reloc_stub::ST_NONE) + if (stub_type == ST_NONE) return; The_stub_table* stub_table = aarch64_relobj->stub_table(relinfo->data_shndx); @@ -3284,20 +3725,20 @@ relocate_stub(The_reloc_stub* stub, typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype; Insntype* ip = reinterpret_cast<Insntype*>(view); - int insn_number = stub->stub_insn_number(); - const uint32_t* insns = stub->stub_insns(); + int insn_number = stub->insn_num(); + const uint32_t* insns = stub->insns(); // Check the insns are really those stub insns. for (int i = 0; i < insn_number; ++i) { Insntype insn = elfcpp::Swap<32,big_endian>::readval(ip + i); - gold_assert(((uint32_t)insn == insns[i+1])); + gold_assert(((uint32_t)insn == insns[i])); } Address dest = stub->destination_address(); - switch(stub->stub_type()) + switch(stub->type()) { - case The_reloc_stub::ST_ADRP_BRANCH: + case ST_ADRP_BRANCH: { // 1st reloc is ADR_PREL_PG_HI21 The_reloc_functions_status status = @@ -3318,12 +3759,12 @@ relocate_stub(The_reloc_stub* stub, } break; - case The_reloc_stub::ST_LONG_BRANCH_ABS: + case ST_LONG_BRANCH_ABS: // 1st reloc is R_AARCH64_PREL64, at offset 8 elfcpp::Swap<64,big_endian>::writeval(view + 8, dest); break; - case The_reloc_stub::ST_LONG_BRANCH_PCREL: + case ST_LONG_BRANCH_PCREL: { // "PC" calculation is the 2nd insn in the stub. uint64_t offset = dest - (address + 4); @@ -4272,7 +4713,6 @@ class AArch64_relocate_functions typedef Relocate_info<size, big_endian> The_relocate_info; typedef AArch64_relobj<size, big_endian> The_aarch64_relobj; typedef Reloc_stub<size, big_endian> The_reloc_stub; - typedef typename The_reloc_stub::Stub_type The_reloc_stub_type; typedef Stub_table<size, big_endian> The_stub_table; typedef elfcpp::Rela<size, big_endian> The_rela; typedef typename elfcpp::Swap<size, big_endian>::Valtype AArch64_valtype; @@ -4405,6 +4845,15 @@ class AArch64_relocate_functions public: + // Construct a B insn. Note, although we group it here with other relocation + // operation, there is actually no 'relocation' involved here. + static inline void + construct_b(unsigned char* view, unsigned int branch_offset) + { + update_view_two_parts<32>(view, 0x05, (branch_offset >> 2), + 26, 0, 0xffffffff); + } + // Do a simple rela relocation at unaligned addresses. template<int valsize> @@ -4650,9 +5099,9 @@ maybe_apply_stub(unsigned int r_type, typename elfcpp::Elf_types<size>::Elf_Swxword addend = rela.get_r_addend(); Address branch_target = psymval->value(object, 0) + addend; - The_reloc_stub_type stub_type = The_reloc_stub:: - stub_type_for_reloc(r_type, address, branch_target); - if (stub_type == The_reloc_stub::ST_NONE) + int stub_type = + The_reloc_stub::stub_type_for_reloc(r_type, address, branch_target); + if (stub_type == ST_NONE) return false; const The_aarch64_relobj* aarch64_relobj = @@ -7258,9 +7707,15 @@ Target_aarch64<size, big_endian>::scan_erratum_843419_span( { Insntype insn2 = ip[1]; Insntype insn3 = ip[2]; + Insntype erratum_insn; + unsigned insn_offset; bool do_report = false; if (is_erratum_843419_sequence(insn1, insn2, insn3)) - do_report = true; + { + do_report = true; + erratum_insn = insn3; + insn_offset = 2 * Insn_utilities::BYTES_PER_INSN; + } else if (offset + 4 * Insn_utilities::BYTES_PER_INSN <= span_length) { // Optionally we can have an insn between ins2 and ins3 @@ -7277,15 +7732,41 @@ Target_aarch64<size, big_endian>::scan_erratum_843419_span( Insntype insn4 = ip[3]; if (is_erratum_843419_sequence(insn1, insn2, insn4)) - do_report = true; + { + do_report = true; + erratum_insn = insn4; + insn_offset = 3 * Insn_utilities::BYTES_PER_INSN; + } } } if (do_report) { - gold_error(_("Erratum 943419 found at \"%s\", section %d, " - "offset 0x%08x."), - relobj->name().c_str(), shndx, - (unsigned int)(span_start + offset)); + gold_warning(_("Erratum 843419 found and fixed at \"%s\", " + "section %d, offset 0x%08x."), + relobj->name().c_str(), shndx, + (unsigned int)(span_start + offset)); + unsigned int errata_insn_offset = + span_start + offset + insn_offset; + The_stub_table* stub_table = relobj->stub_table(shndx); + gold_assert(stub_table != NULL); + if (stub_table->find_erratum_stub(relobj, + shndx, + errata_insn_offset) == NULL) + { + The_erratum_stub* stub = new The_erratum_stub( + relobj, ST_E_843419, shndx, + errata_insn_offset); + Address erratum_address = + output_address + offset + insn_offset; + // Stub destination address is the next insn after the + // erratum. + Address dest_address = erratum_address + + Insn_utilities::BYTES_PER_INSN; + stub->set_erratum_insn(erratum_insn); + stub->set_erratum_address(erratum_address); + stub->set_destination_address(dest_address); + stub_table->add_erratum_stub(stub); + } } } @@ -7297,7 +7778,7 @@ Target_aarch64<size, big_endian>::scan_erratum_843419_span( else // (page_offset == 0xffc), we move to next page's 0xff8. offset += 0xffc; } -} +} // End of "Target_aarch64::scan_erratum_843419_span". // The selector for aarch64 object files. |