Bug 21066 - icf folds template functions with different exception handling semantics
Summary: icf folds template functions with different exception handling semantics
Alias: None
Product: binutils
Classification: Unclassified
Component: gold (show other bugs)
Version: 2.24
: P2 normal
Target Milestone: ---
Assignee: Cary Coutant
: 22575 23482 (view as bug list)
Depends on:
Reported: 2017-01-18 23:20 UTC by gandhi.shaheen
Modified: 2019-06-08 00:29 UTC (History)
3 users (show)

See Also:
Last reconfirmed: 2017-01-24 00:00:00

example source code (309 bytes, text/x-c++src)
2017-01-18 23:20 UTC, gandhi.shaheen
Proposed fix (8.46 KB, patch)
2018-09-26 18:40 UTC, Joshua Oreman
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description gandhi.shaheen 2017-01-18 23:20:07 UTC
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.
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:
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
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
Comment 1 Cary Coutant 2017-01-24 20:48:42 UTC
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.
Comment 2 Markus Trippelsdorf 2017-12-09 08:02:27 UTC
*** Bug 22575 has been marked as a duplicate of this bug. ***
Comment 3 Cary Coutant 2018-08-06 20:19:34 UTC
*** Bug 23482 has been marked as a duplicate of this bug. ***
Comment 4 Joshua Oreman 2018-08-10 21:49:18 UTC
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(*)());

        .section .text._Z6call_1PFvvE,"ax",@progbits
        .globl _Z6call_1PFvvE
        .type _Z6call_1PFvvE, @function
        pushq   %rbx
        .cfi_def_cfa_offset 16
        .cfi_offset 3, -16
        jmp     _Z6call_1PFvvE.cold

        .section .text.unlikely._Z6call_1PFvvE,"ax",@progbits
        .type _Z6call_1PFvvE.cold, @function
        .cfi_def_cfa_offset 16
        .cfi_offset 3, -16
        call    *%rdi

        .section .text._Z6call_2PFvvE,"ax",@progbits
        .globl _Z6call_2PFvvE
        .type _Z6call_2PFvvE, @function
        subq    $24, %rsp
        .cfi_def_cfa_offset 32
        jmp     _Z6call_2PFvvE.cold

        .section .text.unlikely._Z6call_2PFvvE,"ax",@progbits
        .type _Z6call_2PFvvE.cold, @function
        .cfi_def_cfa_offset 32
        call    *%rdi

int main() {
    try {
    } catch (int val) {
        printf("caught 1: %d\n", val);
    try {
    } catch (int val) {
        printf("caught 2: %d\n", val);
Comment 5 Joshua Oreman 2018-09-26 18:40:38 UTC
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
Comment 6 cvs-commit@gcc.gnu.org 2019-05-10 23:27:58 UTC
The master branch has been updated by Cary Coutant <ccoutant@sourceware.org>:


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.
Comment 7 Cary Coutant 2019-06-08 00:29:13 UTC
Fixed on master.