https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86854
Bug ID: 86854 Summary: crash on stack unwinding with reorder-blocks-and-partition + linker code folding + C++ exceptions Product: gcc Version: 8.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: rtl-optimization Assignee: unassigned at gcc dot gnu.org Reporter: oremanj at mit dot edu Target Milestone: --- With -freorder-blocks-and-partition in GCC 8 (but not in 7.x), GCC shows a tendency to move exception-throwing C++ code into .cold fragments. The resulting fragments are often identical between different functions that throw the same type of exception, and thus will get merged by a linker's "identical code folding" feature such as gold's --icf=safe. If the functions from which the identical .cold fragments were split off have different stack layouts, the merging will lead to misbehavior during stack unwinding, since the single shared PC of the __cxa_throw call in the merged .cold fragment can't be associated with the multiple different stack layouts of the multiple functions that jump to it. It's not immediately clear to me whether it's GCC or gold that should be doing something to prevent this, but GCC seems to be much better positioned to identify the danger, and the documentation for -freorder-blocks-and-partition says suggestive things about inapplicability to exception-handling scenarios, so I'm reporting here first. This is an x86_64 Linux system with GCC from SVN 20180719 (r262861), very close to 8.2 RC, and none of the few commits between there and 8.2 final appear related to the issue at hand. gold is version 1.14 from Binutils 2.29.1. Issue still appears with gold pulled from git master just now; haven't tried with a newer gcc yet. $ cat t1.cc extern "C" int random(); struct nontrivial { ~nontrivial() { (void) random(); } }; int fn1(int value) { nontrivial guard; if (value < 0) throw -value; return 0; } $ cat t2.cc extern "C" int random(); struct nontrivial { ~nontrivial() { (void) random(); } }; int fn2(int value) { // try to use a lot of registers int vals[] = {random(), random(), random(), random(), random(), random(), random(), random()}; int res = (((vals[0] + vals[1]) * (vals[2] + vals[3])) / ((vals[4] + vals[5]) * (vals[6] + vals[7]))); nontrivial guard; if (value + (res & 1) < 0) throw -value; return 0; } $ cat tm.cc extern int fn1(int); extern int fn2(int); extern "C" int puts(const char *s); int main() { try { fn1(-1); puts("didn't throw 1"); } catch (int) { puts("caught 1"); } try { fn2(-2); puts("didn't throw 2"); } catch (int) { puts("caught 2"); } } $ g++-8 -O2 -std=c++17 -freorder-blocks-and-partition -fuse-ld=gold -o t t1.cc t2.cc tm.cc $ ./t caught 1 caught 2 $ g++-8 -O2 -std=c++17 -freorder-blocks-and-partition -fuse-ld=gold -Wl,--icf=safe -o t t1.cc t2.cc tm.cc $ ./t caught 1 Segmentation fault (core dumped)