Created attachment 9763 [details] example source code compiling the attached file with either g++ or clang++ and then linking with gold with --icf=safe will fold the two template functions into one, whereas using the standard linker will produce the correct output. > g++ -std=c++14 -o test main.cpp -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.9.4-2ubuntu1~14.04.1' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 4.9.4 (Ubuntu 4.9.4-2ubuntu1~14.04.1) COLLECT_GCC_OPTIONS='-std=c++1y' '-o' 'test' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-linux-gnu/4.9/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE main.cpp -quiet -dumpbase main.cpp -mtune=generic -march=x86-64 -auxbase main -std=c++1y -version -fstack-protector -Wformat -Wformat-security -o /tmp/user/1000/ccgCMiPR.s GNU C++ (Ubuntu 4.9.4-2ubuntu1~14.04.1) version 4.9.4 (x86_64-linux-gnu) compiled by GNU C version 4.9.4, GMP version 5.1.3, MPFR version 3.1.3, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/4.9" ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/include/c++/4.9 /usr/include/x86_64-linux-gnu/c++/4.9 /usr/include/c++/4.9/backward /usr/lib/gcc/x86_64-linux-gnu/4.9/include /usr/local/include /usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed /usr/include/x86_64-linux-gnu /usr/include End of search list. GNU C++ (Ubuntu 4.9.4-2ubuntu1~14.04.1) version 4.9.4 (x86_64-linux-gnu) compiled by GNU C version 4.9.4, GMP version 5.1.3, MPFR version 3.1.3, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: e7137bb4cc46fb63f5e22418eff966b7 COLLECT_GCC_OPTIONS='-std=c++1y' '-o' 'test' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64' as -v --64 -o /tmp/user/1000/cc7eBpBQ.o /tmp/user/1000/ccgCMiPR.s GNU assembler version 2.24 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.24 COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-std=c++1y' '-o' 'test' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-linux-gnu/4.9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/4.9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper -plugin-opt=-fresolution=/tmp/user/1000/ccAUZIoP.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o test /usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.9/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.9 -L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../.. /tmp/user/1000/cc7eBpBQ.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.9/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crtn.o > ./test caught expected exception caught expected exception > g++ -std=c++14 -c -o main.o main.cpp > "../binutils-2.27.90/gold/ld-new" "--sysroot=/" "--build-id" "--eh-frame-hdr" "-m" "elf_x86_64" "--hash-style=gnu" "--as-needed" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-z" "relro" "-o" "test" "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crt1.o" "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crti.o" "/usr/lib/gcc/x86_64-linux-gnu/4.9/crtbegin.o" "-L/usr/lib/gcc/x86_64-linux-gnu/4.9" "-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu" "-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib" "-L/lib/x86_64-linux-gnu" "-L/lib/../lib" "-L/usr/lib/x86_64-linux-gnu" "-L/usr/lib/../lib" "-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../.." "main.o" "--icf=safe" "-lstdc++" "-lm" "-lgcc_s" "-lgcc" "-lc" "-lgcc_s" "-lgcc" "/usr/lib/gcc/x86_64-linux-gnu/4.9/crtend.o" "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crtn.o" > ./test caught expected exception ERROR: caught unexpected exception terminate called after throwing an instance of 'second_exception' [3] 11748 abort (core dumped) ./test
The problem here is that the two functions are identical in every aspect, except for the LSDA in .gcc_except_table. Since the LSDA has relocations pointing to the code, but not the other way around, the linker's ICF code doesn't spot the difference. It looks like we'll have to disable ICF for any functions that have an LSDA associated with them. A more ambitious approach might be to try to parse the LSDAs for each function and include their contents and relocations as part of the function's signature.
*** Bug 22575 has been marked as a duplicate of this bug. ***
*** Bug 23482 has been marked as a duplicate of this bug. ***
As I pointed out in PR 23482, hot/cold function splitting (the new gcc -freorder-blocks-and-partition optimization) can make this an issue even without any LSDA at all. Seems a full fix will need to consider the CIE/FDE contents that are relevant to the function as well. Example uses asm for clarity, but I can obtain analogous issues using gcc-produced code, as demonstrated on the above PR. $ g++ -o t.nofold t.cc -fuse-ld=gold $ g++ -o t.fold t.cc -fuse-ld=gold -Wl,--icf=safe $ ./t.nofold caught 1: 42 caught 2: 42 $ ./t.fold caught 1: 42 terminate called after throwing an instance of 'int' Aborted (core dumped) $ cat t.cc extern "C" int printf(const char *fmt, ...); void throw_it() { throw 42; } extern void call_1(void(*)()); extern void call_2(void(*)()); asm(R"( .section .text._Z6call_1PFvvE,"ax",@progbits .globl _Z6call_1PFvvE .type _Z6call_1PFvvE, @function _Z6call_1PFvvE: .cfi_startproc pushq %rbx .cfi_def_cfa_offset 16 .cfi_offset 3, -16 jmp _Z6call_1PFvvE.cold .cfi_endproc .section .text.unlikely._Z6call_1PFvvE,"ax",@progbits .type _Z6call_1PFvvE.cold, @function _Z6call_1PFvvE.cold: .cfi_startproc .cfi_def_cfa_offset 16 .cfi_offset 3, -16 call *%rdi .cfi_endproc .section .text._Z6call_2PFvvE,"ax",@progbits .globl _Z6call_2PFvvE .type _Z6call_2PFvvE, @function _Z6call_2PFvvE: .cfi_startproc subq $24, %rsp .cfi_def_cfa_offset 32 jmp _Z6call_2PFvvE.cold .cfi_endproc .section .text.unlikely._Z6call_2PFvvE,"ax",@progbits .type _Z6call_2PFvvE.cold, @function _Z6call_2PFvvE.cold: .cfi_startproc .cfi_def_cfa_offset 32 call *%rdi .cfi_endproc )"); int main() { try { call_1(throw_it); } catch (int val) { printf("caught 1: %d\n", val); } try { call_2(throw_it); } catch (int val) { printf("caught 2: %d\n", val); } }
Created attachment 11278 [details] Proposed fix I believe this patch fixes all the issues described in this thread. I also submitted it to the mailing list: https://sourceware.org/ml/binutils/2018-09/msg00346.html
The master branch has been updated by Cary Coutant <ccoutant@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=e173ea00c2941af42ea4e2267446d6039a70da6e commit e173ea00c2941af42ea4e2267446d6039a70da6e Author: Joshua Oreman <oremanj@hudson-trading.com> Date: Sat May 11 07:27:10 2019 +0800 Fix problem with ICF where diffs in EH frame info is ignored. 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.
Fixed on master.