From 1a2dff539bff0b85f89b2f8f0c30dab1851f59a5 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 30 Dec 2009 06:57:17 +0000 Subject: [PATCH] * layout.cc (Layout::Layout): Initialize increase_relro_. (Layout::get_output_section): Add is_relro, is_last_relro, and is_first_non_relro parameters. Change all callers. (Layout::choose_output_section): Likewise. (Layout::add_output_section_data): Likewise. (Layout::make_output_section): Likewise. (Layout::set_segment_offsets): Clear increase_relro when using a linker script. * layout.h (class Layout): Add increase_relro method. Add increase_relro_ field. Update declarations. * output.cc (Output_section::Output_section): Initialize is_last_relro_ and is_first_non_relro_. (Output_segment::add_output_section): Group relro sections is do_sort is true. Handle is_last_relro and is_first_non_relro. (Output_segment::maximum_alignment): Remove relro handling. (Output_segment::set_section_addresses): Add increase_relro parameter. Change all callers. Add initial alignment to align relro sections on separate page. Remove old relro handling. (Output_segment::set_section_list_addresses): Remove in_relro parameter. Change all callers. (Output_segment::set_offset): Add increase parameter. Change all callers. Remove old relro handling. * output.h (class Output_section): Add new methods: is_last_relro, set_is_last_relro, is_first_non_relro, set_is_first_non_relro. Add is_last_relro_ and is_first_non_relro_ fields. * i386.cc (Target_i386::got_section): Don't call set_is_relro. Create separate .got.plt section. Call increase_relro. * x86_64.cc (Target_x86_64::got_section): Likewise. * testsuite/relro_script_test.t: Add .got.plt. --- gold/ChangeLog | 30 +++++++ gold/arm.cc | 19 +++-- gold/common.cc | 3 +- gold/copy-relocs.cc | 3 +- gold/i386.cc | 25 +++--- gold/layout.cc | 121 ++++++++++++++++++-------- gold/layout.h | 27 ++++-- gold/output.cc | 169 ++++++++++++++++++++++--------------- gold/output.h | 40 ++++++++- gold/powerpc.cc | 19 +++-- gold/sparc.cc | 12 +-- gold/testsuite/relro_script_test.t | 2 + gold/x86_64.cc | 25 +++--- 13 files changed, 336 insertions(+), 159 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index a4d6e8e..d1f1043 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,5 +1,35 @@ 2009-12-29 Ian Lance Taylor + * layout.cc (Layout::Layout): Initialize increase_relro_. + (Layout::get_output_section): Add is_relro, is_last_relro, and + is_first_non_relro parameters. Change all callers. + (Layout::choose_output_section): Likewise. + (Layout::add_output_section_data): Likewise. + (Layout::make_output_section): Likewise. + (Layout::set_segment_offsets): Clear increase_relro when using a + linker script. + * layout.h (class Layout): Add increase_relro method. Add + increase_relro_ field. Update declarations. + * output.cc (Output_section::Output_section): Initialize + is_last_relro_ and is_first_non_relro_. + (Output_segment::add_output_section): Group relro sections is + do_sort is true. Handle is_last_relro and is_first_non_relro. + (Output_segment::maximum_alignment): Remove relro handling. + (Output_segment::set_section_addresses): Add increase_relro + parameter. Change all callers. Add initial alignment to align + relro sections on separate page. Remove old relro handling. + (Output_segment::set_section_list_addresses): Remove in_relro + parameter. Change all callers. + (Output_segment::set_offset): Add increase parameter. Change all + callers. Remove old relro handling. + * output.h (class Output_section): Add new methods: is_last_relro, + set_is_last_relro, is_first_non_relro, set_is_first_non_relro. + Add is_last_relro_ and is_first_non_relro_ fields. + * i386.cc (Target_i386::got_section): Don't call set_is_relro. + Create separate .got.plt section. Call increase_relro. + * x86_64.cc (Target_x86_64::got_section): Likewise. + * testsuite/relro_script_test.t: Add .got.plt. + PR 10450 * layout.cc (Layout::Layout): Initialize dynamic_symbol_ field. (Layout::create_initial_dynamic_sections): Set dynamic_symbol_. diff --git a/gold/arm.cc b/gold/arm.cc index afe1bc0..d104033 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -2592,8 +2592,8 @@ Target_arm::got_section(Symbol_table* symtab, Layout* layout) os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_, false); - os->set_is_relro(); + this->got_, false, true, true, + false); // The old GNU linker creates a .got.plt section. We just // create another set of data in the .got section. Note that we @@ -2603,8 +2603,8 @@ Target_arm::got_section(Symbol_table* symtab, Layout* layout) os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_plt_, false); - os->set_is_relro(); + this->got_plt_, false, false, + false, true); // The first three entries are reserved. this->got_plt_->set_current_data_size(3 * 4); @@ -2632,7 +2632,8 @@ Target_arm::rel_dyn_section(Layout* layout) gold_assert(layout != NULL); this->rel_dyn_ = new Reloc_section(parameters->options().combreloc()); layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, - elfcpp::SHF_ALLOC, this->rel_dyn_, true); + elfcpp::SHF_ALLOC, this->rel_dyn_, true, + false, false, false); } return this->rel_dyn_; } @@ -4052,7 +4053,8 @@ Output_data_plt_arm::Output_data_plt_arm(Layout* layout, { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, - elfcpp::SHF_ALLOC, this->rel_, true); + elfcpp::SHF_ALLOC, this->rel_, true, false, + false, false); } template @@ -4214,7 +4216,7 @@ Target_arm::make_plt_entry(Symbol_table* symtab, Layout* layout, layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), - this->plt_, false); + this->plt_, false, false, false, false); } this->plt_->add_entry(gsym); } @@ -4793,7 +4795,8 @@ Target_arm::do_finalize_sections( new Output_attributes_section_data(*this->attributes_section_data_); layout->add_output_section_data(".ARM.attributes", elfcpp::SHT_ARM_ATTRIBUTES, 0, - attributes_section, false); + attributes_section, false, false, false, + false); } // Return whether a direct absolute static relocation needs to be applied. diff --git a/gold/common.cc b/gold/common.cc index 4bc6a51..c5d830e 100644 --- a/gold/common.cc +++ b/gold/common.cc @@ -239,7 +239,8 @@ Symbol_table::do_allocate_commons_list( Output_data_space *poc = new Output_data_space(addralign, ds_name); Output_section *os = layout->add_output_section_data(name, elfcpp::SHT_NOBITS, - flags, poc, false); + flags, poc, false, + false, false, false); if (os != NULL) { if (commons_section_type == COMMONS_SMALL) diff --git a/gold/copy-relocs.cc b/gold/copy-relocs.cc index 345c0c6..4a98f83 100644 --- a/gold/copy-relocs.cc +++ b/gold/copy-relocs.cc @@ -138,7 +138,8 @@ Copy_relocs::emit_copy_reloc( layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->dynbss_, false); + this->dynbss_, false, false, false, + false); } Output_data_space* dynbss = this->dynbss_; diff --git a/gold/i386.cc b/gold/i386.cc index 43edd65..29268c9 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -461,23 +461,22 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_, false); - os->set_is_relro(); + this->got_, false, true, true, + false); - // 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_space(4, "** GOT PLT"); - os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + os = layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_plt_, false); - os->set_is_relro(); + this->got_plt_, false, false, false, + true); // The first three entries are reserved. this->got_plt_->set_current_data_size(3 * 4); + // Those bytes can go into the relro segment. + layout->increase_relro(3 * 4); + // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, Symbol_table::PREDEFINED, @@ -501,7 +500,8 @@ Target_i386::rel_dyn_section(Layout* layout) gold_assert(layout != NULL); this->rel_dyn_ = new Reloc_section(parameters->options().combreloc()); layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, - elfcpp::SHF_ALLOC, this->rel_dyn_, true); + elfcpp::SHF_ALLOC, this->rel_dyn_, true, + false, false, false); } return this->rel_dyn_; } @@ -576,7 +576,8 @@ Output_data_plt_i386::Output_data_plt_i386(Layout* layout, { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, - elfcpp::SHF_ALLOC, this->rel_, true); + elfcpp::SHF_ALLOC, this->rel_, true, + false, false, false); } void @@ -761,7 +762,7 @@ Target_i386::make_plt_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym) layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), - this->plt_, false); + this->plt_, false, false, false, false); } this->plt_->add_entry(gsym); diff --git a/gold/layout.cc b/gold/layout.cc index d0726ae..82f18c4 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -175,6 +175,7 @@ Layout::Layout(int number_of_input_files, Script_options* script_options) section_headers_(NULL), tls_segment_(NULL), relro_segment_(NULL), + increase_relro_(0), symtab_section_(NULL), symtab_xindex_(NULL), dynsym_section_(NULL), @@ -391,12 +392,16 @@ Layout::find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set, // and section flags FLAGS. NAME must be canonicalized in the string // pool, and NAME_KEY is the key. IS_INTERP is true if this is the // .interp section. IS_DYNAMIC_LINKER_SECTION is true if this section -// is used by the dynamic linker. +// is used by the dynamic linker. IS_RELRO is true for a relro +// section. IS_LAST_RELRO is true for the last relro section. +// IS_FIRST_NON_RELRO is true for the first non-relro section. Output_section* Layout::get_output_section(const char* name, Stringpool::Key name_key, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, - bool is_interp, bool is_dynamic_linker_section) + bool is_interp, bool is_dynamic_linker_section, + bool is_relro, bool is_last_relro, + bool is_first_non_relro) { elfcpp::Elf_Xword lookup_flags = flags; @@ -446,7 +451,8 @@ Layout::get_output_section(const char* name, Stringpool::Key name_key, if (os == NULL) os = this->make_output_section(name, type, flags, is_interp, - is_dynamic_linker_section); + is_dynamic_linker_section, is_relro, + is_last_relro, is_first_non_relro); ins.first->second = os; return os; } @@ -458,14 +464,17 @@ Layout::get_output_section(const char* name, Stringpool::Key name_key, // choosing an output section for an input section found in a input // file. IS_INTERP is true if this is the .interp section. // IS_DYNAMIC_LINKER_SECTION is true if this section is used by the -// dynamic linker. This will return NULL if the input section should -// be discarded. +// dynamic linker. IS_RELRO is true for a relro section. +// IS_LAST_RELRO is true for the last relro section. +// IS_FIRST_NON_RELRO is true for the first non-relro section. This +// will return NULL if the input section should be discarded. Output_section* Layout::choose_output_section(const Relobj* relobj, const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, bool is_input_section, bool is_interp, - bool is_dynamic_linker_section) + bool is_dynamic_linker_section, bool is_relro, + bool is_last_relro, bool is_first_non_relro) { // We should not see any input sections after we have attached // sections to segments. @@ -515,7 +524,8 @@ Layout::choose_output_section(const Relobj* relobj, const char* name, Output_section* os = this->make_output_section(name, type, flags, is_interp, - is_dynamic_linker_section); + is_dynamic_linker_section, is_relro, + is_last_relro, is_first_non_relro); os->set_found_in_sections_clause(); *output_section_slot = os; return os; @@ -539,7 +549,8 @@ Layout::choose_output_section(const Relobj* relobj, const char* name, // Find or make the output section. The output section is selected // based on the section name, type, and flags. return this->get_output_section(name, name_key, type, flags, is_interp, - is_dynamic_linker_section); + is_dynamic_linker_section, is_relro, + is_last_relro, is_first_non_relro); } // Return the output section to use for input section SHNDX, with name @@ -572,13 +583,14 @@ Layout::layout(Sized_relobj* object, unsigned int shndx, { name = this->namepool_.add(name, true, NULL); os = this->make_output_section(name, shdr.get_sh_type(), - shdr.get_sh_flags(), false, false); + shdr.get_sh_flags(), false, false, + false, false, false); } else { os = this->choose_output_section(object, name, shdr.get_sh_type(), shdr.get_sh_flags(), true, false, - false); + false, false, false, false); if (os == NULL) return NULL; } @@ -629,6 +641,7 @@ Layout::layout_reloc(Sized_relobj* object, Output_section* os = this->choose_output_section(object, name.c_str(), sh_type, shdr.get_sh_flags(), + false, false, false, false, false, false); os->set_should_link_to_symtab(); @@ -677,6 +690,7 @@ Layout::layout_group(Symbol_table* symtab, Output_section* os = this->make_output_section(group_section_name, elfcpp::SHT_GROUP, shdr.get_sh_flags(), + false, false, false, false, false); // We need to find a symbol with the signature in the symbol table. @@ -731,6 +745,7 @@ Layout::layout_eh_frame(Sized_relobj* object, name, elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC, + false, false, false, false, false, false); if (os == NULL) return NULL; @@ -747,6 +762,7 @@ Layout::layout_eh_frame(Sized_relobj* object, ".eh_frame_hdr", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC, + false, false, false, false, false, false); if (hdr_os != NULL) @@ -814,11 +830,15 @@ Output_section* Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, Output_section_data* posd, - bool is_dynamic_linker_section) + bool is_dynamic_linker_section, + bool is_relro, bool is_last_relro, + bool is_first_non_relro) { Output_section* os = this->choose_output_section(NULL, name, type, flags, false, false, - is_dynamic_linker_section); + is_dynamic_linker_section, + is_relro, is_last_relro, + is_first_non_relro); if (os != NULL) os->add_output_section_data(posd); return os; @@ -855,12 +875,15 @@ is_compressible_debug_section(const char* secname) // Make a new Output_section, and attach it to segments as // appropriate. IS_INTERP is true if this is the .interp section. // IS_DYNAMIC_LINKER_SECTION is true if this section is used by the -// dynamic linker. +// dynamic linker. IS_RELRO is true if this is a relro section. +// IS_LAST_RELRO is true if this is the last relro section. +// IS_FIRST_NON_RELRO is true if this is the first non relro section. Output_section* Layout::make_output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, bool is_interp, - bool is_dynamic_linker_section) + bool is_dynamic_linker_section, bool is_relro, + bool is_last_relro, bool is_first_non_relro) { Output_section* os; if ((flags & elfcpp::SHF_ALLOC) == 0 @@ -897,6 +920,12 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type, os->set_is_interp(); if (is_dynamic_linker_section) os->set_is_dynamic_linker_section(); + if (is_relro) + os->set_is_relro(); + if (is_last_relro) + os->set_is_last_relro(); + if (is_first_non_relro) + os->set_is_first_non_relro(); parameters->target().new_output_section(os); @@ -1089,7 +1118,7 @@ Layout::make_output_section_for_script(const char* name) name = this->namepool_.add(name, false, NULL); Output_section* os = this->make_output_section(name, elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC, false, - false); + false, false, false, false); os->set_found_in_sections_clause(); return os; } @@ -1159,8 +1188,8 @@ Layout::create_initial_dynamic_sections(Symbol_table* symtab) elfcpp::SHT_DYNAMIC, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - false, false, true); - this->dynamic_section_->set_is_relro(); + false, false, true, + true, false, false); this->dynamic_symbol_ = symtab->define_in_output_data("_DYNAMIC", NULL, Symbol_table::PREDEFINED, @@ -1766,7 +1795,7 @@ Layout::create_note(const char* name, int note_type, Output_section* os = this->choose_output_section(NULL, section_name, elfcpp::SHT_NOTE, flags, false, false, - false); + false, false, false, false); if (os == NULL) return NULL; @@ -1846,7 +1875,7 @@ Layout::create_executable_stack_info() if (is_stack_executable) flags |= elfcpp::SHF_EXECINSTR; this->make_output_section(name, elfcpp::SHT_PROGBITS, flags, false, - false); + false, false, false, false); } else { @@ -2007,7 +2036,7 @@ Layout::create_incremental_info_sections() Output_section* inputs_os = this->make_output_section(incremental_inputs_name, elfcpp::SHT_GNU_INCREMENTAL_INPUTS, 0, - false, false); + false, false, false, false, false); Output_section_data* posd = this->incremental_inputs_->create_incremental_inputs_section_data(); inputs_os->add_output_section_data(posd); @@ -2017,7 +2046,8 @@ Layout::create_incremental_info_sections() this->namepool_.add(".gnu_incremental_strtab", false, NULL); Output_section* strtab_os = this->make_output_section(incremental_strtab_name, elfcpp::SHT_STRTAB, - 0, false, false); + 0, false, false, + false, false, false); Output_data_strtab* strtab_data = new Output_data_strtab(this->incremental_inputs_->get_stringpool()); strtab_os->add_output_section_data(strtab_data); @@ -2203,6 +2233,10 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, } } + unsigned int increase_relro = this->increase_relro_; + if (this->script_options_->saw_sections_clause()) + increase_relro = 0; + const bool check_sections = parameters->options().check_sections(); Output_segment* last_load_segment = NULL; @@ -2291,6 +2325,7 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, unsigned int shndx_hold = *pshndx; uint64_t new_addr = (*p)->set_section_addresses(this, false, addr, + increase_relro, &off, pshndx); // Now that we know the size of this segment, we may be able @@ -2317,6 +2352,7 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1)); off = align_file_offset(off, addr, abi_pagesize); new_addr = (*p)->set_section_addresses(this, true, addr, + increase_relro, &off, pshndx); } } @@ -2354,7 +2390,9 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, ++p) { if ((*p)->type() != elfcpp::PT_LOAD) - (*p)->set_offset(); + (*p)->set_offset((*p)->type() == elfcpp::PT_GNU_RELRO + ? increase_relro + : 0); } // Set the TLS offsets for each section in the PT_TLS segment. @@ -2629,7 +2667,8 @@ Layout::create_symtab_sections(const Input_objects* input_objects, const char* symtab_name = this->namepool_.add(".symtab", false, NULL); Output_section* osymtab = this->make_output_section(symtab_name, elfcpp::SHT_SYMTAB, - 0, false, false); + 0, false, false, + false, false, false); this->symtab_section_ = osymtab; Output_section_data* pos = new Output_data_fixed_space(off - startoff, @@ -2651,7 +2690,7 @@ Layout::create_symtab_sections(const Input_objects* input_objects, Output_section* osymtab_xindex = this->make_output_section(symtab_xindex_name, elfcpp::SHT_SYMTAB_SHNDX, 0, false, - false); + false, false, false, false); size_t symcount = (off - startoff) / symsize; this->symtab_xindex_ = new Output_symtab_xindex(symcount); @@ -2673,7 +2712,8 @@ Layout::create_symtab_sections(const Input_objects* input_objects, const char* strtab_name = this->namepool_.add(".strtab", false, NULL); Output_section* ostrtab = this->make_output_section(strtab_name, elfcpp::SHT_STRTAB, - 0, false, false); + 0, false, false, + false, false, false); Output_section_data* pstr = new Output_data_strtab(&this->sympool_); ostrtab->add_output_section_data(pstr); @@ -2701,7 +2741,8 @@ Layout::create_shstrtab() const char* name = this->namepool_.add(".shstrtab", false, NULL); Output_section* os = this->make_output_section(name, elfcpp::SHT_STRTAB, 0, - false, false); + false, false, false, false, + false); if (strcmp(parameters->options().compress_debug_sections(), "none") != 0) { @@ -2818,7 +2859,8 @@ Layout::create_dynamic_symtab(const Input_objects* input_objects, Output_section* dynsym = this->choose_output_section(NULL, ".dynsym", elfcpp::SHT_DYNSYM, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, + false, false, false); Output_section_data* odata = new Output_data_fixed_space(index * symsize, align, @@ -2848,7 +2890,7 @@ Layout::create_dynamic_symtab(const Input_objects* input_objects, this->choose_output_section(NULL, ".dynsym_shndx", elfcpp::SHT_SYMTAB_SHNDX, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, false, false, false); this->dynsym_xindex_ = new Output_symtab_xindex(index); @@ -2871,7 +2913,8 @@ Layout::create_dynamic_symtab(const Input_objects* input_objects, Output_section* dynstr = this->choose_output_section(NULL, ".dynstr", elfcpp::SHT_STRTAB, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, + false, false, false); Output_section_data* strdata = new Output_data_strtab(&this->dynpool_); dynstr->add_output_section_data(strdata); @@ -2897,7 +2940,9 @@ Layout::create_dynamic_symtab(const Input_objects* input_objects, Output_section* hashsec = this->choose_output_section(NULL, ".hash", elfcpp::SHT_HASH, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, + false, false, + false); Output_section_data* hashdata = new Output_data_const_buffer(phash, hashlen, @@ -2922,7 +2967,9 @@ Layout::create_dynamic_symtab(const Input_objects* input_objects, Output_section* hashsec = this->choose_output_section(NULL, ".gnu.hash", elfcpp::SHT_GNU_HASH, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, + false, false, + false); Output_section_data* hashdata = new Output_data_const_buffer(phash, hashlen, @@ -3020,7 +3067,8 @@ Layout::sized_create_version_sections( Output_section* vsec = this->choose_output_section(NULL, ".gnu.version", elfcpp::SHT_GNU_versym, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, + false, false, false); unsigned char* vbuf; unsigned int vsize; @@ -3045,7 +3093,8 @@ Layout::sized_create_version_sections( vdsec= this->choose_output_section(NULL, ".gnu.version_d", elfcpp::SHT_GNU_verdef, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, false, false, + false); unsigned char* vdbuf; unsigned int vdsize; @@ -3070,7 +3119,8 @@ Layout::sized_create_version_sections( vnsec = this->choose_output_section(NULL, ".gnu.version_r", elfcpp::SHT_GNU_verneed, elfcpp::SHF_ALLOC, - false, false, true); + false, false, true, false, false, + false); unsigned char* vnbuf; unsigned int vnsize; @@ -3110,7 +3160,8 @@ Layout::create_interp(const Target* target) Output_section* osec = this->choose_output_section(NULL, ".interp", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC, - false, true, true); + false, true, true, + false, false, false); osec->add_output_section_data(odata); if (!this->script_options_->saw_phdrs_clause()) diff --git a/gold/layout.h b/gold/layout.h index 0437cf1..06060ef 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -362,11 +362,21 @@ class Layout // Add an Output_section_data to the layout. This is used for // special sections like the GOT section. IS_DYNAMIC_LINKER_SECTION // is true for sections which are used by the dynamic linker, such - // as dynamic reloc sections. + // as dynamic reloc sections. IS_RELRO is true for relro sections. + // IS_LAST_RELRO is true for the last relro section. + // IS_FIRST_NON_RELRO is true for the first section after the relro + // sections. Output_section* add_output_section_data(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, - Output_section_data*, bool is_dynamic_linker_section); + Output_section_data*, bool is_dynamic_linker_section, + bool is_relro, bool is_last_relro, + bool is_first_non_relro); + + // Increase the size of the relro segment by this much. + void + increase_relro(unsigned int s) + { this->increase_relro_ += s; } // Create dynamic sections if necessary. void @@ -752,20 +762,24 @@ class Layout Output_section* get_output_section(const char* name, Stringpool::Key name_key, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, - bool is_interp, bool is_dynamic_linker_section); + bool is_interp, bool is_dynamic_linker_section, + bool is_relro, bool is_last_relro, + bool is_first_non_relro); // Choose the output section for NAME in RELOBJ. Output_section* choose_output_section(const Relobj* relobj, const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, bool is_input_section, bool is_interp, - bool is_dynamic_linker_section); + bool is_dynamic_linker_section, bool is_relro, + bool is_last_relro, bool is_first_non_relro); // Create a new Output_section. Output_section* make_output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, bool is_interp, - bool is_dynamic_linker_section); + bool is_dynamic_linker_section, bool is_relro, + bool is_last_relro, bool is_first_non_relro); // Attach a section to a segment. void @@ -941,6 +955,9 @@ class Layout Output_segment* tls_segment_; // A pointer to the PT_GNU_RELRO segment if there is one. Output_segment* relro_segment_; + // A backend may increase the size of the PT_GNU_RELRO segment if + // there is one. This is the amount to increase it by. + unsigned int increase_relro_; // The SHT_SYMTAB output section. Output_section* symtab_section_; // The SHT_SYMTAB_SHNDX for the regular symbol table if there is one. diff --git a/gold/output.cc b/gold/output.cc index d75b45b..e643fb7 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -1795,6 +1795,8 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, attached_input_sections_are_sorted_(false), is_relro_(false), is_relro_local_(false), + is_last_relro_(false), + is_first_non_relro_(false), is_small_section_(false), is_large_section_(false), is_interp_(false), @@ -3157,26 +3159,52 @@ Output_segment::add_output_section(Output_section* os, // location in the section list. } - // For the PT_GNU_RELRO segment, we need to group relro sections, - // and we need to put them before any non-relro sections. Also, - // relro local sections go before relro non-local sections. - if (parameters->options().relro() && os->is_relro()) + if (do_sort) { - gold_assert(pdl == &this->output_data_); - Output_segment::Output_data_list::iterator p; - for (p = pdl->begin(); p != pdl->end(); ++p) + // For the PT_GNU_RELRO segment, we need to group relro + // sections, and we need to put them before any non-relro + // sections. Any relro local sections go before relro non-local + // sections. One section may be marked as the last relro + // section. + if (os->is_relro()) { - if (!(*p)->is_section()) - break; + gold_assert(pdl == &this->output_data_); + Output_segment::Output_data_list::iterator p; + for (p = pdl->begin(); p != pdl->end(); ++p) + { + if (!(*p)->is_section()) + break; - Output_section* pos = (*p)->output_section(); - if (!pos->is_relro() - || (os->is_relro_local() && !pos->is_relro_local())) - break; + Output_section* pos = (*p)->output_section(); + if (!pos->is_relro() + || (os->is_relro_local() && !pos->is_relro_local()) + || (!os->is_last_relro() && pos->is_last_relro())) + break; + } + + pdl->insert(p, os); + return; } - pdl->insert(p, os); - return; + // One section may be marked as the first section which follows + // the relro sections. + if (os->is_first_non_relro()) + { + gold_assert(pdl == &this->output_data_); + Output_segment::Output_data_list::iterator p; + for (p = pdl->begin(); p != pdl->end(); ++p) + { + if (!(*p)->is_section()) + break; + + Output_section* pos = (*p)->output_section(); + if (!pos->is_relro()) + break; + } + + pdl->insert(p, os); + return; + } } // Small data sections go at the end of the list of data sections. @@ -3365,19 +3393,6 @@ Output_segment::maximum_alignment() if (addralign > this->max_align_) this->max_align_ = addralign; - // If -z relro is in effect, and the first section in this - // segment is a relro section, then the segment must be aligned - // to at least the common page size. This ensures that the - // PT_GNU_RELRO segment will start at a page boundary. - if (this->type_ == elfcpp::PT_LOAD - && parameters->options().relro() - && this->is_first_section_relro()) - { - addralign = parameters->target().common_pagesize(); - if (addralign > this->max_align_) - this->max_align_ = addralign; - } - this->is_max_align_known_ = true; } @@ -3431,11 +3446,57 @@ Output_segment::dynamic_reloc_count_list(const Output_data_list* pdl) const uint64_t Output_segment::set_section_addresses(const Layout* layout, bool reset, - uint64_t addr, off_t* poff, + uint64_t addr, + unsigned int increase_relro, + off_t* poff, unsigned int* pshndx) { gold_assert(this->type_ == elfcpp::PT_LOAD); + off_t orig_off = *poff; + + // If we have relro sections, we need to pad forward now so that the + // relro sections plus INCREASE_RELRO end on a common page boundary. + if (parameters->options().relro() + && this->is_first_section_relro() + && (!this->are_addresses_set_ || reset)) + { + uint64_t relro_size = 0; + off_t off = *poff; + for (Output_data_list::iterator p = this->output_data_.begin(); + p != this->output_data_.end(); + ++p) + { + if (!(*p)->is_section()) + break; + Output_section* pos = (*p)->output_section(); + if (!pos->is_relro()) + break; + gold_assert(!(*p)->is_section_flag_set(elfcpp::SHF_TLS)); + if ((*p)->is_address_valid()) + relro_size += (*p)->data_size(); + else + { + // FIXME: This could be faster. + (*p)->set_address_and_file_offset(addr + relro_size, + off + relro_size); + relro_size += (*p)->data_size(); + (*p)->reset_address_and_file_offset(); + } + } + relro_size += increase_relro; + + uint64_t page_align = parameters->target().common_pagesize(); + + // Align to offset N such that (N + RELRO_SIZE) % PAGE_ALIGN == 0. + uint64_t desired_align = page_align - (relro_size % page_align); + if (desired_align < *poff % page_align) + *poff += page_align - *poff % page_align; + *poff += desired_align - *poff % page_align; + addr += *poff - orig_off; + orig_off = *poff; + } + if (!reset && this->are_addresses_set_) { gold_assert(this->paddr_ == addr); @@ -3450,15 +3511,10 @@ Output_segment::set_section_addresses(const Layout* layout, bool reset, bool in_tls = false; - bool in_relro = (parameters->options().relro() - && this->is_first_section_relro()); - - off_t orig_off = *poff; this->offset_ = orig_off; addr = this->set_section_list_addresses(layout, reset, &this->output_data_, - addr, poff, pshndx, &in_tls, - &in_relro); + addr, poff, pshndx, &in_tls); this->filesz_ = *poff - orig_off; off_t off = *poff; @@ -3466,7 +3522,7 @@ Output_segment::set_section_addresses(const Layout* layout, bool reset, uint64_t ret = this->set_section_list_addresses(layout, reset, &this->output_bss_, addr, poff, pshndx, - &in_tls, &in_relro); + &in_tls); // If the last section was a TLS section, align upward to the // alignment of the TLS segment, so that the overall size of the TLS @@ -3477,14 +3533,6 @@ Output_segment::set_section_addresses(const Layout* layout, bool reset, *poff = align_address(*poff, segment_align); } - // If all the sections were relro sections, align upward to the - // common page size. - if (in_relro) - { - uint64_t page_align = parameters->target().common_pagesize(); - *poff = align_address(*poff, page_align); - } - this->memsz_ = *poff - orig_off; // Ignore the file offset adjustments made by the BSS Output_data @@ -3502,7 +3550,7 @@ Output_segment::set_section_list_addresses(const Layout* layout, bool reset, Output_data_list* pdl, uint64_t addr, off_t* poff, unsigned int* pshndx, - bool* in_tls, bool* in_relro) + bool* in_tls) { off_t startoff = *poff; @@ -3553,19 +3601,6 @@ Output_segment::set_section_list_addresses(const Layout* layout, bool reset, } } - // If this is a non-relro section after a relro section, - // align it to a common page boundary so that the dynamic - // linker has a page to mark as read-only. - if (*in_relro - && (!(*p)->is_section() - || !(*p)->output_section()->is_relro())) - { - uint64_t page_align = parameters->target().common_pagesize(); - if (page_align > align) - align = page_align; - *in_relro = false; - } - off = align_address(off, align); (*p)->set_address_and_file_offset(addr + (off - startoff), off); } @@ -3621,10 +3656,10 @@ Output_segment::set_section_list_addresses(const Layout* layout, bool reset, } // For a non-PT_LOAD segment, set the offset from the sections, if -// any. +// any. Add INCREASE to the file size and the memory size. void -Output_segment::set_offset() +Output_segment::set_offset(unsigned int increase) { gold_assert(this->type_ != elfcpp::PT_LOAD); @@ -3632,6 +3667,7 @@ Output_segment::set_offset() if (this->output_data_.empty() && this->output_bss_.empty()) { + gold_assert(increase == 0); this->vaddr_ = 0; this->paddr_ = 0; this->are_addresses_set_ = true; @@ -3673,6 +3709,9 @@ Output_segment::set_offset() + last->data_size() - this->vaddr_); + this->filesz_ += increase; + this->memsz_ += increase; + // If this is a TLS segment, align the memory size. The code in // set_section_list ensures that the section after the TLS segment // is aligned to give us room. @@ -3682,16 +3721,6 @@ Output_segment::set_offset() gold_assert(this->vaddr_ == align_address(this->vaddr_, segment_align)); this->memsz_ = align_address(this->memsz_, segment_align); } - - // If this is a RELRO segment, align the memory size. The code in - // set_section_list ensures that the section after the RELRO segment - // is aligned to give us room. - if (this->type_ == elfcpp::PT_GNU_RELRO) - { - uint64_t page_align = parameters->target().common_pagesize(); - gold_assert(this->vaddr_ == align_address(this->vaddr_, page_align)); - this->memsz_ = align_address(this->memsz_, page_align); - } } // Set the TLS offsets of the sections in the PT_TLS segment. diff --git a/gold/output.h b/gold/output.h index 55e278b..7ce3202 100644 --- a/gold/output.h +++ b/gold/output.h @@ -2351,6 +2351,35 @@ class Output_section : public Output_data set_is_relro_local() { this->is_relro_local_ = true; } + // True if this must be the last relro section. + bool + is_last_relro() const + { return this->is_last_relro_; } + + // Record that this must be the last relro section. + void + set_is_last_relro() + { + gold_assert(this->is_relro_); + this->is_last_relro_ = true; + } + + // True if this must be the first section following the relro sections. + bool + is_first_non_relro() const + { + gold_assert(!this->is_relro_); + return this->is_first_non_relro_; + } + + // Record that this must be the first non-relro section. + void + set_is_first_non_relro() + { + gold_assert(!this->is_relro_); + this->is_first_non_relro_ = true; + } + // True if this is a small section: a section which holds small // variables. bool @@ -3341,6 +3370,10 @@ class Output_section : public Output_data bool is_relro_ : 1; // True if this section holds relro local data. bool is_relro_local_ : 1; + // True if this must be the last relro section. + bool is_last_relro_ : 1; + // True if this must be the first section after the relro sections. + bool is_first_non_relro_ : 1; // True if this is a small section. bool is_small_section_ : 1; // True if this is a large section. @@ -3497,7 +3530,8 @@ class Output_segment // address of the immediately following segment. Update *POFF and // *PSHNDX. This should only be called for a PT_LOAD segment. uint64_t - set_section_addresses(const Layout*, bool reset, uint64_t addr, off_t* poff, + set_section_addresses(const Layout*, bool reset, uint64_t addr, + unsigned int increase_relro, off_t* poff, unsigned int* pshndx); // Set the minimum alignment of this segment. This may be adjusted @@ -3509,7 +3543,7 @@ class Output_segment // Set the offset of this segment based on the section. This should // only be called for a non-PT_LOAD segment. void - set_offset(); + set_offset(unsigned int increase); // Set the TLS offsets of the sections contained in the PT_TLS segment. void @@ -3555,7 +3589,7 @@ class Output_segment uint64_t set_section_list_addresses(const Layout*, bool reset, Output_data_list*, uint64_t addr, off_t* poff, unsigned int* pshndx, - bool* in_tls, bool* in_relro); + bool* in_tls); // Return the number of Output_sections in an Output_data_list. unsigned int diff --git a/gold/powerpc.cc b/gold/powerpc.cc index f27a9f1..f7e62e7 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -716,7 +716,7 @@ Target_powerpc::got_section(Symbol_table* symtab, layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got_, false); + this->got_, false, false, false, false); // Create the GOT2 or TOC in the .got section. if (size == 32) @@ -725,7 +725,8 @@ Target_powerpc::got_section(Symbol_table* symtab, layout->add_output_section_data(".got2", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got2_, false); + this->got2_, false, false, false, + false); } else { @@ -733,7 +734,8 @@ Target_powerpc::got_section(Symbol_table* symtab, layout->add_output_section_data(".toc", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->toc_, false); + this->toc_, false, false, false, + false); } // Define _GLOBAL_OFFSET_TABLE_ at the start of the .got section. @@ -760,7 +762,8 @@ Target_powerpc::rela_dyn_section(Layout* layout) 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_, true); + elfcpp::SHF_ALLOC, this->rela_dyn_, true, + false, false, false); } return this->rela_dyn_; } @@ -820,7 +823,8 @@ Output_data_plt_powerpc::Output_data_plt_powerpc(Layout* layou { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, - elfcpp::SHF_ALLOC, this->rel_, true); + elfcpp::SHF_ALLOC, this->rel_, true, false, + false, false); } template @@ -949,7 +953,7 @@ Target_powerpc::make_plt_entry(Symbol_table* symtab, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR | elfcpp::SHF_WRITE), - this->plt_, false); + this->plt_, false, false, false, false); // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section. symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL, @@ -1512,7 +1516,8 @@ Target_powerpc::scan_relocs( Output_section* os = layout->add_output_section_data(".sdata", 0, elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - sdata, false); + sdata, false, + false, false, false); symtab->define_in_output_data("_SDA_BASE_", NULL, Symbol_table::PREDEFINED, os, diff --git a/gold/sparc.cc b/gold/sparc.cc index 9e1e666..841db2f 100644 --- a/gold/sparc.cc +++ b/gold/sparc.cc @@ -1026,8 +1026,8 @@ Target_sparc::got_section(Symbol_table* symtab, os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_, false); - os->set_is_relro(); + this->got_, false, true, false, + false); // Define _GLOBAL_OFFSET_TABLE_ at the start of the .got section. symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, @@ -1053,7 +1053,8 @@ Target_sparc::rela_dyn_section(Layout* layout) 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_, true); + elfcpp::SHF_ALLOC, this->rela_dyn_, true, + false, false, false); } return this->rela_dyn_; } @@ -1155,7 +1156,8 @@ Output_data_plt_sparc::Output_data_plt_sparc(Layout* layout) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, - elfcpp::SHF_ALLOC, this->rel_, true); + elfcpp::SHF_ALLOC, this->rel_, true, + false, false, false); } template @@ -1372,7 +1374,7 @@ Target_sparc::make_plt_entry(Symbol_table* symtab, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR | elfcpp::SHF_WRITE), - this->plt_, false); + this->plt_, false, false, false, false); // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section. symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL, diff --git a/gold/testsuite/relro_script_test.t b/gold/testsuite/relro_script_test.t index cacb84d..3a6e3e9 100644 --- a/gold/testsuite/relro_script_test.t +++ b/gold/testsuite/relro_script_test.t @@ -46,6 +46,8 @@ SECTIONS . = DATA_SEGMENT_RELRO_END(0, .); + .got.plt : { *(.got.plt) } + .data : { *(.data .data.* .gnu.linkonce.d.*) } . = DATA_SEGMENT_END (.); diff --git a/gold/x86_64.cc b/gold/x86_64.cc index c1597c1..4635799 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -469,23 +469,22 @@ Target_x86_64::got_section(Symbol_table* symtab, Layout* layout) os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_, false); - os->set_is_relro(); + this->got_, false, true, true, + false); - // 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_space(8, "** GOT PLT"); - os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + os = layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_plt_, false); - os->set_is_relro(); + this->got_plt_, false, false, + false, true); // The first three entries are reserved. this->got_plt_->set_current_data_size(3 * 8); + // Those bytes can go into the relro segment. + layout->increase_relro(3 * 8); + // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, Symbol_table::PREDEFINED, @@ -509,7 +508,8 @@ Target_x86_64::rela_dyn_section(Layout* layout) 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_, true); + elfcpp::SHF_ALLOC, this->rela_dyn_, true, + false, false, false); } return this->rela_dyn_; } @@ -609,7 +609,8 @@ Output_data_plt_x86_64::Output_data_plt_x86_64(Layout* layout, { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, - elfcpp::SHF_ALLOC, this->rel_, true); + elfcpp::SHF_ALLOC, this->rel_, true, + false, false, false); } void @@ -805,7 +806,7 @@ Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout) layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), - this->plt_, false); + this->plt_, false, false, false, false); } } -- 2.7.4