| /* Catch segmentation faults and print backtrace. |
| Copyright (C) 1998-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <alloca.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <execinfo.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <_itoa.h> |
| #include <ldsodefs.h> |
| |
| /* This file defines macros to access the content of the sigcontext element |
| passed up by the signal handler. */ |
| #include <sigcontextinfo.h> |
| |
| /* Get code to possibly dump the content of all registers. */ |
| #include <register-dump.h> |
| |
| /* We'll use this a lot. */ |
| #define WRITE_STRING(s) write (fd, s, strlen (s)) |
| |
| /* Name of the output file. */ |
| static const char *fname; |
| |
| |
| /* We better should not use `strerror' since it can call far too many |
| other functions which might fail. Do it here ourselves. */ |
| static void |
| write_strsignal (int fd, int signal) |
| { |
| if (signal < 0 || signal >= _NSIG || _sys_siglist[signal] == NULL) |
| { |
| char buf[30]; |
| char *ptr = _itoa_word (signal, &buf[sizeof (buf)], 10, 0); |
| WRITE_STRING ("signal "); |
| write (fd, buf, &buf[sizeof (buf)] - ptr); |
| } |
| else |
| WRITE_STRING (_sys_siglist[signal]); |
| } |
| |
| |
| /* This function is called when a segmentation fault is caught. The system |
| is in an unstable state now. This means especially that malloc() might |
| not work anymore. */ |
| static void |
| catch_segfault (int signal, SIGCONTEXT ctx) |
| { |
| int fd, cnt, i; |
| void **arr; |
| struct sigaction sa; |
| uintptr_t pc; |
| |
| /* This is the name of the file we are writing to. If none is given |
| or we cannot write to this file write to stderr. */ |
| fd = 2; |
| if (fname != NULL) |
| { |
| fd = open (fname, O_TRUNC | O_WRONLY | O_CREAT, 0666); |
| if (fd == -1) |
| fd = 2; |
| } |
| |
| WRITE_STRING ("*** "); |
| write_strsignal (fd, signal); |
| WRITE_STRING ("\n"); |
| |
| #ifdef REGISTER_DUMP |
| REGISTER_DUMP; |
| #endif |
| |
| WRITE_STRING ("\nBacktrace:\n"); |
| |
| /* Get the backtrace. */ |
| arr = alloca (256 * sizeof (void *)); |
| cnt = backtrace (arr, 256); |
| |
| /* Now try to locate the PC from signal context in the backtrace. |
| Normally it will be found at arr[2], but it might appear later |
| if there were some signal handler wrappers. Allow a few bytes |
| difference to cope with as many arches as possible. */ |
| pc = (uintptr_t) GET_PC (ctx); |
| for (i = 0; i < cnt; ++i) |
| if ((uintptr_t) arr[i] >= pc - 16 && (uintptr_t) arr[i] <= pc + 16) |
| break; |
| |
| /* If we haven't found it, better dump full backtrace even including |
| the signal handler frames instead of not dumping anything. */ |
| if (i == cnt) |
| i = 0; |
| |
| /* Now generate nicely formatted output. */ |
| __backtrace_symbols_fd (arr + i, cnt - i, fd); |
| |
| #ifdef HAVE_PROC_SELF |
| /* Now the link map. */ |
| int mapfd = open ("/proc/self/maps", O_RDONLY); |
| if (mapfd != -1) |
| { |
| write (fd, "\nMemory map:\n\n", 14); |
| |
| char buf[256]; |
| ssize_t n; |
| |
| while ((n = TEMP_FAILURE_RETRY (read (mapfd, buf, sizeof (buf)))) > 0) |
| TEMP_FAILURE_RETRY (write (fd, buf, n)); |
| |
| close (mapfd); |
| } |
| #endif |
| |
| /* Pass on the signal (so that a core file is produced). */ |
| sa.sa_handler = SIG_DFL; |
| sigemptyset (&sa.sa_mask); |
| sa.sa_flags = 0; |
| sigaction (signal, &sa, NULL); |
| raise (signal); |
| } |
| |
| |
| static void |
| __attribute__ ((constructor)) |
| install_handler (void) |
| { |
| struct sigaction sa; |
| const char *sigs = getenv ("SEGFAULT_SIGNALS"); |
| const char *name; |
| |
| sa.sa_handler = (void *) catch_segfault; |
| sigemptyset (&sa.sa_mask); |
| sa.sa_flags = SA_RESTART; |
| |
| /* Maybe we are expected to use an alternative stack. */ |
| if (getenv ("SEGFAULT_USE_ALTSTACK") != 0) |
| { |
| void *stack_mem = malloc (2 * SIGSTKSZ); |
| struct sigaltstack ss; |
| |
| if (stack_mem != NULL) |
| { |
| ss.ss_sp = stack_mem; |
| ss.ss_flags = 0; |
| ss.ss_size = 2 * SIGSTKSZ; |
| |
| if (sigaltstack (&ss, NULL) == 0) |
| sa.sa_flags |= SA_ONSTACK; |
| } |
| } |
| |
| if (sigs == NULL) |
| sigaction (SIGSEGV, &sa, NULL); |
| else if (sigs[0] == '\0') |
| /* Do not do anything. */ |
| return; |
| else |
| { |
| const char *where; |
| int all = __strcasecmp (sigs, "all") == 0; |
| |
| #define INSTALL_FOR_SIG(sig, name) \ |
| where = __strcasestr (sigs, name); \ |
| if (all || (where != NULL \ |
| && (where == sigs || !isalnum (where[-1])) \ |
| && !isalnum (where[sizeof (name) - 1]))) \ |
| sigaction (sig, &sa, NULL); |
| |
| INSTALL_FOR_SIG (SIGSEGV, "segv"); |
| INSTALL_FOR_SIG (SIGILL, "ill"); |
| #ifdef SIGBUS |
| INSTALL_FOR_SIG (SIGBUS, "bus"); |
| #endif |
| #ifdef SIGSTKFLT |
| INSTALL_FOR_SIG (SIGSTKFLT, "stkflt"); |
| #endif |
| INSTALL_FOR_SIG (SIGABRT, "abrt"); |
| INSTALL_FOR_SIG (SIGFPE, "fpe"); |
| } |
| |
| /* Preserve the output file name if there is any given. */ |
| name = getenv ("SEGFAULT_OUTPUT_NAME"); |
| if (name != NULL && name[0] != '\0') |
| { |
| int ret = access (name, R_OK | W_OK); |
| |
| if (ret == 0 || (ret == -1 && errno == ENOENT)) |
| fname = __strdup (name); |
| } |
| } |