From 9baeed3514fe60189f3bf935c380da92659b7f59 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 29 Aug 2017 14:20:47 -0700 Subject: [PATCH] Improve stack-top heuristic This is needed for gcc -Os -flto on x86-64; otherwise, GC misses part of the stack when scanning for heap roots, causing Emacs to crash later (Bug#28213). The problem is that Emacs's hack for getting an address near the stack top does not work when link-time optimization moves stack variables around. * configure.ac (HAVE___BUILTIN_FRAME_ADDRESS): New macro. * lib-src/make-docfile.c (DEFUN_noinline): New constant. (write_globals, scan_c_stream): Support noinline. * src/alloc.c (NEAR_STACK_TOP): New macro. (SET_STACK_TOP_ADDRESS): Use it. (flush_stack_call_func, Fgarbage_collect): Now noinline. --- configure.ac | 9 +++++++++ lib-src/make-docfile.c | 9 +++++++-- src/alloc.c | 20 +++++++++++++++----- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 443344de4c2..3dee40704dd 100644 --- a/configure.ac +++ b/configure.ac @@ -3958,6 +3958,15 @@ AC_CHECK_FUNCS([aligned_alloc posix_memalign], [break]) AC_CHECK_DECLS([aligned_alloc], [], [], [[#include ]]) dnl Cannot use AC_CHECK_FUNCS +AC_CACHE_CHECK([for __builtin_frame_address], + [emacs_cv_func___builtin_frame_address], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [__builtin_frame_address (0);])], + [emacs_cv_func___builtin_frame_address=yes], + [emacs_cv_func___builtin_frame_address=no])]) +if test $emacs_cv_func___builtin_frame_address = yes; then + AC_DEFINE([HAVE___BUILTIN_FRAME_ADDRESS], 1, + [Define to 1 if you have the '__builtin_frame_address' function.]) +fi AC_CACHE_CHECK([for __builtin_unwind_init], emacs_cv_func___builtin_unwind_init, [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [__builtin_unwind_init ();])], diff --git a/lib-src/make-docfile.c b/lib-src/make-docfile.c index ecd6447ab78..c48f202a511 100644 --- a/lib-src/make-docfile.c +++ b/lib-src/make-docfile.c @@ -592,7 +592,7 @@ struct global }; /* Bit values for FLAGS field from the above. Applied for DEFUNs only. */ -enum { DEFUN_noreturn = 1, DEFUN_const = 2 }; +enum { DEFUN_noreturn = 1, DEFUN_const = 2, DEFUN_noinline = 4 }; /* All the variable names we saw while scanning C sources in `-g' mode. */ @@ -742,6 +742,8 @@ write_globals (void) { if (globals[i].flags & DEFUN_noreturn) fputs ("_Noreturn ", stdout); + if (globals[i].flags & DEFUN_noinline) + fputs ("NO_INLINE ", stdout); printf ("EXFUN (%s, ", globals[i].name); if (globals[i].v.value == -1) @@ -1062,7 +1064,8 @@ scan_c_stream (FILE *infile) attributes: attribute1 attribute2 ...) (Lisp_Object arg...) - Now only 'noreturn' and 'const' attributes are used. */ + Now only ’const’, ’noinline’ and 'noreturn' attributes + are used. */ /* Advance to the end of docstring. */ c = getc (infile); @@ -1108,6 +1111,8 @@ scan_c_stream (FILE *infile) g->flags |= DEFUN_noreturn; if (strstr (input_buffer, "const")) g->flags |= DEFUN_const; + if (strstr (input_buffer, "noinline")) + g->flags |= DEFUN_noinline; } continue; } diff --git a/src/alloc.c b/src/alloc.c index 2cee6462564..6e57b2024bc 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -5061,22 +5061,31 @@ typedef union # endif #endif +/* Yield an address close enough to the top of the stack that the + garbage collector need not scan above it. Callers should be + declared NO_INLINE. */ +#ifdef HAVE___BUILTIN_FRAME_ADDRESS +# define NEAR_STACK_TOP(addr) ((void) (addr), __builtin_frame_address (0)) +#else +# define NEAR_STACK_TOP(addr) (addr) +#endif + /* Set *P to the address of the top of the stack. This must be a macro, not a function, so that it is executed in the caller’s environment. It is not inside a do-while so that its storage - survives the macro. */ + survives the macro. Callers should be declared NO_INLINE. */ #ifdef HAVE___BUILTIN_UNWIND_INIT # define SET_STACK_TOP_ADDRESS(p) \ stacktop_sentry sentry; \ __builtin_unwind_init (); \ - *(p) = &sentry + *(p) = NEAR_STACK_TOP (&sentry) #else # define SET_STACK_TOP_ADDRESS(p) \ stacktop_sentry sentry; \ __builtin_unwind_init (); \ test_setjmp (); \ sys_setjmp (sentry.j); \ - *(p) = &sentry + (stack_bottom < &sentry.c) + *(p) = NEAR_STACK_TOP (&sentry + (stack_bottom < &sentry.c)) #endif /* Mark live Lisp objects on the C stack. @@ -5148,7 +5157,7 @@ mark_stack (char *bottom, char *end) It is invalid to run any Lisp code or to allocate any GC memory from FUNC. */ -void +NO_INLINE void flush_stack_call_func (void (*func) (void *arg), void *arg) { void *end; @@ -6097,7 +6106,8 @@ where each entry has the form (NAME SIZE USED FREE), where: to return them to the OS). However, if there was overflow in pure space, `garbage-collect' returns nil, because real GC can't be done. -See Info node `(elisp)Garbage Collection'. */) +See Info node `(elisp)Garbage Collection'. */ + attributes: noinline) (void) { void *end; -- 2.39.2