Fix problem where gold cannot build .eh_frame_hdr from ld -r output.
authorCary Coutant <ccoutant@gmail.com>
Mon, 21 Mar 2016 02:15:56 +0000 (19:15 -0700)
committerCary Coutant <ccoutant@gmail.com>
Mon, 21 Mar 2016 02:17:14 +0000 (19:17 -0700)
When running ld -r on objects that have comdat groups, when gold
deduplicates a function in a comdat group, it removes the relocations
from the EH information that referred to the dropped copy of the function.
When running a final link using the result of the -r link, the missing
relocation cause it to fail to recognize the FDE for the dropped
function.

This patch improves gold's FDE scanning to take into account the
possibility that an FDE corresponds to a dropped function, and drops
that FDE as well.

Gnu ld, on the other hand, leaves the relocations in the ld -r output,
but makes them R_NONE with an r_sym field of 0. This was sufficient to
let both linkers recognize the FDE properly.

With this fix, if you do an ld -r with gold, then do the final link with
Gnu ld, the .eh_frame_hdr section will not be generated. To make it work
with Gnu ld, we would have to leave the R_NONE relocations in, but I
think it's better to drop the relocations entirely. I'd hope that if
you're doing a -r link with gold, you'll also do the final link with
gold.

gold/
PR gold/19002
* ehframe.cc (Eh_frame::read_fde): Check for dropped functions.
* testsuite/Makefile.am (eh_test_2): New test.
* testsuite/Makefile.in: Regenerate.
* testsuite/eh_test_2.sh: New test script.
* testsuite/eh_test_a.cc (bar): Make it comdat.
* testsuite/eh_test_b.cc (bar): Add a duplicate copy.

gold/ChangeLog
gold/ehframe.cc
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/eh_test_2.sh [new file with mode: 0755]
gold/testsuite/eh_test_a.cc
gold/testsuite/eh_test_b.cc

index c903192976e78b942ddec8962df861daf74eec1d..854af4bcf9f47442c99592cc3266a9a2c962ed2b 100644 (file)
@@ -1,3 +1,13 @@
+2016-03-20  Cary Coutant  <ccoutant@gmail.com>
+
+       PR gold/19002
+       * ehframe.cc (Eh_frame::read_fde): Check for dropped functions.
+       * testsuite/Makefile.am (eh_test_2): New test.
+       * testsuite/Makefile.in: Regenerate.
+       * testsuite/eh_test_2.sh: New test script.
+       * testsuite/eh_test_a.cc (bar): Make it comdat.
+       * testsuite/eh_test_b.cc (bar): Add a duplicate copy.
+
 2016-03-18  Vladimir Radosavljevic  <vladimir.radosavljevic@imgtec.com>
 
        * mips.cc (Mips_relobj::is_n64_): Remove.
index 57eb031fafc5eb114e066cd0ee5dcdd6b0daee77..e1a1e741b4213bdddadbe2abeb7f3e4a3ddb303f 100644 (file)
@@ -992,13 +992,68 @@ Eh_frame::read_fde(Sized_relobj_file<size, big_endian>* object,
     return false;
   Cie* cie = pcie->second;
 
+  int pc_size = 0;
+  switch (cie->fde_encoding() & 7)
+    {
+    case elfcpp::DW_EH_PE_udata2:
+      pc_size = 2;
+      break;
+    case elfcpp::DW_EH_PE_udata4:
+      pc_size = 4;
+      break;
+    case elfcpp::DW_EH_PE_udata8:
+      gold_assert(size == 64);
+      pc_size = 8;
+      break;
+    case elfcpp::DW_EH_PE_absptr:
+      pc_size = size == 32 ? 4 : 8;
+      break;
+    default:
+      // All other cases were rejected in Eh_frame::read_cie.
+      gold_unreachable();
+    }
+
   // The FDE should start with a reloc to the start of the code which
   // it describes.
   if (relocs->advance(pfde - pcontents) > 0)
     return false;
-
   if (relocs->next_offset() != pfde - pcontents)
-    return false;
+    {
+      // In an object produced by a relocatable link, gold may have
+      // discarded a COMDAT group in the previous link, but not the
+      // corresponding FDEs. In that case, gold will have discarded
+      // the relocations, so the FDE will have a non-relocatable zero
+      // (regardless of whether the PC encoding is absolute, pc-relative,
+      // or data-relative) instead of a pointer to the start of the code.
+
+      uint64_t pc_value = 0;
+      switch (pc_size)
+       {
+       case 2:
+         pc_value = elfcpp::Swap<16, big_endian>::readval(pfde);
+         break;
+       case 4:
+         pc_value = elfcpp::Swap<32, big_endian>::readval(pfde);
+         break;
+       case 8:
+         pc_value = elfcpp::Swap_unaligned<64, big_endian>::readval(pfde);
+         break;
+       default:
+         gold_unreachable();
+       }
+
+      if (pc_value == 0)
+       {
+         // This FDE applies to a discarded function.  We
+         // can discard this FDE.
+         object->add_merge_mapping(this, shndx, (pfde - 8) - pcontents,
+                                   pfdeend - (pfde - 8), -1);
+         return true;
+       }
+
+      // Otherwise, reject the FDE.
+      return false;
+    }
 
   unsigned int symndx = relocs->next_symndx();
   if (symndx == -1U)
@@ -1031,23 +1086,18 @@ Eh_frame::read_fde(Sized_relobj_file<size, big_endian>* object,
   // FDE corresponds to a function that was discarded during optimization
   // (too late to discard the corresponding FDE).
   uint64_t address_range = 0;
-  int pc_size = cie->fde_encoding() & 7;
-  if (pc_size == elfcpp::DW_EH_PE_absptr)
-    pc_size = size == 32 ? elfcpp::DW_EH_PE_udata4 : elfcpp::DW_EH_PE_udata8;
   switch (pc_size)
     {
-    case elfcpp::DW_EH_PE_udata2:
+    case 2:
       address_range = elfcpp::Swap<16, big_endian>::readval(pfde + 2);
       break;
-    case elfcpp::DW_EH_PE_udata4:
+    case 4:
       address_range = elfcpp::Swap<32, big_endian>::readval(pfde + 4);
       break;
-    case elfcpp::DW_EH_PE_udata8:
-      gold_assert(size == 64);
+    case 8:
       address_range = elfcpp::Swap_unaligned<64, big_endian>::readval(pfde + 8);
       break;
     default:
-      // All other cases were rejected in Eh_frame::read_cie.
       gold_unreachable();
     }
 
index f5528d12a078505b2fdc55e96042cb46b7574b25..fbaee16c5f612c40dc5a04b3f00f9d987efc90b3 100644 (file)
@@ -388,6 +388,16 @@ eh_test_b.o: eh_test_b.cc
 eh_test: eh_test_a.o eh_test_b.o  gcctestdir/ld
        $(CXXLINK_S) -Bgcctestdir/ eh_test_a.o eh_test_b.o
 
+check_SCRIPTS += eh_test_2.sh
+check_DATA += eh_test_2.sects
+MOSTLYCLEANFILES += eh_test_2.sects
+eh_test_r.o: eh_test_a.o eh_test_b.o gcctestdir/ld
+       gcctestdir/ld -r -o $@ eh_test_a.o eh_test_b.o
+eh_test_2: eh_test_r.o gcctestdir/ld
+       $(CXXLINK_S) -Bgcctestdir/ -Wl,--eh-frame-hdr eh_test_r.o
+eh_test_2.sects: eh_test_2
+       $(TEST_READELF) -SW $< >$@ 2>/dev/null
+
 if HAVE_STATIC
 check_PROGRAMS += basic_static_test
 basic_static_test: basic_test.o gcctestdir/ld
index 988d6f7c36f3e293dd3053303d7dae2498e5e683..1246504c4b87d0c47cd3d3295e5c0171268cee83 100644 (file)
@@ -72,7 +72,8 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_string_merge_test.sh \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_sht_rel_addend_test.sh \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ merge_string_literals.sh \
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared.sh weak_plt.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ eh_test_2.sh two_file_shared.sh \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ weak_plt.sh
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_3 = incremental_test.stdout \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_comdat_test.stdout \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_tls_test.stdout \
@@ -95,6 +96,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_string_merge_test.stdout \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_sht_rel_addend_test.stdout \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ merge_string_literals.stdout \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ eh_test_2.sects \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared.dbg \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ weak_plt_shared.so
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_4 = incremental_test \
@@ -119,6 +121,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_string_merge_test \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_sht_rel_addend_test \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ merge_string_literals \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ eh_test_2.sects \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared.dbg \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ alt/weak_undef_lib.so \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ libweak_undef_2.a
@@ -4496,6 +4499,8 @@ icf_sht_rel_addend_test.sh.log: icf_sht_rel_addend_test.sh
        @p='icf_sht_rel_addend_test.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 merge_string_literals.sh.log: merge_string_literals.sh
        @p='merge_string_literals.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
+eh_test_2.sh.log: eh_test_2.sh
+       @p='eh_test_2.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 two_file_shared.sh.log: two_file_shared.sh
        @p='two_file_shared.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 weak_plt.sh.log: weak_plt.sh
@@ -5295,6 +5300,12 @@ uninstall-am:
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -o $@ $<
 @GCC_TRUE@@NATIVE_LINKER_TRUE@eh_test: eh_test_a.o eh_test_b.o  gcctestdir/ld
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK_S) -Bgcctestdir/ eh_test_a.o eh_test_b.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@eh_test_r.o: eh_test_a.o eh_test_b.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ gcctestdir/ld -r -o $@ eh_test_a.o eh_test_b.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@eh_test_2: eh_test_r.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK_S) -Bgcctestdir/ -Wl,--eh-frame-hdr eh_test_r.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@eh_test_2.sects: eh_test_2
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -SW $< >$@ 2>/dev/null
 @GCC_TRUE@@HAVE_STATIC_TRUE@@NATIVE_LINKER_TRUE@basic_static_test: basic_test.o gcctestdir/ld
 @GCC_TRUE@@HAVE_STATIC_TRUE@@NATIVE_LINKER_TRUE@       $(CXXLINK) -Bgcctestdir/ -static basic_test.o
 
diff --git a/gold/testsuite/eh_test_2.sh b/gold/testsuite/eh_test_2.sh
new file mode 100755 (executable)
index 0000000..eb26854
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# eh_test_2.sh -- check that .eh_frame_hdr is valid.
+
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# Written by Cary Coutant <ccoutant@gmail.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.
+
+sections="eh_test_2.sects"
+
+hdr_section=`fgrep .eh_frame_hdr $sections`
+size_field=`echo $hdr_section | sed -e 's/\[//' | awk '{print $6;}'`
+size=`printf %d "0x$size_field"`
+
+if test "$size" -le 8; then
+  echo ".eh_frame_hdr section is too small:"
+  echo "$hdr_section"
+  exit 1
+fi
+
+exit 0
index ad4bcc2e88e9102501f5a202e203daedcb8c14d4..e3c8a16a8b9bf2f1b3d2a79370d105bef4d4a59b 100644 (file)
@@ -1,4 +1,9 @@
+template<typename C>
 void
-bar()
+bar(C*)
 {
 }
+
+template
+void
+bar<int>(int*);
index 50538df4b8c6c25ca5c563965ed8869c5e0dbc09..3bf96e9d3cad0e6f41faa3a1afcd12df5cf072f7 100644 (file)
@@ -6,6 +6,16 @@ foo()
 {
 }
 
+template<typename C>
+void
+bar(C*)
+{
+}
+
+template
+void
+bar<int>(int*);
+
 int
 main()
 {