]> git.ipfire.org Git - thirdparty/glibc.git/blob - debug/segfault.c
Fix REALLOC_ZERO_BYTES_FREES comment to match C17
[thirdparty/glibc.git] / debug / segfault.c
1 /* Catch segmentation faults and print backtrace.
2 Copyright (C) 1998-2021 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 <https://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 #ifdef SA_SIGINFO
39 # define SIGCONTEXT siginfo_t *info, void *
40 #endif
41
42 /* Get code to possibly dump the content of all registers. */
43 #include <register-dump.h>
44
45 /* We'll use this a lot. */
46 #define WRITE_STRING(s) write (fd, s, strlen (s))
47
48 /* Name of the output file. */
49 static const char *fname;
50
51
52 /* Print the signal number SIGNAL. Either strerror or strsignal might
53 call local internal functions and these in turn call far too many
54 other functions and might even allocate memory which might fail. */
55 static void
56 write_strsignal (int fd, int signal)
57 {
58 char buf[30];
59 char *ptr = _itoa_word (signal, &buf[sizeof (buf)], 10, 0);
60 WRITE_STRING ("signal ");
61 write (fd, ptr, &buf[sizeof (buf)] - ptr);
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 = sigcontext_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 #ifdef SA_SIGINFO
152 sa.sa_sigaction = catch_segfault;
153 sa.sa_flags = SA_SIGINFO;
154 #else
155 sa.sa_handler = (void*) catch_segfault;
156 sa.sa_flags = 0;
157 #endif
158 sigemptyset (&sa.sa_mask);
159 sa.sa_flags |= SA_RESTART;
160
161 /* Maybe we are expected to use an alternative stack. */
162 if (getenv ("SEGFAULT_USE_ALTSTACK") != 0)
163 {
164 void *stack_mem = malloc (2 * SIGSTKSZ);
165 stack_t ss;
166
167 if (stack_mem != NULL)
168 {
169 ss.ss_sp = stack_mem;
170 ss.ss_flags = 0;
171 ss.ss_size = 2 * SIGSTKSZ;
172
173 if (sigaltstack (&ss, NULL) == 0)
174 sa.sa_flags |= SA_ONSTACK;
175 }
176 }
177
178 if (sigs == NULL)
179 sigaction (SIGSEGV, &sa, NULL);
180 else if (sigs[0] == '\0')
181 /* Do not do anything. */
182 return;
183 else
184 {
185 const char *where;
186 int all = __strcasecmp (sigs, "all") == 0;
187
188 #define INSTALL_FOR_SIG(sig, name) \
189 where = __strcasestr (sigs, name); \
190 if (all || (where != NULL \
191 && (where == sigs || !isalnum (where[-1])) \
192 && !isalnum (where[sizeof (name) - 1]))) \
193 sigaction (sig, &sa, NULL);
194
195 INSTALL_FOR_SIG (SIGSEGV, "segv");
196 INSTALL_FOR_SIG (SIGILL, "ill");
197 #ifdef SIGBUS
198 INSTALL_FOR_SIG (SIGBUS, "bus");
199 #endif
200 #ifdef SIGSTKFLT
201 INSTALL_FOR_SIG (SIGSTKFLT, "stkflt");
202 #endif
203 INSTALL_FOR_SIG (SIGABRT, "abrt");
204 INSTALL_FOR_SIG (SIGFPE, "fpe");
205 }
206
207 /* Preserve the output file name if there is any given. */
208 name = getenv ("SEGFAULT_OUTPUT_NAME");
209 if (name != NULL && name[0] != '\0')
210 {
211 int ret = access (name, R_OK | W_OK);
212
213 if (ret == 0 || (ret == -1 && errno == ENOENT))
214 fname = __strdup (name);
215 }
216 }