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