diff options
author | Marcin Kościelnicki <koriakin@0x04.net> | 2015-10-05 16:57:13 +0200 |
---|---|---|
committer | Cary Coutant <ccoutant@gmail.com> | 2015-10-28 16:47:27 -0700 |
commit | e79a4bad70029595e43cc536c0d1e60d5d6ee115 (patch) | |
tree | d764e1b819719d1144c649cbed1e9ad2f475e791 /gold/s390.cc | |
parent | Add 64-bit archive support. (diff) | |
download | binutils-gdb-e79a4bad70029595e43cc536c0d1e60d5d6ee115.tar.gz binutils-gdb-e79a4bad70029595e43cc536c0d1e60d5d6ee115.tar.bz2 binutils-gdb-e79a4bad70029595e43cc536c0d1e60d5d6ee115.zip |
Add s390 backend.
elfcpp/
* s390.h: New file.
gold/
* s390.cc: New file.
* Makefile.am (TARGETSOURCES): Add s390.cc.
(ALL_TARGETOBJS): Add s390.o.
* Makefile.in: Regenerate.
* configure.ac: Add s390 support.
* configure: Regenerate.
* configure.tgt: Add s390-*-* and s390x-*-*.
* testsuite/icf_safe_test.sh (arch_specific_safe_fold): Add s390
support.
Diffstat (limited to 'gold/s390.cc')
-rw-r--r-- | gold/s390.cc | 4327 |
1 files changed, 4327 insertions, 0 deletions
diff --git a/gold/s390.cc b/gold/s390.cc new file mode 100644 index 00000000000..020fb15c973 --- /dev/null +++ b/gold/s390.cc @@ -0,0 +1,4327 @@ +// s390.cc -- s390 target support for gold. + +// Copyright (C) 2015 Free Software Foundation, Inc. +// Written by Marcin Kościelnicki <koriakin@0x04.net>. + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include "gold.h" + +#include <cstring> + +#include "elfcpp.h" +#include "dwarf.h" +#include "parameters.h" +#include "reloc.h" +#include "s390.h" +#include "object.h" +#include "symtab.h" +#include "layout.h" +#include "output.h" +#include "copy-relocs.h" +#include "target.h" +#include "target-reloc.h" +#include "target-select.h" +#include "tls.h" +#include "gc.h" +#include "icf.h" + +namespace +{ + +using namespace gold; + +// A class to handle the .got.plt section. + +template<int size> +class Output_data_got_plt_s390 : public Output_section_data_build +{ + public: + Output_data_got_plt_s390(Layout* layout) + : Output_section_data_build(size/8), + layout_(layout) + { } + + Output_data_got_plt_s390(Layout* layout, off_t data_size) + : Output_section_data_build(data_size, size/8), + layout_(layout) + { } + + protected: + // Write out the PLT data. + void + do_write(Output_file*); + + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, "** GOT PLT"); } + + private: + // A pointer to the Layout class, so that we can find the .dynamic + // section when we write out the GOT PLT section. + Layout* layout_; +}; + +// A class to handle the PLT data. + +template<int size> +class Output_data_plt_s390 : public Output_section_data +{ + public: + typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, true> + Reloc_section; + + Output_data_plt_s390(Layout* layout, + Output_data_got<size, true>* got, + Output_data_got_plt_s390<size>* got_plt, + Output_data_space* got_irelative) + : Output_section_data(4), layout_(layout), + irelative_rel_(NULL), got_(got), got_plt_(got_plt), + got_irelative_(got_irelative), count_(0), + irelative_count_(0), free_list_() + { this->init(layout); } + + Output_data_plt_s390(Layout* layout, + Output_data_got<size, true>* got, + Output_data_got_plt_s390<size>* got_plt, + Output_data_space* got_irelative, + unsigned int plt_count) + : Output_section_data((plt_count + 1) * plt_entry_size, + 4, false), + layout_(layout), irelative_rel_(NULL), got_(got), + got_plt_(got_plt), got_irelative_(got_irelative), count_(plt_count), + irelative_count_(0), free_list_() + { + this->init(layout); + + // Initialize the free list and reserve the first entry. + this->free_list_.init((plt_count + 1) * plt_entry_size, false); + this->free_list_.remove(0, plt_entry_size); + } + + // Initialize the PLT section. + void + init(Layout* layout); + + // Add an entry to the PLT. + void + add_entry(Symbol_table*, Layout*, Symbol* gsym); + + // Add an entry to the PLT for a local STT_GNU_IFUNC symbol. + unsigned int + add_local_ifunc_entry(Symbol_table*, Layout*, + Sized_relobj_file<size, true>*, unsigned int); + + // Add the relocation for a PLT entry. + void + add_relocation(Symbol_table*, Layout*, Symbol*, unsigned int); + + // Return the .rela.plt section data. + Reloc_section* + rela_plt() + { return this->rel_; } + + // Return where the IRELATIVE relocations should go in the PLT + // relocations. + Reloc_section* + rela_irelative(Symbol_table*, Layout*); + + // 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 + { return this->count_ + this->irelative_count_; } + + // Return the offset of the first non-reserved PLT entry. + unsigned int + first_plt_entry_offset() + { return plt_entry_size; } + + // Return the size of a PLT entry. + unsigned int + get_plt_entry_size() const + { return plt_entry_size; } + + // Reserve a slot in the PLT for an existing symbol in an incremental update. + void + reserve_slot(unsigned int plt_index) + { + this->free_list_.remove((plt_index + 1) * plt_entry_size, + (plt_index + 2) * 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); + + // Add .eh_frame information for the PLT. + void + add_eh_frame(Layout* layout) + { + (void)layout; + layout->add_eh_frame_for_plt(this, + plt_eh_frame_cie, + plt_eh_frame_cie_size, + plt_eh_frame_fde, + plt_eh_frame_fde_size); + } + + protected: + // Fill in the first PLT entry. + void + fill_first_plt_entry(unsigned char* pov, + typename elfcpp::Elf_types<size>::Elf_Addr got_address, + typename elfcpp::Elf_types<size>::Elf_Addr plt_address); + + // Fill in a normal PLT entry. Returns the offset into the entry that + // should be the initial GOT slot value. + unsigned int + fill_plt_entry(unsigned char* pov, + typename elfcpp::Elf_types<size>::Elf_Addr got_address, + typename elfcpp::Elf_types<size>::Elf_Addr plt_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset); + + void + do_adjust_output_section(Output_section* os); + + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, _("** PLT")); } + + private: + // Set the final size. + void + set_final_data_size(); + + // Write out the PLT data. + void + do_write(Output_file*); + + // A pointer to the Layout class, so that we can find the .dynamic + // section when we write out the GOT PLT section. + Layout* layout_; + // 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<size, true>* got_; + // The .got.plt section. + Output_data_got_plt_s390<size>* got_plt_; + // The part of the .got.plt section used for IRELATIVE relocs. + Output_data_space* got_irelative_; + // The number of PLT entries. + unsigned int count_; + // Number of PLT entries with R_TILEGX_IRELATIVE relocs. These + // follow the regular PLT entries. + unsigned int irelative_count_; + // List of available regions within the section, for incremental + // update links. + Free_list free_list_; + + // The size of an entry in the PLT. + static const int plt_entry_size = 0x20; + // The first entry in the PLT. + static const unsigned char first_plt_entry_32_abs[plt_entry_size]; + static const unsigned char first_plt_entry_32_pic[plt_entry_size]; + static const unsigned char first_plt_entry_64[plt_entry_size]; + // Other entries in the PLT for an executable. + static const unsigned char plt_entry_32_abs[plt_entry_size]; + static const unsigned char plt_entry_32_pic12[plt_entry_size]; + static const unsigned char plt_entry_32_pic16[plt_entry_size]; + static const unsigned char plt_entry_32_pic[plt_entry_size]; + static const unsigned char plt_entry_64[plt_entry_size]; + + // The .eh_frame unwind information for the PLT. + static const int plt_eh_frame_cie_size = 12; + static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size]; + static const int plt_eh_frame_fde_size = 12; + static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size]; +}; + + +template<int size> +class Target_s390 : public Sized_target<size, true> +{ + public: + typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, true> Reloc_section; + + Target_s390() + : Sized_target<size, true>(&s390_info), + got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL), + global_offset_table_(NULL), rela_dyn_(NULL), + rela_irelative_(NULL), copy_relocs_(elfcpp::R_390_COPY), + got_mod_index_offset_(-1U), tls_base_symbol_defined_(false), + layout_(NULL) + { } + + // Scan the relocations to look for symbol adjustments. + void + gc_process_relocs(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_symbols); + + // Scan the relocations to look for symbol adjustments. + void + scan_relocs(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_symbols); + + // Finalize the sections. + void + do_finalize_sections(Layout*, const Input_objects*, Symbol_table*); + + // Return the value to use for a dynamic which requires special + // treatment. + uint64_t + do_dynsym_value(const Symbol*) const; + + // Relocate a section. + void + relocate_section(const Relocate_info<size, true>*, + 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<size>::Elf_Addr view_address, + section_size_type view_size, + const Reloc_symbol_changes*); + + // Scan the relocs during a relocatable link. + void + scan_relocatable_relocs(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Relocatable_relocs*); + + // Return a string used to fill a code section with nops. + std::string + do_code_fill(section_size_type length) const; + + // Emit relocations for a section. + void + relocate_relocs( + const Relocate_info<size, true>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section, + const Relocatable_relocs*, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size); + + // Return whether SYM is defined by the ABI. + bool + do_is_defined_by_abi(const Symbol* sym) const + { return strcmp(sym->name(), "__tls_get_offset") == 0; } + + // Return the PLT address to use for a global symbol. + 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 offset to use for the GOT_INDX'th got entry which is + // for a local tls symbol specified by OBJECT, SYMNDX. + int64_t + do_tls_offset_for_local(const Relobj* object, + unsigned int symndx, + unsigned int got_indx) const; + + // Return the offset to use for the GOT_INDX'th got entry which is + // for global tls symbol GSYM. + int64_t + do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const; + + // This function should be defined in targets that can use relocation + // types to determine (implemented in local_reloc_may_be_function_pointer + // and global_reloc_may_be_function_pointer) + // if a function's pointer is taken. ICF uses this in safe mode to only + // fold those functions whose pointer is defintely not taken. + bool + do_can_check_for_function_pointers() const + { return true; } + + // Return the size of the GOT section. + section_size_type + got_size() const + { + gold_assert(this->got_ != NULL); + return this->got_->data_size(); + } + + // Return the number of entries in the GOT. + unsigned int + got_entry_count() const + { + if (this->got_ == NULL) + return 0; + return this->got_size() / (size / 8); + } + + // Return the number of entries in the PLT. + unsigned int + plt_entry_count() const; + + // Return the offset of the first non-reserved PLT entry. + unsigned int + first_plt_entry_offset() const; + + // Return the size of each PLT entry. + unsigned int + plt_entry_size() const; + + // Create the GOT section for an incremental update. + Output_data_got_base* + init_got_plt_for_update(Symbol_table* symtab, + Layout* layout, + unsigned int got_count, + unsigned int plt_count); + + // Reserve a GOT entry for a local symbol, and regenerate any + // necessary dynamic relocations. + void + reserve_local_got_entry(unsigned int got_index, + Sized_relobj<size, true>* obj, + unsigned int r_sym, + unsigned int got_type); + + // Reserve a GOT entry for a global symbol, and regenerate any + // necessary dynamic relocations. + void + reserve_global_got_entry(unsigned int got_index, Symbol* gsym, + unsigned int got_type); + + // Register an existing PLT entry for a global symbol. + void + register_global_plt_entry(Symbol_table*, Layout*, unsigned int plt_index, + Symbol* gsym); + + // Force a COPY relocation for a given symbol. + void + emit_copy_reloc(Symbol_table*, Symbol*, Output_section*, off_t); + + // Apply an incremental relocation. + void + apply_relocation(const Relocate_info<size, true>* relinfo, + typename elfcpp::Elf_types<size>::Elf_Addr r_offset, + unsigned int r_type, + typename elfcpp::Elf_types<size>::Elf_Swxword r_addend, + const Symbol* gsym, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr address, + section_size_type view_size); + + private: + + // The class which scans relocations. + class Scan + { + public: + Scan() + : issued_non_pic_error_(false) + { } + + static inline int + get_reference_flags(unsigned int r_type); + + inline void + local(Symbol_table* symtab, Layout* layout, Target_s390* target, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<size, true>& reloc, unsigned int r_type, + const elfcpp::Sym<size, true>& lsym, + bool is_discarded); + + inline void + global(Symbol_table* symtab, Layout* layout, Target_s390* target, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<size, true>& reloc, unsigned int r_type, + Symbol* gsym); + + inline bool + local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, + Target_s390* target, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<size, true>& reloc, + unsigned int r_type, + const elfcpp::Sym<size, true>& lsym); + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, + Target_s390* target, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<size, true>& reloc, + unsigned int r_type, + Symbol* gsym); + + private: + static void + unsupported_reloc_local(Sized_relobj_file<size, true>*, + unsigned int r_type); + + static void + unsupported_reloc_global(Sized_relobj_file<size, true>*, + unsigned int r_type, Symbol*); + + void + check_non_pic(Relobj*, unsigned int r_type); + + inline bool + possible_function_pointer_reloc(unsigned int r_type); + + bool + reloc_needs_plt_for_ifunc(Sized_relobj_file<size, true>*, + unsigned int r_type); + + // Whether we have issued an error about a non-PIC compilation. + bool issued_non_pic_error_; + }; + + // The class which implements relocation. + class Relocate + { + public: + // Do a relocation. Return false if the caller should not issue + // any warnings about this relocation. + inline bool + relocate(const Relocate_info<size, true>*, Target_s390*, + Output_section*, + size_t relnum, const elfcpp::Rela<size, true>&, + unsigned int r_type, const Sized_symbol<size>*, + const Symbol_value<size>*, + unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr, + section_size_type); + + private: + // Do a TLS relocation. + inline typename elfcpp::Elf_types<size>::Elf_Addr + relocate_tls(const Relocate_info<size, true>*, Target_s390*, + size_t relnum, const elfcpp::Rela<size, true>&, + unsigned int r_type, const Sized_symbol<size>*, + const Symbol_value<size>*, + unsigned char*, section_size_type); + + // Do a TLS General-Dynamic to Initial-Exec transition. + inline void + tls_gd_to_ie(const Relocate_info<size, true>*, size_t relnum, + const elfcpp::Rela<size, true>&, + unsigned char* view, + section_size_type view_size); + + // Do a TLS General-Dynamic to Local-Exec transition. + inline void + tls_gd_to_le(const Relocate_info<size, true>*, size_t relnum, + const elfcpp::Rela<size, true>&, + unsigned char* view, + section_size_type view_size); + + // Do a TLS Local-Dynamic to Local-Exec transition. + inline void + tls_ld_to_le(const Relocate_info<size, true>*, size_t relnum, + const elfcpp::Rela<size, true>&, + unsigned char* view, + section_size_type view_size); + + // Do a TLS Initial-Exec to Local-Exec transition. + static inline void + tls_ie_to_le(const Relocate_info<size, true>*, size_t relnum, + const elfcpp::Rela<size, true>&, + unsigned char* view, + section_size_type view_size); + }; + + // A class which returns the size required for a relocation type, + // used while scanning relocs during a relocatable link. + class Relocatable_size_for_reloc + { + public: + unsigned int + get_size_for_reloc(unsigned int, Relobj*); + }; + + // Adjust TLS relocation type based on the options and whether this + // is a local symbol. + static tls::Tls_optimization + optimize_tls_reloc(bool is_final, int r_type); + + // Get the GOT section. + const Output_data_got<size, true>* + got_section() const + { + gold_assert(this->got_ != NULL); + return this->got_; + } + + // Get the GOT section, creating it if necessary. + Output_data_got<size, true>* + got_section(Symbol_table*, Layout*); + + typename elfcpp::Elf_types<size>::Elf_Addr + got_address() const + { + gold_assert(this->got_ != NULL); + return this->got_plt_->address(); + } + + typename elfcpp::Elf_types<size>::Elf_Addr + got_main_offset() const + { + gold_assert(this->got_ != NULL); + return this->got_->address() - this->got_address(); + } + + // Create the PLT section. + void + make_plt_section(Symbol_table* symtab, Layout* layout); + + // Create a PLT entry for a global symbol. + void + make_plt_entry(Symbol_table*, Layout*, Symbol*); + + // Create a PLT entry for a local STT_GNU_IFUNC symbol. + void + make_local_ifunc_plt_entry(Symbol_table*, Layout*, + Sized_relobj_file<size, true>* relobj, + unsigned int local_sym_index); + + // Create a GOT entry for the TLS module index. + unsigned int + got_mod_index_entry(Symbol_table* symtab, Layout* layout, + Sized_relobj_file<size, true>* object); + + // Get the PLT section. + Output_data_plt_s390<size>* + plt_section() const + { + gold_assert(this->plt_ != NULL); + return this->plt_; + } + + // Get the dynamic reloc section, creating it if necessary. + Reloc_section* + rela_dyn_section(Layout*); + + // Get the section to use for IRELATIVE relocations. + Reloc_section* + rela_irelative_section(Layout*); + + // Add a potential copy relocation. + void + copy_reloc(Symbol_table* symtab, Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int shndx, Output_section* output_section, + Symbol* sym, const elfcpp::Rela<size, true>& reloc) + { + this->copy_relocs_.copy_reloc(symtab, layout, + symtab->get_sized_symbol<size>(sym), + object, shndx, output_section, + reloc, this->rela_dyn_section(layout)); + } + + // Information about this specific target which we pass to the + // general Target structure. + static Target::Target_info s390_info; + + // The types of GOT entries needed for this platform. + // These values are exposed to the ABI in an incremental link. + // Do not renumber existing values without changing the version + // number of the .gnu_incremental_inputs section. + enum Got_type + { + GOT_TYPE_STANDARD = 0, // GOT entry for a regular symbol + GOT_TYPE_TLS_OFFSET = 1, // GOT entry for TLS offset + GOT_TYPE_TLS_PAIR = 2, // GOT entry for TLS module/offset pair + }; + + // The GOT section. + Output_data_got<size, true>* got_; + // The PLT section. + Output_data_plt_s390<size>* plt_; + // The GOT PLT section. + Output_data_got_plt_s390<size>* got_plt_; + // The GOT section for IRELATIVE relocations. + Output_data_space* got_irelative_; + // The _GLOBAL_OFFSET_TABLE_ symbol. + Symbol* global_offset_table_; + // The dynamic reloc section. + Reloc_section* rela_dyn_; + // The section to use for IRELATIVE relocs. + Reloc_section* rela_irelative_; + // Relocs saved to avoid a COPY reloc. + Copy_relocs<elfcpp::SHT_RELA, size, true> copy_relocs_; + // Offset of the GOT entry for the TLS module index. + unsigned int got_mod_index_offset_; + // True if the _TLS_MODULE_BASE_ symbol has been defined. + bool tls_base_symbol_defined_; + // For use in do_tls_offset_for_* + Layout *layout_; +}; + +template<> +Target::Target_info Target_s390<32>::s390_info = +{ + 32, // size + true, // is_big_endian + elfcpp::EM_S390, // machine_code + false, // has_make_symbol + false, // has_resolve + true, // has_code_fill + true, // is_default_stack_executable + true, // can_icf_inline_merge_sections + '\0', // wrap_char + "/lib/ld.so.1", // dynamic_linker + 0x00400000, // default_text_segment_address + 4 * 1024, // abi_pagesize (overridable by -z max-page-size) + 4 * 1024, // common_pagesize (overridable by -z common-page-size) + false, // isolate_execinstr + 0, // rosegment_gap + elfcpp::SHN_UNDEF, // small_common_shndx + elfcpp::SHN_UNDEF, // large_common_shndx + 0, // small_common_section_flags + 0, // large_common_section_flags + NULL, // attributes_section + NULL, // attributes_vendor + "_start", // entry_symbol_name + 32, // hash_entry_size +}; + +template<> +Target::Target_info Target_s390<64>::s390_info = +{ + 64, // size + true, // is_big_endian + elfcpp::EM_S390, // machine_code + false, // has_make_symbol + false, // has_resolve + true, // has_code_fill + true, // is_default_stack_executable + true, // can_icf_inline_merge_sections + '\0', // wrap_char + "/lib/ld64.so.1", // dynamic_linker + 0x80000000ll, // default_text_segment_address + 4 * 1024, // abi_pagesize (overridable by -z max-page-size) + 4 * 1024, // common_pagesize (overridable by -z common-page-size) + false, // isolate_execinstr + 0, // rosegment_gap + elfcpp::SHN_UNDEF, // small_common_shndx + elfcpp::SHN_UNDEF, // large_common_shndx + 0, // small_common_section_flags + 0, // large_common_section_flags + NULL, // attributes_section + NULL, // attributes_vendor + "_start", // entry_symbol_name + 64, // hash_entry_size +}; + +template<int size> +class S390_relocate_functions +{ +public: + enum Overflow_check + { + CHECK_NONE, + CHECK_SIGNED, + CHECK_UNSIGNED, + CHECK_BITFIELD, + CHECK_LOW_INSN, + CHECK_HIGH_INSN + }; + + enum Status + { + STATUS_OK, + STATUS_OVERFLOW + }; + +private: + typedef S390_relocate_functions<size> This; + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + + template<int valsize> + static inline bool + has_overflow_signed(Address value) + { + // limit = 1 << (valsize - 1) without shift count exceeding size of type + Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1); + limit <<= ((valsize - 1) >> 1); + limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1)); + return value + limit > (limit << 1) - 1; + } + + template<int valsize> + static inline bool + has_overflow_unsigned(Address value) + { + Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1); + limit <<= ((valsize - 1) >> 1); + limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1)); + return value > (limit << 1) - 1; + } + + template<int fieldsize> + static inline void + rela(unsigned char* view, Address mask, Address value) + { + typedef typename elfcpp::Swap<fieldsize, true>::Valtype Valtype; + Valtype* wv = reinterpret_cast<Valtype*>(view); + Valtype val = elfcpp::Swap<fieldsize, true>::readval(view); + val &= ~mask; + value &= mask; + elfcpp::Swap<fieldsize, true>::writeval(wv, val | value); + } + +public: + // R_390_12, R_390_GOT12, R_390_GOTPLT12, R_390_GOTIE12 + static inline Status + rela12(unsigned char* view, Address value) + { + if (This::template has_overflow_unsigned<12>(value)) + return STATUS_OVERFLOW; + This::template rela<16>(view, 0x0fff, value); + return STATUS_OK; + } + + // R_390_16, R_390_GOT16, R_390_GOTPLT16, R_390_GOTOFF16, R_390_PLTOFF16 + static inline Status + rela16(unsigned char* view, Address value) + { + if (This::template has_overflow_signed<16>(value)) + return STATUS_OVERFLOW; + This::template rela<16>(view, 0xffff, value); + return STATUS_OK; + } + + // R_390_20, R_390_GOT20, R_390_GOTPLT20, R_390_GOTIE20 + static inline Status + rela20(unsigned char* view, Address value) + { + if (This::template has_overflow_signed<20>(value)) + return STATUS_OVERFLOW; + This::template rela<16>(view, 0x0fff, value); + This::template rela<16>(view + 2, 0xff00, value >> (12 - 8)); + return STATUS_OK; + } + + // R_390_PC12DBL, R_390_PLT12DBL + static inline Status + pcrela12dbl(unsigned char* view, Address value, Address address) + { + value -= address; + if ((value & 1) != 0) + return STATUS_OVERFLOW; + if (This::template has_overflow_signed<13>(value)) + return STATUS_OVERFLOW; + value >>= 1; + This::template rela<16>(view, 0x0fff, value); + return STATUS_OK; + } + + // R_390_PC16DBL, R_390_PLT16DBL + static inline Status + pcrela16dbl(unsigned char* view, Address value, Address address) + { + value -= address; + if ((value & 1) != 0) + return STATUS_OVERFLOW; + if (This::template has_overflow_signed<17>(value)) + return STATUS_OVERFLOW; + value >>= 1; + This::template rela<16>(view, 0xffff, value); + return STATUS_OK; + } + + // R_390_PC24DBL, R_390_PLT24DBL + static inline Status + pcrela24dbl(unsigned char* view, Address value, Address address) + { + value -= address; + if ((value & 1) != 0) + return STATUS_OVERFLOW; + if (This::template has_overflow_signed<25>(value)) + return STATUS_OVERFLOW; + value >>= 1; + // Swap doesn't take 24-bit fields well... + This::template rela<8>(view, 0xff, value >> 16); + This::template rela<16>(view + 1, 0xffff, value); + return STATUS_OK; + } + + // R_390_PC32DBL, R_390_PLT32DBL, R_390_GOTPCDBL, R_390_GOTENT, R_390_GOTPLTENT + static inline Status + pcrela32dbl(unsigned char* view, Address value, Address address) + { + Address reloc = value - address; + if ((reloc & 1) != 0) + { + gold_warning(_("R_390_PC32DBL target misaligned at %llx"), (long long)address); + // Wait for a fix for https://sourceware.org/bugzilla/show_bug.cgi?id=18960 + // return STATUS_OVERFLOW; + } + if (This::template has_overflow_signed<33>(reloc)) + return STATUS_OVERFLOW; + reloc >>= 1; + if (value < address && size == 32) + reloc |= 0x80000000; + This::template rela<32>(view, 0xffffffff, reloc); + return STATUS_OK; + } + +}; + +// Initialize the PLT section. + +template<int size> +void +Output_data_plt_s390<size>::init(Layout* layout) +{ + this->rel_ = new Reloc_section(false); + layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->rel_, + ORDER_DYNAMIC_PLT_RELOCS, false); +} + +template<int size> +void +Output_data_plt_s390<size>::do_adjust_output_section(Output_section* os) +{ + os->set_entsize(plt_entry_size); +} + +// Add an entry to the PLT. + +template<int size> +void +Output_data_plt_s390<size>::add_entry(Symbol_table* symtab, Layout* layout, + Symbol* gsym) +{ + gold_assert(!gsym->has_plt_offset()); + + unsigned int plt_index; + off_t plt_offset; + section_offset_type got_offset; + + unsigned int* pcount; + unsigned int offset; + unsigned int reserved; + Output_section_data_build* got; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + { + pcount = &this->irelative_count_; + offset = 0; + reserved = 0; + got = this->got_irelative_; + } + else + { + pcount = &this->count_; + offset = 1; + reserved = 3; + got = this->got_plt_; + } + + if (!this->is_data_size_valid()) + { + // Note that when setting the PLT offset for a non-IRELATIVE + // entry we skip the initial reserved PLT entry. + plt_index = *pcount + offset; + plt_offset = plt_index * plt_entry_size; + + ++*pcount; + + got_offset = (plt_index - offset + reserved) * size / 8; + gold_assert(got_offset == got->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). + got->set_current_data_size(got_offset + size / 8); + } + else + { + // FIXME: This is probably not correct for IRELATIVE relocs. + + // For incremental updates, find an available slot. + plt_offset = this->free_list_.allocate(plt_entry_size, + plt_entry_size, 0); + if (plt_offset == -1) + gold_fallback(_("out of patch space (PLT);" + " relink with --incremental-full")); + + // The GOT and PLT entries have a 1-1 correspondance, so the GOT offset + // can be calculated from the PLT index, adjusting for the three + // reserved entries at the beginning of the GOT. + plt_index = plt_offset / plt_entry_size - 1; + got_offset = (plt_index - offset + reserved) * size / 8; + } + + gsym->set_plt_offset(plt_offset); + + // Every PLT entry needs a reloc. + this->add_relocation(symtab, layout, gsym, got_offset); + + // 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. +} + +// Add an entry to the PLT for a local STT_GNU_IFUNC symbol. Return +// the PLT offset. + +template<int size> +unsigned int +Output_data_plt_s390<size>::add_local_ifunc_entry( + Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* relobj, + unsigned int local_sym_index) +{ + unsigned int plt_offset = this->irelative_count_ * plt_entry_size; + ++this->irelative_count_; + + section_offset_type got_offset = this->got_irelative_->current_data_size(); + + // Every PLT entry needs a GOT entry which points back to the PLT + // entry. + this->got_irelative_->set_current_data_size(got_offset + size / 8); + + // Every PLT entry needs a reloc. + Reloc_section* rela = this->rela_irelative(symtab, layout); + rela->add_symbolless_local_addend(relobj, local_sym_index, + elfcpp::R_390_IRELATIVE, + this->got_irelative_, got_offset, 0); + + return plt_offset; +} + +// Add the relocation for a PLT entry. + +template<int size> +void +Output_data_plt_s390<size>::add_relocation(Symbol_table* symtab, + Layout* layout, + Symbol* gsym, + unsigned int got_offset) +{ + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + { + Reloc_section* rela = this->rela_irelative(symtab, layout); + rela->add_symbolless_global_addend(gsym, elfcpp::R_390_IRELATIVE, + this->got_irelative_, got_offset, 0); + } + else + { + gsym->set_needs_dynsym_entry(); + this->rel_->add_global(gsym, elfcpp::R_390_JMP_SLOT, this->got_plt_, + got_offset, 0); + } +} + +// Return where the IRELATIVE relocations should go in the PLT. These +// follow the JUMP_SLOT and the TLSDESC relocations. + +template<int size> +typename Output_data_plt_s390<size>::Reloc_section* +Output_data_plt_s390<size>::rela_irelative(Symbol_table* symtab, + Layout* layout) +{ + if (this->irelative_rel_ == NULL) + { + this->irelative_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->irelative_rel_, + ORDER_DYNAMIC_PLT_RELOCS, false); + gold_assert(this->irelative_rel_->output_section() + == this->rel_->output_section()); + + if (parameters->doing_static_link()) + { + // A statically linked executable will only have a .rela.plt + // section to hold R_390_IRELATIVE relocs for + // STT_GNU_IFUNC symbols. The library will use these + // symbols to locate the IRELATIVE relocs at program startup + // time. + symtab->define_in_output_data("__rela_iplt_start", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, false, true); + symtab->define_in_output_data("__rela_iplt_end", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, true, true); + } + } + return this->irelative_rel_; +} + +// Return the PLT address to use for a global symbol. + +template<int size> +uint64_t +Output_data_plt_s390<size>::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->count_ + 1) * 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<int size> +uint64_t +Output_data_plt_s390<size>::address_for_local(const Relobj* object, + unsigned int r_sym) +{ + return (this->address() + + (this->count_ + 1) * plt_entry_size + + object->local_plt_offset(r_sym)); +} + +// Set the final size. +template<int size> +void +Output_data_plt_s390<size>::set_final_data_size() +{ + unsigned int count = this->count_ + this->irelative_count_; + this->set_data_size((count + 1) * plt_entry_size); +} + +template<int size> +const unsigned char +Output_data_plt_s390<size>::first_plt_entry_32_abs[plt_entry_size] = +{ + 0x50, 0x10, 0xf0, 0x1c, // st %r1, 28(%r15) + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x12, // l %r1, 18(%r1) + 0xd2, 0x03, 0xf0, 0x18, 0x10, 0x04, // mvc 24(4,%r15), 4(%r1) + 0x58, 0x10, 0x10, 0x08, // l %r1, 8(%r1) + 0x07, 0xf1, // br %r1 + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // _GLOBAL_OFFSET_TABLE_ (to fill) + 0x00, 0x00, 0x00, 0x00, // padding +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::first_plt_entry_32_pic[plt_entry_size] = +{ + 0x50, 0x10, 0xf0, 0x1c, // st %r1, 28(%r15) + 0x58, 0x10, 0xc0, 0x04, // l %r1, 4(%r12) + 0x50, 0x10, 0xf0, 0x18, // st %r1, 24(%r15) + 0x58, 0x10, 0xc0, 0x08, // l %r1, 8(%r12) + 0x07, 0xf1, // br %r1 + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // padding +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::first_plt_entry_64[plt_entry_size] = +{ + 0xe3, 0x10, 0xf0, 0x38, 0x00, 0x24, // stg %r1, 56(%r15) + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1, _GLOBAL_OFFSET_TABLE_ (to fill) + 0xd2, 0x07, 0xf0, 0x30, 0x10, 0x08, // mvc 48(8,%r15), 8(%r1) + 0xe3, 0x10, 0x10, 0x10, 0x00, 0x04, // lg %r1, 16(%r1) + 0x07, 0xf1, // br %r1 + 0x07, 0x00, // nopr + 0x07, 0x00, // nopr + 0x07, 0x00, // nopr +}; + +template<int size> +void +Output_data_plt_s390<size>::fill_first_plt_entry( + unsigned char* pov, + typename elfcpp::Elf_types<size>::Elf_Addr got_address, + typename elfcpp::Elf_types<size>::Elf_Addr plt_address) +{ + if (size == 64) + { + memcpy(pov, first_plt_entry_64, plt_entry_size); + S390_relocate_functions<size>::pcrela32dbl(pov + 8, got_address, (plt_address + 6)); + } + else if (!parameters->options().output_is_position_independent()) + { + memcpy(pov, first_plt_entry_32_abs, plt_entry_size); + elfcpp::Swap<32, true>::writeval(pov + 24, got_address); + } + else + { + memcpy(pov, first_plt_entry_32_pic, plt_entry_size); + } +} + +template<int size> +const unsigned char +Output_data_plt_s390<size>::plt_entry_32_abs[plt_entry_size] = +{ + // first part + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x16, // l %r1, 22(%r1) + 0x58, 0x10, 0x10, 0x00, // l %r1, 0(%r1) + 0x07, 0xf1, // br %r1 + // second part + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x0e, // l %r1, 14(%r1) + 0xa7, 0xf4, 0x00, 0x00, // j first_plt_entry (to fill) + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // _GLOBAL_OFFSET_TABLE_+sym@gotplt (to fill) + 0x00, 0x00, 0x00, 0x00, // offset of relocation in .rela.plt (to fill) +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::plt_entry_32_pic12[plt_entry_size] = +{ + // first part + 0x58, 0x10, 0xc0, 0x00, // l %r1, sym@gotplt(%r12) (to fill) + 0x07, 0xf1, // br %r1 + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // padding + // second part + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x0e, // l %r1, 14(%r1) + 0xa7, 0xf4, 0x00, 0x00, // j first_plt_entry (to fill) + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // offset of relocation in .rela.plt (to fill) +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::plt_entry_32_pic16[plt_entry_size] = +{ + // first part + 0xa7, 0x18, 0x00, 0x00, // lhi %r1, sym@gotplt (to fill) + 0x58, 0x11, 0xc0, 0x00, // l %r1, 0(%r1, %r12) + 0x07, 0xf1, // br %r1 + 0x00, 0x00, // padding + // second part + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x0e, // l %r1, 14(%r1) + 0xa7, 0xf4, 0x00, 0x00, // j first_plt_entry (to fill) + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // offset of relocation in .rela.plt (to fill) +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::plt_entry_32_pic[plt_entry_size] = +{ + // first part + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x16, // l %r1, 22(%r1) + 0x58, 0x11, 0xc0, 0x00, // l %r1, 0(%r1, %r12) + 0x07, 0xf1, // br %r1 + // second part + 0x0d, 0x10, // basr %r1, %r0 + 0x58, 0x10, 0x10, 0x0e, // l %r1, 14(%r1) + 0xa7, 0xf4, 0x00, 0x00, // j first_plt_entry (to fill) + 0x00, 0x00, // padding + 0x00, 0x00, 0x00, 0x00, // sym@gotplt (to fill) + 0x00, 0x00, 0x00, 0x00, // offset of relocation in .rela.plt (to fill) +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::plt_entry_64[plt_entry_size] = +{ + // first part + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1, _GLOBAL_OFFSET_TABLE_+off (to fill) + 0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, // lg %r1, 0(%r1) + 0x07, 0xf1, // br %r1 + // second part + 0x0d, 0x10, // basr %r1, %r0 + 0xe3, 0x10, 0x10, 0x0c, 0x00, 0x14, // lgf %r1, 12(%r1) + 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, // jg first_plt_entry (to fill) + 0x00, 0x00, 0x00, 0x00, // offset of relocation in .rela.plt (to fill) +}; + +template<int size> +unsigned int +Output_data_plt_s390<size>::fill_plt_entry( + unsigned char* pov, + typename elfcpp::Elf_types<size>::Elf_Addr got_address, + typename elfcpp::Elf_types<size>::Elf_Addr plt_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) +{ + if (size == 64) + { + memcpy(pov, plt_entry_64, plt_entry_size); + S390_relocate_functions<size>::pcrela32dbl(pov + 2, got_address + got_offset, plt_address + plt_offset); + S390_relocate_functions<size>::pcrela32dbl(pov + 24, plt_address, plt_address + plt_offset + 22); + } + else + { + if (!parameters->options().output_is_position_independent()) + { + memcpy(pov, plt_entry_32_abs, plt_entry_size); + elfcpp::Swap<32, true>::writeval(pov + 24, got_address + got_offset); + } + else + { + if (got_offset < 0x1000) + { + memcpy(pov, plt_entry_32_pic12, plt_entry_size); + S390_relocate_functions<size>::rela12(pov + 2, got_offset); + } + else if (got_offset < 0x8000) + { + memcpy(pov, plt_entry_32_pic16, plt_entry_size); + S390_relocate_functions<size>::rela16(pov + 2, got_offset); + } + else + { + memcpy(pov, plt_entry_32_pic, plt_entry_size); + elfcpp::Swap<32, true>::writeval(pov + 24, got_offset); + } + } + typename elfcpp::Elf_types<size>::Elf_Addr target = plt_address; + if (plt_offset >= 0x10000) + { + // Would overflow pcrela16dbl - aim at the farthest previous jump + // we can reach. + if (plt_offset > 0x10000) + { + // Use the full range of pcrel16dbl. + target = plt_address + plt_offset - 0x10000 + 18; + } + else + { + // if plt_offset is exactly 0x10000, the above would aim at 18th byte + // of first_plt_entry, which doesn't have the jump back like the others. + // Aim at the next entry instead. + target = plt_address + plt_offset - 0xffe0 + 18; + } + } + S390_relocate_functions<size>::pcrela16dbl(pov + 20, target, plt_address + plt_offset + 18); + } + elfcpp::Swap<32, true>::writeval(pov + 28, plt_rel_offset); + if (size == 64) + return 14; + else + return 12; +} + +// The .eh_frame unwind information for the PLT. + +template<> +const unsigned char +Output_data_plt_s390<32>::plt_eh_frame_cie[plt_eh_frame_cie_size] = +{ + 1, // CIE version. + 'z', // Augmentation: augmentation size included. + 'R', // Augmentation: FDE encoding included. + '\0', // End of augmentation string. + 1, // Code alignment factor. + 0x7c, // Data alignment factor. + 14, // Return address column. + 1, // Augmentation size. + (elfcpp::DW_EH_PE_pcrel // FDE encoding. + | elfcpp::DW_EH_PE_sdata4), + elfcpp::DW_CFA_def_cfa, 15, 0x60, // DW_CFA_def_cfa: r15 ofs 0x60. +}; + +template<> +const unsigned char +Output_data_plt_s390<64>::plt_eh_frame_cie[plt_eh_frame_cie_size] = +{ + 1, // CIE version. + 'z', // Augmentation: augmentation size included. + 'R', // Augmentation: FDE encoding included. + '\0', // End of augmentation string. + 1, // Code alignment factor. + 0x78, // Data alignment factor. + 14, // Return address column. + 1, // Augmentation size. + (elfcpp::DW_EH_PE_pcrel // FDE encoding. + | elfcpp::DW_EH_PE_sdata4), + elfcpp::DW_CFA_def_cfa, 15, 0xa0, // DW_CFA_def_cfa: r15 ofs 0xa0. +}; + +template<int size> +const unsigned char +Output_data_plt_s390<size>::plt_eh_frame_fde[plt_eh_frame_fde_size] = +{ + 0, 0, 0, 0, // Replaced with offset to .plt. + 0, 0, 0, 0, // Replaced with size of .plt. + 0, // Augmentation size. + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop +}; + +// Write out the PLT. This uses the hand-coded instructions above, +// and adjusts them as needed. + +template<int size> +void +Output_data_plt_s390<size>::do_write(Output_file* of) +{ + const off_t offset = this->offset(); + const section_size_type oview_size = + convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + const off_t got_file_offset = this->got_plt_->offset(); + gold_assert(parameters->incremental_update() + || (got_file_offset + this->got_plt_->data_size() + == this->got_irelative_->offset())); + const section_size_type got_size = + convert_to_section_size_type(this->got_plt_->data_size() + + this->got_irelative_->data_size()); + unsigned char* const got_view = of->get_output_view(got_file_offset, + got_size); + + unsigned char* pov = oview; + + // The base address of the .plt section. + typename elfcpp::Elf_types<size>::Elf_Addr plt_address = this->address(); + // The base address of the PLT portion of the .got section, + // which is where the GOT pointer will point, and where the + // three reserved GOT entries are located. + typename elfcpp::Elf_types<size>::Elf_Addr got_address + = this->got_plt_->address(); + + this->fill_first_plt_entry(pov, got_address, plt_address); + pov += this->get_plt_entry_size(); + + unsigned char* got_pov = got_view; + + const int rel_size = elfcpp::Elf_sizes<size>::rela_size; + + unsigned int plt_offset = this->get_plt_entry_size(); + unsigned int plt_rel_offset = 0; + unsigned int got_offset = 3 * size / 8; + const unsigned int count = this->count_ + this->irelative_count_; + // The first three entries in the GOT are reserved, and are written + // by Output_data_got_plt_s390::do_write. + got_pov += 3 * size / 8; + + for (unsigned int plt_index = 0; + plt_index < count; + ++plt_index, + pov += plt_entry_size, + got_pov += size / 8, + plt_offset += plt_entry_size, + plt_rel_offset += rel_size, + got_offset += size / 8) + { + // Set and adjust the PLT entry itself. + unsigned int lazy_offset = this->fill_plt_entry(pov, + got_address, plt_address, + got_offset, plt_offset, + plt_rel_offset); + + // Set the entry in the GOT. + elfcpp::Swap<size, true>::writeval(got_pov, + plt_address + plt_offset + lazy_offset); + } + + gold_assert(static_cast<section_size_type>(pov - oview) == oview_size); + gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size); + + of->write_output_view(offset, oview_size, oview); + of->write_output_view(got_file_offset, got_size, got_view); +} + +// Get the GOT section, creating it if necessary. + +template<int size> +Output_data_got<size, true>* +Target_s390<size>::got_section(Symbol_table* symtab, Layout* layout) +{ + if (this->got_ == NULL) + { + gold_assert(symtab != NULL && layout != NULL); + + // When using -z now, we can treat .got as a relro section. + // Without -z now, it is modified after program startup by lazy + // PLT relocations. + bool is_got_relro = parameters->options().now(); + Output_section_order got_order = (is_got_relro + ? ORDER_RELRO_LAST + : ORDER_DATA); + + // The old GNU linker creates a .got.plt section. We just + // create another set of data in the .got section. Note that we + // always create a PLT if we create a GOT, although the PLT + // might be empty. + this->got_plt_ = new Output_data_got_plt_s390<size>(layout); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), + this->got_plt_, got_order, is_got_relro); + + // The first three entries are reserved. + this->got_plt_->set_current_data_size(3 * size / 8); + + // If there are any IRELATIVE relocations, they get GOT entries + // in .got.plt after the jump slot entries. + this->got_irelative_ = new Output_data_space(size / 8, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), + this->got_irelative_, + got_order, is_got_relro); + + // Unlike some targets (.e.g x86), S/390 does not use separate .got and + // .got.plt sections in output. The output .got section contains both + // PLT and non-PLT GOT entries. + this->got_ = new Output_data_got<size, true>(); + + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), + this->got_, got_order, is_got_relro); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the GOT. + this->global_offset_table_ = + symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, + Symbol_table::PREDEFINED, + this->got_plt_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); + + } + return this->got_; +} + +// Get the dynamic reloc section, creating it if necessary. + +template<int size> +typename Target_s390<size>::Reloc_section* +Target_s390<size>::rela_dyn_section(Layout* layout) +{ + if (this->rela_dyn_ == NULL) + { + gold_assert(layout != NULL); + this->rela_dyn_ = new Reloc_section(parameters->options().combreloc()); + layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->rela_dyn_, + ORDER_DYNAMIC_RELOCS, false); + } + return this->rela_dyn_; +} + +// Get the section to use for IRELATIVE relocs, creating it if +// necessary. These go in .rela.dyn, but only after all other dynamic +// relocations. They need to follow the other dynamic relocations so +// that they can refer to global variables initialized by those +// relocs. + +template<int size> +typename Target_s390<size>::Reloc_section* +Target_s390<size>::rela_irelative_section(Layout* layout) +{ + if (this->rela_irelative_ == NULL) + { + // Make sure we have already created the dynamic reloc section. + this->rela_dyn_section(layout); + this->rela_irelative_ = new Reloc_section(false); + layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->rela_irelative_, + ORDER_DYNAMIC_RELOCS, false); + gold_assert(this->rela_dyn_->output_section() + == this->rela_irelative_->output_section()); + } + return this->rela_irelative_; +} + +// Write the first three reserved words of the .got.plt section. +// The remainder of the section is written while writing the PLT +// in Output_data_plt_s390::do_write. + +template<int size> +void +Output_data_got_plt_s390<size>::do_write(Output_file* of) +{ + // The first entry in the GOT is the address of the .dynamic section + // aka the PT_DYNAMIC segment. The next two entries are reserved. + // We saved space for them when we created the section in + // Target_x86_64::got_section. + const off_t got_file_offset = this->offset(); + gold_assert(this->data_size() >= 3 * size / 8); + unsigned char* const got_view = + of->get_output_view(got_file_offset, 3 * size / 8); + Output_section* dynamic = this->layout_->dynamic_section(); + uint64_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address(); + elfcpp::Swap<size, true>::writeval(got_view, dynamic_addr); + memset(got_view + size / 8, 0, 2 * size / 8); + of->write_output_view(got_file_offset, 3 * size / 8, got_view); +} + +// Create the PLT section. + +template<int size> +void +Target_s390<size>::make_plt_section(Symbol_table* symtab, Layout* layout) +{ + if (this->plt_ == NULL) + { + // Create the GOT sections first. + this->got_section(symtab, layout); + + // Ensure that .rela.dyn always appears before .rela.plt This is + // necessary due to how, on 32-bit S/390 and some other targets, + // .rela.dyn needs to include .rela.plt in it's range. + this->rela_dyn_section(layout); + + this->plt_ = new Output_data_plt_s390<size>(layout, + this->got_, this->got_plt_, this->got_irelative_); + + // Add unwind information if requested. + if (parameters->options().ld_generated_unwind_info()) + this->plt_->add_eh_frame(layout); + + 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<int size> +void +Target_s390<size>::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(symtab, layout, gsym); +} + +// Make a PLT entry for a local STT_GNU_IFUNC symbol. + +template<int size> +void +Target_s390<size>::make_local_ifunc_plt_entry( + Symbol_table* symtab, Layout* layout, + Sized_relobj_file<size, true>* relobj, + unsigned int local_sym_index) +{ + if (relobj->local_has_plt_offset(local_sym_index)) + return; + if (this->plt_ == NULL) + this->make_plt_section(symtab, layout); + unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout, + relobj, + local_sym_index); + relobj->set_local_plt_offset(local_sym_index, plt_offset); +} + +// Return the number of entries in the PLT. + +template<int size> +unsigned int +Target_s390<size>::plt_entry_count() const +{ + if (this->plt_ == NULL) + return 0; + return this->plt_->entry_count(); +} + +// Return the offset of the first non-reserved PLT entry. + +template<int size> +unsigned int +Target_s390<size>::first_plt_entry_offset() const +{ + return this->plt_->first_plt_entry_offset(); +} + +// Return the size of each PLT entry. + +template<int size> +unsigned int +Target_s390<size>::plt_entry_size() const +{ + return this->plt_->get_plt_entry_size(); +} + +// Create the GOT and PLT sections for an incremental update. + +template<int size> +Output_data_got_base* +Target_s390<size>::init_got_plt_for_update(Symbol_table* symtab, + Layout* layout, + unsigned int got_count, + unsigned int plt_count) +{ + gold_assert(this->got_ == NULL); + + // Add the three reserved entries. + this->got_plt_ = new Output_data_got_plt_s390<size>(layout, (plt_count + 3) * size / 8); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_plt_, ORDER_NON_RELRO_FIRST, + false); + + // If there are any IRELATIVE relocations, they get GOT entries in + // .got.plt after the jump slot entries. + this->got_irelative_ = new Output_data_space(0, size / 8, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->got_irelative_, + ORDER_NON_RELRO_FIRST, false); + + this->got_ = new Output_data_got<size, true>(got_count * size / 8); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_, ORDER_RELRO_LAST, + true); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. + this->global_offset_table_ = + symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, + Symbol_table::PREDEFINED, + this->got_plt_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); + + // Create the PLT section. + this->plt_ = new Output_data_plt_s390<size>(layout, + this->got_, this->got_plt_, this->got_irelative_, plt_count); + + // Add unwind information if requested. + if (parameters->options().ld_generated_unwind_info()) + this->plt_->add_eh_frame(layout); + + 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 the rela_dyn section. + this->rela_dyn_section(layout); + + return this->got_; +} + +// Reserve a GOT entry for a local symbol, and regenerate any +// necessary dynamic relocations. + +template<int size> +void +Target_s390<size>::reserve_local_got_entry( + unsigned int got_index, + Sized_relobj<size, true>* obj, + unsigned int r_sym, + unsigned int got_type) +{ + unsigned int got_offset = got_index * size / 8; + Reloc_section* rela_dyn = this->rela_dyn_section(NULL); + + this->got_->reserve_local(got_index, obj, r_sym, got_type); + switch (got_type) + { + case GOT_TYPE_STANDARD: + if (parameters->options().output_is_position_independent()) + rela_dyn->add_local_relative(obj, r_sym, elfcpp::R_390_RELATIVE, + this->got_, got_offset, 0, false); + break; + case GOT_TYPE_TLS_OFFSET: + rela_dyn->add_local(obj, r_sym, elfcpp::R_390_TLS_TPOFF, + this->got_, got_offset, 0); + break; + case GOT_TYPE_TLS_PAIR: + this->got_->reserve_slot(got_index + 1); + rela_dyn->add_local(obj, r_sym, elfcpp::R_390_TLS_DTPMOD, + this->got_, got_offset, 0); + break; + default: + gold_unreachable(); + } +} + +// Reserve a GOT entry for a global symbol, and regenerate any +// necessary dynamic relocations. + +template<int size> +void +Target_s390<size>::reserve_global_got_entry(unsigned int got_index, + Symbol* gsym, + unsigned int got_type) +{ + unsigned int got_offset = got_index * size / 8; + Reloc_section* rela_dyn = this->rela_dyn_section(NULL); + + this->got_->reserve_global(got_index, gsym, got_type); + switch (got_type) + { + case GOT_TYPE_STANDARD: + if (!gsym->final_value_is_known()) + { + if (gsym->is_from_dynobj() + || gsym->is_undefined() + || gsym->is_preemptible() + || gsym->type() == elfcpp::STT_GNU_IFUNC) + rela_dyn->add_global(gsym, elfcpp::R_390_GLOB_DAT, + this->got_, got_offset, 0); + else + rela_dyn->add_global_relative(gsym, elfcpp::R_390_RELATIVE, + this->got_, got_offset, 0, false); + } + break; + case GOT_TYPE_TLS_OFFSET: + rela_dyn->add_global_relative(gsym, elfcpp::R_390_TLS_TPOFF, + this->got_, got_offset, 0, false); + break; + case GOT_TYPE_TLS_PAIR: + this->got_->reserve_slot(got_index + 1); + rela_dyn->add_global_relative(gsym, elfcpp::R_390_TLS_DTPMOD, + this->got_, got_offset, 0, false); + rela_dyn->add_global_relative(gsym, elfcpp::R_390_TLS_DTPOFF, + this->got_, got_offset + size / 8, 0, false); + break; + default: + gold_unreachable(); + } +} + +// Register an existing PLT entry for a global symbol. + +template<int size> +void +Target_s390<size>::register_global_plt_entry(Symbol_table* symtab, + Layout* layout, + unsigned int plt_index, + Symbol* gsym) +{ + gold_assert(this->plt_ != NULL); + gold_assert(!gsym->has_plt_offset()); + + this->plt_->reserve_slot(plt_index); + + gsym->set_plt_offset((plt_index + 1) * this->plt_entry_size()); + + unsigned int got_offset = (plt_index + 3) * size / 8; + this->plt_->add_relocation(symtab, layout, gsym, got_offset); +} + +// Force a COPY relocation for a given symbol. + +template<int size> +void +Target_s390<size>::emit_copy_reloc( + Symbol_table* symtab, Symbol* sym, Output_section* os, off_t offset) +{ + this->copy_relocs_.emit_copy_reloc(symtab, + symtab->get_sized_symbol<size>(sym), + os, + offset, + this->rela_dyn_section(NULL)); +} + +// Create a GOT entry for the TLS module index. + +template<int size> +unsigned int +Target_s390<size>::got_mod_index_entry(Symbol_table* symtab, Layout* layout, + Sized_relobj_file<size, true>* object) +{ + if (this->got_mod_index_offset_ == -1U) + { + gold_assert(symtab != NULL && layout != NULL && object != NULL); + Reloc_section* rela_dyn = this->rela_dyn_section(layout); + Output_data_got<size, true>* got = this->got_section(symtab, layout); + unsigned int got_offset = got->add_constant(0); + rela_dyn->add_local(object, 0, elfcpp::R_390_TLS_DTPMOD, got, + got_offset, 0); + got->add_constant(0); + this->got_mod_index_offset_ = got_offset; + } + return this->got_mod_index_offset_; +} + +// Optimize the TLS relocation type based on what we know about the +// symbol. IS_FINAL is true if the final address of this symbol is +// known at link time. + +template<int size> +tls::Tls_optimization +Target_s390<size>::optimize_tls_reloc(bool is_final, int r_type) +{ + // If we are generating a shared library, then we can't do anything + // in the linker. + if (parameters->options().shared()) + return tls::TLSOPT_NONE; + + switch (r_type) + { + case elfcpp::R_390_TLS_GD32: + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + // These are General-Dynamic which permits fully general TLS + // access. Since we know that we are generating an executable, + // we can convert this to Initial-Exec. If we also know that + // this is a local symbol, we can further switch to Local-Exec. + if (is_final) + return tls::TLSOPT_TO_LE; + return tls::TLSOPT_TO_IE; + + case elfcpp::R_390_TLS_LDM32: + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_LDCALL: + // This is Local-Dynamic, which refers to a local symbol in the + // dynamic TLS block. Since we know that we generating an + // executable, we can switch to Local-Exec. + return tls::TLSOPT_TO_LE; + + case elfcpp::R_390_TLS_IE32: + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + // These are Initial-Exec relocs which get the thread offset + // from the GOT. If we know that we are linking against the + // local symbol, we can switch to Local-Exec, which links the + // thread offset into the instruction. + if (is_final) + return tls::TLSOPT_TO_LE; + return tls::TLSOPT_NONE; + + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE20: + // These are Initial-Exec, but cannot be optimized. + return tls::TLSOPT_NONE; + + case elfcpp::R_390_TLS_LE32: + case elfcpp::R_390_TLS_LE64: + // When we already have Local-Exec, there is nothing further we + // can do. + return tls::TLSOPT_NONE; + + default: + gold_unreachable(); + } +} + +// Get the Reference_flags for a particular relocation. + +template<int size> +int +Target_s390<size>::Scan::get_reference_flags(unsigned int r_type) +{ + switch (r_type) + { + case elfcpp::R_390_NONE: + case elfcpp::R_390_GNU_VTINHERIT: + case elfcpp::R_390_GNU_VTENTRY: + case elfcpp::R_390_GOTPC: + case elfcpp::R_390_GOTPCDBL: + // No symbol reference. + return 0; + + case elfcpp::R_390_64: + case elfcpp::R_390_32: + case elfcpp::R_390_20: + case elfcpp::R_390_16: + case elfcpp::R_390_12: + case elfcpp::R_390_8: + return Symbol::ABSOLUTE_REF; + + case elfcpp::R_390_PC12DBL: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PC24DBL: + case elfcpp::R_390_PC32: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_PC64: + case elfcpp::R_390_GOTOFF16: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_GOTOFF64: + return Symbol::RELATIVE_REF; + + case elfcpp::R_390_PLT12DBL: + case elfcpp::R_390_PLT16DBL: + case elfcpp::R_390_PLT24DBL: + case elfcpp::R_390_PLT32: + case elfcpp::R_390_PLT32DBL: + case elfcpp::R_390_PLT64: + case elfcpp::R_390_PLTOFF16: + case elfcpp::R_390_PLTOFF32: + case elfcpp::R_390_PLTOFF64: + return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF; + + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTPLT12: + case elfcpp::R_390_GOTPLT16: + case elfcpp::R_390_GOTPLT20: + case elfcpp::R_390_GOTPLT32: + case elfcpp::R_390_GOTPLT64: + case elfcpp::R_390_GOTPLTENT: + // Absolute in GOT. + return Symbol::ABSOLUTE_REF; + + case elfcpp::R_390_TLS_GD32: // Global-dynamic + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + case elfcpp::R_390_TLS_LDM32: // Local-dynamic + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_LDCALL: + case elfcpp::R_390_TLS_IE32: // Initial-exec + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + return Symbol::TLS_REF; + + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + case elfcpp::R_390_TLS_TPOFF: + case elfcpp::R_390_TLS_DTPOFF: + case elfcpp::R_390_TLS_DTPMOD: + default: + // Not expected. We will give an error later. + return 0; + } +} + +// Report an unsupported relocation against a local symbol. + +template<int size> +void +Target_s390<size>::Scan::unsupported_reloc_local( + Sized_relobj_file<size, true>* object, + unsigned int r_type) +{ + gold_error(_("%s: unsupported reloc %u against local symbol"), + object->name().c_str(), r_type); +} + +// We are about to emit a dynamic relocation of type R_TYPE. If the +// dynamic linker does not support it, issue an error. + +template<int size> +void +Target_s390<size>::Scan::check_non_pic(Relobj* object, unsigned int r_type) +{ + gold_assert(r_type != elfcpp::R_390_NONE); + + if (size == 64) + { + switch (r_type) + { + // These are the relocation types supported by glibc for s390 64-bit. + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_TLS_DTPMOD: + case elfcpp::R_390_TLS_DTPOFF: + case elfcpp::R_390_TLS_TPOFF: + case elfcpp::R_390_8: + case elfcpp::R_390_16: + case elfcpp::R_390_32: + case elfcpp::R_390_64: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PC32: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_PC64: + return; + + default: + break; + } + } + else + { + switch (r_type) + { + // These are the relocation types supported by glibc for s390 32-bit. + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_TLS_DTPMOD: + case elfcpp::R_390_TLS_DTPOFF: + case elfcpp::R_390_TLS_TPOFF: + case elfcpp::R_390_8: + case elfcpp::R_390_16: + case elfcpp::R_390_32: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PC32: + case elfcpp::R_390_PC32DBL: + return; + + default: + break; + } + } + + // This prevents us from issuing more than one error per reloc + // section. But we can still wind up issuing more than one + // error per object file. + if (this->issued_non_pic_error_) + return; + gold_assert(parameters->options().output_is_position_independent()); + object->error(_("requires unsupported dynamic reloc; " + "recompile with -fPIC")); + this->issued_non_pic_error_ = true; + return; +} + +// Return whether we need to make a PLT entry for a relocation of the +// given type against a STT_GNU_IFUNC symbol. + +template<int size> +bool +Target_s390<size>::Scan::reloc_needs_plt_for_ifunc( + Sized_relobj_file<size, true>* object, + unsigned int r_type) +{ + int flags = Scan::get_reference_flags(r_type); + if (flags & Symbol::TLS_REF) + gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"), + object->name().c_str(), r_type); + return flags != 0; +} + +// Scan a relocation for a local symbol. + +template<int size> +inline void +Target_s390<size>::Scan::local(Symbol_table* symtab, + Layout* layout, + Target_s390<size>* target, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<size, true>& reloc, + unsigned int r_type, + const elfcpp::Sym<size, true>& lsym, + bool is_discarded) +{ + if (is_discarded) + return; + + // A local STT_GNU_IFUNC symbol may require a PLT entry. + bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC; + + if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type)) + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym); + } + + switch (r_type) + { + case elfcpp::R_390_NONE: + case elfcpp::R_390_GNU_VTINHERIT: + case elfcpp::R_390_GNU_VTENTRY: + break; + + case elfcpp::R_390_64: + // If building a shared library (or a position-independent + // executable), we need to create a dynamic relocation for this + // location. The relocation applied at link time will apply the + // link-time value, so we flag the location with an + // R_390_RELATIVE relocation so the dynamic loader can + // relocate it easily. + if (parameters->options().output_is_position_independent() && size == 64) + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_local_relative(object, r_sym, + elfcpp::R_390_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend(), is_ifunc); + } + break; + + case elfcpp::R_390_32: + case elfcpp::R_390_20: + case elfcpp::R_390_16: + case elfcpp::R_390_12: + case elfcpp::R_390_8: + if (parameters->options().output_is_position_independent()) + { + if (size == 32 && r_type == elfcpp::R_390_32) + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_local_relative(object, r_sym, + elfcpp::R_390_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend(), is_ifunc); + break; + } + + check_non_pic(object, r_type); + + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + if (lsym.get_st_type() != elfcpp::STT_SECTION) + rela_dyn->add_local(object, r_sym, r_type, output_section, + data_shndx, reloc.get_r_offset(), + reloc.get_r_addend()); + else + { + gold_assert(lsym.get_st_value() == 0); + unsigned int shndx = lsym.get_st_shndx(); + bool is_ordinary; + shndx = object->adjust_sym_shndx(r_sym, shndx, + &is_ordinary); + if (!is_ordinary) + object->error(_("section symbol %u has bad shndx %u"), + r_sym, shndx); + else + rela_dyn->add_local_section(object, shndx, + r_type, output_section, + data_shndx, reloc.get_r_offset(), + reloc.get_r_addend()); + } + } + break; + + case elfcpp::R_390_PC12DBL: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PC24DBL: + case elfcpp::R_390_PC32: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_PC64: + break; + + case elfcpp::R_390_PLT12DBL: + case elfcpp::R_390_PLT16DBL: + case elfcpp::R_390_PLT24DBL: + case elfcpp::R_390_PLT32: + case elfcpp::R_390_PLT32DBL: + case elfcpp::R_390_PLT64: + // Since we know this is a local symbol, we can handle this as a + // PC32 reloc. + break; + + case elfcpp::R_390_GOTPC: + case elfcpp::R_390_GOTPCDBL: + case elfcpp::R_390_GOTOFF16: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_GOTOFF64: + case elfcpp::R_390_PLTOFF16: + case elfcpp::R_390_PLTOFF32: + case elfcpp::R_390_PLTOFF64: + // We need a GOT section. + target->got_section(symtab, layout); + // For PLTOFF*, we'd normally want a PLT section, but since we + // know this is a local symbol, no PLT is needed. + break; + + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTPLT12: + case elfcpp::R_390_GOTPLT16: + case elfcpp::R_390_GOTPLT20: + case elfcpp::R_390_GOTPLT32: + case elfcpp::R_390_GOTPLT64: + case elfcpp::R_390_GOTPLTENT: + { + // The symbol requires a GOT section. + Output_data_got<size, true>* got = target->got_section(symtab, layout); + + // The symbol requires a GOT entry. + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + + // For a STT_GNU_IFUNC symbol we want the PLT offset. That + // lets function pointers compare correctly with shared + // libraries. Otherwise we would need an IRELATIVE reloc. + bool is_new; + if (is_ifunc) + is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD); + else + is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD); + if (is_new) + { + // If we are generating a shared object, we need to add a + // dynamic relocation for this symbol's GOT entry. + if (parameters->options().output_is_position_independent()) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + unsigned int got_offset = + object->local_got_offset(r_sym, GOT_TYPE_STANDARD); + rela_dyn->add_local_relative(object, r_sym, + elfcpp::R_390_RELATIVE, + got, got_offset, 0, is_ifunc); + } + } + // For GOTPLT*, we'd normally want a PLT section, but since + // we know this is a local symbol, no PLT is needed. + } + break; + + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + // These are outstanding tls relocs, which are unexpected when linking + case elfcpp::R_390_TLS_TPOFF: + case elfcpp::R_390_TLS_DTPOFF: + case elfcpp::R_390_TLS_DTPMOD: + gold_error(_("%s: unexpected reloc %u in object file"), + object->name().c_str(), r_type); + break; + + // These are initial tls relocs, which are expected when linking + case elfcpp::R_390_TLS_GD32: // Global-dynamic + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + case elfcpp::R_390_TLS_LDM32: // Local-dynamic + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_LDCALL: + case elfcpp::R_390_TLS_IE32: // Initial-exec + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + { + bool output_is_shared = parameters->options().shared(); + const tls::Tls_optimization optimized_type + = Target_s390<size>::optimize_tls_reloc(!output_is_shared, + r_type); + switch (r_type) + { + case elfcpp::R_390_TLS_GD32: // General-dynamic + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + if (optimized_type == tls::TLSOPT_NONE) + { + // Create a pair of GOT entries for the module index and + // dtv-relative offset. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + unsigned int shndx = lsym.get_st_shndx(); + bool is_ordinary; + shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary); + if (!is_ordinary) + object->error(_("local symbol %u has bad shndx %u"), + r_sym, shndx); + else + got->add_local_pair_with_rel(object, r_sym, + shndx, + GOT_TYPE_TLS_PAIR, + target->rela_dyn_section(layout), + elfcpp::R_390_TLS_DTPMOD); + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_local(object, r_type); + break; + + case elfcpp::R_390_TLS_LDM32: // Local-dynamic + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDCALL: + if (optimized_type == tls::TLSOPT_NONE) + { + // Create a GOT entry for the module index. + target->got_mod_index_entry(symtab, layout, object); + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_local(object, r_type); + break; + + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + break; + + case elfcpp::R_390_TLS_IE32: // Initial-exec + case elfcpp::R_390_TLS_IE64: + // These two involve an absolute address + if (parameters->options().shared() + && optimized_type == tls::TLSOPT_NONE) + { + if ((size == 32 && r_type == elfcpp::R_390_TLS_IE32) || + (size == 64 && r_type == elfcpp::R_390_TLS_IE64)) + { + // We need to create a dynamic relocation. + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + unsigned int r_sym = + elfcpp::elf_r_sym<size>(reloc.get_r_info()); + rela_dyn->add_local_relative(object, r_sym, + elfcpp::R_390_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend(), false); + } + else + { + unsupported_reloc_local(object, r_type); + } + } + // fall through + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + layout->set_has_static_tls(); + if (optimized_type == tls::TLSOPT_NONE) + { + if (!output_is_shared) + { + // We're making an executable, and the symbol is local, but + // we cannot optimize to LE. Make a const GOT entry instead. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + unsigned int r_sym + = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + got->add_local_plt(object, r_sym, GOT_TYPE_TLS_OFFSET); + } + else + { + // Create a GOT entry for the tp-relative offset. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + unsigned int r_sym + = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + got->add_local_with_rel(object, r_sym, GOT_TYPE_TLS_OFFSET, + target->rela_dyn_section(layout), + elfcpp::R_390_TLS_TPOFF); + } + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_local(object, r_type); + break; + + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + layout->set_has_static_tls(); + if (output_is_shared) + { + // We need to create a dynamic relocation. + if ((size == 32 && r_type == elfcpp::R_390_TLS_LE32) || + (size == 64 && r_type == elfcpp::R_390_TLS_LE64)) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + unsigned int r_sym + = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION); + rela_dyn->add_local(object, r_sym, elfcpp::R_390_TLS_TPOFF, + output_section, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); + } + else + { + unsupported_reloc_local(object, r_type); + } + } + break; + + default: + gold_unreachable(); + } + } + break; + + default: + gold_error(_("%s: unsupported reloc %u against local symbol"), + object->name().c_str(), r_type); + break; + } +} + +// Scan a relocation for a global symbol. + +template<int size> +inline void +Target_s390<size>::Scan::global(Symbol_table* symtab, + Layout* layout, + Target_s390<size>* target, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<size, true>& reloc, + unsigned int r_type, + Symbol* gsym) +{ + // A STT_GNU_IFUNC symbol may require a PLT entry. + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && this->reloc_needs_plt_for_ifunc(object, r_type)) + target->make_plt_entry(symtab, layout, gsym); + + switch (r_type) + { + case elfcpp::R_390_NONE: + case elfcpp::R_390_GNU_VTINHERIT: + case elfcpp::R_390_GNU_VTENTRY: + break; + + case elfcpp::R_390_64: + case elfcpp::R_390_32: + case elfcpp::R_390_20: + case elfcpp::R_390_16: + case elfcpp::R_390_12: + case elfcpp::R_390_8: + { + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + { + target->make_plt_entry(symtab, layout, gsym); + // Since this is not a PC-relative relocation, we may be + // taking the address of a function. In that case we need to + // set the entry in the dynamic symbol table to the address of + // the PLT entry. + if (gsym->is_from_dynobj() && !parameters->options().shared()) + gsym->set_needs_dynsym_value(); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) + { + if (!parameters->options().output_is_position_independent() + && gsym->may_need_copy_reloc()) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, reloc); + } + else if (((size == 64 && r_type == elfcpp::R_390_64) + || (size == 32 && r_type == elfcpp::R_390_32)) + && gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false) + && !gsym->is_from_dynobj() + && !gsym->is_undefined() + && !gsym->is_preemptible()) + { + // Use an IRELATIVE reloc for a locally defined + // STT_GNU_IFUNC symbol. This makes a function + // address in a PIE executable match the address in a + // shared library that it links against. + Reloc_section* rela_dyn = + target->rela_irelative_section(layout); + unsigned int r_type = elfcpp::R_390_IRELATIVE; + rela_dyn->add_symbolless_global_addend(gsym, r_type, + output_section, object, + data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); + } + else if (((size == 64 && r_type == elfcpp::R_390_64) + || (size == 32 && r_type == elfcpp::R_390_32)) + && gsym->can_use_relative_reloc(false)) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global_relative(gsym, elfcpp::R_390_RELATIVE, + output_section, object, + data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend(), false); + } + else + { + check_non_pic(object, r_type); + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global(gsym, r_type, output_section, object, + data_shndx, reloc.get_r_offset(), + reloc.get_r_addend()); + } + } + } + break; + + case elfcpp::R_390_PC12DBL: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PC24DBL: + case elfcpp::R_390_PC32: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_PC64: + { + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + { + target->make_plt_entry(symtab, layout, gsym); + // larl is often used to take address of a function. Aim the + // symbol at the PLT entry. + if (gsym->is_from_dynobj() && !parameters->options().shared()) + gsym->set_needs_dynsym_value(); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) + { + if (parameters->options().output_is_executable() + && gsym->may_need_copy_reloc()) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, reloc); + } + else + { + check_non_pic(object, r_type); + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global(gsym, r_type, output_section, object, + data_shndx, reloc.get_r_offset(), + reloc.get_r_addend()); + } + } + } + break; + + case elfcpp::R_390_PLT12DBL: + case elfcpp::R_390_PLT16DBL: + case elfcpp::R_390_PLT24DBL: + case elfcpp::R_390_PLT32: + case elfcpp::R_390_PLT32DBL: + case elfcpp::R_390_PLT64: + // If the symbol is fully resolved, this is just a PC32 reloc. + // Otherwise we need a PLT entry. + if (gsym->final_value_is_known()) + break; + // If building a shared library, we can also skip the PLT entry + // if the symbol is defined in the output file and is protected + // or hidden. + if (gsym->is_defined() + && !gsym->is_from_dynobj() + && !gsym->is_preemptible()) + break; + target->make_plt_entry(symtab, layout, gsym); + break; + + case elfcpp::R_390_GOTPC: + case elfcpp::R_390_GOTPCDBL: + case elfcpp::R_390_GOTOFF16: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_GOTOFF64: + case elfcpp::R_390_PLTOFF16: + case elfcpp::R_390_PLTOFF32: + case elfcpp::R_390_PLTOFF64: + // We need a GOT section. + target->got_section(symtab, layout); + // For PLTOFF*, we also need a PLT entry (but only if the + // symbol is not fully resolved). + if ((r_type == elfcpp::R_390_PLTOFF16 + || r_type == elfcpp::R_390_PLTOFF32 + || r_type == elfcpp::R_390_PLTOFF64) + && !gsym->final_value_is_known()) + target->make_plt_entry(symtab, layout, gsym); + break; + + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTPLT12: + case elfcpp::R_390_GOTPLT16: + case elfcpp::R_390_GOTPLT20: + case elfcpp::R_390_GOTPLT32: + case elfcpp::R_390_GOTPLT64: + case elfcpp::R_390_GOTPLTENT: + { + // The symbol requires a GOT entry. + Output_data_got<size, true>* got = target->got_section(symtab, layout); + + if (gsym->final_value_is_known()) + { + // For a STT_GNU_IFUNC symbol we want the PLT address. + if (gsym->type() == elfcpp::STT_GNU_IFUNC) + got->add_global_plt(gsym, GOT_TYPE_STANDARD); + else + got->add_global(gsym, GOT_TYPE_STANDARD); + } + else + { + // If this symbol is not fully resolved, we need to add a + // dynamic relocation for it. + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + + // Use a GLOB_DAT rather than a RELATIVE reloc if: + // + // 1) The symbol may be defined in some other module. + // + // 2) We are building a shared library and this is a + // protected symbol; using GLOB_DAT means that the dynamic + // linker can use the address of the PLT in the main + // executable when appropriate so that function address + // comparisons work. + // + // 3) This is a STT_GNU_IFUNC symbol in position dependent + // code, again so that function address comparisons work. + if (gsym->is_from_dynobj() + || gsym->is_undefined() + || gsym->is_preemptible() + || (gsym->visibility() == elfcpp::STV_PROTECTED + && parameters->options().shared()) + || (gsym->type() == elfcpp::STT_GNU_IFUNC + && parameters->options().output_is_position_independent())) + got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn, + elfcpp::R_390_GLOB_DAT); + else + { + // For a STT_GNU_IFUNC symbol we want to write the PLT + // offset into the GOT, so that function pointer + // comparisons work correctly. + bool is_new; + if (gsym->type() != elfcpp::STT_GNU_IFUNC) + is_new = got->add_global(gsym, GOT_TYPE_STANDARD); + else + { + is_new = got->add_global_plt(gsym, GOT_TYPE_STANDARD); + // Tell the dynamic linker to use the PLT address + // when resolving relocations. + if (gsym->is_from_dynobj() + && !parameters->options().shared()) + gsym->set_needs_dynsym_value(); + } + if (is_new) + { + unsigned int got_off = gsym->got_offset(GOT_TYPE_STANDARD); + rela_dyn->add_global_relative(gsym, + elfcpp::R_390_RELATIVE, + got, got_off, 0, false); + } + } + } + } + break; + + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + // These are outstanding tls relocs, which are unexpected when linking + case elfcpp::R_390_TLS_TPOFF: + case elfcpp::R_390_TLS_DTPOFF: + case elfcpp::R_390_TLS_DTPMOD: + gold_error(_("%s: unexpected reloc %u in object file"), + object->name().c_str(), r_type); + break; + + // These are initial tls relocs, which are expected for global() + case elfcpp::R_390_TLS_GD32: // Global-dynamic + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + case elfcpp::R_390_TLS_LDM32: // Local-dynamic + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_LDCALL: + case elfcpp::R_390_TLS_IE32: // Initial-exec + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + { + // For the optimizable Initial-Exec model, we can treat undef symbols + // as final when building an executable. + const bool is_final = (gsym->final_value_is_known() || + ((r_type == elfcpp::R_390_TLS_IE32 || + r_type == elfcpp::R_390_TLS_IE64 || + r_type == elfcpp::R_390_TLS_GOTIE32 || + r_type == elfcpp::R_390_TLS_GOTIE64) && + gsym->is_undefined() && + parameters->options().output_is_executable())); + const tls::Tls_optimization optimized_type + = Target_s390<size>::optimize_tls_reloc(is_final, r_type); + switch (r_type) + { + case elfcpp::R_390_TLS_GD32: // General-dynamic + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + if (optimized_type == tls::TLSOPT_NONE) + { + // Create a pair of GOT entries for the module index and + // dtv-relative offset. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR, + target->rela_dyn_section(layout), + elfcpp::R_390_TLS_DTPMOD, + elfcpp::R_390_TLS_DTPOFF); + } + else if (optimized_type == tls::TLSOPT_TO_IE) + { + // Create a GOT entry for the tp-relative offset. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET, + target->rela_dyn_section(layout), + elfcpp::R_390_TLS_TPOFF); + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_global(object, r_type, gsym); + break; + + case elfcpp::R_390_TLS_LDM32: // Local-dynamic + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDCALL: + if (optimized_type == tls::TLSOPT_NONE) + { + // Create a GOT entry for the module index. + target->got_mod_index_entry(symtab, layout, object); + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_global(object, r_type, gsym); + break; + + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + break; + + case elfcpp::R_390_TLS_IE32: // Initial-exec + case elfcpp::R_390_TLS_IE64: + // These two involve an absolute address + if (parameters->options().shared()) + { + if ((size == 32 && r_type == elfcpp::R_390_TLS_IE32) || + (size == 64 && r_type == elfcpp::R_390_TLS_IE64)) + { + // We need to create a dynamic relocation. + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global_relative(gsym, elfcpp::R_390_RELATIVE, + output_section, object, + data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend(), false); + } + else + { + unsupported_reloc_global(object, r_type, gsym); + } + } + // fall through + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + layout->set_has_static_tls(); + if (optimized_type == tls::TLSOPT_NONE) + { + if (is_final && !parameters->options().shared()) + { + // We're making an executable, and the symbol is local, but + // we cannot optimize to LE. Make a const GOT entry instead. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + got->add_global_plt(gsym, GOT_TYPE_TLS_OFFSET); + } + else + { + // Create a GOT entry for the tp-relative offset. + Output_data_got<size, true>* got + = target->got_section(symtab, layout); + got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET, + target->rela_dyn_section(layout), + elfcpp::R_390_TLS_TPOFF); + } + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_global(object, r_type, gsym); + break; + + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + layout->set_has_static_tls(); + if (parameters->options().shared()) + { + // We need to create a dynamic relocation. + if ((size == 32 && r_type == elfcpp::R_390_TLS_LE32) || + (size == 64 && r_type == elfcpp::R_390_TLS_LE64)) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global(gsym, elfcpp::R_390_TLS_TPOFF, + output_section, object, + data_shndx, reloc.get_r_offset(), + reloc.get_r_addend()); + } + else + { + unsupported_reloc_global(object, r_type, gsym); + } + } + break; + + default: + gold_unreachable(); + } + } + break; + + default: + gold_error(_("%s: unsupported reloc %u against global symbol %s"), + object->name().c_str(), r_type, + gsym->demangled_name().c_str()); + break; + } +} + + +// Report an unsupported relocation against a global symbol. + +template<int size> +void +Target_s390<size>::Scan::unsupported_reloc_global( + Sized_relobj_file<size, true>* object, + unsigned int r_type, + Symbol* gsym) +{ + gold_error(_("%s: unsupported reloc %u against global symbol %s"), + object->name().c_str(), r_type, gsym->demangled_name().c_str()); +} + +// Returns true if this relocation type could be that of a function pointer. +template<int size> +inline bool +Target_s390<size>::Scan::possible_function_pointer_reloc(unsigned int r_type) +{ + switch (r_type) + { + case elfcpp::R_390_32: + case elfcpp::R_390_64: + case elfcpp::R_390_PC32DBL: // could be used by larl insn + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTOFF16: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_GOTOFF64: + return true; + } + return false; +} + +// For safe ICF, scan a relocation for a local symbol to check if it +// corresponds to a function pointer being taken. In that case mark +// the function whose pointer was taken as not foldable. + +template<int size> +inline bool +Target_s390<size>::Scan::local_reloc_may_be_function_pointer( + Symbol_table* , + Layout* , + Target_s390<size>* , + Sized_relobj_file<size, true>* , + unsigned int , + Output_section* , + const elfcpp::Rela<size, true>& , + unsigned int r_type, + const elfcpp::Sym<size, true>&) +{ + // When building a shared library, do not fold any local symbols. + return (parameters->options().shared() + || possible_function_pointer_reloc(r_type)); +} + +// For safe ICF, scan a relocation for a global symbol to check if it +// corresponds to a function pointer being taken. In that case mark +// the function whose pointer was taken as not foldable. + +template<int size> +inline bool +Target_s390<size>::Scan::global_reloc_may_be_function_pointer( + Symbol_table*, + Layout* , + Target_s390<size>* , + Sized_relobj_file<size, true>* , + unsigned int , + Output_section* , + const elfcpp::Rela<size, true>& , + unsigned int r_type, + Symbol* gsym) +{ + // 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)); +} + +template<int size> +void +Target_s390<size>::gc_process_relocs(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_symbols) +{ + + if (sh_type == elfcpp::SHT_REL) + return; + + gold::gc_process_relocs<size, true, Target_s390<size>, elfcpp::SHT_RELA, + typename Target_s390<size>::Scan, + typename Target_s390<size>::Relocatable_size_for_reloc>( + symtab, + layout, + this, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_symbols); +} + +// Perform a relocation. + +template<int size> +inline bool +Target_s390<size>::Relocate::relocate( + const Relocate_info<size, true>* relinfo, + Target_s390<size>* target, + Output_section*, + size_t relnum, + const elfcpp::Rela<size, true>& rela, + unsigned int r_type, + const Sized_symbol<size>* gsym, + const Symbol_value<size>* psymval, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr address, + section_size_type view_size) +{ + if (view == NULL) + return true; + + const Sized_relobj_file<size, true>* object = relinfo->object; + + // Pick the value to use for symbols defined in the PLT. + Symbol_value<size> symval; + if (gsym != NULL + && gsym->use_plt_offset(Scan::get_reference_flags(r_type))) + { + 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<size>(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(); + + typename elfcpp::Elf_types<size>::Elf_Addr value = 0; + + switch (r_type) + { + case elfcpp::R_390_PLT64: + case elfcpp::R_390_PLT32: + case elfcpp::R_390_PLT32DBL: + case elfcpp::R_390_PLT24DBL: + case elfcpp::R_390_PLT16DBL: + case elfcpp::R_390_PLT12DBL: + gold_assert(gsym == NULL + || gsym->has_plt_offset() + || gsym->final_value_is_known() + || (gsym->is_defined() + && !gsym->is_from_dynobj() + && !gsym->is_preemptible())); + // fallthru + case elfcpp::R_390_8: + case elfcpp::R_390_12: + case elfcpp::R_390_16: + case elfcpp::R_390_20: + case elfcpp::R_390_32: + case elfcpp::R_390_64: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC32: + case elfcpp::R_390_PC64: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_PC24DBL: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PC12DBL: + value = psymval->value(object, addend); + break; + + case elfcpp::R_390_GOTPC: + case elfcpp::R_390_GOTPCDBL: + gold_assert(gsym != NULL); + value = target->got_address() + addend; + break; + + case elfcpp::R_390_PLTOFF64: + case elfcpp::R_390_PLTOFF32: + case elfcpp::R_390_PLTOFF16: + gold_assert(gsym == NULL + || gsym->has_plt_offset() + || gsym->final_value_is_known()); + // fallthru + case elfcpp::R_390_GOTOFF64: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_GOTOFF16: + value = (psymval->value(object, addend) + - target->got_address()); + break; + + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTPLT12: + case elfcpp::R_390_GOTPLT16: + case elfcpp::R_390_GOTPLT20: + case elfcpp::R_390_GOTPLT32: + case elfcpp::R_390_GOTPLT64: + case elfcpp::R_390_GOTPLTENT: + { + unsigned int got_offset = 0; + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD)); + got_offset = gsym->got_offset(GOT_TYPE_STANDARD); + } + else + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(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); + } + value = got_offset + target->got_main_offset() + addend; + } + break; + + // These are initial tls relocs, which are expected when linking + case elfcpp::R_390_TLS_LOAD: + case elfcpp::R_390_TLS_GDCALL: // Global-dynamic + case elfcpp::R_390_TLS_GD32: + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_LDCALL: // Local-dynamic + case elfcpp::R_390_TLS_LDM32: + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_GOTIE12: // Initial-exec + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_IE32: + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + value = this->relocate_tls(relinfo, target, relnum, rela, r_type, gsym, psymval, + view, view_size); + break; + + default: + break; + } + + typename S390_relocate_functions<size>::Status status + = S390_relocate_functions<size>::STATUS_OK; + + switch (r_type) + { + case elfcpp::R_390_NONE: + case elfcpp::R_390_GNU_VTINHERIT: + case elfcpp::R_390_GNU_VTENTRY: + case elfcpp::R_390_TLS_GDCALL: + case elfcpp::R_390_TLS_LDCALL: + case elfcpp::R_390_TLS_LOAD: + break; + + case elfcpp::R_390_64: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_GOTPLT64: + case elfcpp::R_390_PLTOFF64: + case elfcpp::R_390_GOTOFF64: + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_LE64: + Relocate_functions<size, true>::rela64(view, value, 0); + break; + + case elfcpp::R_390_32: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_GOTPLT32: + case elfcpp::R_390_PLTOFF32: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_TLS_GD32: + case elfcpp::R_390_TLS_LDM32: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_IE32: + case elfcpp::R_390_TLS_LE32: + Relocate_functions<size, true>::rela32(view, value, 0); + break; + + case elfcpp::R_390_20: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOTPLT20: + case elfcpp::R_390_TLS_GOTIE20: + status = S390_relocate_functions<size>::rela20(view, value); + break; + + case elfcpp::R_390_16: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_GOTPLT16: + case elfcpp::R_390_PLTOFF16: + case elfcpp::R_390_GOTOFF16: + status = S390_relocate_functions<size>::rela16(view, value); + break; + + case elfcpp::R_390_12: + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOTPLT12: + case elfcpp::R_390_TLS_GOTIE12: + status = S390_relocate_functions<size>::rela12(view, value); + break; + + case elfcpp::R_390_8: + Relocate_functions<size, true>::rela8(view, value, 0); + break; + + case elfcpp::R_390_PC16: + Relocate_functions<size, true>::pcrela16(view, value, 0, + address); + break; + + case elfcpp::R_390_PLT64: + case elfcpp::R_390_PC64: + Relocate_functions<size, true>::pcrela64(view, value, 0, address); + break; + + case elfcpp::R_390_PLT32: + case elfcpp::R_390_PC32: + case elfcpp::R_390_GOTPC: + Relocate_functions<size, true>::pcrela32(view, value, 0, address); + break; + + case elfcpp::R_390_PLT32DBL: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_GOTPCDBL: + status = S390_relocate_functions<size>::pcrela32dbl(view, value, address); + break; + + case elfcpp::R_390_PLT24DBL: + case elfcpp::R_390_PC24DBL: + status = S390_relocate_functions<size>::pcrela24dbl(view, value, address); + break; + + case elfcpp::R_390_PLT16DBL: + case elfcpp::R_390_PC16DBL: + status = S390_relocate_functions<size>::pcrela16dbl(view, value, address); + break; + + case elfcpp::R_390_PLT12DBL: + case elfcpp::R_390_PC12DBL: + status = S390_relocate_functions<size>::pcrela12dbl(view, value, address); + break; + + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTPLTENT: + case elfcpp::R_390_TLS_IEENT: + value += target->got_address(); + status = S390_relocate_functions<size>::pcrela32dbl(view, value, address); + break; + + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + // These are outstanding tls relocs, which are unexpected when linking + case elfcpp::R_390_TLS_TPOFF: + case elfcpp::R_390_TLS_DTPMOD: + case elfcpp::R_390_TLS_DTPOFF: + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unexpected reloc %u in object file"), + r_type); + break; + + default: + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc %u"), + r_type); + break; + } + + if (status != S390_relocate_functions<size>::STATUS_OK) + { + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("relocation overflow")); + } + + return true; +} + +// Perform a TLS relocation. + +template<int size> +inline typename elfcpp::Elf_types<size>::Elf_Addr +Target_s390<size>::Relocate::relocate_tls( + const Relocate_info<size, true>* relinfo, + Target_s390<size>* target, + size_t relnum, + const elfcpp::Rela<size, true>& rela, + unsigned int r_type, + const Sized_symbol<size>* gsym, + const Symbol_value<size>* psymval, + unsigned char* view, + section_size_type view_size) +{ + Output_segment* tls_segment = relinfo->layout->tls_segment(); + + const Sized_relobj_file<size, true>* object = relinfo->object; + const elfcpp::Elf_Xword addend = rela.get_r_addend(); + elfcpp::Shdr<size, true> data_shdr(relinfo->data_shdr); + bool is_allocatable = (data_shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0; + + typename elfcpp::Elf_types<size>::Elf_Addr value + = psymval->value(relinfo->object, addend); + + const bool is_final = (gsym == NULL + ? !parameters->options().shared() + : gsym->final_value_is_known()); + tls::Tls_optimization optimized_type + = Target_s390<size>::optimize_tls_reloc(is_final, r_type); + switch (r_type) + { + case elfcpp::R_390_TLS_GDCALL: // Global-dynamic marker + if (optimized_type == tls::TLSOPT_TO_LE) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + this->tls_gd_to_le(relinfo, relnum, rela, view, view_size); + break; + } + else + { + if (optimized_type == tls::TLSOPT_TO_IE) + { + this->tls_gd_to_ie(relinfo, relnum, rela, view, view_size); + break; + } + else if (optimized_type == tls::TLSOPT_NONE) + { + break; + } + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc %u"), r_type); + break; + + case elfcpp::R_390_TLS_GD32: // Global-dynamic + case elfcpp::R_390_TLS_GD64: + if (optimized_type == tls::TLSOPT_TO_LE) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + return value - tls_segment->memsz(); + } + else + { + unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE + ? GOT_TYPE_TLS_OFFSET + : GOT_TYPE_TLS_PAIR); + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(got_type)); + return (gsym->got_offset(got_type) + + target->got_main_offset() + + addend); + } + else + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info()); + gold_assert(object->local_has_got_offset(r_sym, got_type)); + return (object->local_got_offset(r_sym, got_type) + + target->got_main_offset() + + addend); + } + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc %u"), r_type); + break; + + case elfcpp::R_390_TLS_LDCALL: // Local-dynamic marker + // This is a marker relocation. If the sequence is being turned to LE, + // we modify the instruction, otherwise the instruction is untouched. + if (optimized_type == tls::TLSOPT_TO_LE) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + this->tls_ld_to_le(relinfo, relnum, rela, view, view_size); + break; + } + else if (optimized_type == tls::TLSOPT_NONE) + { + break; + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc %u"), r_type); + break; + + case elfcpp::R_390_TLS_LDM32: // Local-dynamic module + case elfcpp::R_390_TLS_LDM64: + if (optimized_type == tls::TLSOPT_TO_LE) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + // Doesn't matter what we fill it with - it's going to be unused. + return 0; + } + else if (optimized_type == tls::TLSOPT_NONE) + { + // Relocate the field with the offset of the GOT entry for + // the module index. + return (target->got_mod_index_entry(NULL, NULL, NULL) + + addend + + target->got_main_offset()); + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc %u"), r_type); + break; + + case elfcpp::R_390_TLS_LDO32: // Local-dynamic offset + case elfcpp::R_390_TLS_LDO64: + // This relocation type is used in debugging information. + // In that case we need to not optimize the value. If the + // section is not allocatable, then we assume we should not + // optimize this reloc. + if (optimized_type == tls::TLSOPT_TO_LE && is_allocatable) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + value -= tls_segment->memsz(); + } + return value; + + case elfcpp::R_390_TLS_LOAD: // Initial-exec marker + // This is a marker relocation. If the sequence is being turned to LE, + // we modify the instruction, otherwise the instruction is untouched. + if (gsym != NULL + && gsym->is_undefined() + && parameters->options().output_is_executable()) + { + Target_s390<size>::Relocate::tls_ie_to_le(relinfo, relnum, + rela, view, + view_size); + break; + } + else if (optimized_type == tls::TLSOPT_TO_LE) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + Target_s390<size>::Relocate::tls_ie_to_le(relinfo, relnum, + rela, view, + view_size); + break; + } + else if (optimized_type == tls::TLSOPT_NONE) + { + break; + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc type %u"), + r_type); + break; + + case elfcpp::R_390_TLS_GOTIE12: // Initial-exec, not optimizable + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE32: // Initial-exec, optimizable + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_IE32: + case elfcpp::R_390_TLS_IE64: + if (gsym != NULL + && gsym->is_undefined() + && parameters->options().output_is_executable() + // These three cannot be optimized to LE, no matter what + && r_type != elfcpp::R_390_TLS_GOTIE12 + && r_type != elfcpp::R_390_TLS_GOTIE20 + && r_type != elfcpp::R_390_TLS_IEENT) + { + return value; + } + else if (optimized_type == tls::TLSOPT_TO_LE) + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + return value - tls_segment->memsz(); + } + else if (optimized_type == tls::TLSOPT_NONE) + { + // Relocate the field with the offset of the GOT entry for + // the tp-relative offset of the symbol. + unsigned int got_offset; + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(GOT_TYPE_TLS_OFFSET)); + got_offset = gsym->got_offset(GOT_TYPE_TLS_OFFSET); + } + else + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info()); + gold_assert(object->local_has_got_offset(r_sym, + GOT_TYPE_TLS_OFFSET)); + got_offset = object->local_got_offset(r_sym, GOT_TYPE_TLS_OFFSET); + } + got_offset += target->got_main_offset(); + if (r_type == elfcpp::R_390_TLS_IE32 + || r_type == elfcpp::R_390_TLS_IE64) + return target->got_address() + got_offset + addend; + else + return got_offset + addend; + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc type %u"), + r_type); + break; + + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return 0; + } + return value - tls_segment->memsz(); + } + return 0; +} + +// Do a relocation in which we convert a TLS General-Dynamic to an +// Initial-Exec. + +template<int size> +inline void +Target_s390<size>::Relocate::tls_gd_to_ie( + const Relocate_info<size, true>* relinfo, + size_t relnum, + const elfcpp::Rela<size, true>& rela, + unsigned char* view, + section_size_type view_size) +{ + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4); + if (view[0] == 0x4d) + { + // bas, don't care about details + // Change to l %r2, 0(%r2, %r12) + view[0] = 0x58; + view[1] = 0x22; + view[2] = 0xc0; + view[3] = 0x00; + return; + } + else if (view[0] == 0xc0) + { + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 6); + // brasl %r14, __tls_get_offset@plt + if (view[1] == 0xe5) + { + // Change to l/lg %r2, 0(%r2, %r12) + // There was a PLT32DBL reloc at the last 4 bytes, overwrite its result. + if (size == 32) + { + // l + view[0] = 0x58; + view[1] = 0x22; + view[2] = 0xc0; + view[3] = 0x00; + // nop + view[4] = 0x07; + view[5] = 0x07; + } + else + { + // lg + view[0] = 0xe3; + view[1] = 0x22; + view[2] = 0xc0; + view[3] = 0; + view[4] = 0; + view[5] = 0x04; + } + return; + } + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported op for GD to IE")); +} + +// Do a relocation in which we convert a TLS General-Dynamic to a +// Local-Exec. + +template<int size> +inline void +Target_s390<size>::Relocate::tls_gd_to_le( + const Relocate_info<size, true>* relinfo, + size_t relnum, + const elfcpp::Rela<size, true>& rela, + unsigned char* view, + section_size_type view_size) +{ + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 2); + if (view[0] == 0x0d) + { + // basr, change to nop + view[0] = 0x07; + view[1] = 0x07; + } + else if (view[0] == 0x4d) + { + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4); + // bas, don't care about details, change to nop + view[0] = 0x47; + view[1] = 0; + view[2] = 0; + view[3] = 0; + return; + } + else if (view[0] == 0xc0) + { + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 6); + // brasl %r14, __tls_get_offset@plt + if (view[1] == 0xe5) + { + // Change to nop jump. There was a PLT32DBL reloc at the last + // 4 bytes, overwrite its result. + view[1] = 0x04; + view[2] = 0; + view[3] = 0; + view[4] = 0; + view[5] = 0; + return; + } + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported op for GD to LE")); +} + +template<int size> +inline void +Target_s390<size>::Relocate::tls_ld_to_le( + const Relocate_info<size, true>* relinfo, + size_t relnum, + const elfcpp::Rela<size, true>& rela, + unsigned char* view, + section_size_type view_size) +{ + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4); + + if (view[0] == 0x0d) + { + // basr, change to nop + view[0] = 0x07; + view[1] = 0x07; + } + else if (view[0] == 0x4d) + { + // bas, don't care about details, change to nop + view[0] = 0x47; + view[1] = 0; + view[2] = 0; + view[3] = 0; + return; + } + else if (view[0] == 0xc0) + { + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 6); + // brasl %r14, __tls_get_offset@plt + if (view[1] == 0xe5) + { + // Change to nop jump. There was a PLT32DBL reloc at the last + // 4 bytes, overwrite its result. + view[1] = 0x04; + view[2] = 0; + view[3] = 0; + view[4] = 0; + view[5] = 0; + return; + } + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported op for LD to LE")); +} + +// Do a relocation in which we convert a TLS Initial-Exec to a +// Local-Exec. + +template<int size> +inline void +Target_s390<size>::Relocate::tls_ie_to_le( + const Relocate_info<size, true>* relinfo, + size_t relnum, + const elfcpp::Rela<size, true>& rela, + unsigned char* view, + section_size_type view_size) +{ + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4); + + if (view[0] == 0x58) + { + // l %rX, 0(%rY) or l %rX, 0(%rY, %r12) + if ((view[2] & 0x0f) != 0 || view[3] != 0) + goto err; + int rx = view[1] >> 4 & 0xf; + int ry = view[1] & 0xf; + int rz = view[2] >> 4 & 0xf; + if (rz == 0) + { + } + else if (ry == 0) + { + ry = rz; + } + else if (rz == 12) + { + } + else if (ry == 12) + { + ry = rz; + } + else + goto err; + // to lr %rX, $rY + view[0] = 0x18; + view[1] = rx << 4 | ry; + // and insert a nop + view[2] = 0x07; + view[3] = 0x00; + } + else if (view[0] == 0xe3) + { + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 6); + // lg %rX, 0(%rY) or lg %rX, 0(%rY, %r12) + if ((view[2] & 0x0f) != 0 || + view[3] != 0 || + view[4] != 0 || + view[5] != 0x04) + goto err; + int rx = view[1] >> 4 & 0xf; + int ry = view[1] & 0xf; + int rz = view[2] >> 4 & 0xf; + if (rz == 0) + { + } + else if (ry == 0) + { + ry = rz; + } + else if (rz == 12) + { + } + else if (ry == 12) + { + ry = rz; + } + else + goto err; + // to sllg %rX, $rY, 0 + view[0] = 0xeb; + view[1] = rx << 4 | ry; + view[2] = 0x00; + view[3] = 0x00; + view[4] = 0x00; + view[5] = 0x0d; + } + else + { +err: + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported op for IE to LE")); + } +} + +// Scan relocations for a section. + +template<int size> +void +Target_s390<size>::scan_relocs(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_symbols) +{ + if (sh_type == elfcpp::SHT_REL) + { + gold_error(_("%s: unsupported REL reloc section"), + object->name().c_str()); + return; + } + + gold::scan_relocs<size, true, Target_s390<size>, elfcpp::SHT_RELA, + typename Target_s390<size>::Scan>( + symtab, + layout, + this, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_symbols); +} + +// Finalize the sections. + +template<int size> +void +Target_s390<size>::do_finalize_sections( + Layout* layout, + const Input_objects*, + Symbol_table* symtab) +{ + 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, size == 32); + + this->layout_ = layout; + + // Emit any relocs we saved in an attempt to avoid generating COPY + // relocs. + if (this->copy_relocs_.any_saved_relocs()) + this->copy_relocs_.emit(this->rela_dyn_section(layout)); + + // Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of + // the .got section. + Symbol* sym = this->global_offset_table_; + if (sym != NULL) + { + uint64_t data_size = this->got_->current_data_size(); + symtab->get_sized_symbol<size>(sym)->set_symsize(data_size); + } + + 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 the size of a relocation while scanning during a relocatable +// link. + +template<int size> +unsigned int +Target_s390<size>::Relocatable_size_for_reloc::get_size_for_reloc( + unsigned int r_type, + Relobj* object) +{ + switch (r_type) + { + case elfcpp::R_390_NONE: + case elfcpp::R_390_GNU_VTINHERIT: + case elfcpp::R_390_GNU_VTENTRY: + case elfcpp::R_390_TLS_GD32: // Global-dynamic + case elfcpp::R_390_TLS_GD64: + case elfcpp::R_390_TLS_GDCALL: + case elfcpp::R_390_TLS_LDM32: // Local-dynamic + case elfcpp::R_390_TLS_LDM64: + case elfcpp::R_390_TLS_LDO32: + case elfcpp::R_390_TLS_LDO64: + case elfcpp::R_390_TLS_LDCALL: + case elfcpp::R_390_TLS_IE32: // Initial-exec + case elfcpp::R_390_TLS_IE64: + case elfcpp::R_390_TLS_IEENT: + case elfcpp::R_390_TLS_GOTIE12: + case elfcpp::R_390_TLS_GOTIE20: + case elfcpp::R_390_TLS_GOTIE32: + case elfcpp::R_390_TLS_GOTIE64: + case elfcpp::R_390_TLS_LOAD: + case elfcpp::R_390_TLS_LE32: // Local-exec + case elfcpp::R_390_TLS_LE64: + return 0; + + case elfcpp::R_390_64: + case elfcpp::R_390_PC64: + case elfcpp::R_390_GOT64: + case elfcpp::R_390_PLT64: + case elfcpp::R_390_GOTOFF64: + case elfcpp::R_390_GOTPLT64: + case elfcpp::R_390_PLTOFF64: + return 8; + + case elfcpp::R_390_32: + case elfcpp::R_390_PC32: + case elfcpp::R_390_GOT32: + case elfcpp::R_390_PLT32: + case elfcpp::R_390_GOTOFF32: + case elfcpp::R_390_GOTPC: + case elfcpp::R_390_PC32DBL: + case elfcpp::R_390_PLT32DBL: + case elfcpp::R_390_GOTPCDBL: + case elfcpp::R_390_GOTENT: + case elfcpp::R_390_GOTPLT32: + case elfcpp::R_390_GOTPLTENT: + case elfcpp::R_390_PLTOFF32: + case elfcpp::R_390_20: + case elfcpp::R_390_GOT20: + case elfcpp::R_390_GOTPLT20: + return 4; + + case elfcpp::R_390_PC24DBL: + case elfcpp::R_390_PLT24DBL: + return 3; + + case elfcpp::R_390_12: + case elfcpp::R_390_GOT12: + case elfcpp::R_390_GOTPLT12: + case elfcpp::R_390_PC12DBL: + case elfcpp::R_390_PLT12DBL: + case elfcpp::R_390_16: + case elfcpp::R_390_GOT16: + case elfcpp::R_390_PC16: + case elfcpp::R_390_PC16DBL: + case elfcpp::R_390_PLT16DBL: + case elfcpp::R_390_GOTOFF16: + case elfcpp::R_390_GOTPLT16: + case elfcpp::R_390_PLTOFF16: + return 2; + + case elfcpp::R_390_8: + return 1; + + // These are relocations which should only be seen by the + // dynamic linker, and should never be seen here. + case elfcpp::R_390_COPY: + case elfcpp::R_390_GLOB_DAT: + case elfcpp::R_390_JMP_SLOT: + case elfcpp::R_390_RELATIVE: + case elfcpp::R_390_IRELATIVE: + case elfcpp::R_390_TLS_DTPMOD: + case elfcpp::R_390_TLS_DTPOFF: + case elfcpp::R_390_TLS_TPOFF: + object->error(_("unexpected reloc %u in object file"), r_type); + return 0; + + default: + object->error(_("unsupported reloc %u in object file"), r_type); + return 0; + } +} + +// Scan the relocs during a relocatable link. + +template<int size> +void +Target_s390<size>::scan_relocatable_relocs( + Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<size, true>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Relocatable_relocs* rr) +{ + gold_assert(sh_type == elfcpp::SHT_RELA); + + typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_RELA, + Relocatable_size_for_reloc> Scan_relocatable_relocs; + + gold::scan_relocatable_relocs<size, true, elfcpp::SHT_RELA, + Scan_relocatable_relocs>( + symtab, + layout, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_symbols, + rr); +} + +// Relocate a section during a relocatable link. + +template<int size> +void +Target_s390<size>::relocate_relocs( + const Relocate_info<size, true>* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section, + const Relocatable_relocs* rr, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size) +{ + gold_assert(sh_type == elfcpp::SHT_RELA); + + gold::relocate_relocs<size, true, elfcpp::SHT_RELA>( + relinfo, + prelocs, + reloc_count, + output_section, + offset_in_output_section, + rr, + view, + view_address, + view_size, + reloc_view, + reloc_view_size); +} + +// Return the offset to use for the GOT_INDX'th got entry which is +// for a local tls symbol specified by OBJECT, SYMNDX. +template<int size> +int64_t +Target_s390<size>::do_tls_offset_for_local( + const Relobj*, + unsigned int, + unsigned int) const +{ + // The only way we can get called is when IEENT/GOTIE12/GOTIE20 + // couldn't be optimised to LE. + Output_segment* tls_segment = layout_->tls_segment(); + return -tls_segment->memsz(); +} + +// Return the offset to use for the GOT_INDX'th got entry which is +// for global tls symbol GSYM. +template<int size> +int64_t +Target_s390<size>::do_tls_offset_for_global( + Symbol*, + unsigned int) const +{ + Output_segment* tls_segment = layout_->tls_segment(); + return -tls_segment->memsz(); +} + +// Return the value to use for a dynamic which requires special +// treatment. This is how we support equality comparisons of function +// pointers across shared library boundaries, as described in the +// processor specific ABI supplement. + +template<int size> +uint64_t +Target_s390<size>::do_dynsym_value(const Symbol* gsym) const +{ + gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset()); + return this->plt_address_for_global(gsym); +} + +// Return a string used to fill a code section with nops to take up +// the specified length. + +template<int size> +std::string +Target_s390<size>::do_code_fill(section_size_type length) const +{ + if (length & 1) + gold_warning(_("S/390 code fill of odd length requested")); + return std::string(length, static_cast<char>(0x07)); +} + +// Relocate section data. + +template<int size> +void +Target_s390<size>::relocate_section( + const Relocate_info<size, true>* 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<size>::Elf_Addr address, + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) +{ + gold_assert(sh_type == elfcpp::SHT_RELA); + + gold::relocate_section<size, true, Target_s390<size>, elfcpp::SHT_RELA, + typename Target_s390<size>::Relocate, + gold::Default_comdat_behavior>( + relinfo, + this, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + view, + address, + view_size, + reloc_symbol_changes); +} + +// Apply an incremental relocation. Incremental relocations always refer +// to global symbols. + +template<int size> +void +Target_s390<size>::apply_relocation( + const Relocate_info<size, true>* relinfo, + typename elfcpp::Elf_types<size>::Elf_Addr r_offset, + unsigned int r_type, + typename elfcpp::Elf_types<size>::Elf_Swxword r_addend, + const Symbol* gsym, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr address, + section_size_type view_size) +{ + gold::apply_relocation<size, true, Target_s390<size>, + typename Target_s390<size>::Relocate>( + relinfo, + this, + r_offset, + r_type, + r_addend, + gsym, + view, + address, + view_size); +} + +// The selector for s390 object files. + +template<int size> +class Target_selector_s390 : public Target_selector +{ +public: + Target_selector_s390() + : Target_selector(elfcpp::EM_S390, size, true, + (size == 64 ? "elf64-s390" : "elf32-s390"), + (size == 64 ? "elf64_s390" : "elf32_s390")) + { } + + virtual Target* + do_instantiate_target() + { return new Target_s390<size>(); } +}; + +Target_selector_s390<32> target_selector_s390; +Target_selector_s390<64> target_selector_s390x; + +} // End anonymous namespace. |