@ -2119,8 +2119,8 @@ class Target_arm : public Sized_target<32, big_endian>
Target_arm ( const Target : : Target_info * info = & arm_info )
: Sized_target < 32 , big_endian > ( info ) ,
got_ ( NULL ) , plt_ ( NULL ) , got_plt_ ( NULL ) , rel_dyn _( NULL ) ,
copy_relocs_ ( elfcpp : : R_ARM_COPY ) ,
got_ ( NULL ) , plt_ ( NULL ) , got_plt_ ( NULL ) , got_irelative _( NULL ) ,
rel_dyn_ ( NULL ) , rel_irelative_ ( NULL ) , copy_relocs_ ( elfcpp : : R_ARM_COPY ) ,
got_mod_index_offset_ ( - 1U ) , tls_base_symbol_defined_ ( false ) ,
stub_tables_ ( ) , stub_factory_ ( Stub_factory : : get_instance ( ) ) ,
should_force_pic_veneer_ ( false ) ,
@ -2258,6 +2258,18 @@ class Target_arm : public Sized_target<32, big_endian>
uint64_t
do_dynsym_value ( const Symbol * ) const ;
// Return the plt address for globals. Since we have irelative plt entries,
// address calculation is not as straightforward as plt_address + plt_offset.
uint64_t
do_plt_address_for_global ( const Symbol * gsym ) const
{ return this - > plt_section ( ) - > address_for_global ( gsym ) ; }
// Return the plt address for locals. Since we have irelative plt entries,
// address calculation is not as straightforward as plt_address + plt_offset.
uint64_t
do_plt_address_for_local ( const Relobj * relobj , unsigned int symndx ) const
{ return this - > plt_section ( ) - > address_for_local ( relobj , symndx ) ; }
// Relocate a section.
void
relocate_section ( const Relocate_info < 32 , big_endian > * ,
@ -2357,6 +2369,10 @@ class Target_arm : public Sized_target<32, big_endian>
unsigned int
plt_entry_size ( ) const ;
// Get the section to use for IRELATIVE relocations, create it if necessary.
Reloc_section *
rel_irelative_section ( Layout * ) ;
// Map platform-specific reloc types
static unsigned int
get_real_reloc_type ( unsigned int r_type ) ;
@ -2448,8 +2464,11 @@ class Target_arm : public Sized_target<32, big_endian>
protected :
// Make the PLT-generator object.
Output_data_plt_arm < big_endian > *
make_data_plt ( Layout * layout , Output_data_space * got_plt )
{ return this - > do_make_data_plt ( layout , got_plt ) ; }
make_data_plt ( Layout * layout ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative )
{ return this - > do_make_data_plt ( layout , got , got_plt , got_irelative ) ; }
// Make an ELF object.
Object *
@ -2530,9 +2549,14 @@ class Target_arm : public Sized_target<32, big_endian>
do_define_standard_symbols ( Symbol_table * , Layout * ) ;
virtual Output_data_plt_arm < big_endian > *
do_make_data_plt ( Layout * layout , Output_data_space * got_plt )
do_make_data_plt ( Layout * layout ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative )
{
return new Output_data_plt_arm_standard < big_endian > ( layout , got_plt ) ;
gold_assert ( got_plt ! = NULL & & got_irelative ! = NULL ) ;
return new Output_data_plt_arm_standard < big_endian > (
layout , got , got_plt , got_irelative ) ;
}
private :
@ -2602,6 +2626,9 @@ class Target_arm : public Sized_target<32, big_endian>
if ( sym - > is_undefined ( ) & & ! parameters - > options ( ) . shared ( ) )
return false ;
if ( sym - > type ( ) = = elfcpp : : STT_GNU_IFUNC )
return true ;
return ( ! parameters - > doing_static_link ( )
& & ( sym - > type ( ) = = elfcpp : : STT_FUNC
| | sym - > type ( ) = = elfcpp : : STT_ARM_TFUNC )
@ -2613,6 +2640,11 @@ class Target_arm : public Sized_target<32, big_endian>
inline bool
possible_function_pointer_reloc ( unsigned int r_type ) ;
// Whether a plt entry is needed for ifunc.
bool
reloc_needs_plt_for_ifunc ( Sized_relobj_file < 32 , big_endian > * ,
unsigned int r_type ) ;
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_ ;
} ;
@ -2718,10 +2750,20 @@ class Target_arm : public Sized_target<32, big_endian>
return this - > got_plt_ ;
}
// 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 < 32 , big_endian > * relobj ,
unsigned int local_sym_index ) ;
// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
void
define_tls_base_symbol ( Symbol_table * , Layout * ) ;
@ -2903,8 +2945,12 @@ class Target_arm : public Sized_target<32, big_endian>
Output_data_plt_arm < big_endian > * plt_ ;
// The GOT PLT section.
Output_data_space * got_plt_ ;
// The GOT section for IRELATIVE relocations.
Output_data_space * got_irelative_ ;
// The dynamic reloc section.
Reloc_section * rel_dyn_ ;
// The section to use for IRELATIVE relocs.
Reloc_section * rel_irelative_ ;
// Relocs saved to avoid a COPY reloc.
Copy_relocs < elfcpp : : SHT_REL , 32 , big_endian > copy_relocs_ ;
// Offset of the GOT entry for the TLS module index.
@ -4244,6 +4290,15 @@ Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
elfcpp : : STB_LOCAL ,
elfcpp : : STV_HIDDEN , 0 ,
false , 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 ( 4 , " ** 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 ) ;
}
return this - > got_ ;
}
@ -4257,14 +4312,43 @@ Target_arm<big_endian>::rel_dyn_section(Layout* layout)
if ( this - > rel_dyn_ = = NULL )
{
gold_assert ( layout ! = NULL ) ;
// Create both relocation sections in the same place, so as to ensure
// their relative order in the output section.
this - > rel_dyn_ = new Reloc_section ( parameters - > options ( ) . combreloc ( ) ) ;
this - > rel_irelative_ = new Reloc_section ( false ) ;
layout - > add_output_section_data ( " .rel.dyn " , elfcpp : : SHT_REL ,
elfcpp : : SHF_ALLOC , this - > rel_dyn_ ,
ORDER_DYNAMIC_RELOCS , false ) ;
layout - > add_output_section_data ( " .rel.dyn " , elfcpp : : SHT_REL ,
elfcpp : : SHF_ALLOC , this - > rel_irelative_ ,
ORDER_DYNAMIC_RELOCS , false ) ;
}
return this - > rel_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 < bool big_endian >
typename Target_arm < big_endian > : : Reloc_section *
Target_arm < big_endian > : : rel_irelative_section ( Layout * layout )
{
if ( this - > rel_irelative_ = = NULL )
{
// Delegate the creation to rel_dyn_section so as to ensure their order in
// the output section.
this - > rel_dyn_section ( layout ) ;
gold_assert ( this - > rel_irelative_ ! = NULL
& & ( this - > rel_dyn_ - > output_section ( )
= = this - > rel_irelative_ - > output_section ( ) ) ) ;
}
return this - > rel_irelative_ ;
}
// Insn_template methods.
// Return byte size of an instruction template.
@ -7221,24 +7305,80 @@ template<bool big_endian>
class Output_data_plt_arm : public Output_section_data
{
public :
// Unlike aarch64, which records symbol value in "addend" field of relocations
// and could be done at the same time an IRelative reloc is created for the
// symbol, arm puts the symbol value into "GOT" table, which, however, is
// issued later in Output_data_plt_arm::do_write(). So we have a struct here
// to keep necessary symbol information for later use in do_write. We usually
// have only a very limited number of ifuncs, so the extra data required here
// is also limited.
struct IRelative_data
{
IRelative_data ( Sized_symbol < 32 > * sized_symbol )
: symbol_is_global_ ( true )
{
u_ . global = sized_symbol ;
}
IRelative_data ( Sized_relobj_file < 32 , big_endian > * relobj ,
unsigned int index )
: symbol_is_global_ ( false )
{
u_ . local . relobj = relobj ;
u_ . local . index = index ;
}
union
{
Sized_symbol < 32 > * global ;
struct
{
Sized_relobj_file < 32 , big_endian > * relobj ;
unsigned int index ;
} local ;
} u_ ;
bool symbol_is_global_ ;
} ;
typedef Output_data_reloc < elfcpp : : SHT_REL , true , 32 , big_endian >
Reloc_section ;
Output_data_plt_arm ( Layout * , uint64_t addralign , Output_data_space * ) ;
Output_data_plt_arm ( Layout * layout , uint64_t addralign ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative ) ;
// Add an entry to the PLT.
void
add_entry ( Symbol * gsym ) ;
add_entry ( Symbol_table * symtab , Layout * layout , Symbol * gsym ) ;
// Add the relocation for a plt entry.
void
add_relocation ( Symbol_table * symtab , Layout * layout ,
Symbol * gsym , unsigned int got_offset ) ;
// Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
unsigned int
add_local_ifunc_entry ( Symbol_table * symtab , Layout * ,
Sized_relobj_file < 32 , big_endian > * relobj ,
unsigned int local_sym_index ) ;
// Return the .rel.plt section data.
const Reloc_section *
rel_plt ( ) const
{ return this - > rel_ ; }
// Return the PLT relocation container for IRELATIVE.
Reloc_section *
rel_irelative ( Symbol_table * , Layout * ) ;
// Return the number of PLT entries.
unsigned int
entry_count ( ) const
{ return this - > count_ ; }
{ return this - > count_ + this - > irelative_count_ ; }
// Return the offset of the first non-reserved PLT entry.
unsigned int
@ -7250,6 +7390,14 @@ class Output_data_plt_arm : public Output_section_data
get_plt_entry_size ( ) const
{ return this - > do_get_plt_entry_size ( ) ; }
// Return the PLT address for globals.
uint32_t
address_for_global ( const Symbol * ) const ;
// Return the PLT address for locals.
uint32_t
address_for_local ( const Relobj * , unsigned int symndx ) const ;
protected :
// Fill in the first PLT entry.
void
@ -7298,19 +7446,37 @@ class Output_data_plt_arm : public Output_section_data
set_final_data_size ( )
{
this - > set_data_size ( this - > first_plt_entry_offset ( )
+ this - > count_ * this - > get_plt_entry_size ( ) ) ;
+ ( ( this - > count_ + this - > irelative_count_ )
* this - > get_plt_entry_size ( ) ) ) ;
}
// Write out the PLT data.
void
do_write ( Output_file * ) ;
// Record irelative symbol data.
void insert_irelative_data ( const IRelative_data & idata )
{ irelative_data_vec_ . push_back ( idata ) ; }
// 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.
Arm_output_data_got < big_endian > * got_ ;
// The .got.plt section.
Output_data_space * 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_ARM_IRELATIVE relocs. These
// follow the regular PLT entries.
unsigned int irelative_count_ ;
// Vector for irelative data.
typedef std : : vector < IRelative_data > IRelative_data_vec ;
IRelative_data_vec irelative_data_vec_ ;
} ;
// Create the PLT section. The ordinary .got section is an argument,
@ -7318,10 +7484,14 @@ class Output_data_plt_arm : public Output_section_data
// section just for PLT entries.
template < bool big_endian >
Output_data_plt_arm < big_endian > : : Output_data_plt_arm ( Layout * layout ,
uint64_t addralign ,
Output_data_space * got_plt )
: Output_section_data ( addralign ) , got_plt_ ( got_plt ) , count_ ( 0 )
Output_data_plt_arm < big_endian > : : Output_data_plt_arm (
Layout * layout , uint64_t addralign ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative )
: Output_section_data ( addralign ) , irelative_rel_ ( NULL ) ,
got_ ( got ) , got_plt_ ( got_plt ) , got_irelative_ ( got_irelative ) ,
count_ ( 0 ) , irelative_count_ ( 0 )
{
this - > rel_ = new Reloc_section ( false ) ;
layout - > add_output_section_data ( " .rel.plt " , elfcpp : : SHT_REL ,
@ -7340,40 +7510,210 @@ Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os)
template < bool big_endian >
void
Output_data_plt_arm < big_endian > : : add_entry ( Symbol * gsym )
Output_data_plt_arm < big_endian > : : add_entry ( Symbol_table * symtab ,
Layout * layout ,
Symbol * gsym )
{
gold_assert ( ! gsym - > has_plt_offset ( ) ) ;
// Note that when setting the PLT offset we skip the initial
// reserved PLT entry.
gsym - > set_plt_offset ( ( this - > count_ ) * this - > get_plt_entry_size ( )
+ this - > first_plt_entry_offset ( ) ) ;
unsigned int * entry_count ;
Output_section_data_build * got ;
// We have 2 different types of plt entry here, normal and ifunc.
// For normal plt, the offset begins with first_plt_entry_offset(20), and the
// 1st entry offset would be 20, the second 32, third 44 ... etc.
// For ifunc plt, the offset begins with 0. So the first offset would 0,
// second 12, third 24 ... etc.
// IFunc plt entries *always* come after *normal* plt entries.
// Notice, when computing the plt address of a certain symbol, "plt_address +
// plt_offset" is no longer correct. Use target->plt_address_for_global() or
// target->plt_address_for_local() instead.
int begin_offset = 0 ;
if ( gsym - > type ( ) = = elfcpp : : STT_GNU_IFUNC
& & gsym - > can_use_relative_reloc ( false ) )
{
entry_count = & this - > irelative_count_ ;
got = this - > got_irelative_ ;
// For irelative plt entries, offset is relative to the end of normal plt
// entries, so it starts from 0.
begin_offset = 0 ;
// Record symbol information.
this - > insert_irelative_data (
IRelative_data ( symtab - > get_sized_symbol < 32 > ( gsym ) ) ) ;
}
else
{
entry_count = & this - > count_ ;
got = this - > got_plt_ ;
// Note that for normal plt entries, when setting the PLT offset we skip
// the initial reserved PLT entry.
begin_offset = this - > first_plt_entry_offset ( ) ;
}
gsym - > set_plt_offset ( begin_offset
+ ( * entry_count ) * this - > get_plt_entry_size ( ) ) ;
+ + this - > count_ ;
+ + ( * entry_count ) ;
section_offset_type got_offset = this - > got_plt_ - > current_data_size ( ) ;
section_offset_type 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).
this - > got_plt_ - > set_current_data_size ( got_offset + 4 ) ;
got - > set_current_data_size ( got_offset + 4 ) ;
// Every PLT entry needs a reloc.
gsym - > set_needs_dynsym_entry ( ) ;
this - > rel_ - > add_global ( gsym , elfcpp : : R_ARM_JUMP_SLOT , this - > got_plt_ ,
got_offset ) ;
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 < bool big_endian >
unsigned int
Output_data_plt_arm < big_endian > : : add_local_ifunc_entry (
Symbol_table * symtab ,
Layout * layout ,
Sized_relobj_file < 32 , big_endian > * relobj ,
unsigned int local_sym_index )
{
this - > insert_irelative_data ( IRelative_data ( relobj , local_sym_index ) ) ;
// Notice, when computingthe plt entry address, "plt_address + plt_offset" is
// no longer correct. Use target->plt_address_for_local() instead.
unsigned int plt_offset = this - > irelative_count_ * this - > get_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 + 4 ) ;
// Every PLT entry needs a reloc.
Reloc_section * rel = this - > rel_irelative ( symtab , layout ) ;
rel - > add_symbolless_local_addend ( relobj , local_sym_index ,
elfcpp : : R_ARM_IRELATIVE ,
this - > got_irelative_ , got_offset ) ;
return plt_offset ;
}
// Add the relocation for a PLT entry.
template < bool big_endian >
void
Output_data_plt_arm < big_endian > : : 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 * rel = this - > rel_irelative ( symtab , layout ) ;
rel - > add_symbolless_global_addend ( gsym , elfcpp : : R_ARM_IRELATIVE ,
this - > got_irelative_ , got_offset ) ;
}
else
{
gsym - > set_needs_dynsym_entry ( ) ;
this - > rel_ - > add_global ( gsym , elfcpp : : R_ARM_JUMP_SLOT , this - > got_plt_ ,
got_offset ) ;
}
}
// Create the irelative relocation data.
template < bool big_endian >
typename Output_data_plt_arm < big_endian > : : Reloc_section *
Output_data_plt_arm < big_endian > : : rel_irelative ( Symbol_table * symtab ,
Layout * layout )
{
if ( this - > irelative_rel_ = = NULL )
{
// Since irelative relocations goes into 'rel.dyn', we delegate the
// creation of irelative_rel_ to where rel_dyn section gets created.
Target_arm < big_endian > * arm_target =
Target_arm < big_endian > : : default_target ( ) ;
this - > irelative_rel_ = arm_target - > rel_irelative_section ( layout ) ;
// Make sure we have a place for the TLSDESC relocations, in
// case we see any later on.
// this->rel_tlsdesc(layout);
if ( parameters - > doing_static_link ( ) )
{
// A statically linked executable will only have a .rel.plt section to
// hold R_ARM_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 ( " __rel_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 ( " __rel_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 for a global symbol.
template < bool big_endian >
uint32_t
Output_data_plt_arm < big_endian > : : address_for_global ( const Symbol * gsym ) const
{
uint64_t begin_offset = 0 ;
if ( gsym - > type ( ) = = elfcpp : : STT_GNU_IFUNC
& & gsym - > can_use_relative_reloc ( false ) )
{
begin_offset = ( this - > first_plt_entry_offset ( ) +
this - > count_ * this - > get_plt_entry_size ( ) ) ;
}
return this - > address ( ) + begin_offset + gsym - > plt_offset ( ) ;
}
// Return the PLT address for a local symbol. These are always
// IRELATIVE relocs.
template < bool big_endian >
uint32_t
Output_data_plt_arm < big_endian > : : address_for_local (
const Relobj * object ,
unsigned int r_sym ) const
{
return ( this - > address ( )
+ this - > first_plt_entry_offset ( )
+ this - > count_ * this - > get_plt_entry_size ( )
+ object - > local_plt_offset ( r_sym ) ) ;
}
template < bool big_endian >
class Output_data_plt_arm_standard : public Output_data_plt_arm < big_endian >
{
public :
Output_data_plt_arm_standard ( Layout * layout , Output_data_space * got_plt )
: Output_data_plt_arm < big_endian > ( layout , 4 , got_plt )
Output_data_plt_arm_standard ( Layout * layout ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative )
: Output_data_plt_arm < big_endian > ( layout , 4 , got , got_plt , got_irelative )
{ }
protected :
@ -7485,8 +7825,11 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
unsigned char * const oview = of - > get_output_view ( offset , oview_size ) ;
const off_t got_file_offset = this - > got_plt_ - > offset ( ) ;
gold_assert ( 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 ( ) ) ;
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 ;
@ -7505,7 +7848,8 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
unsigned int plt_offset = this - > first_plt_entry_offset ( ) ;
unsigned int got_offset = 12 ;
const unsigned int count = this - > count_ ;
const unsigned int count = this - > count_ + this - > irelative_count_ ;
gold_assert ( this - > irelative_count_ = = this - > irelative_data_vec_ . size ( ) ) ;
for ( unsigned int i = 0 ;
i < count ;
+ + i ,
@ -7518,8 +7862,33 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
this - > fill_plt_entry ( pov , got_address , plt_address ,
got_offset , plt_offset ) ;
// Set the entry in the GOT.
elfcpp : : Swap < 32 , big_endian > : : writeval ( got_pov , plt_address ) ;
Arm_address value ;
if ( i < this - > count_ )
{
// For non-irelative got entries, the value is the beginning of plt.
value = plt_address ;
}
else
{
// For irelative got entries, the value is the (global/local) symbol
// address.
const IRelative_data & idata =
this - > irelative_data_vec_ [ i - this - > count_ ] ;
if ( idata . symbol_is_global_ )
{
// Set the entry in the GOT for irelative symbols. The content is
// the address of the ifunc, not the address of plt start.
const Sized_symbol < 32 > * sized_symbol = idata . u_ . global ;
gold_assert ( sized_symbol - > type ( ) = = elfcpp : : STT_GNU_IFUNC ) ;
value = sized_symbol - > value ( ) ;
}
else
{
value = idata . u_ . local . relobj - > local_symbol_value (
idata . u_ . local . index , 0 ) ;
}
}
elfcpp : : Swap < 32 , big_endian > : : writeval ( got_pov , value ) ;
}
gold_assert ( static_cast < section_size_type > ( pov - oview ) = = oview_size ) ;
@ -7529,6 +7898,7 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
of - > write_output_view ( got_file_offset , got_size , got_view ) ;
}
// Create a PLT entry for a global symbol.
template < bool big_endian >
@ -7539,21 +7909,59 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
if ( gsym - > has_plt_offset ( ) )
return ;
if ( this - > plt_ = = NULL )
this - > make_plt_section ( symtab , layout ) ;
this - > plt_ - > add_entry ( symtab , layout , gsym ) ;
}
// Create the PLT section.
template < bool big_endian >
void
Target_arm < big_endian > : : make_plt_section (
Symbol_table * symtab , Layout * layout )
{
if ( this - > plt_ = = NULL )
{
// Create the GOT sections first.
// Create the GOT section first.
this - > got_section ( symtab , layout ) ;
this - > plt_ = this - > make_data_plt ( layout , this - > got_plt_ ) ;
// GOT for irelatives is create along with got.plt.
gold_assert ( this - > got_ ! = NULL
& & this - > got_plt_ ! = NULL
& & this - > got_irelative_ ! = NULL ) ;
this - > plt_ = this - > make_data_plt ( layout , this - > got_ , this - > got_plt_ ,
this - > got_irelative_ ) ;
layout - > add_output_section_data ( " .plt " , elfcpp : : SHT_PROGBITS ,
( elfcpp : : SHF_ALLOC
| elfcpp : : SHF_EXECINSTR ) ,
this - > plt_ , ORDER_PLT , false ) ;
}
this - > plt_ - > add_entry ( gsym ) ;
}
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template < bool big_endian >
void
Target_arm < big_endian > : : make_local_ifunc_plt_entry (
Symbol_table * symtab , Layout * layout ,
Sized_relobj_file < 32 , big_endian > * 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 < bool big_endian >
@ -7823,6 +8231,7 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
case elfcpp : : R_ARM_JUMP_SLOT :
case elfcpp : : R_ARM_ABS32 :
case elfcpp : : R_ARM_ABS32_NOI :
case elfcpp : : R_ARM_IRELATIVE :
case elfcpp : : R_ARM_PC24 :
// FIXME: The following 3 types are not supported by Android's dynamic
// linker.
@ -7853,6 +8262,27 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
}
}
// Return whether we need to make a PLT entry for a relocation of the
// given type against a STT_GNU_IFUNC symbol.
template < bool big_endian >
bool
Target_arm < big_endian > : : Scan : : reloc_needs_plt_for_ifunc (
Sized_relobj_file < 32 , big_endian > * 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 false ;
}
return flags ! = 0 ;
}
// Scan a relocation for a local symbol.
// FIXME: This only handles a subset of relocation types used by Android
// on ARM v5te devices.
@ -7874,6 +8304,15 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
return ;
r_type = get_real_reloc_type ( r_type ) ;
// 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 < 32 > ( reloc . get_r_info ( ) ) ;
target - > make_local_ifunc_plt_entry ( symtab , layout , object , r_sym ) ;
}
switch ( r_type )
{
case elfcpp : : R_ARM_NONE :
@ -7898,7 +8337,7 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
// we need to add check_non_pic(object, r_type) here.
rel_dyn - > add_local_relative ( object , r_sym , elfcpp : : R_ARM_RELATIVE ,
output_section , data_shndx ,
reloc . get_r_offset ( ) ) ;
reloc . get_r_offset ( ) , is_ifunc ) ;
}
break ;
@ -8265,6 +8704,11 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
& & strcmp ( gsym - > name ( ) , " _GLOBAL_OFFSET_TABLE_ " ) = = 0 )
target - > got_section ( symtab , layout ) ;
// 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 ) ;
r_type = get_real_reloc_type ( r_type ) ;
switch ( r_type )
{
@ -8307,6 +8751,24 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
target - > copy_reloc ( symtab , layout , object ,
data_shndx , output_section , gsym , reloc ) ;
}
else if ( ( r_type = = elfcpp : : R_ARM_ABS32
| | r_type = = elfcpp : : R_ARM_ABS32_NOI )
& & 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 * rel_irelative =
target - > rel_irelative_section ( layout ) ;
unsigned int r_type = elfcpp : : R_ARM_IRELATIVE ;
rel_irelative - > add_symbolless_global_addend (
gsym , r_type , output_section , object ,
data_shndx , reloc . get_r_offset ( ) ) ;
}
else if ( ( r_type = = elfcpp : : R_ARM_ABS32
| | r_type = = elfcpp : : R_ARM_ABS32_NOI )
& & gsym - > can_use_relative_reloc ( false ) )
@ -8442,7 +8904,13 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
Arm_output_data_got < big_endian > * got =
target - > got_section ( symtab , layout ) ;
if ( gsym - > final_value_is_known ( ) )
got - > add_global ( gsym , GOT_TYPE_STANDARD ) ;
{
// 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
@ -8452,12 +8920,29 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
| | gsym - > is_undefined ( )
| | gsym - > is_preemptible ( )
| | ( gsym - > visibility ( ) = = elfcpp : : STV_PROTECTED
& & parameters - > options ( ) . shared ( ) ) )
& & parameters - > options ( ) . shared ( ) )
| | ( gsym - > type ( ) = = elfcpp : : STT_GNU_IFUNC
& & parameters - > options ( ) . output_is_position_independent ( ) ) )
got - > add_global_with_rel ( gsym , GOT_TYPE_STANDARD ,
rel_dyn , elfcpp : : R_ARM_GLOB_DAT ) ;
else
{
if ( got - > add_global ( gsym , GOT_TYPE_STANDARD ) )
// 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 )
rel_dyn - > add_global_relative (
gsym , elfcpp : : R_ARM_RELATIVE , got ,
gsym - > got_offset ( GOT_TYPE_STANDARD ) ) ;
@ -8919,8 +9404,7 @@ Target_arm<big_endian>::Relocate::relocate(
if ( gsym - > use_plt_offset ( Scan : : get_reference_flags ( r_type ) ) )
{
// This uses a PLT, change the symbol value.
symval . set_output_value ( target - > plt_section ( ) - > address ( )
+ gsym - > plt_offset ( ) ) ;
symval . set_output_value ( target - > plt_address_for_global ( gsym ) ) ;
psymval = & symval ;
}
else if ( gsym - > is_weak_undefined ( ) )
@ -8958,6 +9442,13 @@ Target_arm<big_endian>::Relocate::relocate(
elfcpp : : Elf_types < 32 > : : Elf_WXword r_info = rel . get_r_info ( ) ;
unsigned int r_sym = elfcpp : : elf_r_sym < 32 > ( r_info ) ;
thumb_bit = object - > local_symbol_is_thumb_function ( r_sym ) ? 1 : 0 ;
if ( psymval - > is_ifunc_symbol ( ) & & object - > local_has_plt_offset ( r_sym ) )
{
symval . set_output_value (
target - > plt_address_for_local ( object , r_sym ) ) ;
psymval = & symval ;
}
}
}
else
@ -9936,7 +10427,7 @@ uint64_t
Target_arm < big_endian > : : do_dynsym_value ( const Symbol * gsym ) const
{
gold_assert ( gsym - > is_from_dynobj ( ) & & gsym - > has_plt_offset ( ) ) ;
return this - > plt_section ( ) - > address ( ) + gsym - > plt_offset ( ) ;
return this - > plt_address_for_global ( gsym ) ;
}
// Map platform-specific relocs to real relocs
@ -11091,8 +11582,7 @@ Target_arm<big_endian>::scan_reloc_for_stub(
if ( gsym - > use_plt_offset ( Scan : : get_reference_flags ( r_type ) ) )
{
// This uses a PLT, change the symbol value.
symval . set_output_value ( this - > plt_section ( ) - > address ( )
+ gsym - > plt_offset ( ) ) ;
symval . set_output_value ( this - > plt_address_for_global ( gsym ) ) ;
psymval = & symval ;
target_is_thumb = false ;
}
@ -12195,8 +12685,13 @@ class Target_arm_nacl : public Target_arm<big_endian>
protected :
virtual Output_data_plt_arm < big_endian > *
do_make_data_plt ( Layout * layout , Output_data_space * got_plt )
{ return new Output_data_plt_arm_nacl < big_endian > ( layout , got_plt ) ; }
do_make_data_plt (
Layout * layout ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative )
{ return new Output_data_plt_arm_nacl < big_endian > (
layout , got , got_plt , got_irelative ) ; }
private :
static const Target : : Target_info arm_nacl_info ;
@ -12233,8 +12728,12 @@ template<bool big_endian>
class Output_data_plt_arm_nacl : public Output_data_plt_arm < big_endian >
{
public :
Output_data_plt_arm_nacl ( Layout * layout , Output_data_space * got_plt )
: Output_data_plt_arm < big_endian > ( layout , 16 , got_plt )
Output_data_plt_arm_nacl (
Layout * layout ,
Arm_output_data_got < big_endian > * got ,
Output_data_space * got_plt ,
Output_data_space * got_irelative )
: Output_data_plt_arm < big_endian > ( layout , 16 , got , got_plt , got_irelative )
{ }
protected :