[gold patch] Fix problem with x86_64 TLS optimization when code is reordered

Cary Coutant ccoutant@google.com
Tue Sep 14 20:27:00 GMT 2010


This patch fixes a problem with link-time GD-to-LE optimization of TLS
sequences on x86_64 when code has been reordered by the compiler.

The GD-to-LE optimization for x86_64 needs to optimize a series of
three relocations: R_X86_64_TLSGD/LD, R_X86_64_GOTPC32_TLSDESC, and
R_X86_64_DTPOFF32/64. The R_X86_64_DTPOFF32/64 relocations, however,
are used in debug sections, so gold sets a flag, saw_tls_block_reloc_,
when it sees any of the first relocations in the series, and applies
the optimization on the third only if the flag is set. The compiler,
however, might rearrange the code (and has been observed to do so at
-Os) such that the R_X86_64_DTPOFF32 appears in the section before
either of the other two relocations, causing gold to leave the
R_X86_64_DTPOFF32 unoptimized. It goes on to optimize the other two
relocations, however, leaving the code in an inconsistent state that
results in a crash when executed.

This patch changes the logic so that the optimization is applied
consistently -- we always apply it in executable sections, and never
apply it in non-executable sections. As a result, the lone use of
R_X86_64_DTPOFF32/64 in a debug section works as intended, and the
code sequence in an executable section is properly optimized
regardless of static code arrangement. The code sequence in a
non-executable section will still work, but will not be optimized.

OK?

(Ian, if you prefer, I can submit the original patch we discussed
offline, which test for is_executable in addition to the existing
saw_tls_block_reloc_ flag. I prefer the stateless nature of this
patch, though, as it's simpler to verify that we apply the related
relocations consistently.)

-cary

        * x86_64.cc (Target_x86_64::saw_tls_block_reloc_): Remove member.
        (Target_x86_64::Relocate::relocate_tls): Replace check for
        saw_tls_block_reloc_ with test for executable section.
-------------- next part --------------
	* x86_64.cc (Target_x86_64::saw_tls_block_reloc_): Remove member.
	(Target_x86_64::Relocate::relocate_tls): Replace check for
	saw_tls_block_reloc_ with test for executable section.

Index: x86_64.cc
===================================================================
RCS file: /cvs/src/src/gold/x86_64.cc,v
retrieving revision 1.116
diff -u -p -r1.116 x86_64.cc
--- x86_64.cc	25 Aug 2010 08:36:54 -0000	1.116
+++ x86_64.cc	14 Sep 2010 19:07:41 -0000
@@ -420,7 +420,7 @@ class Target_x86_64 : public Target_free
   {
    public:
     Relocate()
-      : skip_call_tls_get_addr_(false), saw_tls_block_reloc_(false)
+      : skip_call_tls_get_addr_(false)
     { }
 
     ~Relocate()
@@ -511,12 +511,6 @@ class Target_x86_64 : public Target_free
     // This is set if we should skip the next reloc, which should be a
     // PLT32 reloc against ___tls_get_addr.
     bool skip_call_tls_get_addr_;
-
-    // This is set if we see a relocation which could load the address
-    // of the TLS block.  Whether we see such a relocation determines
-    // how we handle the R_X86_64_DTPOFF32 relocation, which is used
-    // in debugging sections.
-    bool saw_tls_block_reloc_;
   };
 
   // A class which returns the size required for a relocation type,
@@ -2492,18 +2486,29 @@ Target_x86_64::Relocate::relocate_tls(co
 
   const Sized_relobj<64, false>* object = relinfo->object;
   const elfcpp::Elf_Xword addend = rela.get_r_addend();
+  elfcpp::Shdr<64, false> data_shdr(relinfo->data_shdr);
+  bool is_executable = (data_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0;
 
   elfcpp::Elf_types<64>::Elf_Addr value = psymval->value(relinfo->object, 0);
 
   const bool is_final = (gsym == NULL
 			 ? !parameters->options().shared()
 			 : gsym->final_value_is_known());
-  const tls::Tls_optimization optimized_type
+  tls::Tls_optimization optimized_type
       = Target_x86_64::optimize_tls_reloc(is_final, r_type);
   switch (r_type)
     {
     case elfcpp::R_X86_64_TLSGD:            // Global-dynamic
-      this->saw_tls_block_reloc_ = true;
+      if (!is_executable && optimized_type == tls::TLSOPT_TO_LE)
+	{
+	  // If this code sequence is used in a non-executable section,
+	  // we will not optimize the R_X86_64_DTPOFF32/64 relocation,
+	  // on the assumption that it's being used by itself in a debug
+	  // section.  Therefore, in the unlikely event that the code
+	  // sequence appears in a non-executable section, we simply
+	  // leave it unoptimized.
+	  optimized_type = tls::TLSOPT_NONE;
+	}
       if (optimized_type == tls::TLSOPT_TO_LE)
 	{
 	  gold_assert(tls_segment != NULL);
@@ -2554,7 +2559,11 @@ Target_x86_64::Relocate::relocate_tls(co
 
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:  // Global-dynamic (from ~oliva url)
     case elfcpp::R_X86_64_TLSDESC_CALL:
-      this->saw_tls_block_reloc_ = true;
+      if (!is_executable && optimized_type == tls::TLSOPT_TO_LE)
+	{
+	  // See above comment for R_X86_64_TLSGD.
+	  optimized_type = tls::TLSOPT_NONE;
+	}
       if (optimized_type == tls::TLSOPT_TO_LE)
 	{
 	  gold_assert(tls_segment != NULL);
@@ -2617,7 +2626,11 @@ Target_x86_64::Relocate::relocate_tls(co
       break;
 
     case elfcpp::R_X86_64_TLSLD:            // Local-dynamic
-      this->saw_tls_block_reloc_ = true;
+      if (!is_executable && optimized_type == tls::TLSOPT_TO_LE)
+	{
+	  // See above comment for R_X86_64_TLSGD.
+	  optimized_type = tls::TLSOPT_NONE;
+	}
       if (optimized_type == tls::TLSOPT_TO_LE)
         {
           gold_assert(tls_segment != NULL);
@@ -2642,31 +2655,27 @@ Target_x86_64::Relocate::relocate_tls(co
       break;
 
     case elfcpp::R_X86_64_DTPOFF32:
-      if (optimized_type == tls::TLSOPT_TO_LE)
-        {
-          // This relocation type is used in debugging information.
-          // In that case we need to not optimize the value.  If we
-          // haven't seen a TLSLD reloc, then we assume we should not
-          // optimize this reloc.
-          if (this->saw_tls_block_reloc_)
-	    {
-              gold_assert(tls_segment != NULL);
-              value -= tls_segment->memsz();
-	    }
-        }
+      // This relocation type is used in debugging information.
+      // In that case we need to not optimize the value.  If the
+      // section is not executable, then we assume we should not
+      // optimize this reloc.  See comments above for R_X86_64_TLSGD,
+      // R_X86_64_GOTPC32_TLSDESC, R_X86_64_TLSDESC_CALL, and
+      // R_X86_64_TLSLD.
+      if (optimized_type == tls::TLSOPT_TO_LE && is_executable)
+	{
+	  gold_assert(tls_segment != NULL);
+	  value -= tls_segment->memsz();
+	}
       Relocate_functions<64, false>::rela32(view, value, addend);
       break;
 
     case elfcpp::R_X86_64_DTPOFF64:
-      if (optimized_type == tls::TLSOPT_TO_LE)
-        {
-          // See R_X86_64_DTPOFF32, just above, for why we test this.
-          if (this->saw_tls_block_reloc_)
-	    {
-	      gold_assert(tls_segment != NULL);
-	      value -= tls_segment->memsz();
-	    }
-        }
+      // See R_X86_64_DTPOFF32, just above, for why we check for is_executable.
+      if (optimized_type == tls::TLSOPT_TO_LE && is_executable)
+	{
+	  gold_assert(tls_segment != NULL);
+	  value -= tls_segment->memsz();
+	}
       Relocate_functions<64, false>::rela64(view, value, addend);
       break;
 


More information about the Binutils mailing list