about summary refs log tree commit diff
path: root/sysdeps/generic/abort.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/generic/abort.c')
-rw-r--r--sysdeps/generic/abort.c99
1 files changed, 73 insertions, 26 deletions
diff --git a/sysdeps/generic/abort.c b/sysdeps/generic/abort.c
index 61b99377b9..604eadc4f5 100644
--- a/sysdeps/generic/abort.c
+++ b/sysdeps/generic/abort.c
@@ -16,16 +16,26 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <libc-lock.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <signal.h>
 
+/* Try to get a machine dependent instruction which will make the
+   program crash.  This is used in case everything else fails.  */
+#include "abort-instr.h"
+#ifndef ABORT_INSTRUCTION
+/* No such instruction is available.  */
+# define ABORT_INSTRUCTION
+#endif
+
+/* We must avoid to run in circles.  Therefore we remember how far we
+   already got.  */
+static int stage;
 
-/* Function to close all streams and also make sure we don't loop by
-   calling abort while closing the streams.  */
-extern void __close_all_streams (void);
+/* We should be prepared for multiple threads trying to run abort.  */
+__libc_lock_define_initialized_recursive (static, lock);
 
 
 /* Cause an abnormal program termination with core-dump.  */
@@ -35,41 +45,78 @@ abort (void)
   struct sigaction act;
   sigset_t sigs;
 
-  if (__sigemptyset (&sigs) == 0 &&
-      __sigaddset (&sigs, SIGABRT) == 0)
-    __sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL);
+  /* First acquire the lock.  */
+  __libc_lock_lock (lock);
+
+  /* Now it's for sure we are alone.  But recursive calls are possible.  */
 
-  /* If there is a user handler installed use it.  We don't close or
-     flush streams.  */
-  if (__sigaction (SIGABRT, NULL, &act) >= 0
-      && act.sa_handler != SIG_DFL)
+  /* Unlock SIGABRT.  */
+  if (stage == 0)
     {
-      /* Send signal to call user handler.  */
-      raise (SIGABRT);
+      ++stage;
+      if (__sigemptyset (&sigs) == 0 &&
+	  __sigaddset (&sigs, SIGABRT) == 0)
+	__sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL);
+    }
+
+  /* Flush all streams.  We cannot close them now because the user
+     might have registered a handler for SIGABRT.  */
+  if (stage == 1)
+    {
+      ++stage;
+      fflush (NULL);
+    }
 
-      /* It returns, so we are responsible for closing the streams.  */
-      __close_all_streams ();
+  /* Send signal which possibly calls a user handler.  */
+  if (stage == 2)
+    {
+      ++stage;
+      raise (SIGABRT);
+    }
 
-      /* There was a handler installed.  Now remove it.  */
+  /* There was a handler installed.  Now remove it.  */
+  if (stage == 3)
+    {
+      ++stage;
       memset (&act, '\0', sizeof (struct sigaction));
       act.sa_handler = SIG_DFL;
       __sigfillset (&act.sa_mask);
       act.sa_flags = 0;
       __sigaction (SIGABRT, &act, NULL);
     }
-  else
-    /* No handler installed so the next `raise' will hopefully
-       terminate the process.  Therefore we must close the streams.  */
-    __close_all_streams ();
+
+  /* Now close the streams which also flushes the output the user
+     defined handler might has produced.  */
+  if (stage == 4)
+    {
+      ++stage;
+      fclose (NULL);
+    }
 
   /* Try again.  */
-  raise (SIGABRT);
+  if (stage == 5)
+    {
+      ++stage;
+      raise (SIGABRT);
+    }
 
-  /* If we can't signal ourselves, exit.  */
-  _exit (127);
+  /* Now try to abort using the system specific command.  */
+  if (stage == 6)
+    {
+      ++stage;
+      ABORT_INSTRUCTION;
+    }
+
+  /* If we can't signal ourselves and the abort instruction failed, exit.  */
+  if (stage == 7)
+    {
+      ++stage;
+      _exit (127);
+    }
 
-  /* If even this fails make sure we never return.  */
+  /* If even this fails try to use the provided instruction to crash
+     or otherwise make sure we never return.  */
   while (1)
-    /* For ever and ever.  */
-    ;
+    /* Try for ever and ever.  */
+    ABORT_INSTRUCTION;
 }