>From 17538f342318cb804a60c392a84ae58ec716c291 Mon Sep 17 00:00:00 2001
From: Paul Eggert
Date: Tue, 29 Aug 2017 14:20:47 -0700
Subject: [PATCH 2/4] 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 443344de4c..3dee40704d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3958,6 +3958,15 @@ AC_DEFUN
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 ecd6447ab7..c48f202a51 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 2cee646256..6e57b2024b 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.13.5