Browse Source

buildsys: terminate .eh_frame in shared objects

uClibc-ng links its shared objects with -nostdlib, so gcc's crtendS.o -
which normally contributes the terminating zero length-word of
.eh_frame - is never pulled in.  All uClibc-ng shared objects therefore
carry an unterminated .eh_frame, on every architecture.

Where ld can build the .eh_frame_hdr binary search table this stays
latent: the unwinder finds FDEs via the table and never walks the
section.  But when no search table exists, libgcc's unwinder walks
.eh_frame linearly and relies on the terminator to stop.  On microblaze
(DW_EH_PE_aligned encodings -> relocations in .eh_frame -> writable
section -> ld emits only the 8-byte stub header) every lookup for a PC
without an FDE - e.g. unwinding through start_thread during NPTL
cancellation - ran past the end of the section, misparsed garbage as a
CIE and crashed in strlen(cie->augmentation): tst-cancel9 and most
tst-cancelx* failed with SIGSEGV.

Add the 4-byte zero terminator the way crtstuff.c does and link it at
the end of every shared object.  The dependency is attached to both the
versioned soname targets and the unversioned recipe-bearing targets:
the ldso rule runs link.so on $(ldso:.$(ABI_VERSION)=), so a fresh
parallel build would otherwise link ld-uClibc.so before
lib/ehframe-term.o exists.

Together with the toolchain's new signal-frame unwinder this fixes the
remaining NPTL cancellation failures on microblaze (full suite:
38 -> 20 failures).

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi 1 week ago
parent
commit
0a688ba5b5
2 changed files with 27 additions and 1 deletions
  1. 12 1
      Makerules
  2. 15 0
      libc/sysdeps/linux/common/ehframe-term.c

+ 12 - 1
Makerules

@@ -336,7 +336,7 @@ define link.so
 		$(CFLAG_-nostdlib) $(CFLAG_-nostartfiles) \
 		-o $(dir $@)$(1) $(START_FILE-$(notdir $@)) \
 		-Wl,--whole-archive $(firstword $^) -Wl,--no-whole-archive -shared \
-		$(LIBS-$(notdir $@)) $(LIBGCC) $(END_FILE-$(notdir $@))
+		$(LIBS-$(notdir $@)) $(LIBGCC) $(EHFRAME_TERM) $(END_FILE-$(notdir $@))
 	$(Q)$(LN) -sf $(1) $@.$(2)
 	$(Q)$(LN) -sf $(1) $@
 endef
@@ -429,6 +429,17 @@ ifeq ($(HAS_NO_THREADS)$(UCLIBC_HAS_THREADS_NATIVE),)
 $(lib-so-y): $(CTOR_TARGETS)
 endif
 
+# .eh_frame zero terminator, linked at the end of every shared object:
+# we link with -nostdlib, so gcc's crtendS.o never provides it
+EHFRAME_TERM := $(top_builddir)lib/ehframe-term.o
+CFLAGS-ehframe-term.c := -fno-unwind-tables -fno-asynchronous-unwind-tables
+$(EHFRAME_TERM): $(top_builddir)lib/%.o : $(top_srcdir)libc/sysdeps/linux/common/%.c | $(top_builddir)lib
+	$(compile.c)
+# both the soname targets and the recipe-bearing unversioned targets
+# (the ldso rule runs link.so on $(ldso:.$(ABI_VERSION)=))
+$(lib-so-y): $(EHFRAME_TERM)
+$(lib-so-y:.$(ABI_VERSION)=): $(EHFRAME_TERM)
+
 ifeq ($(UCLIBC_FORMAT_FDPIC_ELF),y)
 CRTRELOC=$(top_builddir)lib/crtreloc.o
 $(CRTRELOC): $(top_builddir)lib/%.o : $(top_srcdir)libc/sysdeps/linux/$(TARGET_ARCH)/%.c

+ 15 - 0
libc/sysdeps/linux/common/ehframe-term.c

@@ -0,0 +1,15 @@
+/*
+ * Zero terminator for the .eh_frame section of shared objects.
+ *
+ * uClibc-ng links its shared objects with -nostdlib/-nostartfiles, so
+ * gcc's crtendS.o - which normally contributes this terminator - is
+ * never pulled in.  Unwinders that walk .eh_frame linearly (used
+ * whenever no .eh_frame_hdr search table is available) rely on a
+ * terminating zero length-word to stop; without it they run past the
+ * end of the section into unrelated data and crash.
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+static const int __FRAME_END__[1]
+	__attribute__ ((used, section (".eh_frame"), aligned (4))) = { 0 };