OSDN Git Service

* options.h (class General_options): Add -z relro.
authorIan Lance Taylor <ian@airs.com>
Tue, 20 May 2008 04:00:47 +0000 (04:00 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 20 May 2008 04:00:47 +0000 (04:00 +0000)
* 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.

13 files changed:
gold/ChangeLog
gold/i386.cc
gold/layout.cc
gold/layout.h
gold/options.h
gold/output.cc
gold/output.h
gold/sparc.cc
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/relro_test.cc [new file with mode: 0644]
gold/testsuite/relro_test_main.cc [new file with mode: 0644]
gold/x86_64.cc

index 1012610..bd4db9d 100644 (file)
@@ -1,3 +1,54 @@
+2008-05-19  Ian Lance Taylor  <iant@google.com>
+
+       * 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  <iant@google.com>
 
        * output.cc (Output_segment::add_output_section): Remove front
index 1d04f4f..4785fa6 100644 (file)
@@ -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);
index 07e9056..eae6679 100644 (file)
@@ -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<size, big_endian>* 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)
     {
index 2a173d7..ede2604 100644 (file)
@@ -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.
index bfb40c7..9a474df 100644 (file)
@@ -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;
index 5459a57..b5791f4 100644 (file)
@@ -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.
index b4dbbd1..a8f2f39 100644 (file)
@@ -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
index 0472fa1..13a069e 100644 (file)
@@ -1001,9 +1001,12 @@ Target_sparc<size, big_endian>::got_section(Symbol_table* symtab,
 
       this->got_ = new Output_data_got<size, big_endian>();
 
-      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,
index 75c9c92..ab9294e 100644 (file)
@@ -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
index 5231c98..931d043 100644 (file)
@@ -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 (file)
index 0000000..d1bd9dd
--- /dev/null
@@ -0,0 +1,114 @@
+// relro_test.cc -- test -z relro for gold
+
+// Copyright 2008 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// 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 <cassert>
+#include <csignal>
+#include <stdint.h>
+#include <unistd.h>
+
+// 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<void*>(&i1);
+  void* i2addr = static_cast<void*>(&i2);
+  const void* p1addr = static_cast<const void*>(&p1);
+  const void* p2addr = static_cast<const void*>(&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<uintptr_t>(i1addr) & ~ (page_size - 1);
+  uintptr_t i2page = reinterpret_cast<uintptr_t>(i2addr) & ~ (page_size - 1);
+  uintptr_t p1page = reinterpret_cast<uintptr_t>(p1addr) & ~ (page_size - 1);
+  uintptr_t p2page = reinterpret_cast<uintptr_t>(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<int**>(&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 (file)
index 0000000..6f5ea2b
--- /dev/null
@@ -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 <iant@google.com>.
+
+// 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 <cassert>
+
+extern bool t1();
+extern bool t2();
+
+int
+main()
+{
+  assert(t1());
+  assert(t2());
+}
index b7d4933..f523ae9 100644 (file)
@@ -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);