https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118685
Bug ID: 118685 Summary: FreeBSD static executables segfault due to libgcc missing crtbeginT.o Product: gcc Version: 15.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libgcc Assignee: unassigned at gcc dot gnu.org Reporter: dimitry at andric dot com Target Milestone: --- On one of the FreeBSD mailing lists, Steve Kargl reported an issue with statically linked executables segfaulting, if they were compiled with gcc 14 [1]. The segfaults were caused by a bad .dtors section in such executables, looking like: Hex dump of section '.dtors': 0x004efca8 ffffffff ffffffff ........ The function that walks over the dtors section is called __do_global_dtors_aux [2]. It skips over the first element, since it should always be -1, but it expects the array to be terminated by either a -1 or a 0 value. Because the section is too short, it overruns into whatever section follows, usually resulting in a segfault. Further investigation shows that the cause for the strange .dtors section is that gcc with the -static option attempts to link crtbeginT.o and crtend.o: $ gcc -v -static helloworld.c -o helloworld ... /usr/local/libexec/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/collect2 \ -plugin /usr/local/libexec/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/liblto_plugin.so \ -plugin-opt=/usr/local/libexec/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/lto-wrapper \ -plugin-opt=-fresolution=/tmp/ccRgJPpe.res \ -plugin-opt=-pass-through=-lgcc \ -plugin-opt=-pass-through=-lgcc_eh \ -plugin-opt=-pass-through=-lc \ -plugin-opt=-pass-through=-lgcc \ -plugin-opt=-pass-through=-lgcc_eh \ -m elf_x86_64_fbsd \ -V \ -Bstatic \ -o helloworld \ /usr/lib/crt1.o \ /usr/lib/crti.o \ /usr/local/lib/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/crtbeginT.o \ -L/usr/local/lib/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0 \ -L/usr/local/lib/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/../../../../../x86_64-portbld-freebsd15.0/lib \ -L/usr/local/lib/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/../../.. \ /tmp/ccEhqbzH.o \ -lgcc \ -lgcc_eh \ -lc \ -lgcc \ -lgcc_eh \ /usr/local/lib/gcc14/gcc/x86_64-portbld-freebsd15.0/14.2.0/crtend.o \ /usr/lib/crtn.o Because the gcc port does not contain a crtbeginT.o file, it finds the corresponding file in /usr/lib instead, thus "mixing" the base system crtbeginT.o with the gcc-provided crtend.o. The base system crtbeginT.o contains the first part of the .dtors section: static crt_func __DTOR_LIST__[] __section(".dtors") __used = { (crt_func)-1 }; and normally the base system's crtend.o contains the last part: static crt_func __DTOR_END__[] __section(".dtors") __used = { (crt_func)0 }; However, the gcc-provided crtend.o does _not_ contain any .dtors section, since gcc's configure script detects support for .preinit_array/.init_array/.fini_array, and thus defines HAVE_INITFINI_ARRAY_SUPPORT. Therefore, the final .dtors section in a static executable only contains the first part, which results in the segfault. Note that dynamic executables never see this issue, because then gcc will use its _own_ crtbeginS.o and crtendS.so: there will be no .ctors or .dtors sections in the executable, but .init and .fini sections. As to fixing the problem, on the FreeBSD side we are going to implement seatbelts in the code that walks over the .ctors and .dtors tables, ensuring that they will not overrun the actual section boundaries. But this is really only a workaround. On the gcc side, we would like to suggest adding crtbeginT.o to the libgcc-provided crt objects, so it will prefer that one over the base system's version. In that case, static executables will also end up without any .ctors/dtors sections, and segfaults will be avoided. (Alternatively, gcc could stop providing crt startup objects completely, and rely on the base system ones, but that is a lot more drastic.) I will submit a patch to gcc-patches@ for this. [1] https://lists.freebsd.org/archives/freebsd-hackers/2025-January/004236.html [2] https://cgit.freebsd.org/src/tree/lib/csu/common/crtbegin.c#n69