binutils-gdb/gold/s390.cc
Cary Coutant bce5a025d2 Fix problem where mixed section types can cause internal error during a -r link.
During a -r (or --emit-relocs) link, if two sections had the same name but
different section types, gold would put relocations for both sections into
the same relocation section even though the data sections remained separate.

For .eh_frame sections, when one section is PROGBITS and another is
X86_64_UNWIND, we really should be using the UNWIND section type and
combining the sections anyway.  For other sections, we should be
creating one relocation section for each output data section.

gold/
	PR gold/23016
	* incremental.cc (can_incremental_update): Check for unwind section
	type.
	* layout.h (Layout::layout): Add sh_type parameter.
	* layout.cc (Layout::layout): Likewise.
	(Layout::layout_reloc): Create new output reloc section if data
	section does not already have one.
	(Layout::layout_eh_frame): Check for unwind section type.
	(Layout::make_eh_frame_section): Use unwind section type for .eh_frame
	and .eh_frame_hdr.
	* object.h (Sized_relobj_file::Shdr_write): New typedef.
	(Sized_relobj_file::layout_section): Add sh_type parameter.
	(Sized_relobj_file::Deferred_layout::Deferred_layout): Add sh_type
	parameter.
	* object.cc (Sized_relobj_file::check_eh_frame_flags): Check for
	unwind section type.
	(Sized_relobj_file::layout_section): Add sh_type parameter; pass it
	to Layout::layout.
	(Sized_relobj_file::do_layout): Make local copy of sh_type.
	Force .eh_frame sections to unwind section type.
	Pass sh_type to layout_section.
	(Sized_relobj_file<size, big_endian>::do_layout_deferred_sections):
	Pass sh_type to layout_section.
	* output.cc (Output_section::Output_section): Initialize reloc_section_.
	* output.h (Output_section::reloc_section): New method.
	(Output_section::set_reloc_section): New method.
	(Output_section::reloc_section_): New data member.
	* target.h (Target::unwind_section_type): New method.
	(Target::Target_info::unwind_section_type): New data member.

	* aarch64.cc (aarch64_info): Add unwind_section_type.
	* arm.cc (arm_info, arm_nacl_info): Likewise.
	* i386.cc (i386_info, i386_nacl_info, iamcu_info): Likewise.
	* mips.cc (mips_info, mips_nacl_info): Likewise.
	* powerpc.cc (powerpc_info): Likewise.
	* s390.cc (s390_info): Likewise.
	* sparc.cc (sparc_info): Likewise.
	* tilegx.cc (tilegx_info): Likewise.
	* x86_64.cc (x86_64_info, x86_64_nacl_info): Likewise.

	* testsuite/Makefile.am (pr23016_1, pr23016_2): New test cases.
	* testsuite/Makefile.in: Regenerate.
	* testsuite/testfile.cc: Add unwind_section_type.
	* testsuite/pr23016_1.sh: New test script.
	* testsuite/pr23016_1a.s: New source file.
	* testsuite/pr23016_1b.s: New source file.
	* testsuite/pr23016_2.sh: New test script.
	* testsuite/pr23016_2a.s: New source file.
	* testsuite/pr23016_2b.s: New source file.
2018-04-02 19:07:04 -07:00

4966 lines
146 KiB
C++

// s390.cc -- s390 target support for gold.
// Copyright (C) 2015-2018 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*);
// Scan the relocs for --emit-relocs.
void
emit_relocs_scan(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_syms,
Relocatable_relocs* rr);
// 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,
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 whether SYM is call to a non-split function.
bool
do_is_call_to_non_split(const Symbol* sym, const unsigned char* preloc,
const unsigned char* view,
section_size_type view_size) const;
// Adjust -fsplit-stack code which calls non-split-stack code.
void
do_calls_non_split(Relobj* object, unsigned int shndx,
section_offset_type fnoffset, section_size_type fnsize,
const unsigned char* prelocs, size_t reloc_count,
unsigned char* view, section_size_type view_size,
std::string* from, std::string* to) const;
// 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>*, unsigned int,
Target_s390*, Output_section*, size_t, const unsigned char*,
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);
};
// 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)
{
unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
r_type, reloc.get_r_offset(),
reloc.get_r_addend(),
this->rela_dyn_section(layout));
}
// A function for targets to call. Return whether BYTES/LEN matches
// VIEW/VIEW_SIZE at OFFSET. Like the one in Target, but takes
// an unsigned char * parameter.
bool
match_view_u(const unsigned char* view, section_size_type view_size,
section_offset_type offset, const unsigned char* bytes, size_t len) const
{
return this->match_view(view, view_size, offset,
reinterpret_cast<const char*>(bytes), len);
}
// 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_;
// Code sequences for -fsplit-stack matching.
static const unsigned char ss_code_bras_8[];
static const unsigned char ss_code_l_basr[];
static const unsigned char ss_code_a_basr[];
static const unsigned char ss_code_larl[];
static const unsigned char ss_code_brasl[];
static const unsigned char ss_code_jg[];
static const unsigned char ss_code_jgl[];
// Variable code sequence matchers for -fsplit-stack.
bool ss_match_st_r14(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const;
bool ss_match_l_r14(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const;
bool ss_match_mcount(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const;
bool ss_match_ear(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const;
bool ss_match_c(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const;
bool ss_match_l(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int *guard_reg) const;
bool ss_match_ahi(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg,
uint32_t *arg) const;
bool ss_match_alfi(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg,
uint32_t *arg) const;
bool ss_match_cr(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg) const;
};
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
elfcpp::SHT_PROGBITS, // unwind_section_type
};
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
elfcpp::SHT_PROGBITS, // unwind_section_type
};
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)
{
typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, true>
Classify_reloc;
if (sh_type == elfcpp::SHT_REL)
return;
gold::gc_process_relocs<size, true, Target_s390<size>, Scan, Classify_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,
unsigned int,
Target_s390<size>* target,
Output_section*,
size_t relnum,
const unsigned char* preloc,
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 elfcpp::Rela<size, true> rela(preloc);
unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
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()));
// Fall through.
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());
// Fall through.
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)
{
typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, true>
Classify_reloc;
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>, Scan, Classify_reloc>(
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());
}
}
// 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)
{
typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, true>
Classify_reloc;
typedef gold::Default_scan_relocatable_relocs<Classify_reloc>
Scan_relocatable_relocs;
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::scan_relocatable_relocs<size, true, Scan_relocatable_relocs>(
symtab,
layout,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols,
rr);
}
// Scan the relocs for --emit-relocs.
template<int size>
void
Target_s390<size>::emit_relocs_scan(
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_syms,
Relocatable_relocs* rr)
{
typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, true>
Classify_reloc;
typedef gold::Default_emit_relocs_strategy<Classify_reloc>
Emit_relocs_strategy;
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::scan_relocatable_relocs<size, true, Emit_relocs_strategy>(
symtab,
layout,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_syms,
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,
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)
{
typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, true>
Classify_reloc;
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_relocs<size, true, Classify_reloc>(
relinfo,
prelocs,
reloc_count,
output_section,
offset_in_output_section,
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));
}
// Return whether SYM should be treated as a call to a non-split
// function. We don't want that to be true of a larl instruction
// that merely loads its address.
template<int size>
bool
Target_s390<size>::do_is_call_to_non_split(const Symbol* sym,
const unsigned char* preloc,
const unsigned char* view,
section_size_type view_size) const
{
if (sym->type() != elfcpp::STT_FUNC)
return false;
typename Reloc_types<elfcpp::SHT_RELA, size, true>::Reloc reloc(preloc);
typename elfcpp::Elf_types<size>::Elf_WXword r_info
= reloc.get_r_info();
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
section_offset_type offset = reloc.get_r_offset();
switch (r_type)
{
// PLT refs always involve calling the function.
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:
// Could be used for calls for -msmall-exec.
case elfcpp::R_390_PC16DBL:
return true;
// Tricky case. When used in a brasl, jg, and other branch instructions,
// it's a call or a sibcall. However, when used in larl, it only loads
// the function's address - not a call.
case elfcpp::R_390_PC32DBL:
{
if (offset < 2
|| offset + 4 > static_cast<section_offset_type>(view_size))
{
// Should not happen.
gold_error(_("instruction with PC32DBL not wholly within section"));
return false;
}
uint8_t op0 = view[offset-2];
uint8_t op1 = view[offset-1] & 0xf;
// LARL
if (op0 == 0xc0 && op1 == 0)
return false;
// Otherwise, it's either a call instruction, a branch instruction
// (used as a sibcall), or a data manipulation instruction (which
// has no business being used on a function, and can be ignored).
return true;
}
// Otherwise, it's probably not a call.
default:
return false;
}
}
// Code sequences to match below.
template<int size>
const unsigned char
Target_s390<size>::ss_code_bras_8[] = {
0xa7, 0x15, 0x00, 0x06, // bras %r1, .+0xc
};
template<int size>
const unsigned char
Target_s390<size>::ss_code_l_basr[] = {
0x58, 0xe0, 0x10, 0x00, // l %r14, 0(%r1)
0x58, 0x10, 0x10, 0x04, // l %r1, 4(%r1)
0x0d, 0xee, // basr %r14, %r14
};
template<int size>
const unsigned char
Target_s390<size>::ss_code_a_basr[] = {
0x18, 0xe1, // lr %r14, %r1
0x5a, 0xe0, 0x10, 0x00, // a %r14, 0(%r1)
0x5a, 0x10, 0x10, 0x04, // a %r1, 4(%r1)
0x0d, 0xee, // basr %r14, %r14
};
template<int size>
const unsigned char
Target_s390<size>::ss_code_larl[] = {
0xc0, 0x10, // larl %r1, ...
};
template<int size>
const unsigned char
Target_s390<size>::ss_code_brasl[] = {
0xc0, 0xe5, // brasl %r14, ...
};
template<int size>
const unsigned char
Target_s390<size>::ss_code_jg[] = {
0xc0, 0xf4, // jg ...
};
template<int size>
const unsigned char
Target_s390<size>::ss_code_jgl[] = {
0xc0, 0x44, // jgl ...
};
template<>
bool
Target_s390<32>::ss_match_st_r14(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_st_r14[] = {
0x50, 0xe0, 0xf0, 0x04, // st %r14, 4(%r15)
};
if (!this->match_view_u(view, view_size, *offset, ss_code_st_r14,
sizeof ss_code_st_r14))
return false;
*offset += sizeof ss_code_st_r14;
return true;
}
template<>
bool
Target_s390<64>::ss_match_st_r14(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_st_r14[] = {
0xe3, 0xe0, 0xf0, 0x08, 0x00, 0x24 // stg %r14, 8(%r15)
};
if (!this->match_view_u(view, view_size, *offset, ss_code_st_r14,
sizeof ss_code_st_r14))
return false;
*offset += sizeof ss_code_st_r14;
return true;
}
template<>
bool
Target_s390<32>::ss_match_l_r14(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_l_r14[] = {
0x58, 0xe0, 0xf0, 0x04, // l %r14, 4(%r15)
};
if (!this->match_view_u(view, view_size, *offset, ss_code_l_r14,
sizeof ss_code_l_r14))
return false;
*offset += sizeof ss_code_l_r14;
return true;
}
template<>
bool
Target_s390<64>::ss_match_l_r14(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_l_r14[] = {
0xe3, 0xe0, 0xf0, 0x08, 0x00, 0x04 // lg %r14, 8(%r15)
};
if (!this->match_view_u(view, view_size, *offset, ss_code_l_r14,
sizeof ss_code_l_r14))
return false;
*offset += sizeof ss_code_l_r14;
return true;
}
template<int size>
bool
Target_s390<size>::ss_match_mcount(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
// Match the mcount call sequence.
section_offset_type myoff = *offset;
// First, look for the store instruction saving %r14.
if (!this->ss_match_st_r14(view, view_size, &myoff))
return false;
// Now, param load and the actual call.
if (this->match_view_u(view, view_size, myoff, ss_code_larl,
sizeof ss_code_larl))
{
myoff += sizeof ss_code_larl + 4;
// After larl, expect a brasl.
if (!this->match_view_u(view, view_size, myoff, ss_code_brasl,
sizeof ss_code_brasl))
return false;
myoff += sizeof ss_code_brasl + 4;
}
else if (size == 32 &&
this->match_view_u(view, view_size, myoff, ss_code_bras_8,
sizeof ss_code_bras_8))
{
// The bras skips over a block of 8 bytes, loading its address
// to %r1.
myoff += sizeof ss_code_bras_8 + 8;
// Now, there are two sequences used for actual load and call,
// absolute and PIC.
if (this->match_view_u(view, view_size, myoff, ss_code_l_basr,
sizeof ss_code_l_basr))
myoff += sizeof ss_code_l_basr;
else if (this->match_view_u(view, view_size, myoff, ss_code_a_basr,
sizeof ss_code_a_basr))
myoff += sizeof ss_code_a_basr;
else
return false;
}
else
return false;
// Finally, a load bringing %r14 back.
if (!this->ss_match_l_r14(view, view_size, &myoff))
return false;
// Found it.
*offset = myoff;
return true;
}
template<>
bool
Target_s390<32>::ss_match_ear(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_ear[] = {
0xb2, 0x4f, 0x00, 0x10, // ear %r1, %a0
};
if (!this->match_view_u(view, view_size, *offset, ss_code_ear,
sizeof ss_code_ear))
return false;
*offset += sizeof ss_code_ear;
return true;
}
template<>
bool
Target_s390<64>::ss_match_ear(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_ear[] = {
0xb2, 0x4f, 0x00, 0x10, // ear %r1, %a0
0xeb, 0x11, 0x00, 0x20, 0x00, 0x0d, // sllg %r1,%r1,32
0xb2, 0x4f, 0x00, 0x11, // ear %r1, %a1
};
if (!this->match_view_u(view, view_size, *offset, ss_code_ear,
sizeof ss_code_ear))
return false;
*offset += sizeof ss_code_ear;
return true;
}
template<>
bool
Target_s390<32>::ss_match_c(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_c[] = {
0x59, 0xf0, 0x10, 0x20, // c %r15, 0x20(%r1)
};
if (!this->match_view_u(view, view_size, *offset, ss_code_c,
sizeof ss_code_c))
return false;
*offset += sizeof ss_code_c;
return true;
}
template<>
bool
Target_s390<64>::ss_match_c(unsigned char* view,
section_size_type view_size,
section_offset_type *offset) const
{
static const unsigned char ss_code_c[] = {
0xe3, 0xf0, 0x10, 0x38, 0x00, 0x20, // cg %r15, 0x38(%r1)
};
if (!this->match_view_u(view, view_size, *offset, ss_code_c,
sizeof ss_code_c))
return false;
*offset += sizeof ss_code_c;
return true;
}
template<>
bool
Target_s390<32>::ss_match_l(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int *guard_reg) const
{
// l %guard_reg, 0x20(%r1)
if (convert_to_section_size_type(*offset + 4) > view_size
|| view[*offset] != 0x58
|| (view[*offset + 1] & 0xf) != 0x0
|| view[*offset + 2] != 0x10
|| view[*offset + 3] != 0x20)
return false;
*offset += 4;
*guard_reg = view[*offset + 1] >> 4 & 0xf;
return true;
}
template<>
bool
Target_s390<64>::ss_match_l(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int *guard_reg) const
{
// lg %guard_reg, 0x38(%r1)
if (convert_to_section_size_type(*offset + 6) > view_size
|| view[*offset] != 0xe3
|| (view[*offset + 1] & 0xf) != 0x0
|| view[*offset + 2] != 0x10
|| view[*offset + 3] != 0x38
|| view[*offset + 4] != 0x00
|| view[*offset + 5] != 0x04)
return false;
*offset += 6;
*guard_reg = view[*offset + 1] >> 4 & 0xf;
return true;
}
template<int size>
bool
Target_s390<size>::ss_match_ahi(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg,
uint32_t *arg) const
{
int op = size == 32 ? 0xa : 0xb;
// a[g]hi %guard_reg, <arg>
if (convert_to_section_size_type(*offset + 4) > view_size
|| view[*offset] != 0xa7
|| view[*offset + 1] != (guard_reg << 4 | op)
// Disallow negative size.
|| view[*offset + 2] & 0x80)
return false;
*arg = elfcpp::Swap<16, true>::readval(view + *offset + 2);
*offset += 4;
return true;
}
template<int size>
bool
Target_s390<size>::ss_match_alfi(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg,
uint32_t *arg) const
{
int op = size == 32 ? 0xb : 0xa;
// al[g]fi %guard_reg, <arg>
if (convert_to_section_size_type(*offset + 6) > view_size
|| view[*offset] != 0xc2
|| view[*offset + 1] != (guard_reg << 4 | op))
return false;
*arg = elfcpp::Swap<32, true>::readval(view + *offset + 2);
*offset += 6;
return true;
}
template<>
bool
Target_s390<32>::ss_match_cr(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg) const
{
// cr %r15, %guard_reg
if (convert_to_section_size_type(*offset + 2) > view_size
|| view[*offset] != 0x19
|| view[*offset + 1] != (0xf0 | guard_reg))
return false;
*offset += 2;
return true;
}
template<>
bool
Target_s390<64>::ss_match_cr(unsigned char* view,
section_size_type view_size,
section_offset_type *offset,
int guard_reg) const
{
// cgr %r15, %guard_reg
if (convert_to_section_size_type(*offset + 4) > view_size
|| view[*offset] != 0xb9
|| view[*offset + 1] != 0x20
|| view[*offset + 2] != 0x00
|| view[*offset + 3] != (0xf0 | guard_reg))
return false;
*offset += 4;
return true;
}
// FNOFFSET in section SHNDX in OBJECT is the start of a function
// compiled with -fsplit-stack. The function calls non-split-stack
// code. We have to change the function so that it always ensures
// that it has enough stack space to run some random function.
template<int size>
void
Target_s390<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
section_offset_type fnoffset,
section_size_type,
const unsigned char *prelocs,
size_t reloc_count,
unsigned char* view,
section_size_type view_size,
std::string*,
std::string*) const
{
// true if there's a conditional call to __morestack in the function,
// false if there's an unconditional one.
bool conditional = false;
// Offset of the byte after the compare insn, if conditional.
section_offset_type cmpend = 0;
// Type and immediate offset of the add instruction that adds frame size
// to guard.
enum {
SS_ADD_NONE,
SS_ADD_AHI,
SS_ADD_ALFI,
} fsadd_type = SS_ADD_NONE;
section_offset_type fsadd_offset = 0;
uint32_t fsadd_frame_size = 0;
// Register used for loading guard. Usually r1, but can also be r0 or r2-r5.
int guard_reg;
// Offset of the conditional jump.
section_offset_type jump_offset = 0;
// Section view and offset of param block.
section_offset_type param_offset = 0;
unsigned char *param_view = 0;
section_size_type param_view_size = 0;
// Current position in function.
section_offset_type curoffset = fnoffset;
// And the position of split-stack prologue.
section_offset_type ssoffset;
// Frame size.
typename elfcpp::Elf_types<size>::Elf_Addr frame_size;
// Relocation parsing.
typedef typename Reloc_types<elfcpp::SHT_RELA, size, true>::Reloc Reltype;
const int reloc_size = Reloc_types<elfcpp::SHT_RELA, size, true>::reloc_size;
const unsigned char *pr = prelocs;
// If the function was compiled with -pg, the profiling code may come before
// the split-stack prologue. Skip it.
this->ss_match_mcount(view, view_size, &curoffset);
ssoffset = curoffset;
// First, figure out if there's a conditional call by looking for the
// extract-tp, add, cmp sequence.
if (this->ss_match_ear(view, view_size, &curoffset))
{
// Found extract-tp, now look for an add and compare.
conditional = true;
if (this->ss_match_c(view, view_size, &curoffset))
{
// Found a direct compare of stack pointer with the guard,
// we're done here.
}
else if (this->ss_match_l(view, view_size, &curoffset, &guard_reg))
{
// Found a load of guard to register, look for an add and compare.
if (this->ss_match_ahi(view, view_size, &curoffset, guard_reg,
&fsadd_frame_size))
{
fsadd_type = SS_ADD_AHI;
fsadd_offset = curoffset - 2;
}
else if (this->ss_match_alfi(view, view_size, &curoffset, guard_reg,
&fsadd_frame_size))
{
fsadd_type = SS_ADD_ALFI;
fsadd_offset = curoffset - 4;
}
else
{
goto bad;
}
// Now, there has to be a compare.
if (!this->ss_match_cr(view, view_size, &curoffset, guard_reg))
goto bad;
}
else
{
goto bad;
}
cmpend = curoffset;
}
// Second, look for the call.
if (!this->match_view_u(view, view_size, curoffset, ss_code_larl,
sizeof ss_code_larl))
goto bad;
curoffset += sizeof ss_code_larl;
// Find out larl's operand. It should be a local symbol in .rodata
// section.
for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size)
{
Reltype reloc(pr);
if (static_cast<section_offset_type>(reloc.get_r_offset())
== curoffset)
{
typename elfcpp::Elf_types<size>::Elf_WXword r_info
= reloc.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
if (r_type != elfcpp::R_390_PC32DBL)
goto bad;
if (r_sym >= object->local_symbol_count())
goto bad;
Sized_relobj_file<size, true> *object_sized =
static_cast<Sized_relobj_file<size, true> *>(object);
const Symbol_value<size>* sym = object_sized->local_symbol(r_sym);
bool param_shndx_ordinary;
const unsigned int param_shndx =
sym->input_shndx(&param_shndx_ordinary);
if (!param_shndx_ordinary)
goto bad;
param_offset = sym->input_value() + reloc.get_r_addend() - 2
- object->output_section(param_shndx)->address()
- object->output_section_offset(param_shndx);
param_view = object->get_output_view(param_shndx,
&param_view_size);
break;
}
}
if (!param_view)
goto bad;
curoffset += 4;
// Now, there has to be a jump to __morestack.
jump_offset = curoffset;
if (this->match_view_u(view, view_size, curoffset,
conditional ? ss_code_jgl : ss_code_jg,
sizeof ss_code_jg))
curoffset += sizeof ss_code_jg;
else
goto bad;
curoffset += 4;
// Read the frame size.
if (convert_to_section_size_type(param_offset + size / 8) > param_view_size)
goto bad;
frame_size = elfcpp::Swap<size, true>::readval(param_view + param_offset);
// Sanity check.
if (fsadd_type != SS_ADD_NONE && fsadd_frame_size != frame_size)
goto bad;
// Bump the frame size.
frame_size += parameters->options().split_stack_adjust_size();
// Store it to the param block.
elfcpp::Swap<size, true>::writeval(param_view + param_offset, frame_size);
if (!conditional)
{
// If the call was already unconditional, we're done.
}
else if (frame_size <= 0xffffffff && fsadd_type == SS_ADD_ALFI)
{
// Using alfi to add the frame size, and it still fits. Adjust it.
elfcpp::Swap_unaligned<32, true>::writeval(view + fsadd_offset,
frame_size);
}
else
{
// We were either relying on the backoff area, or used ahi to load
// frame size. This won't fly, as our new frame size is too large.
// Convert the sequence to unconditional by nopping out the comparison,
// and rewiring the jump.
this->set_view_to_nop(view, view_size, ssoffset, cmpend - ssoffset);
// The jump is jgl, we'll mutate it to jg.
view[jump_offset+1] = 0xf4;
}
return;
bad:
if (!object->has_no_split_stack())
object->error(_("failed to match split-stack sequence at "
"section %u offset %0zx"),
shndx, static_cast<size_t>(fnoffset));
}
// 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)
{
typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, true>
Classify_reloc;
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_section<size, true, Target_s390<size>, Relocate,
gold::Default_comdat_behavior, Classify_reloc>(
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.