https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108994
--- Comment #18 from Jakub Jelinek <jakub at gcc dot gnu.org> --- It is a fundamental assumption that the FDE/CIE chain is zero terminated, after all, all the registration APIs (__register_frame_table, __register_frame_info_table, __register_frame_info_table_bases) take just a begin pointer, not a begin and end pointer nor begin + count or something similar, nor assume there is just one FDE (after all, a FDE needs some CIE, so there would need to be a routine that expects exactly two or something similar). When not using .eh_frame_hdr (which is used most of the time on glibc since end of 2001 when I've added it and somewhat later on Solaris etc.), crtbegin.o/crtend.o ensures this, crtbegin.o provides a label at the start of .eh_frame and is linked first among the objects: /* Stick a label at the beginning of the frame unwind info so we can register and deregister it with the exception handling library code. */ STATIC EH_FRAME_SECTION_CONST char __EH_FRAME_BEGIN__[] __attribute__((section(__LIBGCC_EH_FRAME_SECTION_NAME__), aligned(4))) = { }; while crtend.o provides the zero termination: /* Terminate the frame unwind info section with a 4byte 0 as a sentinel; this would be the 'length' field in a real FDE. */ # if __INT_MAX__ == 2147483647 typedef int int32; # elif __LONG_MAX__ == 2147483647 typedef long int32; # elif __SHRT_MAX__ == 2147483647 typedef short int32; # else # error "Missing a 4 byte integer" # endif STATIC EH_FRAME_SECTION_CONST int32 __FRAME_END__[] __attribute__ ((used, section(__LIBGCC_EH_FRAME_SECTION_NAME__), aligned(__alignof__(int32)))) = { 0 }; and is linked last. I believe LLVM does the same thing in compiler-rt/lib/crt/crtbegin.c and compiler-rt/lib/crt/crtend.c . If you readelf -wf any_binary_or_library it should show 00330010 ZERO terminator line (with whatever address) at the end.