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