This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH gold/21066] Consider C++ exception handling metadata during ICF
- From: Joshua Oreman <oremanj at hudson-trading dot com>
- To: binutils at sourceware dot org
- Date: Mon, 24 Sep 2018 16:12:19 -0700
- Subject: [PATCH gold/21066] Consider C++ exception handling metadata during ICF
Two functions with the same code might not be combinable during ICF if
they have different C++ exception handling metadata. PR gold/21066
demonstrates this for the case where only the LSDA differs. New
optimizations in GCC 8 can also create cases where the code is
identical but the unwind tables differ; see gold/23482 for more
details. This patch extends ICF to consider a function's associated
FDE and CIE, including any relocations therein (e.g. to the LSDA),
when deciding whether two functions are identical or not.
2018-09-24 Joshua Oreman <oremanj@hudson-trading.com>
PR gold/21066
* gc.h (gc_process_relocs): Track relocations in .eh_frame sections
when ICF is enabled, even though the .eh_frame sections themselves
are not foldable.
* icf.cc (get_section_contents): Change arguments to permit operation
on just part of a section. Include extra identity regions in the
referring section's contents recursively.
(match_sections): Lock object here instead of in get_section_contents
so that get_section_contents can operate recursively.
(Icf::add_ehframe_links): New method.
(Icf::find_identical_sections): Pass .eh_frame sections to
add_ehframe_links(). Increase default iteration count from 2 to 3
because handling exception info typically requires one extra iteration.
* icf.h (Icf::extra_identity_list_): New data member with accessor.
(is_section_foldable_candidate): Include .gcc_except_table sections.
* options.h: Update documentation for new default ICF iteration count.
* testsuite/Makefile.am (icf_test_pr21066): New test case.
* testsuite/Makefile.in: Regenerate.
* testsuite/icf_test_pr21066.cc: New source file.
* testsuite/icf_test_pr21066.sh: New test script.
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 8f1b02d287..7d0a7c80f9 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,26 @@
+2018-09-24 Joshua Oreman <oremanj@hudson-trading.com>
+
+ PR gold/21066
+ * gc.h (gc_process_relocs): Track relocations in .eh_frame sections
+ when ICF is enabled, even though the .eh_frame sections themselves
+ are not foldable.
+ * icf.cc (get_section_contents): Change arguments to permit operation
+ on just part of a section. Include extra identity regions in the
+ referring section's contents recursively.
+ (match_sections): Lock object here instead of in get_section_contents
+ so that get_section_contents can operate recursively.
+ (Icf::add_ehframe_links): New method.
+ (Icf::find_identical_sections): Pass .eh_frame sections to
+ add_ehframe_links(). Increase default iteration count from 2 to 3
+ because handling exception info typically requires one extra iteration.
+ * icf.h (Icf::extra_identity_list_): New data member with accessor.
+ (is_section_foldable_candidate): Include .gcc_except_table sections.
+ * options.h: Update documentation for new default ICF iteration count.
+ * testsuite/Makefile.am (icf_test_pr21066): New test case.
+ * testsuite/Makefile.in: Regenerate.
+ * testsuite/icf_test_pr21066.cc: New source file.
+ * testsuite/icf_test_pr21066.sh: New test script.
+
2018-08-29 Chenghua Xu <paul.hua.gm@gmail.com>
* mips.cc (Mips_mach, add_machine_extensions, elf_mips_mach):
diff --git a/gold/gc.h b/gold/gc.h
index 78ead4132f..3c4ef73f07 100644
--- a/gold/gc.h
+++ b/gold/gc.h
@@ -200,7 +200,8 @@ gc_process_relocs(
bool check_section_for_function_pointers = false;
if (parameters->options().icf_enabled()
- && is_section_foldable_candidate(src_section_name.c_str()))
+ && (is_section_foldable_candidate(src_section_name)
+ || is_prefix_of(".eh_frame", src_section_name.c_str())))
{
is_icf_tracked = true;
Section_id src_id(src_obj, src_indx);
diff --git a/gold/icf.cc b/gold/icf.cc
index 378a56bc54..67083ea3ab 100644
--- a/gold/icf.cc
+++ b/gold/icf.cc
@@ -130,6 +130,26 @@
// folded causing unpredictable run-time behaviour if the pointers were used
// in comparisons.
//
+// Notes regarding C++ exception handling :
+// --------------------------------------
+//
+// It is possible for two sections to have identical text, identical
+// relocations, but different exception handling metadata (unwind
+// information in the .eh_frame section, and/or handler information in
+// a .gcc_except_table section). Thus, if a foldable section is
+// referenced from a .eh_frame FDE, we must include in its checksum
+// the contents of that FDE as well as of the CIE that the FDE refers
+// to. The CIE and FDE in turn probably contain relocations to the
+// personality routine and LSDA, which are handled like any other
+// relocation for ICF purposes. This logic is helped by the fact that
+// gcc with -ffunction-sections puts each function's LSDA in its own
+// .gcc_except_table.<functionname> section. Given sections for two
+// functions with nontrivial exception handling logic, we will
+// determine on the first iteration that their .gcc_except_table
+// sections are identical and can be folded, and on the second
+// iteration that their .text and .eh_frame contents (including the
+// now-merged .gcc_except_table relocations for the LSDA) are
+// identical and can be folded.
//
//
// How to run : --icf=[safe|all|none]
@@ -148,6 +168,8 @@
#include "elfcpp.h"
#include "int_encoding.h"
+#include <limits>
+
namespace gold
{
@@ -259,29 +281,35 @@ get_rel_addend(const unsigned char* reloc_addend_ptr,
// subsequent invocations of this function.
// Parameters :
// FIRST_ITERATION : true if it is the first invocation.
+// FIXED_CACHE : String that stores the portion of the result that
+// does not change from iteration to iteration;
+// written if first_iteration is true, read if it's false.
// SECN : Section for which contents are desired.
-// SECTION_NUM : Unique section number of this section.
+// SELF_SECN : Relocations that target this section will be
+// considered "relocations to self" so that recursive
+// functions can be folded. Should normally be the
+// same as `secn` except when processing extra identity
+// regions.
// NUM_TRACKED_RELOCS : Vector reference to store the number of relocs
// to ICF sections.
// KEPT_SECTION_ID : Vector which maps folded sections to kept sections.
-// SECTION_CONTENTS : Store the section's text and relocs to non-ICF
-// sections.
+// START_OFFSET : Only consider the part of the section at and after
+// this offset.
+// END_OFFSET : Only consider the part of the section before this
+// offset.
static std::string
get_section_contents(bool first_iteration,
+ std::string* fixed_cache,
const Section_id& secn,
- unsigned int section_num,
+ const Section_id& self_secn,
unsigned int* num_tracked_relocs,
Symbol_table* symtab,
const std::vector<unsigned int>& kept_section_id,
- std::vector<std::string>* section_contents)
+ section_offset_type start_offset = 0,
+ section_offset_type end_offset =
+ std::numeric_limits<section_offset_type>::max())
{
- // Lock the object so we can read from it. This is only called
- // single-threaded from queue_middle_tasks, so it is OK to lock.
- // Unfortunately we have no way to pass in a Task token.
- const Task* dummy_task = reinterpret_cast<const Task*>(-1);
- Task_lock_obj<Object> tl(dummy_task, secn.first);
-
section_size_type plen;
const unsigned char* contents = NULL;
if (first_iteration)
@@ -292,9 +320,6 @@ get_section_contents(bool first_iteration,
std::string buffer;
std::string icf_reloc_buffer;
- if (num_tracked_relocs)
- *num_tracked_relocs = 0;
-
Icf::Reloc_info_list& reloc_info_list =
symtab->icf()->reloc_info_list();
@@ -330,6 +355,11 @@ get_section_contents(bool first_iteration,
Symbol* gsym = *it_s;
bool is_section_symbol = false;
+ // Ignore relocations outside the region we were told to look at
+ if (static_cast<section_offset_type>(*it_o) < start_offset
+ || static_cast<section_offset_type>(*it_o) >= end_offset)
+ continue;
+
// A -1 value in the symbol vector indicates a local section symbol.
if (gsym == reinterpret_cast<Symbol*>(-1))
{
@@ -367,7 +397,7 @@ get_section_contents(bool first_iteration,
snprintf(addend_str, sizeof(addend_str), "%llx %llx %llx",
static_cast<long long>((*it_a).first),
static_cast<long long>((*it_a).second),
- static_cast<unsigned long long>(*it_o));
+ static_cast<unsigned long long>(*it_o - start_offset));
// If the symbol pointed to by the reloc is not in an ordinary
// section or if the symbol type is not FROM_OBJECT, then the
@@ -390,8 +420,8 @@ get_section_contents(bool first_iteration,
// If this reloc turns back and points to the same section,
// like a recursive call, use a special symbol to mark this.
- if (reloc_secn.first == secn.first
- && reloc_secn.second == secn.second)
+ if (reloc_secn.first == self_secn.first
+ && reloc_secn.second == self_secn.second)
{
if (first_iteration)
{
@@ -568,16 +598,48 @@ get_section_contents(bool first_iteration,
if (first_iteration)
{
buffer.append("Contents = ");
- buffer.append(reinterpret_cast<const char*>(contents), plen);
+
+ const unsigned char* slice_end =
+ contents + std::min<section_offset_type>(plen, end_offset);
+
+ if (contents + start_offset < slice_end)
+ {
+ buffer.append(reinterpret_cast<const char*>(contents + start_offset),
+ slice_end - (contents + start_offset));
+ }
+ }
+
+ // Add any extra identity regions.
+ std::pair<Icf::Extra_identity_list::const_iterator,
+ Icf::Extra_identity_list::const_iterator>
+ extra_range = symtab->icf()->extra_identity_list().equal_range(secn);
+ for (Icf::Extra_identity_list::const_iterator it_ext = extra_range.first;
+ it_ext != extra_range.second; ++it_ext)
+ {
+ std::string external_fixed;
+ std::string external_all =
+ get_section_contents(first_iteration, &external_fixed,
+ it_ext->second.section, self_secn,
+ num_tracked_relocs, symtab,
+ kept_section_id, it_ext->second.offset,
+ it_ext->second.offset + it_ext->second.length);
+ buffer.append(external_fixed);
+ icf_reloc_buffer.append(external_all, external_fixed.length(),
+ std::string::npos);
+ }
+
+ if (first_iteration)
+ {
// Store the section contents that don't change to avoid recomputing
// during the next call to this function.
- (*section_contents)[section_num] = buffer;
+ *fixed_cache = buffer;
}
else
{
gold_assert(buffer.empty());
+
// Reuse the contents computed in the previous iteration.
- buffer.append((*section_contents)[section_num]);
+ buffer.append(*fixed_cache);
}
buffer.append(icf_reloc_buffer);
@@ -641,14 +703,22 @@ match_sections(unsigned int iteration_num,
continue;
Section_id secn = id_section[i];
+
+ // Lock the object so we can read from it. This is only called
+ // single-threaded from queue_middle_tasks, so it is OK to lock.
+ // Unfortunately we have no way to pass in a Task token.
+ const Task* dummy_task = reinterpret_cast<const Task*>(-1);
+ Task_lock_obj<Object> tl(dummy_task, secn.first);
+
std::string this_secn_contents;
uint32_t cksum;
+ std::string* this_secn_cache = &((*section_contents)[i]);
if (iteration_num == 1)
{
unsigned int num_relocs = 0;
- this_secn_contents = get_section_contents(true, secn, i, &num_relocs,
- symtab, (*kept_section_id),
- section_contents);
+ this_secn_contents = get_section_contents(true, this_secn_cache,
+ secn, secn, &num_relocs,
+ symtab, (*kept_section_id));
(*num_tracked_relocs)[i] = num_relocs;
}
else
@@ -658,9 +728,9 @@ match_sections(unsigned int iteration_num,
// This section is already folded into something.
continue;
}
- this_secn_contents = get_section_contents(false, secn, i, NULL,
- symtab, (*kept_section_id),
- section_contents);
+ this_secn_contents = get_section_contents(false, this_secn_cache,
+ secn, secn, NULL,
+ symtab, (*kept_section_id));
}
const unsigned char* this_secn_contents_array =
@@ -766,6 +836,107 @@ is_function_ctor_or_dtor(const std::string& section_name)
return false;
}
+bool
+Icf::add_ehframe_links(Relobj* object, unsigned int ehframe_shndx,
+ Reloc_info& relocs)
+{
+ section_size_type contents_len;
+ const unsigned char* pcontents = object->section_contents(ehframe_shndx,
+ &contents_len,
+ false);
+ const unsigned char* p = pcontents;
+ const unsigned char* pend = pcontents + contents_len;
+
+ Sections_reachable_info::iterator it_target = relocs.section_info.begin();
+ Sections_reachable_info::iterator it_target_end = relocs.section_info.end();
+ Offset_info::iterator it_offset = relocs.offset_info.begin();
+ Offset_info::iterator it_offset_end = relocs.offset_info.end();
+
+ // Maps section offset to the length of the CIE defined at that offset.
+ std::map<section_offset_type, section_size_type> cies;
+
+ uint32_t (*read_swap_32)(const unsigned char*);
+ if (object->is_big_endian())
+ read_swap_32 = &elfcpp::Swap<32, true>::readval;
+ else
+ read_swap_32 = &elfcpp::Swap<32, false>::readval;
+
+ // TODO: The logic for parsing the CIE/FDE framing is copied from
+ // Eh_frame::do_add_ehframe_input_section() and might want to be
+ // factored into a shared helper function.
+ while (p < pend)
+ {
+ if (pend - p < 4)
+ return false;
+
+ unsigned int len = read_swap_32(p);
+ p += 4;
+ if (len == 0)
+ {
+ // We should only find a zero-length entry at the end of the
+ // section.
+ if (p < pend)
+ return false;
+ break;
+ }
+ // We don't support a 64-bit .eh_frame.
+ if (len == 0xffffffff)
+ return false;
+ if (static_cast<unsigned int>(pend - p) < len)
+ return false;
+
+ const unsigned char* const pentend = p + len;
+
+ if (pend - p < 4)
+ return false;
+
+ unsigned int id = read_swap_32(p);
+ p += 4;
+
+ if (id == 0)
+ {
+ // CIE.
+ cies.insert(std::make_pair(p - pcontents, len - 4));
+ }
+ else
+ {
+ // FDE.
+ std::map<section_offset_type, section_size_type>::const_iterator it;
+ it = cies.find((p - pcontents) - (id - 4));
+ if (it == cies.end())
+ return false;
+
+ // Figure out which section this FDE refers into. The word at `p`
+ // is an address, and we expect to see a relocation there. If not,
+ // this FDE isn't ICF-relevant.
+ while (it_offset != it_offset_end
+ && it_target != it_target_end
+ && static_cast<ptrdiff_t>(*it_offset) < (p - pcontents))
+ {
+ ++it_offset;
+ ++it_target;
+ }
+ if (it_offset != it_offset_end
+ && it_target != it_target_end
+ && static_cast<ptrdiff_t>(*it_offset) == (p - pcontents))
+ {
+ // Found a reloc. Add this FDE and its CIE as extra identity
+ // info for the section it refers to.
+ Extra_identity_info rec_fde = {Section_id(object, ehframe_shndx),
+ p - pcontents, len - 4};
+ Extra_identity_info rec_cie = {Section_id(object, ehframe_shndx),
+ it->first, it->second};
+ extra_identity_list_.insert(std::make_pair(*it_target, rec_fde));
+ extra_identity_list_.insert(std::make_pair(*it_target, rec_cie));
+ }
+ }
+
+ p = pentend;
+ }
+
+ return true;
+}
+
// This is the main ICF function called in gold.cc. This does the
// initialization and calls match_sections repeatedly (twice by default)
// which computes the crc checksums and detects identical functions.
@@ -792,12 +963,18 @@ Icf::find_identical_sections(const Input_objects* input_objects,
// Unfortunately we have no way to pass in a Task token.
const Task* dummy_task = reinterpret_cast<const Task*>(-1);
Task_lock_obj<Object> tl(dummy_task, *p);
+ std::vector<unsigned int> eh_frame_ind;
- for (unsigned int i = 0;i < (*p)->shnum(); ++i)
+ for (unsigned int i = 0; i < (*p)->shnum(); ++i)
{
const std::string section_name = (*p)->section_name(i);
if (!is_section_foldable_candidate(section_name))
- continue;
+ {
+ if (is_prefix_of(".eh_frame", section_name.c_str()))
+ eh_frame_ind.push_back(i);
+ continue;
+ }
+
if (!(*p)->is_section_included(i))
continue;
if (parameters->options().gc_sections()
@@ -822,14 +999,39 @@ Icf::find_identical_sections(const Input_objects* input_objects,
section_contents.push_back("");
section_num++;
}
+
+ for (std::vector<unsigned int>::iterator it_eh_ind = eh_frame_ind.begin();
+ it_eh_ind != eh_frame_ind.end(); ++it_eh_ind)
+ {
+ // gc_process_relocs() recorded relocations for this
+ // section even though we can't fold it. We need to
+ // use those relocations to associate other foldable
+ // sections with the FDEs and CIEs that are relevant
+ // to them, so we can avoid merging sections that
+ // don't have identical exception-handling behavior.
+
+ Section_id sect(*p, *it_eh_ind);
+ Reloc_info_list::iterator it_rel = this->reloc_info_list().find(sect);
+ if (it_rel != this->reloc_info_list().end())
+ {
+ if (!add_ehframe_links(*p, *it_eh_ind, it_rel->second))
+ {
+ gold_warning(_("could not parse eh_frame section %s(%s); ICF "
+ "might not preserve exception handling "
+ "behavior"),
+ (*p)->name().c_str(),
+ (*p)->section_name(*it_eh_ind).c_str());
+ }
+ }
+ }
}
unsigned int num_iterations = 0;
- // Default number of iterations to run ICF is 2.
+ // Default number of iterations to run ICF is 3.
unsigned int max_iterations = (parameters->options().icf_iterations() > 0)
? parameters->options().icf_iterations()
- : 2;
+ : 3;
bool converged = false;
diff --git a/gold/icf.h b/gold/icf.h
index a2002949bf..d16ebd425c 100644
--- a/gold/icf.h
+++ b/gold/icf.h
@@ -64,6 +64,19 @@ class Icf
typedef Unordered_map<Section_id, Reloc_info,
Section_id_hash> Reloc_info_list;
+ // A region of some other section that should be considered part of
+ // a section for ICF purposes. This is used to avoid folding sections
+ // that have identical text and relocations but different .eh_frame
+ // information.
+ typedef struct
+ {
+ Section_id section;
+ section_offset_type offset;
+ section_size_type length;
+ } Extra_identity_info;
+
+ typedef std::multimap<Section_id, Extra_identity_info> Extra_identity_list;
+
Icf()
: id_section_(), section_id_(), kept_section_id_(),
fptr_section_id_(),
@@ -137,6 +150,12 @@ class Icf
reloc_info_list()
{ return this->reloc_info_list_; }
+ // Returns a map from section to region of a different section that should
+ // be considered part of the key section for ICF purposes.
+ Extra_identity_list&
+ extra_identity_list()
+ { return this->extra_identity_list_; }
+
// Returns a mapping of each section to a unique integer.
Uniq_secn_id_map&
section_to_int_map()
@@ -144,6 +163,10 @@ class Icf
private:
+ bool
+ add_ehframe_links(Relobj* object, unsigned int ehframe_shndx,
+ Reloc_info& ehframe_relocs);
+
// Maps integers to sections.
std::vector<Section_id> id_section_;
// Does the reverse.
@@ -160,17 +183,24 @@ class Icf
bool icf_ready_;
// This list is populated by gc_process_relocs in gc.h.
Reloc_info_list reloc_info_list_;
+ // Regions of other sections that should be considered part of
+ // each section for ICF purposes.
+ Extra_identity_list extra_identity_list_;
};
// This function returns true if this section corresponds to a function that
// should be considered by icf as a possible candidate for folding. Some
// earlier gcc versions, like 4.0.3, put constructors and destructors in
// .gnu.linkonce.t sections and hence should be included too.
+// The mechanism used to safely fold functions referenced by .eh_frame
+// requires folding .gcc_except_table sections as well; see "Notes regarding
+// C++ exception handling" at the top of icf.cc for an explanation why.
inline bool
is_section_foldable_candidate(const std::string& section_name)
{
const char* section_name_cstr = section_name.c_str();
return (is_prefix_of(".text", section_name_cstr)
+ || is_prefix_of(".gcc_except_table", section_name_cstr)
|| is_prefix_of(".gnu.linkonce.t", section_name_cstr));
}
diff --git a/gold/options.h b/gold/options.h
index 11054981c9..ce7e4eb025 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -946,7 +946,7 @@ class General_options
{"none", "all", "safe"});
DEFINE_uint(icf_iterations, options::TWO_DASHES , '\0', 0,
- N_("Number of iterations of ICF (default 2)"), N_("COUNT"));
+ N_("Number of iterations of ICF (default 3)"), N_("COUNT"));
DEFINE_special(incremental, options::TWO_DASHES, '\0',
N_("Do an incremental link if possible; "
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index c1c5539cdb..0f110ff8b7 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -264,6 +264,16 @@ icf_test: icf_test.o gcctestdir/ld
icf_test.map: icf_test
@touch icf_test.map
+check_SCRIPTS += icf_test_pr21066.sh
+check_DATA += icf_test_pr21066.map
+MOSTLYCLEANFILES += icf_test_pr21066 icf_test_pr21066.map
+icf_test_pr21066.o: icf_test_pr21066.cc
+ $(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $<
+icf_test_pr21066: icf_test_pr21066.o gcctestdir/ld
+ $(CXXLINK) -o icf_test_pr21066 -Bgcctestdir/ -Wl,--icf=all,-Map,icf_test_pr21066.map icf_test_pr21066.o
+icf_test_pr21066.map: icf_test_pr21066
+ @touch icf_test_pr21066.map
+
check_SCRIPTS += icf_keep_unique_test.sh
check_DATA += icf_keep_unique_test.stdout
MOSTLYCLEANFILES += icf_keep_unique_test
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index 08b5c07a67..075ae6f583 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -129,7 +129,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_orphan_section_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ pr14265.sh pr20717.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_dynamic_list_test.sh \
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test.sh \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test.sh icf_test_pr21066.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_keep_unique_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_pie_test.sh \
@@ -152,6 +152,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ pr14265.stdout pr20717.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_dynamic_list_test.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test.map \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test_pr21066.map \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_keep_unique_test.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test_1.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test_2.stdout \
@@ -182,6 +183,8 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_orphan_section_test pr14265 \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ pr20717 gc_dynamic_list_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test icf_test.map \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test_pr21066 \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test_pr21066.map \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_keep_unique_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test icf_safe_test.map \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_pie_test \
@@ -2723,6 +2726,7 @@ pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
@@ -5536,6 +5540,13 @@ icf_test.sh.log: icf_test.sh
--log-file $$b.log --trs-file $$b.trs \
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
"$$tst" $(AM_TESTS_FD_REDIRECT)
+icf_test_pr21066.sh.log: icf_test_pr21066.sh
+ @p='icf_test_pr21066.sh'; \
+ b='icf_test_pr21066.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
icf_keep_unique_test.sh.log: icf_keep_unique_test.sh
@p='icf_keep_unique_test.sh'; \
b='icf_keep_unique_test.sh'; \
@@ -7941,6 +7952,12 @@ uninstall-am:
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -o icf_test -Bgcctestdir/ -Wl,--icf=all,-Map,icf_test.map icf_test.o
@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_test.map: icf_test
@GCC_TRUE@@NATIVE_LINKER_TRUE@ @touch icf_test.map
+@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_test_pr21066.o: icf_test_pr21066.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $<
+@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_test_pr21066: icf_test_pr21066.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -o icf_test_pr21066 -Bgcctestdir/ -Wl,--icf=all,-Map,icf_test_pr21066.map icf_test_pr21066.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_test_pr21066.map: icf_test_pr21066
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ @touch icf_test_pr21066.map
@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_keep_unique_test.o: icf_keep_unique_test.cc
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $<
@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_keep_unique_test: icf_keep_unique_test.o gcctestdir/ld
diff --git a/gold/testsuite/icf_test_pr21066.cc b/gold/testsuite/icf_test_pr21066.cc
new file mode 100644
index 0000000000..568873d5a8
--- /dev/null
+++ b/gold/testsuite/icf_test_pr21066.cc
@@ -0,0 +1,67 @@
+// icf_test.cc -- a test case for gold
+
+// Copyright (C) 2009-2018 Free Software Foundation, Inc.
+// Test case from PR 21066 submitted by Gandhi Shaheen
+
+// 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.
+
+// The goal of this program is to verify if identical code folding
+// correctly identifies and folds functions. folded_func must be
+// folded into kept_func.
+
+// Written by Sriraman Tallam <tmsriram@google.com>.
+
+#include <stdio.h>
+
+struct first_exception {
+};
+
+struct second_exception {
+};
+
+typedef void (*callback_fn_t)();
+
+void raise_first_exception()
+{
+ throw first_exception();
+}
+
+void raise_second_exception()
+{
+ throw second_exception();
+}
+
+template<typename E>
+void capture_exception_of_type(volatile callback_fn_t f)
+{
+ try {
+ f();
+ } catch (E& e) {
+ puts("caught expected exception");
+ } catch (...) {
+ puts("ERROR: caught unexpected exception");
+ throw;
+ }
+}
+
+int main()
+{
+ capture_exception_of_type<first_exception>(raise_first_exception);
+ capture_exception_of_type<second_exception>(raise_second_exception);
+ return 0;
+}
diff --git a/gold/testsuite/icf_test_pr21066.sh b/gold/testsuite/icf_test_pr21066.sh
new file mode 100755
index 0000000000..2f3e85d611
--- /dev/null
+++ b/gold/testsuite/icf_test_pr21066.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# icf_test_pr21066.sh -- regression test for ICF tracking exception handling
+# metadata differences
+
+# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+# Written by Joshua Oreman <oremanj@hudson-trading.com>, based on icf_test.sh
+
+# 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.
+
+set -e
+
+check()
+{
+ awk "
+BEGIN { discard = 0; }
+/^Discarded input/ { discard = 1; }
+/^Memory map/ { discard = 0; }
+/.*\\.text\\..*capture_exception_of_type.*($2|$3).*/ {
+ act[discard] = act[discard] \" \" \$0;
+}
+END {
+ # printf \"kept\" act[0] \"\\nfolded\" act[1] \"\\n\";
+ if (length(act[0]) != 0 && length(act[1]) != 0)
+ {
+ printf \"Identical Code Folding improperly folded functions\\n\"
+ printf \"with same code but different .gcc_except_table\\n\"
+ exit 1;
+ }
+ }" $1
+}
+
+check icf_test_pr21066.map "first_exception" "second_exception"