summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Koƛcielnicki <koriakin@0x04.net>2016-06-03 16:44:23 +0200
committerMarcin Koƛcielnicki <koriakin@0x04.net>2016-07-04 11:04:10 +0200
commit4b32e696e10ed6107da0a9b597f861d2d9a57c88 (patch)
treeeb6ef2b8d39aa9c36ddf7e7ea1925f28e18ac271
parent6257e073193332dd18558686eda4c59ee93644de (diff)
downloadbinutils-gdb-users/mwk/s390-relro-rel.tar.gz
gold/s390: Support partial got relro.users/mwk/s390-relro-rel
-rw-r--r--gold/s390.cc269
1 files changed, 223 insertions, 46 deletions
diff --git a/gold/s390.cc b/gold/s390.cc
index c4960182001..8e20a41cdfe 100644
--- a/gold/s390.cc
+++ b/gold/s390.cc
@@ -78,6 +78,38 @@ class Output_data_got_plt_s390 : public Output_section_data_build
Layout* layout_;
};
+// A class to handle the first entry of .got when partial relro is in use.
+
+template<int size>
+class Output_data_got_dynamic_s390 : public Output_section_data_build
+{
+ public:
+ Output_data_got_dynamic_s390(Layout* layout)
+ : Output_section_data_build(size/8),
+ layout_(layout)
+ { }
+
+ Output_data_got_dynamic_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 DYNAMIC"); }
+
+ private:
+ // A pointer to the Layout class, so that we can find the .dynamic
+ // section when we write out the GOT section.
+ Layout* layout_;
+};
+
// A class to handle the PLT data.
template<int size>
@@ -90,10 +122,11 @@ class Output_data_plt_s390 : public Output_section_data
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_data_space* got_irelative,
+ Output_section_data_build* got_dynamic)
: Output_section_data(4), layout_(layout),
irelative_rel_(NULL), got_(got), got_plt_(got_plt),
- got_irelative_(got_irelative), count_(0),
+ got_irelative_(got_irelative), got_dynamic_(got_dynamic), count_(0),
irelative_count_(0), free_list_()
{ this->init(layout); }
@@ -101,11 +134,13 @@ class Output_data_plt_s390 : public Output_section_data
Output_data_got<size, true>* got,
Output_data_got_plt_s390<size>* got_plt,
Output_data_space* got_irelative,
+ Output_section_data_build* got_dynamic,
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),
+ got_plt_(got_plt), got_irelative_(got_irelative),
+ got_dynamic_(got_dynamic), count_(plt_count),
irelative_count_(0), free_list_()
{
this->init(layout);
@@ -195,7 +230,8 @@ class Output_data_plt_s390 : public Output_section_data
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);
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ unsigned int got_plt_offset);
// Fill in a normal PLT entry. Returns the offset into the entry that
// should be the initial GOT slot value.
@@ -238,6 +274,8 @@ class Output_data_plt_s390 : public Output_section_data
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 header of .got section.
+ Output_section_data_build* got_dynamic_;
// The number of PLT entries.
unsigned int count_;
// Number of PLT entries with R_TILEGX_IRELATIVE relocs. These
@@ -251,6 +289,7 @@ class Output_data_plt_s390 : public Output_section_data
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_pic12[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.
@@ -646,7 +685,7 @@ class Target_s390 : public Sized_target<size, true>
got_address() const
{
gold_assert(this->got_ != NULL);
- return this->got_plt_->address();
+ return this->got_dynamic_->address();
}
typename elfcpp::Elf_types<size>::Elf_Addr
@@ -741,6 +780,10 @@ class Target_s390 : public Sized_target<size, true>
Output_data_got_plt_s390<size>* got_plt_;
// The GOT section for IRELATIVE relocations.
Output_data_space* got_irelative_;
+ // The section containing the first GOT entry (_DYNAMIC) - this is
+ // Output_data_got_dynamic_s390 if partial relro is in use, same
+ // as got_plt_ otherwise.
+ Output_section_data_build* got_dynamic_;
// The _GLOBAL_OFFSET_TABLE_ symbol.
Symbol* global_offset_table_;
// The dynamic reloc section.
@@ -1247,18 +1290,18 @@ Output_data_plt_s390<size>::first_plt_entry_32_abs[plt_entry_size] =
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, // DT_GOTPLT (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] =
+Output_data_plt_s390<size>::first_plt_entry_32_pic12[plt_entry_size] =
{
0x50, 0x10, 0xf0, 0x1c, // st %r1, 28(%r15)
- 0x58, 0x10, 0xc0, 0x04, // l %r1, 4(%r12)
+ 0x58, 0x10, 0xc0, 0x00, // l %r1, DT_GOTPLT@got+4(%r12) (to fill)
0x50, 0x10, 0xf0, 0x18, // st %r1, 24(%r15)
- 0x58, 0x10, 0xc0, 0x08, // l %r1, 8(%r12)
+ 0x58, 0x10, 0xc0, 0x00, // l %r1, DT_GOTPLT@got+8(%r12) (to fill)
0x07, 0xf1, // br %r1
0x00, 0x00, // padding
0x00, 0x00, 0x00, 0x00, // padding
@@ -1268,10 +1311,25 @@ Output_data_plt_s390<size>::first_plt_entry_32_pic[plt_entry_size] =
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)
+ 0x0d, 0x10, // basr %r1, %r0
+ 0x5a, 0x10, 0x10, 0x12, // a %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, // DT_GOTPLT - . + 18 (to fill)
+ 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)
+ 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, // larl %r1, DT_GOTPLT (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
@@ -1284,22 +1342,33 @@ 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)
+ typename elfcpp::Elf_types<size>::Elf_Addr got_plt_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ unsigned int got_plt_offset)
{
if (size == 64)
{
memcpy(pov, first_plt_entry_64, plt_entry_size);
- S390_relocate_functions<size>::pcrela32dbl(pov + 8, got_address, (plt_address + 6));
+ S390_relocate_functions<size>::pcrela32dbl(pov + 8, got_plt_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);
+ elfcpp::Swap<32, true>::writeval(pov + 24, got_plt_address);
}
else
{
- memcpy(pov, first_plt_entry_32_pic, plt_entry_size);
+ if (got_plt_offset < 0xff8)
+ {
+ memcpy(pov, first_plt_entry_32_pic12, plt_entry_size);
+ S390_relocate_functions<size>::rela12(pov + 6, got_plt_offset + 4);
+ S390_relocate_functions<size>::rela12(pov + 14, got_plt_offset + 8);
+ }
+ else
+ {
+ memcpy(pov, first_plt_entry_32_pic, plt_entry_size);
+ elfcpp::Swap<32, true>::writeval(pov + 24, got_plt_address - (plt_address + 6));
+ }
}
}
@@ -1536,9 +1605,12 @@ Output_data_plt_s390<size>::do_write(Output_file* of)
// 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->got_dynamic_->address();
+
+ unsigned int got_plt_offset = this->got_plt_->address() - got_address;
- this->fill_first_plt_entry(pov, got_address, plt_address);
+ this->fill_first_plt_entry(pov, this->got_plt_->address(), plt_address,
+ got_plt_offset);
pov += this->get_plt_entry_size();
unsigned char* got_pov = got_view;
@@ -1547,7 +1619,7 @@ Output_data_plt_s390<size>::do_write(Output_file* of)
unsigned int plt_offset = this->get_plt_entry_size();
unsigned int plt_rel_offset = 0;
- unsigned int got_offset = 3 * size / 8;
+ unsigned int got_offset = 3 * size / 8 + got_plt_offset;
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.
@@ -1590,22 +1662,43 @@ Target_s390<size>::got_section(Symbol_table* symtab, Layout* layout)
{
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);
+ bool is_got_relro, is_got_plt_relro;
+ Output_section_order got_order, got_plt_order;
+ const char *got_plt_name;
+ if (parameters->options().relro())
+ {
+ // Partial GOT relro.
+ is_got_relro = true;
+ is_got_plt_relro = false;
+ got_order = ORDER_RELRO_LAST;
+ got_plt_order = ORDER_NON_RELRO_FIRST;
+ got_plt_name = ".got.plt";
+ }
+ else if (parameters->options().now())
+ {
+ // When using -z now, we can treat the whole .got as a relro section.
+ is_got_plt_relro = is_got_relro = true;
+ got_order = got_plt_order = ORDER_RELRO_LAST;
+ got_plt_name = ".got";
+ }
+ else
+ {
+ // Without -z now, it is modified after program startup by lazy
+ // PLT relocations.
+ is_got_plt_relro = is_got_relro = false;
+ got_order = got_plt_order = ORDER_DATA;
+ got_plt_name = ".got";
+ }
// 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,
+ layout->add_output_section_data(got_plt_name, elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
- this->got_plt_, got_order, is_got_relro);
+ this->got_plt_, got_plt_order,
+ is_got_plt_relro);
// The first three entries are reserved.
this->got_plt_->set_current_data_size(3 * size / 8);
@@ -1613,10 +1706,23 @@ Target_s390<size>::got_section(Symbol_table* symtab, Layout* layout)
// 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,
+ layout->add_output_section_data(got_plt_name, elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
this->got_irelative_,
- got_order, is_got_relro);
+ got_plt_order, is_got_plt_relro);
+
+ if (parameters->options().relro())
+ {
+ // For partial relro, we need a _DYNAMIC pointer at
+ // _GLOBAL_OFFSET_TABLE_, not at DT_GOTPLT.
+ this->got_dynamic_ = new Output_data_got_dynamic_s390<size>(layout);
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+ this->got_dynamic_, got_order, true);
+ this->got_dynamic_->set_current_data_size(size / 8);
+ }
+ else
+ this->got_dynamic_ = this->got_plt_;
// 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
@@ -1629,9 +1735,9 @@ Target_s390<size>::got_section(Symbol_table* symtab, Layout* layout)
// Define _GLOBAL_OFFSET_TABLE_ at the start of the GOT.
this->global_offset_table_ =
- symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
+ symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
Symbol_table::PREDEFINED,
- this->got_plt_,
+ this->got_dynamic_,
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
@@ -1693,7 +1799,7 @@ 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.
+ // Target_s390::got_section.
const off_t got_file_offset = this->offset();
gold_assert(this->data_size() >= 3 * size / 8);
unsigned char* const got_view =
@@ -1705,6 +1811,24 @@ Output_data_got_plt_s390<size>::do_write(Output_file* of)
of->write_output_view(got_file_offset, 3 * size / 8, got_view);
}
+// Write the first reserved word of the .got section.
+
+template<int size>
+void
+Output_data_got_dynamic_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.
+ const off_t got_file_offset = this->offset();
+ gold_assert(this->data_size() == size / 8);
+ unsigned char* const got_view =
+ of->get_output_view(got_file_offset, 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);
+ of->write_output_view(got_file_offset, size / 8, got_view);
+}
+
// Create the PLT section.
template<int size>
@@ -1722,7 +1846,8 @@ Target_s390<size>::make_plt_section(Symbol_table* symtab, Layout* layout)
this->rela_dyn_section(layout);
this->plt_ = new Output_data_plt_s390<size>(layout,
- this->got_, this->got_plt_, this->got_irelative_);
+ this->got_, this->got_plt_, this->got_irelative_,
+ this->got_dynamic_);
// Add unwind information if requested.
if (parameters->options().ld_generated_unwind_info())
@@ -1814,34 +1939,72 @@ Target_s390<size>::init_got_plt_for_update(Symbol_table* symtab,
{
gold_assert(this->got_ == NULL);
+ bool is_got_relro, is_got_plt_relro;
+ Output_section_order got_order, got_plt_order;
+ const char *got_plt_name;
+ if (parameters->options().relro())
+ {
+ // Partial GOT relro.
+ is_got_relro = true;
+ is_got_plt_relro = false;
+ got_order = ORDER_RELRO_LAST;
+ got_plt_order = ORDER_NON_RELRO_FIRST;
+ got_plt_name = ".got.plt";
+ }
+ else if (parameters->options().now())
+ {
+ // When using -z now, we can treat the whole .got as a relro section.
+ is_got_plt_relro = is_got_relro = true;
+ got_order = got_plt_order = ORDER_RELRO_LAST;
+ got_plt_name = ".got";
+ }
+ else
+ {
+ // Without -z now, it is modified after program startup by lazy
+ // PLT relocations.
+ is_got_plt_relro = is_got_relro = false;
+ got_order = got_plt_order = ORDER_DATA;
+ got_plt_name = ".got";
+ }
+
// 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);
+ layout->add_output_section_data(got_plt_name, elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+ this->got_plt_, got_plt_order,
+ is_got_plt_relro);
// 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,
+ layout->add_output_section_data(got_plt_name, elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->got_irelative_,
- ORDER_NON_RELRO_FIRST, false);
+ got_plt_order, is_got_plt_relro);
+
+ if (parameters->options().relro())
+ {
+ // For partial relro, we need a _DYNAMIC pointer at
+ // _GLOBAL_OFFSET_TABLE_, not at DT_GOTPLT.
+ this->got_dynamic_ = new Output_data_got_dynamic_s390<size>(layout);
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+ this->got_dynamic_, got_order, true);
+ this->got_dynamic_->set_current_data_size(size / 8);
+ }
+ else
+ this->got_dynamic_ = this->got_plt_;
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);
+ (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+ this->got_, got_order, is_got_relro);
// 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_,
+ this->got_dynamic_,
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
@@ -1849,7 +2012,8 @@ Target_s390<size>::init_got_plt_for_update(Symbol_table* symtab,
// Create the PLT section.
this->plt_ = new Output_data_plt_s390<size>(layout,
- this->got_, this->got_plt_, this->got_irelative_, plt_count);
+ this->got_, this->got_plt_, this->got_irelative_,
+ this->got_dynamic_, plt_count);
// Add unwind information if requested.
if (parameters->options().ld_generated_unwind_info())
@@ -4045,12 +4209,25 @@ Target_s390<size>::do_finalize_sections(
if (this->copy_relocs_.any_saved_relocs())
this->copy_relocs_.emit(this->rela_dyn_section(layout));
+ Output_section* dynamic = layout->dynamic_section();
+ if (this->got_plt_ != this->got_dynamic_ && dynamic != NULL &&
+ parameters->options().output_is_position_independent())
+ {
+ Reloc_section* rela_dyn = rela_dyn_section(layout);
+ rela_dyn->add_output_section_relative(dynamic, elfcpp::R_390_RELATIVE,
+ this->got_dynamic_, 0, 0);
+ }
+
// 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();
+ data_size += this->got_plt_->current_data_size();
+ data_size += this->got_irelative_->current_data_size();
+ if (this->got_plt_ != this->got_dynamic_)
+ data_size += this->got_dynamic_->current_data_size();
symtab->get_sized_symbol<size>(sym)->set_symsize(data_size);
}