From 9f1d377b33ab688f86e1cc9a454d87f991d65f19 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 20 May 2008 04:00:47 +0000 Subject: [PATCH] * options.h (class General_options): Add -z relro. * layout.cc (Layout::Layout): Initialize relro_segment_. (Layout::add_output_section_data): Return the output section. (Layout::make_output_section): Rcognize relro sections and mark them appropriately. (Layout::attach_allocated_section_to_segment): Put relro sections in a PT_GNU_RELRO segment. (Layout::create_initial_dynamic_sections): Mark the .dynamic section as relro. (Layout::segment_precedes): Sort PT_GNU_RELRO segments after PT_TLS segments. (Layout::linkonce_mapping): Map d.rel.ro.local to .data.rel.ro.local. (Layout::output_section_name): Us .data.rel.ro.local for any section which begins with that. * layout.h (class Layout): Update add_output_section_data declaration. Add relro_segment_ field. * output.cc (Output_section::Output_section): Initialize is_relro_ and is_relro_local_ fields. (Output_segment::add_output_section): Group relro sections. (Output_segment::is_first_section_relro): New function. (Output_segment::maximum_alignment): If there is a relro section, align the segment to the common page size. (Output_segment::set_section_addresses): Track whether we are looking at relro sections. If the last section is a relro section, align to the common page size. (Output_segment::set_section_list_addresses): Add in_relro parameter. Change all callers. Align to the page size when moving from relro to non-relro section. (Output_segment::set_offset): Align memsz of a PT_GNU_RELRO segment. * output.h (class Output_section): Add is_relro_ and is_relro_local_ fields. (Output_section::is_relro): New function. (Output_section::set_is_relro): New function. (Output_section::is_relro_local): New function. (Output_section::set_is_relro_local): New function. (class Output_segment): Update declarations. * i386.cc (Target_i386::got_section): Mark .got section as relro. * sparc.cc (Target_sparc::got_section): Likewise. * x86_64.cc (Target_x86_64::got_section): Likewise. * testsuite/relro_test_main.cc: New file. * testsuite/relro_test.cc: New file. * testsuite/Makefile.am (check_PROGRAMS): Add relro_test. (relro_test_SOURCES, relro_test_DEPENDENCIES): New variables. (relro_test_LDFLAGS, relro_test_LDADD): New variables. (relro_test.so, relro_test_pic.o): New targets. * testsuite/Makefile.in: Rebuild. --- gold/ChangeLog | 51 +++++++++++++ gold/i386.cc | 17 +++-- gold/layout.cc | 72 ++++++++++++++++--- gold/layout.h | 4 +- gold/options.h | 3 + gold/output.cc | 88 +++++++++++++++++++++-- gold/output.h | 33 ++++++++- gold/sparc.cc | 9 ++- gold/testsuite/Makefile.am | 10 +++ gold/testsuite/Makefile.in | 31 ++++++-- gold/testsuite/relro_test.cc | 114 ++++++++++++++++++++++++++++++ gold/testsuite/relro_test_main.cc | 33 +++++++++ gold/x86_64.cc | 17 +++-- 13 files changed, 446 insertions(+), 36 deletions(-) create mode 100644 gold/testsuite/relro_test.cc create mode 100644 gold/testsuite/relro_test_main.cc diff --git a/gold/ChangeLog b/gold/ChangeLog index 10126102d0c..bd4db9de4c6 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,54 @@ +2008-05-19 Ian Lance Taylor + + * options.h (class General_options): Add -z relro. + * layout.cc (Layout::Layout): Initialize relro_segment_. + (Layout::add_output_section_data): Return the output section. + (Layout::make_output_section): Rcognize relro sections and mark + them appropriately. + (Layout::attach_allocated_section_to_segment): Put relro sections + in a PT_GNU_RELRO segment. + (Layout::create_initial_dynamic_sections): Mark the .dynamic + section as relro. + (Layout::segment_precedes): Sort PT_GNU_RELRO segments after + PT_TLS segments. + (Layout::linkonce_mapping): Map d.rel.ro.local to + .data.rel.ro.local. + (Layout::output_section_name): Us .data.rel.ro.local for any + section which begins with that. + * layout.h (class Layout): Update add_output_section_data + declaration. Add relro_segment_ field. + * output.cc (Output_section::Output_section): Initialize is_relro_ + and is_relro_local_ fields. + (Output_segment::add_output_section): Group relro sections. + (Output_segment::is_first_section_relro): New function. + (Output_segment::maximum_alignment): If there is a relro section, + align the segment to the common page size. + (Output_segment::set_section_addresses): Track whether we are + looking at relro sections. If the last section is a relro + section, align to the common page size. + (Output_segment::set_section_list_addresses): Add in_relro + parameter. Change all callers. Align to the page size when + moving from relro to non-relro section. + (Output_segment::set_offset): Align memsz of a PT_GNU_RELRO + segment. + * output.h (class Output_section): Add is_relro_ and + is_relro_local_ fields. + (Output_section::is_relro): New function. + (Output_section::set_is_relro): New function. + (Output_section::is_relro_local): New function. + (Output_section::set_is_relro_local): New function. + (class Output_segment): Update declarations. + * i386.cc (Target_i386::got_section): Mark .got section as relro. + * sparc.cc (Target_sparc::got_section): Likewise. + * x86_64.cc (Target_x86_64::got_section): Likewise. + * testsuite/relro_test_main.cc: New file. + * testsuite/relro_test.cc: New file. + * testsuite/Makefile.am (check_PROGRAMS): Add relro_test. + (relro_test_SOURCES, relro_test_DEPENDENCIES): New variables. + (relro_test_LDFLAGS, relro_test_LDADD): New variables. + (relro_test.so, relro_test_pic.o): New targets. + * testsuite/Makefile.in: Rebuild. + 2008-05-16 Ian Lance Taylor * output.cc (Output_segment::add_output_section): Remove front diff --git a/gold/i386.cc b/gold/i386.cc index 1d04f4f9c39..4785fa66c21 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -426,18 +426,23 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) this->got_ = new Output_data_got<32, false>(); - layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got_); + Output_section* os; + os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_); + os->set_is_relro(); // 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); - layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got_plt_); + os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_plt_); + os->set_is_relro(); // The first three entries are reserved. this->got_plt_->set_current_data_size(3 * 4); diff --git a/gold/layout.cc b/gold/layout.cc index 07e905642e7..eae66794eb5 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -90,6 +90,7 @@ Layout::Layout(const General_options& options, Script_options* script_options) special_output_list_(), section_headers_(NULL), tls_segment_(NULL), + relro_segment_(NULL), symtab_section_(NULL), symtab_xindex_(NULL), dynsym_section_(NULL), @@ -637,9 +638,10 @@ Layout::layout_eh_frame(Sized_relobj* object, return os; } -// Add POSD to an output section using NAME, TYPE, and FLAGS. +// Add POSD to an output section using NAME, TYPE, and FLAGS. Return +// the output section. -void +Output_section* Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, Output_section_data* posd) @@ -648,6 +650,7 @@ Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type, false); if (os != NULL) os->add_output_section_data(posd); + return os; } // Map section flags to segment flags. @@ -706,6 +709,23 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type, || strcmp(name, ".fini_array") == 0)) os->set_may_sort_attached_input_sections(); + // With -z relro, we have to recognize the special sections by name. + // There is no other way. + if (!this->script_options_->saw_sections_clause() + && parameters->options().relro() + && type == elfcpp::SHT_PROGBITS + && (flags & elfcpp::SHF_ALLOC) != 0 + && (flags & elfcpp::SHF_WRITE) != 0) + { + if (strcmp(name, ".data.rel.ro") == 0) + os->set_is_relro(); + else if (strcmp(name, ".data.rel.ro.local") == 0) + { + os->set_is_relro(); + os->set_is_relro_local(); + } + } + // If we have already attached the sections to segments, then we // need to attach this one now. This happens for sections created // directly by the linker. @@ -831,6 +851,17 @@ Layout::attach_allocated_section_to_segment(Output_section* os) seg_flags); this->tls_segment_->add_output_section(os, seg_flags); } + + // If -z relro is in effect, and we see a relro section, we create a + // PT_GNU_RELRO segment. There can only be one such segment. + if (os->is_relro() && parameters->options().relro()) + { + gold_assert(seg_flags == (elfcpp::PF_R | elfcpp::PF_W)); + if (this->relro_segment_ == NULL) + this->relro_segment_ = this->make_output_segment(elfcpp::PT_GNU_RELRO, + seg_flags); + this->relro_segment_->add_output_section(os, seg_flags); + } } // Make an output section for a script. @@ -901,6 +932,7 @@ Layout::create_initial_dynamic_sections(Symbol_table* symtab) (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), false); + this->dynamic_section_->set_is_relro(); symtab->define_in_output_data("_DYNAMIC", NULL, this->dynamic_section_, 0, 0, elfcpp::STT_OBJECT, elfcpp::STB_LOCAL, @@ -1508,12 +1540,25 @@ Layout::segment_precedes(const Output_segment* seg1, if (type2 == elfcpp::PT_LOAD && type1 != elfcpp::PT_LOAD) return false; - // We put the PT_TLS segment last, because that is where the dynamic - // linker expects to find it (this is just for efficiency; other - // positions would also work correctly). - if (type1 == elfcpp::PT_TLS && type2 != elfcpp::PT_TLS) + // We put the PT_TLS segment last except for the PT_GNU_RELRO + // segment, because that is where the dynamic linker expects to find + // it (this is just for efficiency; other positions would also work + // correctly). + if (type1 == elfcpp::PT_TLS + && type2 != elfcpp::PT_TLS + && type2 != elfcpp::PT_GNU_RELRO) + return false; + if (type2 == elfcpp::PT_TLS + && type1 != elfcpp::PT_TLS + && type1 != elfcpp::PT_GNU_RELRO) + return true; + + // We put the PT_GNU_RELRO segment last, because that is where the + // dynamic linker expects to find it (as with PT_TLS, this is just + // for efficiency). + if (type1 == elfcpp::PT_GNU_RELRO && type2 != elfcpp::PT_GNU_RELRO) return false; - if (type2 == elfcpp::PT_TLS && type1 != elfcpp::PT_TLS) + if (type2 == elfcpp::PT_GNU_RELRO && type1 != elfcpp::PT_GNU_RELRO) return true; const elfcpp::Elf_Word flags1 = seg1->flags(); @@ -2634,7 +2679,8 @@ Layout::finish_dynamic_section(const Input_objects* input_objects, #define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t, sizeof(t) - 1 } const Layout::Linkonce_mapping Layout::linkonce_mapping[] = { - MAPPING_INIT("d.rel.ro", ".data.rel.ro"), // Must be before "d". + MAPPING_INIT("d.rel.ro.local", ".data.rel.ro.local"), // Before "d.rel.ro". + MAPPING_INIT("d.rel.ro", ".data.rel.ro"), // Before "d". MAPPING_INIT("t", ".text"), MAPPING_INIT("r", ".rodata"), MAPPING_INIT("d", ".data"), @@ -2736,6 +2782,9 @@ Layout::output_section_name(const char* name, size_t* plen) // initial '.', we use the name unchanged (i.e., "mysection" and // ".text" are unchanged). + // If the name starts with ".data.rel.ro.local" we use + // ".data.rel.ro.local". + // If the name starts with ".data.rel.ro" we use ".data.rel.ro". // Otherwise, we drop the second '.' and everything that comes after @@ -2749,6 +2798,13 @@ Layout::output_section_name(const char* name, size_t* plen) if (sdot == NULL) return name; + const char* const data_rel_ro_local = ".data.rel.ro.local"; + if (strncmp(name, data_rel_ro_local, strlen(data_rel_ro_local)) == 0) + { + *plen = strlen(data_rel_ro_local); + return data_rel_ro_local; + } + const char* const data_rel_ro = ".data.rel.ro"; if (strncmp(name, data_rel_ro, strlen(data_rel_ro)) == 0) { diff --git a/gold/layout.h b/gold/layout.h index 2a173d700f5..ede2604cb9c 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -158,7 +158,7 @@ class Layout // Add an Output_section_data to the layout. This is used for // special sections like the GOT section. - void + Output_section* add_output_section_data(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags, Output_section_data*); @@ -636,6 +636,8 @@ class Layout Output_section_headers* section_headers_; // A pointer to the PT_TLS segment if there is one. Output_segment* tls_segment_; + // A pointer to the PT_GNU_RELRO segment if there is one. + Output_segment* relro_segment_; // 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/options.h b/gold/options.h index bfb40c74757..9a474df5c4e 100644 --- a/gold/options.h +++ b/gold/options.h @@ -811,6 +811,9 @@ class General_options DEFINE_bool(nodump, options::DASH_Z, '\0', false, N_("Mark DSO not available to dldump"), NULL); + DEFINE_bool(relro, options::DASH_Z, '\0', false, + N_("Where possible mark variables read-only after relocation"), + N_("Don't mark variables read-only after relocation")); public: typedef options::Dir_list Dir_list; diff --git a/gold/output.cc b/gold/output.cc index 5459a574bf1..b5791f4db64 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -1737,6 +1737,8 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, may_sort_attached_input_sections_(false), must_sort_attached_input_sections_(false), attached_input_sections_are_sorted_(false), + is_relro_(false), + is_relro_local_(false), tls_offset_(0) { // An unallocated section has no address. Forcing this means that @@ -2645,7 +2647,7 @@ Output_segment::add_output_section(Output_section* os, { sawtls = true; // Put a NOBITS section after the first TLS section. - // But a PROGBITS section after the first TLS/PROGBITS + // Put a PROGBITS section after the first TLS/PROGBITS // section. insert = nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS); } @@ -2669,6 +2671,28 @@ 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()) + { + 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; + } + + pdl->insert(p, os); + return; + } + pdl->push_back(os); } @@ -2703,6 +2727,16 @@ Output_segment::add_initial_output_data(Output_data* od) this->output_data_.push_front(od); } +// Return whether the first data section is a relro section. + +bool +Output_segment::is_first_section_relro() const +{ + return (!this->output_data_.empty() + && this->output_data_.front()->is_section() + && this->output_data_.front()->output_section()->is_relro()); +} + // Return the maximum alignment of the Output_data in Output_segment. uint64_t @@ -2720,6 +2754,17 @@ 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 (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; } @@ -2792,11 +2837,15 @@ 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); + addr, poff, pshndx, &in_tls, + &in_relro); this->filesz_ = *poff - orig_off; off_t off = *poff; @@ -2804,7 +2853,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_tls, &in_relro); // 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 @@ -2815,6 +2864,14 @@ 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 @@ -2832,7 +2889,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_tls, bool* in_relro) { off_t startoff = *poff; @@ -2883,6 +2940,19 @@ 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); } @@ -2976,6 +3046,16 @@ 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 b4dbbd18f8d..a8f2f39017d 100644 --- a/gold/output.h +++ b/gold/output.h @@ -2028,6 +2028,29 @@ class Output_section : public Output_data set_must_sort_attached_input_sections() { this->must_sort_attached_input_sections_ = true; } + // Return whether this section holds relro data--data which has + // dynamic relocations but which may be marked read-only after the + // dynamic relocations have been completed. + bool + is_relro() const + { return this->is_relro_; } + + // Record that this section holds relro data. + void + set_is_relro() + { this->is_relro_ = true; } + + // True if this section holds relro local data--relro data for which + // the dynamic relocations are all RELATIVE relocations. + bool + is_relro_local() const + { return this->is_relro_local_; } + + // Record that this section holds relro local data. + void + set_is_relro_local() + { this->is_relro_local_ = true; } + // Return whether this section should be written after all the input // sections are complete. bool @@ -2626,6 +2649,10 @@ class Output_section : public Output_data // True if the input sections attached to this output section have // already been sorted. bool attached_input_sections_are_sorted_ : 1; + // True if this section holds relro data. + bool is_relro_ : 1; + // True if this section holds relro local data. + bool is_relro_local_ : 1; // For SHT_TLS sections, the offset of this section relative to the base // of the TLS segment. uint64_t tls_offset_; @@ -2785,11 +2812,15 @@ class Output_segment static uint64_t maximum_alignment_list(const Output_data_list*); + // Return whether the first data section is a relro section. + bool + is_first_section_relro() const; + // Set the section addresses in an Output_data_list. 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_tls, bool* in_relro); // Return the number of Output_sections in an Output_data_list. unsigned int diff --git a/gold/sparc.cc b/gold/sparc.cc index 0472fa14c9f..13a069ec4cf 100644 --- a/gold/sparc.cc +++ b/gold/sparc.cc @@ -1001,9 +1001,12 @@ Target_sparc::got_section(Symbol_table* symtab, this->got_ = new Output_data_got(); - layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got_); + Output_section* os; + os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_); + os->set_is_relro(); // Define _GLOBAL_OFFSET_TABLE_ at the start of the .got section. symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index 75c9c92eb5d..ab9294e73e4 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -782,6 +782,16 @@ protected_2_DEPENDENCIES = gcctestdir/ld protected_1.so protected_2_LDFLAGS = -Bgcctestdir/ -Wl,-R,. protected_2_LDADD = protected_1.so +check_PROGRAMS += relro_test +relro_test_SOURCES = relro_test_main.cc +relro_test_DEPENDENCIES = gcctestdir/ld relro_test.so +relro_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. +relro_test_LDADD = relro_test.so +relro_test.so: gcctestdir/ld relro_test_pic.o + $(CXXLINK) -Bgcctestdir/ -shared -Wl,-z,relro relro_test_pic.o +relro_test_pic.o: relro_test.cc + $(CXXCOMPILE) -c -fpic -o $@ $< + check_PROGRAMS += script_test_1 script_test_1_SOURCES = script_test_1.cc script_test_1_DEPENDENCIES = gcctestdir/ld script_test_1.t diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index 5231c983ea9..931d04348fa 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -228,9 +228,9 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_19 = ver_test ver_test_2 \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ ver_test_6 ver_test_8 \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ protected_1 protected_2 \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_1 script_test_2 \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ justsyms binary_test \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_3 +@GCC_TRUE@@NATIVE_LINKER_TRUE@ relro_test script_test_1 \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_2 justsyms \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ binary_test script_test_3 @GCC_FALSE@script_test_1_DEPENDENCIES = libgoldtest.a ../libgold.a \ @GCC_FALSE@ ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \ @GCC_FALSE@ $(am__DEPENDENCIES_1) @@ -346,6 +346,7 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS) @GCC_TRUE@@NATIVE_LINKER_TRUE@ ver_test_8$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ protected_1$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ protected_2$(EXEEXT) \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ relro_test$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_1$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_2$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ justsyms$(EXEEXT) \ @@ -514,6 +515,10 @@ am__protected_2_SOURCES_DIST = protected_main_1.cc protected_3.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@ protected_main_1.$(OBJEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ protected_3.$(OBJEXT) protected_2_OBJECTS = $(am_protected_2_OBJECTS) +am__relro_test_SOURCES_DIST = relro_test_main.cc +@GCC_TRUE@@NATIVE_LINKER_TRUE@am_relro_test_OBJECTS = \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ relro_test_main.$(OBJEXT) +relro_test_OBJECTS = $(am_relro_test_OBJECTS) am__script_test_1_SOURCES_DIST = script_test_1.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@am_script_test_1_OBJECTS = \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_1.$(OBJEXT) @@ -760,9 +765,9 @@ SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \ $(initpri1_SOURCES) $(justsyms_SOURCES) many_sections_r_test.c \ $(many_sections_test_SOURCES) $(object_unittest_SOURCES) \ $(protected_1_SOURCES) $(protected_2_SOURCES) \ - $(script_test_1_SOURCES) $(script_test_2_SOURCES) \ - script_test_3.c $(tls_pic_test_SOURCES) \ - $(tls_shared_gd_to_ie_test_SOURCES) \ + $(relro_test_SOURCES) $(script_test_1_SOURCES) \ + $(script_test_2_SOURCES) script_test_3.c \ + $(tls_pic_test_SOURCES) $(tls_shared_gd_to_ie_test_SOURCES) \ $(tls_shared_gnu2_gd_to_ie_test_SOURCES) \ $(tls_shared_gnu2_test_SOURCES) $(tls_shared_ie_test_SOURCES) \ $(tls_shared_nonpic_test_SOURCES) $(tls_shared_test_SOURCES) \ @@ -808,7 +813,7 @@ DIST_SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \ $(am__initpri1_SOURCES_DIST) $(am__justsyms_SOURCES_DIST) \ many_sections_r_test.c $(am__many_sections_test_SOURCES_DIST) \ $(object_unittest_SOURCES) $(am__protected_1_SOURCES_DIST) \ - $(am__protected_2_SOURCES_DIST) \ + $(am__protected_2_SOURCES_DIST) $(am__relro_test_SOURCES_DIST) \ $(am__script_test_1_SOURCES_DIST) \ $(am__script_test_2_SOURCES_DIST) script_test_3.c \ $(am__tls_pic_test_SOURCES_DIST) \ @@ -1304,6 +1309,10 @@ binary_unittest_SOURCES = binary_unittest.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@protected_2_DEPENDENCIES = gcctestdir/ld protected_1.so @GCC_TRUE@@NATIVE_LINKER_TRUE@protected_2_LDFLAGS = -Bgcctestdir/ -Wl,-R,. @GCC_TRUE@@NATIVE_LINKER_TRUE@protected_2_LDADD = protected_1.so +@GCC_TRUE@@NATIVE_LINKER_TRUE@relro_test_SOURCES = relro_test_main.cc +@GCC_TRUE@@NATIVE_LINKER_TRUE@relro_test_DEPENDENCIES = gcctestdir/ld relro_test.so +@GCC_TRUE@@NATIVE_LINKER_TRUE@relro_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. +@GCC_TRUE@@NATIVE_LINKER_TRUE@relro_test_LDADD = relro_test.so @GCC_TRUE@@NATIVE_LINKER_TRUE@script_test_1_SOURCES = script_test_1.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@script_test_1_DEPENDENCIES = gcctestdir/ld script_test_1.t @GCC_TRUE@@NATIVE_LINKER_TRUE@script_test_1_LDFLAGS = -Bgcctestdir/ -Wl,-R,. -T $(srcdir)/script_test_1.t @@ -1468,6 +1477,9 @@ protected_1$(EXEEXT): $(protected_1_OBJECTS) $(protected_1_DEPENDENCIES) protected_2$(EXEEXT): $(protected_2_OBJECTS) $(protected_2_DEPENDENCIES) @rm -f protected_2$(EXEEXT) $(CXXLINK) $(protected_2_LDFLAGS) $(protected_2_OBJECTS) $(protected_2_LDADD) $(LIBS) +relro_test$(EXEEXT): $(relro_test_OBJECTS) $(relro_test_DEPENDENCIES) + @rm -f relro_test$(EXEEXT) + $(CXXLINK) $(relro_test_LDFLAGS) $(relro_test_OBJECTS) $(relro_test_LDADD) $(LIBS) script_test_1$(EXEEXT): $(script_test_1_OBJECTS) $(script_test_1_DEPENDENCIES) @rm -f script_test_1$(EXEEXT) $(CXXLINK) $(script_test_1_LDFLAGS) $(script_test_1_OBJECTS) $(script_test_1_LDADD) $(LIBS) @@ -1627,6 +1639,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protected_main_1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protected_main_2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protected_main_3.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/relro_test_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_2a.Po@am__quote@ @@ -2191,6 +2204,10 @@ uninstall-am: uninstall-info-am @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@protected_3_pic.o: protected_3.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $< +@GCC_TRUE@@NATIVE_LINKER_TRUE@relro_test.so: gcctestdir/ld relro_test_pic.o +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared -Wl,-z,relro relro_test_pic.o +@GCC_TRUE@@NATIVE_LINKER_TRUE@relro_test_pic.o: relro_test.cc +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@justsyms_2.o: justsyms_2.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@justsyms_2r.o: justsyms_2.o gcctestdir/ld $(srcdir)/justsyms.t diff --git a/gold/testsuite/relro_test.cc b/gold/testsuite/relro_test.cc new file mode 100644 index 00000000000..d1bd9dd1db0 --- /dev/null +++ b/gold/testsuite/relro_test.cc @@ -0,0 +1,114 @@ +// relro_test.cc -- test -z relro for gold + +// Copyright 2008 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include +#include +#include +#include + +// This code is put into a shared library linked with -z relro. + +// i1 and i2 are not relro variables. +int i1 = 1; +static int i2 = 2; + +// P1 is a global relro variable. +int* const p1 = &i1; + +// P2 is a local relro variable. +int* const p2 = &i2; + +// Test symbol addresses. + +bool +t1() +{ + void* i1addr = static_cast(&i1); + void* i2addr = static_cast(&i2); + const void* p1addr = static_cast(&p1); + const void* p2addr = static_cast(&p2); + + // The relro variables should precede the non-relro variables in the + // memory image. + assert(i1addr > p1addr); + assert(i1addr > p2addr); + assert(i2addr > p1addr); + assert(i2addr > p2addr); + + // The relro variables should not be on the same page as the + // non-relro variables. + const size_t page_size = getpagesize(); + uintptr_t i1page = reinterpret_cast(i1addr) & ~ (page_size - 1); + uintptr_t i2page = reinterpret_cast(i2addr) & ~ (page_size - 1); + uintptr_t p1page = reinterpret_cast(p1addr) & ~ (page_size - 1); + uintptr_t p2page = reinterpret_cast(p2addr) & ~ (page_size - 1); + assert(i1page != p1page); + assert(i1page != p2page); + assert(i2page != p1page); + assert(i2page != p2page); + + return true; +} + +// A signal handler for SIGSEGV. + +extern "C" +void +sigsegv_handler(int) +{ + throw 0; +} + +// Use a separate function to throw the exception, so that we don't +// need to use -fnon-call-exceptions. + +void f2() __attribute__ ((noinline)); +void +f2() +{ + int** pp1 = const_cast(&p1); + *pp1 = &i2; + + // We shouldn't get here--the assignment to *pp1 should write to + // memory which the dynamic linker marked as read-only, giving us a + // SIGSEGV, causing sigsegv_handler to be invoked, to throw past us. + assert(0); +} + +// Changing a relro variable should give us a SIGSEGV. + +bool +t2() +{ + signal(SIGSEGV, sigsegv_handler); + + try + { + f2(); + return false; + } + catch (int i) + { + assert(i == 0); + return true; + } +} diff --git a/gold/testsuite/relro_test_main.cc b/gold/testsuite/relro_test_main.cc new file mode 100644 index 00000000000..6f5ea2bb513 --- /dev/null +++ b/gold/testsuite/relro_test_main.cc @@ -0,0 +1,33 @@ +// relro_test_main.cc -- test -z relro for gold, main function + +// Copyright 2008 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include + +extern bool t1(); +extern bool t2(); + +int +main() +{ + assert(t1()); + assert(t2()); +} diff --git a/gold/x86_64.cc b/gold/x86_64.cc index b7d49337a3c..f523ae9f264 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -435,18 +435,23 @@ Target_x86_64::got_section(Symbol_table* symtab, Layout* layout) this->got_ = new Output_data_got<64, false>(); - layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got_); + Output_section* os; + os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_); + os->set_is_relro(); // 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); - layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, - this->got_plt_); + os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_plt_); + os->set_is_relro(); // The first three entries are reserved. this->got_plt_->set_current_data_size(3 * 8); -- 2.30.2