Распечатать причину ошибки сегментации

Скажем, у меня есть код, который вызывает ошибку сегментации.

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

Как напечатать на время выполнения, адрес в памяти, в котором произошла ошибка сегментации, и причина сбоя сегментации (доступ к области запрещенной памяти или что-то еще).

Я читал о файлах дампа ядра, но я не уверен, что это правильное решение.

Как я могу это сделать?

PS, мне известно, что я могу добиться этого, используя gdb или другой отладчик, но цель состоит в том, чтобы сделать это, используя код и только код.

Ответ 1

Если вы хотите узнать причину, вы можете зарегистрировать обработчик сигнала, например:

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);
}

И тогда где-то вам нужно его зарегистрировать:

  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");
  }

В основном вы регистрируете сигнал, который срабатывает при доставке SIGSEGV, и вы получаете дополнительную информацию, чтобы процитировать справочную страницу:

   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

Они сопоставляются с двумя основными причинами получения ошибки seg: либо страница, к которой вы обращались, не была отображена вообще, либо вам не разрешалось выполнять любую операцию, которую вы пытались выполнить на этой странице.

Здесь, после срабатывания обработчика сигнала, он отменяет регистрацию и заменяет действие по умолчанию. Это приводит к тому, что операция не выполняется снова, поэтому ее можно поймать обычным маршрутом. Это нормальное поведение ошибки страницы (предшественник для получения seg-сбоя), так что такие вещи, как работа с поисковым вызовом, работают.

Ответ 2

Как уже было сказано здесь: Как сгенерировать stacktrace при сбое моего приложения gcc С++

Вы можете (в случае GCC с Linux/BSD как минимум) сделать это довольно легко:

Пример кода:

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


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

  // get void* 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 */
}

Пример вывода:

# 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