lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* Catch segmentation faults and print backtrace. |
| 2 | Copyright (C) 1998-2015 Free Software Foundation, Inc. |
| 3 | This file is part of the GNU C Library. |
| 4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. |
| 5 | |
| 6 | The GNU C Library is free software; you can redistribute it and/or |
| 7 | modify it under the terms of the GNU Lesser General Public |
| 8 | License as published by the Free Software Foundation; either |
| 9 | version 2.1 of the License, or (at your option) any later version. |
| 10 | |
| 11 | The GNU C Library is distributed in the hope that it will be useful, |
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | Lesser General Public License for more details. |
| 15 | |
| 16 | You should have received a copy of the GNU Lesser General Public |
| 17 | License along with the GNU C Library; if not, see |
| 18 | <http://www.gnu.org/licenses/>. */ |
| 19 | |
| 20 | #include <alloca.h> |
| 21 | #include <ctype.h> |
| 22 | #include <errno.h> |
| 23 | #include <execinfo.h> |
| 24 | #include <fcntl.h> |
| 25 | #include <signal.h> |
| 26 | #include <stdint.h> |
| 27 | #include <stdio.h> |
| 28 | #include <stdlib.h> |
| 29 | #include <string.h> |
| 30 | #include <unistd.h> |
| 31 | #include <_itoa.h> |
| 32 | #include <ldsodefs.h> |
| 33 | |
| 34 | /* This file defines macros to access the content of the sigcontext element |
| 35 | passed up by the signal handler. */ |
| 36 | #include <sigcontextinfo.h> |
| 37 | |
| 38 | /* Get code to possibly dump the content of all registers. */ |
| 39 | #include <register-dump.h> |
| 40 | |
| 41 | /* We'll use this a lot. */ |
| 42 | #define WRITE_STRING(s) write (fd, s, strlen (s)) |
| 43 | |
| 44 | /* Name of the output file. */ |
| 45 | static const char *fname; |
| 46 | |
| 47 | |
| 48 | /* We better should not use `strerror' since it can call far too many |
| 49 | other functions which might fail. Do it here ourselves. */ |
| 50 | static void |
| 51 | write_strsignal (int fd, int signal) |
| 52 | { |
| 53 | if (signal < 0 || signal >= _NSIG || _sys_siglist[signal] == NULL) |
| 54 | { |
| 55 | char buf[30]; |
| 56 | char *ptr = _itoa_word (signal, &buf[sizeof (buf)], 10, 0); |
| 57 | WRITE_STRING ("signal "); |
| 58 | write (fd, buf, &buf[sizeof (buf)] - ptr); |
| 59 | } |
| 60 | else |
| 61 | WRITE_STRING (_sys_siglist[signal]); |
| 62 | } |
| 63 | |
| 64 | |
| 65 | /* This function is called when a segmentation fault is caught. The system |
| 66 | is in an unstable state now. This means especially that malloc() might |
| 67 | not work anymore. */ |
| 68 | static void |
| 69 | catch_segfault (int signal, SIGCONTEXT ctx) |
| 70 | { |
| 71 | int fd, cnt, i; |
| 72 | void **arr; |
| 73 | struct sigaction sa; |
| 74 | uintptr_t pc; |
| 75 | |
| 76 | /* This is the name of the file we are writing to. If none is given |
| 77 | or we cannot write to this file write to stderr. */ |
| 78 | fd = 2; |
| 79 | if (fname != NULL) |
| 80 | { |
| 81 | fd = open (fname, O_TRUNC | O_WRONLY | O_CREAT, 0666); |
| 82 | if (fd == -1) |
| 83 | fd = 2; |
| 84 | } |
| 85 | |
| 86 | WRITE_STRING ("*** "); |
| 87 | write_strsignal (fd, signal); |
| 88 | WRITE_STRING ("\n"); |
| 89 | |
| 90 | #ifdef REGISTER_DUMP |
| 91 | REGISTER_DUMP; |
| 92 | #endif |
| 93 | |
| 94 | WRITE_STRING ("\nBacktrace:\n"); |
| 95 | |
| 96 | /* Get the backtrace. */ |
| 97 | arr = alloca (256 * sizeof (void *)); |
| 98 | cnt = backtrace (arr, 256); |
| 99 | |
| 100 | /* Now try to locate the PC from signal context in the backtrace. |
| 101 | Normally it will be found at arr[2], but it might appear later |
| 102 | if there were some signal handler wrappers. Allow a few bytes |
| 103 | difference to cope with as many arches as possible. */ |
| 104 | pc = (uintptr_t) GET_PC (ctx); |
| 105 | for (i = 0; i < cnt; ++i) |
| 106 | if ((uintptr_t) arr[i] >= pc - 16 && (uintptr_t) arr[i] <= pc + 16) |
| 107 | break; |
| 108 | |
| 109 | /* If we haven't found it, better dump full backtrace even including |
| 110 | the signal handler frames instead of not dumping anything. */ |
| 111 | if (i == cnt) |
| 112 | i = 0; |
| 113 | |
| 114 | /* Now generate nicely formatted output. */ |
| 115 | __backtrace_symbols_fd (arr + i, cnt - i, fd); |
| 116 | |
| 117 | #ifdef HAVE_PROC_SELF |
| 118 | /* Now the link map. */ |
| 119 | int mapfd = open ("/proc/self/maps", O_RDONLY); |
| 120 | if (mapfd != -1) |
| 121 | { |
| 122 | write (fd, "\nMemory map:\n\n", 14); |
| 123 | |
| 124 | char buf[256]; |
| 125 | ssize_t n; |
| 126 | |
| 127 | while ((n = TEMP_FAILURE_RETRY (read (mapfd, buf, sizeof (buf)))) > 0) |
| 128 | TEMP_FAILURE_RETRY (write (fd, buf, n)); |
| 129 | |
| 130 | close (mapfd); |
| 131 | } |
| 132 | #endif |
| 133 | |
| 134 | /* Pass on the signal (so that a core file is produced). */ |
| 135 | sa.sa_handler = SIG_DFL; |
| 136 | sigemptyset (&sa.sa_mask); |
| 137 | sa.sa_flags = 0; |
| 138 | sigaction (signal, &sa, NULL); |
| 139 | raise (signal); |
| 140 | } |
| 141 | |
| 142 | |
| 143 | static void |
| 144 | __attribute__ ((constructor)) |
| 145 | install_handler (void) |
| 146 | { |
| 147 | struct sigaction sa; |
| 148 | const char *sigs = getenv ("SEGFAULT_SIGNALS"); |
| 149 | const char *name; |
| 150 | |
| 151 | sa.sa_handler = (void *) catch_segfault; |
| 152 | sigemptyset (&sa.sa_mask); |
| 153 | sa.sa_flags = SA_RESTART; |
| 154 | |
| 155 | /* Maybe we are expected to use an alternative stack. */ |
| 156 | if (getenv ("SEGFAULT_USE_ALTSTACK") != 0) |
| 157 | { |
| 158 | void *stack_mem = malloc (2 * SIGSTKSZ); |
| 159 | struct sigaltstack ss; |
| 160 | |
| 161 | if (stack_mem != NULL) |
| 162 | { |
| 163 | ss.ss_sp = stack_mem; |
| 164 | ss.ss_flags = 0; |
| 165 | ss.ss_size = 2 * SIGSTKSZ; |
| 166 | |
| 167 | if (sigaltstack (&ss, NULL) == 0) |
| 168 | sa.sa_flags |= SA_ONSTACK; |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | if (sigs == NULL) |
| 173 | sigaction (SIGSEGV, &sa, NULL); |
| 174 | else if (sigs[0] == '\0') |
| 175 | /* Do not do anything. */ |
| 176 | return; |
| 177 | else |
| 178 | { |
| 179 | const char *where; |
| 180 | int all = __strcasecmp (sigs, "all") == 0; |
| 181 | |
| 182 | #define INSTALL_FOR_SIG(sig, name) \ |
| 183 | where = __strcasestr (sigs, name); \ |
| 184 | if (all || (where != NULL \ |
| 185 | && (where == sigs || !isalnum (where[-1])) \ |
| 186 | && !isalnum (where[sizeof (name) - 1]))) \ |
| 187 | sigaction (sig, &sa, NULL); |
| 188 | |
| 189 | INSTALL_FOR_SIG (SIGSEGV, "segv"); |
| 190 | INSTALL_FOR_SIG (SIGILL, "ill"); |
| 191 | #ifdef SIGBUS |
| 192 | INSTALL_FOR_SIG (SIGBUS, "bus"); |
| 193 | #endif |
| 194 | #ifdef SIGSTKFLT |
| 195 | INSTALL_FOR_SIG (SIGSTKFLT, "stkflt"); |
| 196 | #endif |
| 197 | INSTALL_FOR_SIG (SIGABRT, "abrt"); |
| 198 | INSTALL_FOR_SIG (SIGFPE, "fpe"); |
| 199 | } |
| 200 | |
| 201 | /* Preserve the output file name if there is any given. */ |
| 202 | name = getenv ("SEGFAULT_OUTPUT_NAME"); |
| 203 | if (name != NULL && name[0] != '\0') |
| 204 | { |
| 205 | int ret = access (name, R_OK | W_OK); |
| 206 | |
| 207 | if (ret == 0 || (ret == -1 && errno == ENOENT)) |
| 208 | fname = __strdup (name); |
| 209 | } |
| 210 | } |