print the segmentation fault reason

11,454

Solution 1

If you want to know the cause you can register a signal handler, something like:

void handler(int signum, siginfo_t *info, void *context)
{
  struct sigaction action = {
    .sa_handler = SIG_DFL,
    .sa_sigaction = NULL,
    .sa_mask = 0,
    .sa_flags = 0,
    .sa_restorer = NULL
  };

  fprintf(stderr, "Fault address: %p\n", info->si_addr);
  switch (info->si_code) {
  case SEGV_MAPERR:
    fprintf(stderr, "Address not mapped.\n");
    break;

  case SEGV_ACCERR:
    fprintf(stderr, "Access to this address is not allowed.\n");
    break;

  default:
    fprintf(stderr, "Unknown reason.\n");
    break;
  }

  /* unregister and let the default action occur */
  sigaction(SIGSEGV, &action, NULL);
}

And then somewhere you need to register it:

  struct sigaction action = {
    .sa_handler = NULL,
    .sa_sigaction = handler,
    .sa_mask = 0,
    .sa_flags = SA_SIGINFO,
    .sa_restorer = NULL
  };


  if (sigaction(SIGSEGV, &action, NULL) < 0) {
    perror("sigaction");
  }

Basically you register a signal that fires when SIGSEGV is delivered, and you get some additional info, to quote the man page:

   The following values can be placed in si_code for a SIGSEGV signal:

       SEGV_MAPERR    address not mapped to object

       SEGV_ACCERR    invalid permissions for mapped object

These map to the two basic reasons for getting a seg fault -- either the page you accessed wasn't mapped at all, or you weren't allowed to perform whatever operation you attempted to that page.

Here after the signal handler fires it unregisters itself and replaces the default action. This causes the operation that failed to be performed again so it can be caught by the normal route. This is the normal behavior of a page fault (the precursor to getting a seg fault) so that things like demand paging work.

Solution 2

As already answered here: How to generate a stacktrace when my gcc C++ app crashes

You can (in the case of GCC with Linux/BSD at least) do this fairly easy:

Example code:

#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>


void handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, 2);
  exit(1);
}

int main(int argc, char **argv) {
  signal(SIGSEGV, handler);   // install our handler

  char * ptr = NULL;
  *ptr = "hello"; /* this will cause a segmentation fault */
}

Example output:

# gcc -g -rdynamic -o test test.c
# ./test
Error: signal 11:
0   test                                0x000000010e99dcfa handler + 42
1   libsystem_c.dylib                   0x00007fff95c1194a _sigtramp + 26
2   ???                                 0x0000000000000000 0x0 + 0
3   libdyld.dylib                       0x00007fff8fa177e1 start + 0
4   ???                                 0x0000000000000001 0x0 + 1
Share:
11,454
Lior
Author by

Lior

SOreadytohelp

Updated on June 03, 2022

Comments

  • Lior
    Lior almost 2 years

    Let's say I have a code that causes segmentation fault.

    char * ptr = NULL;
    *ptr = "hello"; /* this will cause a segmentation fault */
    

    How do I print on runtime, the address in memory that the segmentation fault occurred at, and the reason for the segmentation fault (accessing to forbidden memory region, or something else).

    I read about core dump files, but I'm not sure if it's the correct solution.

    How can I do this?

    P.S, I'm aware to do fact that I can achieve that by using gdb, or an other debugger, but the purpose is to do this by using code, and only code.

  • Christian.K
    Christian.K about 11 years
    This will not produce a core dump anymore, at least not one with the original cause of the error. Fine, you have some sort of stacktrace, but with much less value. No offence, you we're only answering the OPs question, but warnings do apply anyway ;-). Also, consider using a combination of sprintf and write(2, buf, ...) to avoid fprintf which could be tricky in a signal handler (well at least for async signals). Also, I wouldn't call exit() but only _exit() or abort(). But YMMV.
  • Wolph
    Wolph about 11 years
    I agree with you Christian, arguably you could enable this for certain builds if you really don't want to send a core dump. But personally I would just wrap the executable in a script that lets gdb (if available) show the stacktrace from the generated core dump... Seems a much more sensible solution.