Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_signals.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include <signal.h>
29#include <stdio.h>
30#include <assert.h>
31#include <stdlib.h>
32#include <stdbool.h>
33#include <stdint.h>
34#include <errno.h>
35#include <string.h>
36
37#include "nilorea/n_log.h"
38#include "nilorea/n_signals.h"
39#include "nilorea/n_str.h"
40
42#define LOGFPRT(...) fprintf(stderr, "Error: " __VA_ARGS__)
43
45#define LOGNLOG(...) n_log(LOG_ERR, __VA_ARGS__)
46
48#define LOGSIG LOGNLOG
49
52
53#ifdef __windows__
54#include "nilorea/n_windows.h"
55#include <imagehlp.h>
56#else
57#include <err.h>
58#include <execinfo.h>
59#endif
60
61// void almost_c99_signal_handler(int sig)
62// {
63// switch(sig)
64// {
65// case SIGABRT:
66// LOGSIG("Caught SIGABRT: usually caused by an abort() or assert()" );
67// break;
68// case SIGFPE:
69// LOGSIG("Caught SIGFPE: arithmetic exception, such as divide by zero" );
70// break;
71// case SIGILL:
72// LOGSIG("Caught SIGILL: illegal instruction" );
73// break;
74// case SIGINT:
75// LOGSIG("Caught SIGINT: interactive attention signal, probably a ctrl+c" );
76// break;
77// case SIGSEGV:
78// LOGSIG("Caught SIGSEGV: segfault" );
79// break;
80// case SIGTERM:
81// default:
82// LOGSIG("Caught SIGTERM: a termination request was sent to the program" );
83// break;
84// }
85// _Exit(1);
86// }
87
88// void set_signal_handler()
89// {
90// signal(SIGABRT, almost_c99_signal_handler);
91// signal(SIGFPE, almost_c99_signal_handler);
92// signal(SIGILL, almost_c99_signal_handler);
93// signal(SIGINT, almost_c99_signal_handler);
94// signal(SIGSEGV, almost_c99_signal_handler);
95// signal(SIGTERM, almost_c99_signal_handler);
96// }
97
104int addr2line(char const* const program_name, void const* const addr) {
105 char addr2line_cmd[4093] = "";
106
107 /* have addr2line map the address to the relent line in the code */
108#ifdef __APPLE__
109 /* apple does things differently... */
110 sprintf(addr2line_cmd, "atos -o %.256s ./%p", program_name, addr);
111#else
112#ifdef __windows__
113 snprintf(addr2line_cmd, sizeof(addr2line_cmd), "addr2line -f -p -e ./%s %p", program_name, addr);
114 LOGSIG("%s", addr2line_cmd);
115 return 0;
116#else
117 sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %p", program_name, addr);
118#endif
119#endif
120 N_STR* output = NULL;
121 int ret = -1;
122 n_popen(addr2line_cmd, 1024, (void**)&output, &ret);
123 if (!output || !output->data) {
124 free_nstr(&output);
125 return -1;
126 }
127 LOGSIG("%s", output->data);
128 free_nstr(&output);
129 return ret;
130} /* addr2line(...) */
131
132#ifdef __windows__
137void windows_print_stacktrace(CONTEXT* context) {
138 SymInitialize(GetCurrentProcess(), 0, true);
139
140 STACKFRAME frame = {0};
141
142 /* setup initial stack frame */
143#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
144 /* Wrong values for W10 ? */
145 frame.AddrPC.Offset = context->Eip;
146 frame.AddrPC.Mode = AddrModeFlat;
147 frame.AddrStack.Offset = context->Esp;
148 frame.AddrStack.Mode = AddrModeFlat;
149 frame.AddrFrame.Offset = context->Ebp;
150 frame.AddrFrame.Mode = AddrModeFlat;
151#else
152 frame.AddrPC.Offset = context->Rip;
153 frame.AddrPC.Mode = AddrModeFlat;
154 frame.AddrStack.Offset = context->Rsp;
155 frame.AddrStack.Mode = AddrModeFlat;
156 frame.AddrFrame.Offset = context->Rbp;
157 frame.AddrFrame.Mode = AddrModeFlat;
158#endif
159
160 while (StackWalk(IMAGE_FILE_MACHINE_I386,
161 GetCurrentProcess(),
162 GetCurrentThread(),
163 &frame,
164 context,
165 0,
166 SymFunctionTableAccess,
167 SymGetModuleBase,
168 0)) {
169 addr2line(__n_stack_traced_progam_name, (void*)frame.AddrPC.Offset);
170 }
171
172 SymCleanup(GetCurrentProcess());
173} /* windows_print_stacktrace(...) */
174
180LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
181 switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
182 case EXCEPTION_ACCESS_VIOLATION:
183 LOGSIG("Error: EXCEPTION_ACCESS_VIOLATION");
184 break;
185 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
186 LOGSIG("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
187 break;
188 case EXCEPTION_BREAKPOINT:
189 LOGSIG("Error: EXCEPTION_BREAKPOINT");
190 break;
191 case EXCEPTION_DATATYPE_MISALIGNMENT:
192 LOGSIG("Error: EXCEPTION_DATATYPE_MISALIGNMENT");
193 break;
194 case EXCEPTION_FLT_DENORMAL_OPERAND:
195 LOGSIG("Error: EXCEPTION_FLT_DENORMAL_OPERAND");
196 break;
197 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
198 LOGSIG("Error: EXCEPTION_FLT_DIVIDE_BY_ZERO");
199 break;
200 case EXCEPTION_FLT_INEXACT_RESULT:
201 LOGSIG("Error: EXCEPTION_FLT_INEXACT_RESULT");
202 break;
203 case EXCEPTION_FLT_INVALID_OPERATION:
204 LOGSIG("Error: EXCEPTION_FLT_INVALID_OPERATION");
205 break;
206 case EXCEPTION_FLT_OVERFLOW:
207 LOGSIG("Error: EXCEPTION_FLT_OVERFLOW");
208 break;
209 case EXCEPTION_FLT_STACK_CHECK:
210 LOGSIG("Error: EXCEPTION_FLT_STACK_CHECK");
211 break;
212 case EXCEPTION_FLT_UNDERFLOW:
213 LOGSIG("Error: EXCEPTION_FLT_UNDERFLOW");
214 break;
215 case EXCEPTION_ILLEGAL_INSTRUCTION:
216 LOGSIG("Error: EXCEPTION_ILLEGAL_INSTRUCTION");
217 break;
218 case EXCEPTION_IN_PAGE_ERROR:
219 LOGSIG("Error: EXCEPTION_IN_PAGE_ERROR");
220 break;
221 case EXCEPTION_INT_DIVIDE_BY_ZERO:
222 LOGSIG("Error: EXCEPTION_INT_DIVIDE_BY_ZERO");
223 break;
224 case EXCEPTION_INT_OVERFLOW:
225 LOGSIG("Error: EXCEPTION_INT_OVERFLOW");
226 break;
227 case EXCEPTION_INVALID_DISPOSITION:
228 LOGSIG("Error: EXCEPTION_INVALID_DISPOSITION");
229 break;
230 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
231 LOGSIG("Error: EXCEPTION_NONCONTINUABLE_EXCEPTION");
232 break;
233 case EXCEPTION_PRIV_INSTRUCTION:
234 LOGSIG("Error: EXCEPTION_PRIV_INSTRUCTION");
235 break;
236 case EXCEPTION_SINGLE_STEP:
237 LOGSIG("Error: EXCEPTION_SINGLE_STEP");
238 break;
239 case EXCEPTION_STACK_OVERFLOW:
240 LOGSIG("Error: EXCEPTION_STACK_OVERFLOW");
241 break;
242 default:
243 LOGSIG("Error: Unrecognized Exception");
244 break;
245 }
246 /* If this is a stack overflow then we can't walk the stack, so just show
247 where the error happened */
248 if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
249 windows_print_stacktrace(ExceptionInfo->ContextRecord);
250 } else {
251 addr2line(__n_stack_traced_progam_name, (void*)ExceptionInfo->ContextRecord->Rip);
252 }
253 fflush(stdout);
254 fflush(stderr);
255 return EXCEPTION_EXECUTE_HANDLER;
256} /* windows_exception_handler( ... ) */
257
262void set_signal_handler(const char* progname) {
264 SetUnhandledExceptionFilter(windows_exception_handler);
265}
266#else
269
274 int i, trace_size = 0;
275 char** messages = (char**)NULL;
276
277 memset(stack_traces, 0, MAX_STACK_FRAMES * sizeof(void*));
278
279 trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
280 messages = backtrace_symbols(stack_traces, trace_size);
281 if (!messages) return;
282
283 /* skip the first couple stack frames (as they are this function and
284 our handler) and also skip the last frame as it's (always?) junk. */
285 // for (i = 3; i < (trace_size - 1); ++i)
286 for (i = 0; i < trace_size; ++i) // we'll use this for now so you can see what's going on
287 {
289 LOGSIG(" error determining line # for: %s", messages[i]);
290 }
291 }
292 if (messages) {
293 free(messages);
294 }
295}
296
303// cppcheck-suppress constParameterCallback ; callback signature must match system typedef
304void posix_signal_handler(int sig, siginfo_t* siginfo, void* context) {
305 (void)context;
306 switch (sig) {
307 case SIGSEGV:
308 LOGSIG("Caught SIGSEGV: Segmentation Fault");
309 break;
310 case SIGINT:
311 LOGSIG("Caught SIGINT: Interactive attention signal, (usually ctrl+c)");
312 break;
313 case SIGFPE:
314 switch (siginfo->si_code) {
315 case FPE_INTDIV:
316 LOGSIG("Caught SIGFPE: (integer divide by zero)");
317 break;
318 case FPE_INTOVF:
319 LOGSIG("Caught SIGFPE: (integer overflow)");
320 break;
321 case FPE_FLTDIV:
322 LOGSIG("Caught SIGFPE: (floating-point divide by zero)");
323 break;
324 case FPE_FLTOVF:
325 LOGSIG("Caught SIGFPE: (floating-point overflow)");
326 break;
327 case FPE_FLTUND:
328 LOGSIG("Caught SIGFPE: (floating-point underflow)");
329 break;
330 case FPE_FLTRES:
331 LOGSIG("Caught SIGFPE: (floating-point inexact result)");
332 break;
333 case FPE_FLTINV:
334 LOGSIG("Caught SIGFPE: (floating-point invalid operation)");
335 break;
336 case FPE_FLTSUB:
337 LOGSIG("Caught SIGFPE: (subscript out of range)");
338 break;
339 default:
340 LOGSIG("Caught SIGFPE: Arithmetic Exception");
341 break;
342 }
343 break;
344 case SIGILL:
345 switch (siginfo->si_code) {
346 case ILL_ILLOPC:
347 LOGSIG("Caught SIGILL: (illegal opcode)");
348 break;
349 case ILL_ILLOPN:
350 LOGSIG("Caught SIGILL: (illegal operand)");
351 break;
352 case ILL_ILLADR:
353 LOGSIG("Caught SIGILL: (illegal addressing mode)");
354 break;
355 case ILL_ILLTRP:
356 LOGSIG("Caught SIGILL: (illegal trap)");
357 break;
358 case ILL_PRVOPC:
359 LOGSIG("Caught SIGILL: (privileged opcode)");
360 break;
361 case ILL_PRVREG:
362 LOGSIG("Caught SIGILL: (privileged register)");
363 break;
364 case ILL_COPROC:
365 LOGSIG("Caught SIGILL: (coprocessor error)");
366 break;
367 case ILL_BADSTK:
368 LOGSIG("Caught SIGILL: (internal stack error)");
369 break;
370 default:
371 LOGSIG("Caught SIGILL: Illegal Instruction");
372 break;
373 }
374 break;
375 case SIGTERM:
376 LOGSIG("Caught SIGTERM: a termination request was sent to the program");
377 break;
378 case SIGABRT:
379 LOGSIG("Caught SIGABRT: usually caused by an abort() or assert()");
380 break;
381 default:
382 break;
383 }
385 _Exit(1);
386}
387
392void set_signal_handler(const char* progname) {
394
395#ifdef RLIMIT_STACK
396 /* Before starting the endless recursion, try to be friendly to the user's
397 machine. On some Linux 2.2.x systems, there is no stack limit for user
398 processes at all. We don't want to kill such systems. */
399 struct rlimit rl;
400 rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
401 /* cppcheck-suppress ConfigurationNotChecked ; RLIMIT_STACK is a system macro, value not relevant to static analysis */
402 setrlimit(RLIMIT_STACK, &rl);
403#endif
404
405#ifdef __linux__
406 /* setup alternate stack */
407 {
409 static uint8_t alternate_stack[SIGALTSTACK_SIZE];
410 stack_t ss;
411
412 /* malloc is usually used here, I'm not 100% sure my static allocation
413 is valid but it seems to work just fine. */
414 ss.ss_sp = alternate_stack;
415 ss.ss_size = sizeof(alternate_stack);
416 ss.ss_flags = 0;
417
418 if (sigaltstack(&ss, NULL) != 0) {
419 err(1, "sigaltstack");
420 }
421 }
422#endif
423 /* register our signal handlers */
424 {
425 struct sigaction sig_action;
426 sigemptyset(&sig_action.sa_mask);
427 sig_action.sa_sigaction = posix_signal_handler;
428
429#ifdef __APPLE__
430 /* for some reason we backtrace() doesn't work on osx
431 when we use an alternate stack */
432 sig_action.sa_flags = SA_SIGINFO;
433#else
434 sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
435#endif
436
437 if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
438 err(1, "sigaction");
439 }
440 if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
441 err(1, "sigaction");
442 }
443 if (sigaction(SIGINT, &sig_action, NULL) != 0) {
444 err(1, "sigaction");
445 }
446 if (sigaction(SIGILL, &sig_action, NULL) != 0) {
447 err(1, "sigaction");
448 }
449 /* No TERM catch
450 if (sigaction(SIGTERM, &sig_action, NULL) != 0) { err(1, "sigaction"); } */
451 if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
452 err(1, "sigaction");
453 }
454 }
455}
456#endif
char * addr
char * progname
Definition ex_signals.c:50
int n_popen(char *cmd, size_t read_buf_size, void **nstr_output, int *ret)
launch a command and return output and status
Definition n_common.c:180
char * data
the string
Definition n_str.h:63
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
A box including a string and his lenght.
Definition n_str.h:61
#define SIGALTSTACK_SIZE
Size of the signal handler alternate stack.
Definition n_signals.h:40
#define MAX_STACK_FRAMES
Number of backtrace log lines.
Definition n_signals.h:42
void set_signal_handler(const char *progname)
Install a signal handler for progname.
Definition n_signals.c:392
Generic log system.
static void * stack_traces[32]
static frame list
Definition n_signals.c:268
static const char * __n_stack_traced_progam_name
name of program to debug (addr2line & co)
Definition n_signals.c:51
int addr2line(char const *const program_name, void const *const addr)
Resolve symbol name and source location given the path to the executable and an address.
Definition n_signals.c:104
#define LOGSIG
internal: output to syslog
Definition n_signals.c:48
void posix_print_stack_trace()
print current stack
Definition n_signals.c:273
void posix_signal_handler(int sig, siginfo_t *siginfo, void *context)
decode a signal and call stack print
Definition n_signals.c:304
Signals general handling with stack printing, from https://gist.github.com/jvranish/4441299.
N_STR and string function declaration.