mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 03:03:31 +08:00
bce5a025d2
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.
4966 lines
146 KiB
C++
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(¶m_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,
|
|
¶m_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.
|