Compare commits

...

1 Commits

Author SHA1 Message Date
Cary Coutant
8c13bcd220 Experimental implementation of RELR-style relocations.
For x86-64 only. Instead of R_X86_64_RELATIVE relocations, we
write the offsets of the relocation targets to a new section,
.relr.dyn, with section type SHT_RELR.
2017-04-25 20:07:52 -07:00
9 changed files with 446 additions and 35 deletions

View File

@@ -358,6 +358,7 @@ enum SHT
SHT_PREINIT_ARRAY = 16,
SHT_GROUP = 17,
SHT_SYMTAB_SHNDX = 18,
SHT_RELR = 19, // Experimental
SHT_LOOS = 0x60000000,
SHT_HIOS = 0x6fffffff,
SHT_LOPROC = 0x70000000,
@@ -719,6 +720,11 @@ enum DT
DT_PREINIT_ARRAY = 32,
DT_PREINIT_ARRAYSZ = 33,
DT_SYMTAB_SHNDX = 34,
DT_RELRSZ = 35, // Experimental
DT_RELR = 36, // Experimental
DT_RELRENT = 37, // Experimental
DT_LOOS = 0x6000000d,
DT_HIOS = 0x6ffff000,
DT_LOPROC = 0x70000000,
@@ -1021,6 +1027,7 @@ struct Elf_sizes
// Sizes of ELF reloc entries.
static const int rel_size = sizeof(internal::Rel_data<size>);
static const int rela_size = sizeof(internal::Rela_data<size>);
static const int relr_size = sizeof(internal::Relr_data<size>);
// Size of ELF dynamic entry.
static const int dyn_size = sizeof(internal::Dyn_data<size>);
// Size of ELF version structures.
@@ -1666,6 +1673,48 @@ class Rela_write
internal::Rela_data<size>* p_;
};
// Accessor class for an ELF Relr relocation.
template<int size, bool big_endian>
class Relr
{
public:
Relr(const unsigned char* p)
: p_(reinterpret_cast<const internal::Relr_data<size>*>(p))
{ }
template<typename File>
Relr(File* file, typename File::Location loc)
: p_(reinterpret_cast<const internal::Relr_data<size>*>(
file->view(loc.file_offset, loc.data_size).data()))
{ }
typename Elf_types<size>::Elf_Addr
get_r_offset() const
{ return Convert<size, big_endian>::convert_host(this->p_->r_offset); }
private:
const internal::Relr_data<size>* p_;
};
// Writer class for an ELF Relr relocation.
template<int size, bool big_endian>
class Relr_write
{
public:
Relr_write(unsigned char* p)
: p_(reinterpret_cast<internal::Relr_data<size>*>(p))
{ }
void
put_r_offset(typename Elf_types<size>::Elf_Addr v)
{ this->p_->r_offset = Convert<size, big_endian>::convert_host(v); }
private:
internal::Relr_data<size>* p_;
};
// MIPS-64 has a non-standard relocation layout.
template<bool big_endian>

View File

@@ -180,6 +180,12 @@ struct Rela_data
typename Elf_types<size>::Elf_Swxword r_addend;
};
template<int size>
struct Relr_data
{
typename Elf_types<size>::Elf_Addr r_offset;
};
// MIPS-64 has a non-standard layout for relocations.
struct Mips64_rel_data

View File

@@ -4710,7 +4710,8 @@ void
Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
const Output_data* plt_rel,
const Output_data_reloc_generic* dyn_rel,
bool add_debug, bool dynrel_includes_plt)
bool add_debug, bool dynrel_includes_plt,
const Output_data_reloc_generic* dyn_relr)
{
Output_data_dynamic* odyn = this->dynamic_data_;
if (odyn == NULL)
@@ -4783,6 +4784,14 @@ Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
}
}
if (dyn_relr != NULL && dyn_relr->output_section() != NULL)
{
const int size = parameters->target().get_size();
odyn->add_section_address(elfcpp::DT_RELR, dyn_relr->output_section());
odyn->add_section_size(elfcpp::DT_RELRSZ, dyn_relr->output_section());
odyn->add_constant(elfcpp::DT_RELRENT, size / 8);
}
if (add_debug && !parameters->options().shared())
{
// The value of the DT_DEBUG tag is filled in by the dynamic

View File

@@ -900,7 +900,8 @@ class Layout
add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
const Output_data* plt_rel,
const Output_data_reloc_generic* dyn_rel,
bool add_debug, bool dynrel_includes_plt);
bool add_debug, bool dynrel_includes_plt,
const Output_data_reloc_generic* dyn_relr = NULL);
// Add a target-specific dynamic tag with constant value.
void

View File

@@ -834,6 +834,10 @@ class General_options
N_("Exclude libraries from automatic export"),
N_(("lib,lib ...")));
DEFINE_bool(experimental_use_relr, options::TWO_DASHES, '\0', false,
N_("(x86-64 only) Generate RELR dynamic relocations"),
N_("Do not generate RELR dynamic relocations"));
DEFINE_bool(export_dynamic, options::TWO_DASHES, 'E', false,
N_("Export all dynamic symbols"),
N_("Do not export all dynamic symbols"));

View File

@@ -1224,6 +1224,17 @@ Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write(
orel.put_r_addend(addend);
}
// Write out a Relr relocation.
template<bool dynamic, int size, bool big_endian>
void
Output_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>::write(
unsigned char* pov) const
{
elfcpp::Relr_write<size, big_endian> orel(pov);
orel.put_r_offset(this->rel_.get_address());
}
// Output_data_reloc_base methods.
// Adjust the output section.
@@ -1237,6 +1248,8 @@ Output_data_reloc_base<sh_type, dynamic, size, big_endian>
os->set_entsize(elfcpp::Elf_sizes<size>::rel_size);
else if (sh_type == elfcpp::SHT_RELA)
os->set_entsize(elfcpp::Elf_sizes<size>::rela_size);
else if (sh_type == elfcpp::SHT_RELR)
os->set_entsize(elfcpp::Elf_sizes<size>::relr_size);
else
gold_unreachable();
@@ -5526,6 +5539,26 @@ template
class Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>;
#endif
#ifdef HAVE_TARGET_32_LITTLE
template
class Output_data_reloc<elfcpp::SHT_RELR, true, 32, false>;
#endif
#ifdef HAVE_TARGET_32_BIG
template
class Output_data_reloc<elfcpp::SHT_RELR, true, 32, true>;
#endif
#ifdef HAVE_TARGET_64_LITTLE
template
class Output_data_reloc<elfcpp::SHT_RELR, true, 64, false>;
#endif
#ifdef HAVE_TARGET_64_BIG
template
class Output_data_reloc<elfcpp::SHT_RELR, true, 64, true>;
#endif
#ifdef HAVE_TARGET_32_LITTLE
template
class Output_relocatable_relocs<elfcpp::SHT_REL, 32, false>;

View File

@@ -1500,6 +1500,104 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
Addend addend_;
};
// The SHT_RELR version of Output_reloc<>. This is a relative reloc,
// and holds nothing but an offset. Rather than duplicate all the fields
// of the SHT_REL version except for the symbol and relocation type, we
// simply use an SHT_REL as a proxy.
template<bool dynamic, int size, bool big_endian>
class Output_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
// An uninitialized entry.
Output_reloc()
: rel_()
{ }
// A reloc against a global symbol.
Output_reloc(Symbol* gsym, Output_data* od, Address address)
: rel_(gsym, 0, od, address, true, true, false)
{ }
Output_reloc(Symbol* gsym, Sized_relobj<size, big_endian>* relobj,
unsigned int shndx, Address address)
: rel_(gsym, 0, relobj, shndx, address, true, true, false)
{ }
// A reloc against a local symbol.
Output_reloc(Sized_relobj<size, big_endian>* relobj,
unsigned int local_sym_index, Output_data* od, Address address,
bool is_section_symbol)
: rel_(relobj, local_sym_index, 0, od, address, true,
true, is_section_symbol, false)
{ }
Output_reloc(Sized_relobj<size, big_endian>* relobj,
unsigned int local_sym_index, unsigned int shndx,
Address address, bool is_section_symbol)
: rel_(relobj, local_sym_index, 0, shndx, address, true,
true, is_section_symbol, false)
{ }
// A reloc against the STT_SECTION symbol of an output section.
Output_reloc(Output_section* os, Output_data* od, Address address)
: rel_(os, 0, od, address, true)
{ }
Output_reloc(Output_section* os, Sized_relobj<size, big_endian>* relobj,
unsigned int shndx, Address address)
: rel_(os, 0, relobj, shndx, address, true)
{ }
// A relative relocation with no symbol.
Output_reloc(Output_data* od, Address address)
: rel_(0, od, address, true)
{ }
Output_reloc(Sized_relobj<size, big_endian>* relobj,
unsigned int shndx, Address address)
: rel_(0, relobj, shndx, address, true)
{ }
// Return whether this is a RELATIVE relocation.
bool
is_relative() const
{ return true; }
// Return whether this is a relocation which should not use
// a symbol, but which obtains its addend from a symbol.
bool
is_symbolless() const
{ return true; }
// If this relocation is against an input section, return the
// relocatable object containing the input section.
Sized_relobj<size, big_endian>*
get_relobj() const
{ return this->rel_.get_relobj(); }
// Write the reloc entry to an output view.
void
write(unsigned char* pov) const;
// Return whether this reloc should be sorted before the argument
// when sorting dynamic relocs.
bool
sort_before(const Output_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>&
r2) const
{ return this->rel_.compare(r2.rel_) < 0; }
private:
// The basic reloc.
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> rel_;
};
// Output_data_reloc_generic is a non-template base class for
// Output_data_reloc_base. This gives the generic code a way to hold
// a pointer to a reloc section.
@@ -2344,6 +2442,127 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
}
};
// The SHT_RELR version of Output_data_reloc.
template<bool dynamic, int size, bool big_endian>
class Output_data_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>
: public Output_data_reloc_base<elfcpp::SHT_RELR, dynamic, size, big_endian>
{
private:
typedef Output_data_reloc_base<elfcpp::SHT_RELR, dynamic, size,
big_endian> Base;
public:
typedef typename Base::Output_reloc_type Output_reloc_type;
typedef typename Output_reloc_type::Address Address;
Output_data_reloc()
: Output_data_reloc_base<elfcpp::SHT_RELR, dynamic, size, big_endian>(false)
{ }
void
add_global_generic(Symbol*, unsigned int, Output_data*, uint64_t, uint64_t)
{
gold_unreachable();
}
void
add_global_generic(Symbol*, unsigned int, Output_data*, Relobj*,
unsigned int, uint64_t, uint64_t)
{
gold_unreachable();
}
// Add a RELATIVE reloc against a global symbol. The final relocation
// will not reference the symbol.
void
add_global_relative(Symbol* gsym, Output_data* od, Address address)
{
this->add(od, Output_reloc_type(gsym, od, address));
}
void
add_global_relative(Symbol* gsym, Output_data* od,
Sized_relobj<size, big_endian>* relobj,
unsigned int shndx, Address address)
{
this->add(od, Output_reloc_type(gsym, relobj, shndx, address));
}
void
add_local_generic(Relobj*, unsigned int, unsigned int, Output_data*, uint64_t,
uint64_t)
{
gold_unreachable();
}
void
add_local_generic(Relobj*, unsigned int, unsigned int, Output_data*,
unsigned int, uint64_t, uint64_t)
{
gold_unreachable();
}
// Add a RELATIVE reloc against a local symbol.
void
add_local_relative(Sized_relobj<size, big_endian>* relobj,
unsigned int local_sym_index, Output_data* od,
Address address)
{
this->add(od, Output_reloc_type(relobj, local_sym_index, od, address,
false));
}
void
add_local_relative(Sized_relobj<size, big_endian>* relobj,
unsigned int local_sym_index, Output_data* od,
unsigned int shndx, Address address)
{
this->add(od, Output_reloc_type(relobj, local_sym_index, shndx, address,
false));
}
void
add_output_section_generic(Output_section*, unsigned int, Output_data*,
uint64_t, uint64_t)
{
gold_unreachable();
}
void
add_output_section_generic(Output_section*, unsigned int, Output_data*,
Relobj*, unsigned int, uint64_t, uint64_t)
{
gold_unreachable();
}
// Add a RELATIVE reloc against an output section symbol.
void
add_output_section_relative(Output_section* os, Output_data* od,
Address address)
{ this->add(od, Output_reloc_type(os, od, address)); }
void
add_output_section_relative(Output_section* os, Output_data* od,
Sized_relobj<size, big_endian>* relobj,
unsigned int shndx, Address address)
{ this->add(od, Output_reloc_type(os, relobj, shndx, address)); }
// Add a relative relocation
void
add_relative(Output_data* od, Address address)
{ this->add(od, Output_reloc_type(od, address)); }
void
add_relative(Output_data* od, Sized_relobj<size, big_endian>* relobj,
unsigned int shndx, Address address)
{ this->add(od, Output_reloc_type(relobj, shndx, address)); }
};
// Output_relocatable_relocs represents a relocation section in a
// relocatable link. The actual data is written out in the target
// hook relocate_relocs. This just saves space for it.

View File

@@ -79,6 +79,27 @@ struct Reloc_types<elfcpp::SHT_RELA, size, big_endian>
{ p->put_r_addend(val); }
};
template<int size, bool big_endian>
struct Reloc_types<elfcpp::SHT_RELR, size, big_endian>
{
typedef typename elfcpp::Relr<size, big_endian> Reloc;
typedef typename elfcpp::Relr_write<size, big_endian> Reloc_write;
static const int reloc_size = elfcpp::Elf_sizes<size>::relr_size;
static inline typename elfcpp::Elf_types<size>::Elf_Swxword
get_reloc_addend(const Reloc*)
{ gold_unreachable(); }
static inline typename elfcpp::Elf_types<size>::Elf_Swxword
get_reloc_addend_noerror(const Reloc*)
{ return 0; }
static inline void
set_reloc_addend(Reloc_write*,
typename elfcpp::Elf_types<size>::Elf_Swxword)
{ gold_unreachable(); }
};
}; // End namespace gold.
#endif // !defined(GOLD_RELOC_TYPE_SH)

View File

@@ -584,13 +584,15 @@ class Target_x86_64 : public Sized_target<size, false>
// uses only Elf64_Rela relocation entries with explicit addends."
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, false> Reloc_section;
typedef Output_data_reloc<elfcpp::SHT_RELR, true, size, false> Relr_section;
Target_x86_64(const Target::Target_info* info = &x86_64_info)
: Sized_target<size, false>(info),
got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY),
got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
tls_base_symbol_defined_(false)
rela_irelative_(NULL), relr_dyn_(NULL),
copy_relocs_(elfcpp::R_X86_64_COPY), got_mod_index_offset_(-1U),
tlsdesc_reloc_info_(), tls_base_symbol_defined_(false)
{ }
// Hook for a new output section.
@@ -1172,6 +1174,10 @@ class Target_x86_64 : public Sized_target<size, false>
Reloc_section*
rela_irelative_section(Layout*);
// Get the RELR dynamic reloc section, creating it if necessary.
Relr_section*
relr_dyn_section(Layout*);
// Add a potential copy relocation.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -1235,6 +1241,8 @@ class Target_x86_64 : public Sized_target<size, false>
Reloc_section* rela_dyn_;
// The section to use for IRELATIVE relocs.
Reloc_section* rela_irelative_;
// The RELR dynamic reloc section.
Relr_section* relr_dyn_;
// Relocs saved to avoid a COPY reloc.
Copy_relocs<elfcpp::SHT_RELA, size, false> copy_relocs_;
// Offset of the GOT entry for the TLS module index.
@@ -1431,6 +1439,23 @@ Target_x86_64<size>::rela_irelative_section(Layout* layout)
return this->rela_irelative_;
}
// Get the RELR dynamic reloc section, creating it if necessary.
template<int size>
typename Target_x86_64<size>::Relr_section*
Target_x86_64<size>::relr_dyn_section(Layout* layout)
{
if (this->relr_dyn_ == NULL)
{
gold_assert(layout != NULL);
this->relr_dyn_ = new Relr_section();
layout->add_output_section_data(".relr.dyn", elfcpp::SHT_RELR,
elfcpp::SHF_ALLOC, this->relr_dyn_,
ORDER_DYNAMIC_RELOCS, false);
}
return this->relr_dyn_;
}
// 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_i386::do_write.
@@ -2966,14 +2991,25 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
if (parameters->options().output_is_position_independent())
{
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,
(size == 32
? elfcpp::R_X86_64_RELATIVE64
: elfcpp::R_X86_64_RELATIVE),
output_section, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), is_ifunc);
if (size == 64
&& !is_ifunc
&& parameters->options().experimental_use_relr())
{
Relr_section* relr_dyn = target->relr_dyn_section(layout);
relr_dyn->add_local_relative(object, r_sym, output_section,
data_shndx, reloc.get_r_offset());
}
else
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_local_relative(object, r_sym,
(size == 32
? elfcpp::R_X86_64_RELATIVE64
: elfcpp::R_X86_64_RELATIVE),
output_section, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), is_ifunc);
}
}
break;
@@ -2991,12 +3027,22 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
if (size == 32 && r_type == elfcpp::R_X86_64_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_X86_64_RELATIVE,
output_section, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), is_ifunc);
if (!is_ifunc && parameters->options().experimental_use_relr())
{
Relr_section* relr_dyn = target->relr_dyn_section(layout);
relr_dyn->add_local_relative(object, r_sym, output_section,
data_shndx,
reloc.get_r_offset());
}
else
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_local_relative(object, r_sym,
elfcpp::R_X86_64_RELATIVE,
output_section, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), is_ifunc);
}
break;
}
@@ -3099,15 +3145,7 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
// R_X86_64_RELATIVE assumes a 64-bit relocation.
if (r_type != elfcpp::R_X86_64_GOT32)
{
unsigned int got_offset =
object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
rela_dyn->add_local_relative(object, r_sym,
elfcpp::R_X86_64_RELATIVE,
got, got_offset, 0, is_ifunc);
}
else
if (r_type == elfcpp::R_X86_64_GOT32)
{
this->check_non_pic(object, r_type, NULL);
@@ -3116,6 +3154,24 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
object, r_sym, r_type, got,
object->local_got_offset(r_sym, GOT_TYPE_STANDARD), 0);
}
else if (size == 64
&& !is_ifunc
&& parameters->options().experimental_use_relr())
{
Relr_section* relr_dyn = target->relr_dyn_section(layout);
unsigned int got_offset =
object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
relr_dyn->add_local_relative(object, r_sym, got,
got_offset);
}
else
{
unsigned int got_offset =
object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
rela_dyn->add_local_relative(object, r_sym,
elfcpp::R_X86_64_RELATIVE,
got, got_offset, 0, is_ifunc);
}
}
}
// For GOTPLT64, we'd normally want a PLT section, but since
@@ -3478,12 +3534,24 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
|| (size == 32 && r_type == elfcpp::R_X86_64_32))
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_global_relative(gsym, elfcpp::R_X86_64_RELATIVE,
output_section, object,
data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), false);
if (parameters->options().experimental_use_relr())
{
Relr_section* relr_dyn = target->relr_dyn_section(layout);
relr_dyn->add_global_relative(gsym,
output_section, object,
data_shndx,
reloc.get_r_offset());
}
else
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_global_relative(gsym,
elfcpp::R_X86_64_RELATIVE,
output_section, object,
data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), false);
}
}
else
{
@@ -3886,7 +3954,8 @@ Target_x86_64<size>::do_finalize_sections(
? NULL
: this->plt_->rela_plt());
layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt,
this->rela_dyn_, true, false);
this->rela_dyn_, true, false,
this->relr_dyn_);
// Fill in some more dynamic tags.
Output_data_dynamic* const odyn = layout->dynamic_data();