/home/arjun/llvm-project/llvm/lib/Support/Unix/Signals.inc
Line | Count | Source (jump to first uncovered line) |
1 | | //===- Signals.cpp - Generic Unix Signals Implementation -----*- C++ -*-===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines some helpful functions for dealing with the possibility of |
10 | | // Unix signals occurring while your program is running. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | // |
14 | | // This file is extremely careful to only do signal-safe things while in a |
15 | | // signal handler. In particular, memory allocation and acquiring a mutex |
16 | | // while in a signal handler should never occur. ManagedStatic isn't usable from |
17 | | // a signal handler for 2 reasons: |
18 | | // |
19 | | // 1. Creating a new one allocates. |
20 | | // 2. The signal handler could fire while llvm_shutdown is being processed, in |
21 | | // which case the ManagedStatic is in an unknown state because it could |
22 | | // already have been destroyed, or be in the process of being destroyed. |
23 | | // |
24 | | // Modifying the behavior of the signal handlers (such as registering new ones) |
25 | | // can acquire a mutex, but all this guarantees is that the signal handler |
26 | | // behavior is only modified by one thread at a time. A signal handler can still |
27 | | // fire while this occurs! |
28 | | // |
29 | | // Adding work to a signal handler requires lock-freedom (and assume atomics are |
30 | | // always lock-free) because the signal handler could fire while new work is |
31 | | // being added. |
32 | | // |
33 | | //===----------------------------------------------------------------------===// |
34 | | |
35 | | #include "Unix.h" |
36 | | #include "llvm/ADT/STLExtras.h" |
37 | | #include "llvm/Config/config.h" |
38 | | #include "llvm/Demangle/Demangle.h" |
39 | | #include "llvm/Support/FileSystem.h" |
40 | | #include "llvm/Support/FileUtilities.h" |
41 | | #include "llvm/Support/Format.h" |
42 | | #include "llvm/Support/MemoryBuffer.h" |
43 | | #include "llvm/Support/Mutex.h" |
44 | | #include "llvm/Support/Program.h" |
45 | | #include "llvm/Support/SaveAndRestore.h" |
46 | | #include "llvm/Support/raw_ostream.h" |
47 | | #include <algorithm> |
48 | | #include <string> |
49 | | #include <sysexits.h> |
50 | | #ifdef HAVE_BACKTRACE |
51 | | # include BACKTRACE_HEADER // For backtrace(). |
52 | | #endif |
53 | | #if HAVE_SIGNAL_H |
54 | | #include <signal.h> |
55 | | #endif |
56 | | #if HAVE_SYS_STAT_H |
57 | | #include <sys/stat.h> |
58 | | #endif |
59 | | #if HAVE_DLFCN_H |
60 | | #include <dlfcn.h> |
61 | | #endif |
62 | | #if HAVE_MACH_MACH_H |
63 | | #include <mach/mach.h> |
64 | | #endif |
65 | | #if HAVE_LINK_H |
66 | | #include <link.h> |
67 | | #endif |
68 | | #ifdef HAVE__UNWIND_BACKTRACE |
69 | | // FIXME: We should be able to use <unwind.h> for any target that has an |
70 | | // _Unwind_Backtrace function, but on FreeBSD the configure test passes |
71 | | // despite the function not existing, and on Android, <unwind.h> conflicts |
72 | | // with <link.h>. |
73 | | #ifdef __GLIBC__ |
74 | | #include <unwind.h> |
75 | | #else |
76 | | #undef HAVE__UNWIND_BACKTRACE |
77 | | #endif |
78 | | #endif |
79 | | |
80 | | using namespace llvm; |
81 | | |
82 | | static RETSIGTYPE SignalHandler(int Sig); // defined below. |
83 | | static RETSIGTYPE InfoSignalHandler(int Sig); // defined below. |
84 | | |
85 | | using SignalHandlerFunctionType = void (*)(); |
86 | | /// The function to call if ctrl-c is pressed. |
87 | | static std::atomic<SignalHandlerFunctionType> InterruptFunction = |
88 | | ATOMIC_VAR_INIT(nullptr); |
89 | | static std::atomic<SignalHandlerFunctionType> InfoSignalFunction = |
90 | | ATOMIC_VAR_INIT(nullptr); |
91 | | /// The function to call on SIGPIPE (one-time use only). |
92 | | static std::atomic<SignalHandlerFunctionType> OneShotPipeSignalFunction = |
93 | | ATOMIC_VAR_INIT(nullptr); |
94 | | |
95 | | namespace { |
96 | | /// Signal-safe removal of files. |
97 | | /// Inserting and erasing from the list isn't signal-safe, but removal of files |
98 | | /// themselves is signal-safe. Memory is freed when the head is freed, deletion |
99 | | /// is therefore not signal-safe either. |
100 | | class FileToRemoveList { |
101 | | std::atomic<char *> Filename = ATOMIC_VAR_INIT(nullptr); |
102 | | std::atomic<FileToRemoveList *> Next = ATOMIC_VAR_INIT(nullptr); |
103 | | |
104 | | FileToRemoveList() = default; |
105 | | // Not signal-safe. |
106 | 0 | FileToRemoveList(const std::string &str) : Filename(strdup(str.c_str())) {} |
107 | | |
108 | | public: |
109 | | // Not signal-safe. |
110 | 0 | ~FileToRemoveList() { |
111 | 0 | if (FileToRemoveList *N = Next.exchange(nullptr)) |
112 | 0 | delete N; |
113 | 0 | if (char *F = Filename.exchange(nullptr)) |
114 | 0 | free(F); |
115 | 0 | } |
116 | | |
117 | | // Not signal-safe. |
118 | | static void insert(std::atomic<FileToRemoveList *> &Head, |
119 | 0 | const std::string &Filename) { |
120 | 0 | // Insert the new file at the end of the list. |
121 | 0 | FileToRemoveList *NewHead = new FileToRemoveList(Filename); |
122 | 0 | std::atomic<FileToRemoveList *> *InsertionPoint = &Head; |
123 | 0 | FileToRemoveList *OldHead = nullptr; |
124 | 0 | while (!InsertionPoint->compare_exchange_strong(OldHead, NewHead)) { |
125 | 0 | InsertionPoint = &OldHead->Next; |
126 | 0 | OldHead = nullptr; |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | // Not signal-safe. |
131 | | static void erase(std::atomic<FileToRemoveList *> &Head, |
132 | 0 | const std::string &Filename) { |
133 | 0 | // Use a lock to avoid concurrent erase: the comparison would access |
134 | 0 | // free'd memory. |
135 | 0 | static ManagedStatic<sys::SmartMutex<true>> Lock; |
136 | 0 | sys::SmartScopedLock<true> Writer(*Lock); |
137 | 0 |
|
138 | 0 | for (FileToRemoveList *Current = Head.load(); Current; |
139 | 0 | Current = Current->Next.load()) { |
140 | 0 | if (char *OldFilename = Current->Filename.load()) { |
141 | 0 | if (OldFilename != Filename) |
142 | 0 | continue; |
143 | 0 | // Leave an empty filename. |
144 | 0 | OldFilename = Current->Filename.exchange(nullptr); |
145 | 0 | // The filename might have become null between the time we |
146 | 0 | // compared it and we exchanged it. |
147 | 0 | if (OldFilename) |
148 | 0 | free(OldFilename); |
149 | 0 | } |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | // Signal-safe. |
154 | 0 | static void removeAllFiles(std::atomic<FileToRemoveList *> &Head) { |
155 | 0 | // If cleanup were to occur while we're removing files we'd have a bad time. |
156 | 0 | // Make sure we're OK by preventing cleanup from doing anything while we're |
157 | 0 | // removing files. If cleanup races with us and we win we'll have a leak, |
158 | 0 | // but we won't crash. |
159 | 0 | FileToRemoveList *OldHead = Head.exchange(nullptr); |
160 | 0 |
|
161 | 0 | for (FileToRemoveList *currentFile = OldHead; currentFile; |
162 | 0 | currentFile = currentFile->Next.load()) { |
163 | 0 | // If erasing was occuring while we're trying to remove files we'd look |
164 | 0 | // at free'd data. Take away the path and put it back when done. |
165 | 0 | if (char *path = currentFile->Filename.exchange(nullptr)) { |
166 | 0 | // Get the status so we can determine if it's a file or directory. If we |
167 | 0 | // can't stat the file, ignore it. |
168 | 0 | struct stat buf; |
169 | 0 | if (stat(path, &buf) != 0) |
170 | 0 | continue; |
171 | 0 | |
172 | 0 | // If this is not a regular file, ignore it. We want to prevent removal |
173 | 0 | // of special files like /dev/null, even if the compiler is being run |
174 | 0 | // with the super-user permissions. |
175 | 0 | if (!S_ISREG(buf.st_mode)) |
176 | 0 | continue; |
177 | 0 | |
178 | 0 | // Otherwise, remove the file. We ignore any errors here as there is |
179 | 0 | // nothing else we can do. |
180 | 0 | unlink(path); |
181 | 0 |
|
182 | 0 | // We're done removing the file, erasing can safely proceed. |
183 | 0 | currentFile->Filename.exchange(path); |
184 | 0 | } |
185 | 0 | } |
186 | 0 |
|
187 | 0 | // We're done removing files, cleanup can safely proceed. |
188 | 0 | Head.exchange(OldHead); |
189 | 0 | } |
190 | | }; |
191 | | static std::atomic<FileToRemoveList *> FilesToRemove = ATOMIC_VAR_INIT(nullptr); |
192 | | |
193 | | /// Clean up the list in a signal-friendly manner. |
194 | | /// Recall that signals can fire during llvm_shutdown. If this occurs we should |
195 | | /// either clean something up or nothing at all, but we shouldn't crash! |
196 | | struct FilesToRemoveCleanup { |
197 | | // Not signal-safe. |
198 | 0 | ~FilesToRemoveCleanup() { |
199 | 0 | FileToRemoveList *Head = FilesToRemove.exchange(nullptr); |
200 | 0 | if (Head) |
201 | 0 | delete Head; |
202 | 0 | } |
203 | | }; |
204 | | } // namespace |
205 | | |
206 | | static StringRef Argv0; |
207 | | |
208 | | /// Signals that represent requested termination. There's no bug or failure, or |
209 | | /// if there is, it's not our direct responsibility. For whatever reason, our |
210 | | /// continued execution is no longer desirable. |
211 | | static const int IntSigs[] = { |
212 | | SIGHUP, SIGINT, SIGTERM, SIGUSR2 |
213 | | }; |
214 | | |
215 | | /// Signals that represent that we have a bug, and our prompt termination has |
216 | | /// been ordered. |
217 | | static const int KillSigs[] = { |
218 | | SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGQUIT |
219 | | #ifdef SIGSYS |
220 | | , SIGSYS |
221 | | #endif |
222 | | #ifdef SIGXCPU |
223 | | , SIGXCPU |
224 | | #endif |
225 | | #ifdef SIGXFSZ |
226 | | , SIGXFSZ |
227 | | #endif |
228 | | #ifdef SIGEMT |
229 | | , SIGEMT |
230 | | #endif |
231 | | }; |
232 | | |
233 | | /// Signals that represent requests for status. |
234 | | static const int InfoSigs[] = { |
235 | | SIGUSR1 |
236 | | #ifdef SIGINFO |
237 | | , SIGINFO |
238 | | #endif |
239 | | }; |
240 | | |
241 | | static const size_t NumSigs = |
242 | | array_lengthof(IntSigs) + array_lengthof(KillSigs) + |
243 | | array_lengthof(InfoSigs) + 1 /* SIGPIPE */; |
244 | | |
245 | | |
246 | | static std::atomic<unsigned> NumRegisteredSignals = ATOMIC_VAR_INIT(0); |
247 | | static struct { |
248 | | struct sigaction SA; |
249 | | int SigNo; |
250 | | } RegisteredSignalInfo[NumSigs]; |
251 | | |
252 | | #if defined(HAVE_SIGALTSTACK) |
253 | | // Hold onto both the old and new alternate signal stack so that it's not |
254 | | // reported as a leak. We don't make any attempt to remove our alt signal |
255 | | // stack if we remove our signal handlers; that can't be done reliably if |
256 | | // someone else is also trying to do the same thing. |
257 | | static stack_t OldAltStack; |
258 | | static void* NewAltStackPointer; |
259 | | |
260 | 2 | static void CreateSigAltStack() { |
261 | 2 | const size_t AltStackSize = MINSIGSTKSZ + 64 * 1024; |
262 | 2 | |
263 | 2 | // If we're executing on the alternate stack, or we already have an alternate |
264 | 2 | // signal stack that we're happy with, there's nothing for us to do. Don't |
265 | 2 | // reduce the size, some other part of the process might need a larger stack |
266 | 2 | // than we do. |
267 | 2 | if (sigaltstack(nullptr, &OldAltStack) != 0 || |
268 | 2 | OldAltStack.ss_flags & SS_ONSTACK || |
269 | 2 | (OldAltStack.ss_sp && OldAltStack.ss_size >= AltStackSize)) |
270 | 0 | return; |
271 | 2 | |
272 | 2 | stack_t AltStack = {}; |
273 | 2 | AltStack.ss_sp = static_cast<char *>(safe_malloc(AltStackSize)); |
274 | 2 | NewAltStackPointer = AltStack.ss_sp; // Save to avoid reporting a leak. |
275 | 2 | AltStack.ss_size = AltStackSize; |
276 | 2 | if (sigaltstack(&AltStack, &OldAltStack) != 0) |
277 | 0 | free(AltStack.ss_sp); |
278 | 2 | } |
279 | | #else |
280 | | static void CreateSigAltStack() {} |
281 | | #endif |
282 | | |
283 | 2 | static void RegisterHandlers() { // Not signal-safe. |
284 | 2 | // The mutex prevents other threads from registering handlers while we're |
285 | 2 | // doing it. We also have to protect the handlers and their count because |
286 | 2 | // a signal handler could fire while we're registeting handlers. |
287 | 2 | static ManagedStatic<sys::SmartMutex<true>> SignalHandlerRegistrationMutex; |
288 | 2 | sys::SmartScopedLock<true> Guard(*SignalHandlerRegistrationMutex); |
289 | 2 | |
290 | 2 | // If the handlers are already registered, we're done. |
291 | 2 | if (NumRegisteredSignals.load() != 0) |
292 | 0 | return; |
293 | 2 | |
294 | 2 | // Create an alternate stack for signal handling. This is necessary for us to |
295 | 2 | // be able to reliably handle signals due to stack overflow. |
296 | 2 | CreateSigAltStack(); |
297 | 2 | |
298 | 2 | enum class SignalKind { IsKill, IsInfo }; |
299 | 30 | auto registerHandler = [&](int Signal, SignalKind Kind) { |
300 | 30 | unsigned Index = NumRegisteredSignals.load(); |
301 | 30 | assert(Index < array_lengthof(RegisteredSignalInfo) && |
302 | 30 | "Out of space for signal handlers!"); |
303 | 30 | |
304 | 30 | struct sigaction NewHandler; |
305 | 30 | |
306 | 30 | switch (Kind) { |
307 | 30 | case SignalKind::IsKill: |
308 | 28 | NewHandler.sa_handler = SignalHandler; |
309 | 28 | NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK; |
310 | 28 | break; |
311 | 30 | case SignalKind::IsInfo: |
312 | 2 | NewHandler.sa_handler = InfoSignalHandler; |
313 | 2 | NewHandler.sa_flags = SA_ONSTACK; |
314 | 2 | break; |
315 | 30 | } |
316 | 30 | sigemptyset(&NewHandler.sa_mask); |
317 | 30 | |
318 | 30 | // Install the new handler, save the old one in RegisteredSignalInfo. |
319 | 30 | sigaction(Signal, &NewHandler, &RegisteredSignalInfo[Index].SA); |
320 | 30 | RegisteredSignalInfo[Index].SigNo = Signal; |
321 | 30 | ++NumRegisteredSignals; |
322 | 30 | }; |
323 | 2 | |
324 | 2 | for (auto S : IntSigs) |
325 | 8 | registerHandler(S, SignalKind::IsKill); |
326 | 2 | for (auto S : KillSigs) |
327 | 20 | registerHandler(S, SignalKind::IsKill); |
328 | 2 | if (OneShotPipeSignalFunction) |
329 | 0 | registerHandler(SIGPIPE, SignalKind::IsKill); |
330 | 2 | for (auto S : InfoSigs) |
331 | 2 | registerHandler(S, SignalKind::IsInfo); |
332 | 2 | } |
333 | | |
334 | 0 | static void UnregisterHandlers() { |
335 | 0 | // Restore all of the signal handlers to how they were before we showed up. |
336 | 0 | for (unsigned i = 0, e = NumRegisteredSignals.load(); i != e; ++i) { |
337 | 0 | sigaction(RegisteredSignalInfo[i].SigNo, |
338 | 0 | &RegisteredSignalInfo[i].SA, nullptr); |
339 | 0 | --NumRegisteredSignals; |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | | /// Process the FilesToRemove list. |
344 | 0 | static void RemoveFilesToRemove() { |
345 | 0 | FileToRemoveList::removeAllFiles(FilesToRemove); |
346 | 0 | } |
347 | | |
348 | 0 | void sys::CleanupOnSignal(uintptr_t Context) { |
349 | 0 | int Sig = (int)Context; |
350 | 0 |
|
351 | 0 | if (llvm::is_contained(InfoSigs, Sig)) { |
352 | 0 | InfoSignalHandler(Sig); |
353 | 0 | return; |
354 | 0 | } |
355 | 0 | |
356 | 0 | RemoveFilesToRemove(); |
357 | 0 |
|
358 | 0 | if (llvm::is_contained(IntSigs, Sig) || Sig == SIGPIPE) |
359 | 0 | return; |
360 | 0 | |
361 | 0 | llvm::sys::RunSignalHandlers(); |
362 | 0 | } |
363 | | |
364 | | // The signal handler that runs. |
365 | 0 | static RETSIGTYPE SignalHandler(int Sig) { |
366 | 0 | // Restore the signal behavior to default, so that the program actually |
367 | 0 | // crashes when we return and the signal reissues. This also ensures that if |
368 | 0 | // we crash in our signal handler that the program will terminate immediately |
369 | 0 | // instead of recursing in the signal handler. |
370 | 0 | UnregisterHandlers(); |
371 | 0 |
|
372 | 0 | // Unmask all potentially blocked kill signals. |
373 | 0 | sigset_t SigMask; |
374 | 0 | sigfillset(&SigMask); |
375 | 0 | sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); |
376 | 0 |
|
377 | 0 | { |
378 | 0 | RemoveFilesToRemove(); |
379 | 0 |
|
380 | 0 | if (Sig == SIGPIPE) |
381 | 0 | if (auto OldOneShotPipeFunction = |
382 | 0 | OneShotPipeSignalFunction.exchange(nullptr)) |
383 | 0 | return OldOneShotPipeFunction(); |
384 | 0 | |
385 | 0 | if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig) |
386 | 0 | != std::end(IntSigs)) { |
387 | 0 | if (auto OldInterruptFunction = InterruptFunction.exchange(nullptr)) |
388 | 0 | return OldInterruptFunction(); |
389 | 0 | |
390 | 0 | raise(Sig); // Execute the default handler. |
391 | 0 | return; |
392 | 0 | } |
393 | 0 | } |
394 | 0 | |
395 | 0 | // Otherwise if it is a fault (like SEGV) run any handler. |
396 | 0 | llvm::sys::RunSignalHandlers(); |
397 | 0 |
|
398 | | #ifdef __s390__ |
399 | | // On S/390, certain signals are delivered with PSW Address pointing to |
400 | | // *after* the faulting instruction. Simply returning from the signal |
401 | | // handler would continue execution after that point, instead of |
402 | | // re-raising the signal. Raise the signal manually in those cases. |
403 | | if (Sig == SIGILL || Sig == SIGFPE || Sig == SIGTRAP) |
404 | | raise(Sig); |
405 | | #endif |
406 | | } |
407 | | |
408 | 0 | static RETSIGTYPE InfoSignalHandler(int Sig) { |
409 | 0 | SaveAndRestore<int> SaveErrnoDuringASignalHandler(errno); |
410 | 0 | if (SignalHandlerFunctionType CurrentInfoFunction = InfoSignalFunction) |
411 | 0 | CurrentInfoFunction(); |
412 | 0 | } |
413 | | |
414 | 0 | void llvm::sys::RunInterruptHandlers() { |
415 | 0 | RemoveFilesToRemove(); |
416 | 0 | } |
417 | | |
418 | 0 | void llvm::sys::SetInterruptFunction(void (*IF)()) { |
419 | 0 | InterruptFunction.exchange(IF); |
420 | 0 | RegisterHandlers(); |
421 | 0 | } |
422 | | |
423 | 0 | void llvm::sys::SetInfoSignalFunction(void (*Handler)()) { |
424 | 0 | InfoSignalFunction.exchange(Handler); |
425 | 0 | RegisterHandlers(); |
426 | 0 | } |
427 | | |
428 | 0 | void llvm::sys::SetOneShotPipeSignalFunction(void (*Handler)()) { |
429 | 0 | OneShotPipeSignalFunction.exchange(Handler); |
430 | 0 | RegisterHandlers(); |
431 | 0 | } |
432 | | |
433 | 0 | void llvm::sys::DefaultOneShotPipeSignalHandler() { |
434 | 0 | // Send a special return code that drivers can check for, from sysexits.h. |
435 | 0 | exit(EX_IOERR); |
436 | 0 | } |
437 | | |
438 | | // The public API |
439 | | bool llvm::sys::RemoveFileOnSignal(StringRef Filename, |
440 | 0 | std::string* ErrMsg) { |
441 | 0 | // Ensure that cleanup will occur as soon as one file is added. |
442 | 0 | static ManagedStatic<FilesToRemoveCleanup> FilesToRemoveCleanup; |
443 | 0 | *FilesToRemoveCleanup; |
444 | 0 | FileToRemoveList::insert(FilesToRemove, Filename.str()); |
445 | 0 | RegisterHandlers(); |
446 | 0 | return false; |
447 | 0 | } |
448 | | |
449 | | // The public API |
450 | 0 | void llvm::sys::DontRemoveFileOnSignal(StringRef Filename) { |
451 | 0 | FileToRemoveList::erase(FilesToRemove, Filename.str()); |
452 | 0 | } |
453 | | |
454 | | /// Add a function to be called when a signal is delivered to the process. The |
455 | | /// handler can have a cookie passed to it to identify what instance of the |
456 | | /// handler it is. |
457 | | void llvm::sys::AddSignalHandler(sys::SignalHandlerCallback FnPtr, |
458 | 2 | void *Cookie) { // Signal-safe. |
459 | 2 | insertSignalHandler(FnPtr, Cookie); |
460 | 2 | RegisterHandlers(); |
461 | 2 | } |
462 | | |
463 | | #if defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES && HAVE_LINK_H && \ |
464 | | (defined(__linux__) || defined(__FreeBSD__) || \ |
465 | | defined(__FreeBSD_kernel__) || defined(__NetBSD__)) |
466 | | struct DlIteratePhdrData { |
467 | | void **StackTrace; |
468 | | int depth; |
469 | | bool first; |
470 | | const char **modules; |
471 | | intptr_t *offsets; |
472 | | const char *main_exec_name; |
473 | | }; |
474 | | |
475 | 0 | static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { |
476 | 0 | DlIteratePhdrData *data = (DlIteratePhdrData*)arg; |
477 | 0 | const char *name = data->first ? data->main_exec_name : info->dlpi_name; |
478 | 0 | data->first = false; |
479 | 0 | for (int i = 0; i < info->dlpi_phnum; i++) { |
480 | 0 | const auto *phdr = &info->dlpi_phdr[i]; |
481 | 0 | if (phdr->p_type != PT_LOAD) |
482 | 0 | continue; |
483 | 0 | intptr_t beg = info->dlpi_addr + phdr->p_vaddr; |
484 | 0 | intptr_t end = beg + phdr->p_memsz; |
485 | 0 | for (int j = 0; j < data->depth; j++) { |
486 | 0 | if (data->modules[j]) |
487 | 0 | continue; |
488 | 0 | intptr_t addr = (intptr_t)data->StackTrace[j]; |
489 | 0 | if (beg <= addr && addr < end) { |
490 | 0 | data->modules[j] = name; |
491 | 0 | data->offsets[j] = addr - info->dlpi_addr; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | } |
495 | 0 | return 0; |
496 | 0 | } |
497 | | |
498 | | /// If this is an ELF platform, we can find all loaded modules and their virtual |
499 | | /// addresses with dl_iterate_phdr. |
500 | | static bool findModulesAndOffsets(void **StackTrace, int Depth, |
501 | | const char **Modules, intptr_t *Offsets, |
502 | | const char *MainExecutableName, |
503 | 0 | StringSaver &StrPool) { |
504 | 0 | DlIteratePhdrData data = {StackTrace, Depth, true, |
505 | 0 | Modules, Offsets, MainExecutableName}; |
506 | 0 | dl_iterate_phdr(dl_iterate_phdr_cb, &data); |
507 | 0 | return true; |
508 | 0 | } |
509 | | #else |
510 | | /// This platform does not have dl_iterate_phdr, so we do not yet know how to |
511 | | /// find all loaded DSOs. |
512 | | static bool findModulesAndOffsets(void **StackTrace, int Depth, |
513 | | const char **Modules, intptr_t *Offsets, |
514 | | const char *MainExecutableName, |
515 | | StringSaver &StrPool) { |
516 | | return false; |
517 | | } |
518 | | #endif // defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES && ... |
519 | | |
520 | | #if ENABLE_BACKTRACES && defined(HAVE__UNWIND_BACKTRACE) |
521 | 0 | static int unwindBacktrace(void **StackTrace, int MaxEntries) { |
522 | 0 | if (MaxEntries < 0) |
523 | 0 | return 0; |
524 | 0 | |
525 | 0 | // Skip the first frame ('unwindBacktrace' itself). |
526 | 0 | int Entries = -1; |
527 | 0 |
|
528 | 0 | auto HandleFrame = [&](_Unwind_Context *Context) -> _Unwind_Reason_Code { |
529 | 0 | // Apparently we need to detect reaching the end of the stack ourselves. |
530 | 0 | void *IP = (void *)_Unwind_GetIP(Context); |
531 | 0 | if (!IP) |
532 | 0 | return _URC_END_OF_STACK; |
533 | 0 | |
534 | 0 | assert(Entries < MaxEntries && "recursively called after END_OF_STACK?"); |
535 | 0 | if (Entries >= 0) |
536 | 0 | StackTrace[Entries] = IP; |
537 | 0 |
|
538 | 0 | if (++Entries == MaxEntries) |
539 | 0 | return _URC_END_OF_STACK; |
540 | 0 | return _URC_NO_REASON; |
541 | 0 | }; |
542 | 0 |
|
543 | 0 | _Unwind_Backtrace( |
544 | 0 | [](_Unwind_Context *Context, void *Handler) { |
545 | 0 | return (*static_cast<decltype(HandleFrame) *>(Handler))(Context); |
546 | 0 | }, |
547 | 0 | static_cast<void *>(&HandleFrame)); |
548 | 0 | return std::max(Entries, 0); |
549 | 0 | } |
550 | | #endif |
551 | | |
552 | | // In the case of a program crash or fault, print out a stack trace so that the |
553 | | // user has an indication of why and where we died. |
554 | | // |
555 | | // On glibc systems we have the 'backtrace' function, which works nicely, but |
556 | | // doesn't demangle symbols. |
557 | 0 | void llvm::sys::PrintStackTrace(raw_ostream &OS) { |
558 | 0 | #if ENABLE_BACKTRACES |
559 | 0 | static void *StackTrace[256]; |
560 | 0 | int depth = 0; |
561 | 0 | #if defined(HAVE_BACKTRACE) |
562 | 0 | // Use backtrace() to output a backtrace on Linux systems with glibc. |
563 | 0 | if (!depth) |
564 | 0 | depth = backtrace(StackTrace, static_cast<int>(array_lengthof(StackTrace))); |
565 | 0 | #endif |
566 | 0 | #if defined(HAVE__UNWIND_BACKTRACE) |
567 | 0 | // Try _Unwind_Backtrace() if backtrace() failed. |
568 | 0 | if (!depth) |
569 | 0 | depth = unwindBacktrace(StackTrace, |
570 | 0 | static_cast<int>(array_lengthof(StackTrace))); |
571 | 0 | #endif |
572 | 0 | if (!depth) |
573 | 0 | return; |
574 | 0 | |
575 | 0 | if (printSymbolizedStackTrace(Argv0, StackTrace, depth, OS)) |
576 | 0 | return; |
577 | | #if HAVE_DLFCN_H && HAVE_DLADDR |
578 | | int width = 0; |
579 | | for (int i = 0; i < depth; ++i) { |
580 | | Dl_info dlinfo; |
581 | | dladdr(StackTrace[i], &dlinfo); |
582 | | const char* name = strrchr(dlinfo.dli_fname, '/'); |
583 | | |
584 | | int nwidth; |
585 | | if (!name) nwidth = strlen(dlinfo.dli_fname); |
586 | | else nwidth = strlen(name) - 1; |
587 | | |
588 | | if (nwidth > width) width = nwidth; |
589 | | } |
590 | | |
591 | | for (int i = 0; i < depth; ++i) { |
592 | | Dl_info dlinfo; |
593 | | dladdr(StackTrace[i], &dlinfo); |
594 | | |
595 | | OS << format("%-2d", i); |
596 | | |
597 | | const char* name = strrchr(dlinfo.dli_fname, '/'); |
598 | | if (!name) OS << format(" %-*s", width, dlinfo.dli_fname); |
599 | | else OS << format(" %-*s", width, name+1); |
600 | | |
601 | | OS << format(" %#0*lx", (int)(sizeof(void*) * 2) + 2, |
602 | | (unsigned long)StackTrace[i]); |
603 | | |
604 | | if (dlinfo.dli_sname != nullptr) { |
605 | | OS << ' '; |
606 | | int res; |
607 | | char* d = itaniumDemangle(dlinfo.dli_sname, nullptr, nullptr, &res); |
608 | | if (!d) OS << dlinfo.dli_sname; |
609 | | else OS << d; |
610 | | free(d); |
611 | | |
612 | | OS << format(" + %tu", (static_cast<const char*>(StackTrace[i])- |
613 | | static_cast<const char*>(dlinfo.dli_saddr))); |
614 | | } |
615 | | OS << '\n'; |
616 | | } |
617 | | #elif defined(HAVE_BACKTRACE) |
618 | 0 | backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); |
619 | 0 | #endif |
620 | 0 | #endif |
621 | 0 | } |
622 | | |
623 | 0 | static void PrintStackTraceSignalHandler(void *) { |
624 | 0 | sys::PrintStackTrace(llvm::errs()); |
625 | 0 | } |
626 | | |
627 | 0 | void llvm::sys::DisableSystemDialogsOnCrash() {} |
628 | | |
629 | | /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the |
630 | | /// process, print a stack trace and then exit. |
631 | | void llvm::sys::PrintStackTraceOnErrorSignal(StringRef Argv0, |
632 | 2 | bool DisableCrashReporting) { |
633 | 2 | ::Argv0 = Argv0; |
634 | 2 | |
635 | 2 | AddSignalHandler(PrintStackTraceSignalHandler, nullptr); |
636 | 2 | |
637 | | #if defined(__APPLE__) && ENABLE_CRASH_OVERRIDES |
638 | | // Environment variable to disable any kind of crash dialog. |
639 | | if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT")) { |
640 | | mach_port_t self = mach_task_self(); |
641 | | |
642 | | exception_mask_t mask = EXC_MASK_CRASH; |
643 | | |
644 | | kern_return_t ret = task_set_exception_ports(self, |
645 | | mask, |
646 | | MACH_PORT_NULL, |
647 | | EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, |
648 | | THREAD_STATE_NONE); |
649 | | (void)ret; |
650 | | } |
651 | | #endif |
652 | | } |