Bug 23482

Summary: Identical code folding can break C++ exception handling invariants
Product: binutils Reporter: Joshua Oreman <oremanj>
Component: goldAssignee: Cary Coutant <ccoutant>
Status: RESOLVED DUPLICATE    
Severity: normal CC: guillaume, ian
Priority: P2    
Version: 2.32   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Attachments: Reproducer for reported issue

Description Joshua Oreman 2018-08-04 00:57:50 UTC
Recent versions of GCC at reasonable optimization levels will automatically split predicted-rarely-taken portions of a function into a separate "funcname.cold" symbol, which goes in a different executable section so as to improve locality of frequently-used code. (This happens on GCC 8 at -O2 or higher; the specific flag is -freorder-blocks-and-partition.) Suppose that two functions with different stack frame layouts happen to have identical .cold fragments split off, the .cold fragment throws an exception (common due to GCC considering exception throwing unlikely), and identical code folding is enabled with eg --icf=safe. Then the one merged .cold fragment can only be associated with one unwinding state, so at least one of the functions will crash or otherwise misbehave when it throws.

I previously reported this to GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86854) and they claim it's gold's bug: "the eh/unwind information from GCC is correct for the landing location but --icf=safe does not take into account the difference in landing locations before merging them".

The attached tarball contains three small C++ source files that reproduce the issue when compiled and linked together using GCC 8, as well as the assembly and object files produced by that compilation, plus the resulting working (without --icf=safe) and failing (with --icf=safe) binaries. The issue was observed on an x86_64 Linux system with gold 1.14 from binutils 2.29.1, and is also seen on a gold built from latest sources today.

Binaries produced using:

$ gold --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt{1,i}.o /usr/lib/gcc/x86_64-linux-gnu/8/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/8 -L/usr/lib/x86_64-linux-gnu t{1,2,m}.o -lstdc++ -lgcc_s -lgcc -lc /usr/lib/gcc/x86_64-linux-gnu/8/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -o t.nofold

$ gold --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt{1,i}.o /usr/lib/gcc/x86_64-linux-gnu/8/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/8 -L/usr/lib/x86_64-linux-gnu t{1,2,m}.o -lstdc++ -lgcc_s -lgcc -lc /usr/lib/gcc/x86_64-linux-gnu/8/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -o t.fold --icf=safe

Demonstration:

$ ./t.nofold
caught 1
caught 2

$ ./t.fold
caught 1
Segmentation fault (core dumped)
Comment 1 Joshua Oreman 2018-08-04 00:59:40 UTC
Created attachment 11162 [details]
Reproducer for reported issue
Comment 2 Cary Coutant 2018-08-06 20:19:34 UTC
I think this is the same issue as PR 21066. Gold is not considering the LSDA when comparing the contents of the functions (or function fragments, as in this case).

*** This bug has been marked as a duplicate of bug 21066 ***