It’s kind of annoying to have to go back and run GDB after your program crashes. Here’s a signal handler I’ve been using which drops you into GDB straight away:

static void gdb_sighandler(int sig, siginfo_t *info)
{
   char exe[256];
   if (readlink("/proc/self/exe", exe, sizeof(exe)) < 0) {
      perror("readlink");
      exit(EXIT_FAILURE);
   }
 
   char pid[16];
   snprintf(pid, sizeof(pid), "%d", getpid());
 
   pid_t p = fork();
   if (p == 0) {
      execl("/usr/bin/gdb", "gdb", "-ex", "cont", 
            exe, pid, NULL);
      perror("execl");
      exit(EXIT_FAILURE);
   }
   else if (p < 0) {
      perror("fork");
      exit(EXIT_FAILURE);
   }
   else {
      // Allow a little time for GDB to start before 
      // dropping into the default signal handler
      sleep(1);
      signal(sig, SIG_DFL);
   }
}

This probably breaks all sorts of rules on what you shouldn’t do in a signal handler but hey, it was going to crash anyway. Resetting the handler to SIG_DFL at the end is important or you’ll end up stuck in a loop when you try to quit GDB. Install it for all the unpleasant signals like this:

struct sigaction sa;
sa.sa_sigaction = (void*)gdb_sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
 
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);

It’s probably worth checking whether GDB is already running before you do this. On Linux you can check this by trying to ptrace yourself: if it succeeds then a debugger isn’t attached.

static int is_debugger_running(void)
{
   pid_t pid = fork();
   if (pid == -1) {
      perror("fork");
      exit(EXIT_FAILURE);
   }
   else if (pid == 0) {
      int ppid = getppid();
      if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0) {
         waitpid(ppid, NULL, 0);
         ptrace(PTRACE_CONT, NULL, NULL);
         ptrace(PTRACE_DETACH, getppid(), NULL, NULL);
         exit(0);
      }
      else
         exit(1);
   }
   else {
      int status;
      waitpid(pid, &status, 0);
      return WEXITSTATUS(status);
   }
}