Cómo atrapar fallas de segmentación en Linux?

Necesito detectar errores de segmentación en operaciones de limpieza de bibliotecas de terceros. Esto sucede a veces justo antes de que mi progtwig salga, y no puedo arreglar la verdadera razón de esto. En la progtwigción de Windows, podría hacer esto con __try – __catch. ¿Hay una forma multiplataforma o específica de la plataforma para hacer lo mismo? Necesito esto en Linux, gcc.

En Linux podemos tener estos como excepciones, también.

Normalmente, cuando su progtwig realiza una falla de segmentación, se envía una señal SIGSEGV . Puede configurar su propio controlador para esta señal y mitigar las consecuencias. Por supuesto, debes estar seguro de que puedes recuperarte de la situación. En su caso, creo que debería depurar su código.

Regresar al tema Hace poco encontré una biblioteca ( manual breve ) que transforma tales señales en excepciones, para que pueda escribir código como este:

 try { *(int*) 0 = 0; } catch (std::exception& e) { std::cerr < < "Exception catched : " << e.what() << std::endl; } 

No lo revisé, sin embargo. Funciona en mi caja Gentoo x86-64. Tiene un backend específico de la plataforma (tomado de la implementación java de gcc), por lo que puede funcionar en muchas plataformas. Solo admite x86 y x86-64 de fábrica, pero puede obtener los backends de libjava, que reside en fonts de gcc.

Aquí hay un ejemplo de cómo hacerlo en C.

 #include  #include  #include  #include  void segfault_sigaction(int signal, siginfo_t *si, void *arg) { printf("Caught segfault at address %p\n", si->si_addr); exit(0); } int main(void) { int *foo = NULL; struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sigemptyset(&sa.sa_mask); sa.sa_sigaction = segfault_sigaction; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); /* Cause a seg fault */ *foo = 1; return 0; } 

La solución C ++ se encuentra aquí ( http://www.cplusplus.com/forum/unices/16430/ )

 #include  #include  #include  void ouch(int sig) { printf("OUCH! - I got signal %d\n", sig); } int main() { struct sigaction act; act.sa_handler = ouch; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, 0); while(1) { printf("Hello World!\n"); sleep(1); } } 

A veces queremos capturar un SIGSEGV para averiguar si un puntero es válido, es decir, si hace referencia a una dirección de memoria válida. (O incluso compruebe si algún valor arbitrario puede ser un puntero).

Una opción es verificarlo con isValidPtr() (trabajado en Android):

 int isValidPtr(const void*p, int len) { if (!p) { return 0; } int ret = 1; int nullfd = open("/dev/random", O_WRONLY); if (write(nullfd, p, len) < 0) { ret = 0; /* Not OK */ } close(nullfd); return ret; } int isValidOrNullPtr(const void*p, int len) { return !p||isValidPtr(p, len); } 

Otra opción es leer los atributos de protección de memoria, que es un poco más complicado (funcionó en Android):

re_mprot.c:

 #include  #include  //#define PAGE_SIZE 4096 #include "dlog.h" #include "stdlib.h" #include "re_mprot.h" struct buffer { int pos; int size; char* mem; }; char* _buf_reset(struct buffer*b) { b->mem[b->pos] = 0; b->pos = 0; return b->mem; } struct buffer* _new_buffer(int length) { struct buffer* res = malloc(sizeof(struct buffer)+length+4); res->pos = 0; res->size = length; res->mem = (void*)(res+1); return res; } int _buf_putchar(struct buffer*b, int c) { b->mem[b->pos++] = c; return b->pos >= b->size; } void show_mappings(void) { DLOG("-----------------------------------------------\n"); int a; FILE *f = fopen("/proc/self/maps", "r"); struct buffer* b = _new_buffer(1024); while ((a = fgetc(f)) >= 0) { if (_buf_putchar(b,a) || a == '\n') { DLOG("/proc/self/maps: %s",_buf_reset(b)); } } if (b->pos) { DLOG("/proc/self/maps: %s",_buf_reset(b)); } free(b); fclose(f); DLOG("-----------------------------------------------\n"); } unsigned int read_mprotection(void* addr) { int a; unsigned int res = MPROT_0; FILE *f = fopen("/proc/self/maps", "r"); struct buffer* b = _new_buffer(1024); while ((a = fgetc(f)) >= 0) { if (_buf_putchar(b,a) || a == '\n') { char*end0 = (void*)0; unsigned long addr0 = strtoul(b->mem, &end0, 0x10); char*end1 = (void*)0; unsigned long addr1 = strtoul(end0+1, &end1, 0x10); if ((void*)addr0 < addr && addr < (void*)addr1) { res |= (end1+1)[0] == 'r' ? MPROT_R : 0; res |= (end1+1)[1] == 'w' ? MPROT_W : 0; res |= (end1+1)[2] == 'x' ? MPROT_X : 0; res |= (end1+1)[3] == 'p' ? MPROT_P : (end1+1)[3] == 's' ? MPROT_S : 0; break; } _buf_reset(b); } } free(b); fclose(f); return res; } int has_mprotection(void* addr, unsigned int prot, unsigned int prot_mask) { unsigned prot1 = read_mprotection(addr); return (prot1 & prot_mask) == prot; } char* _mprot_tostring_(char*buf, unsigned int prot) { buf[0] = prot & MPROT_R ? 'r' : '-'; buf[1] = prot & MPROT_W ? 'w' : '-'; buf[2] = prot & MPROT_X ? 'x' : '-'; buf[3] = prot & MPROT_S ? 's' : prot & MPROT_P ? 'p' : '-'; buf[4] = 0; return buf; } 

re_mprot.h:

 #include  #include "re_bits.h" #include  void show_mappings(void); enum { MPROT_0 = 0, // not found at all MPROT_R = PROT_READ, // readable MPROT_W = PROT_WRITE, // writable MPROT_X = PROT_EXEC, // executable MPROT_S = FIRST_UNUSED_BIT(MPROT_R|MPROT_W|MPROT_X), // shared MPROT_P = MPROT_S< <1, // private }; // returns a non-zero value if the address is mapped (because either MPROT_P or MPROT_S will be set for valid addresses) unsigned int read_mprotection(void* addr); // check memory protection against the mask // returns true if all bits corresponding to non-zero bits in the mask // are the same in prot and read_mprotection(addr) int has_mprotection(void* addr, unsigned int prot, unsigned int prot_mask); // convert the protection mask into a string. Uses alloca(), no need to free() the memory! #define mprot_tostring(x) ( _mprot_tostring_( (char*)alloca(8) , (x) ) ) char* _mprot_tostring_(char*buf, unsigned int prot); 

PS DLOG() es printf() para el registro de Android. FIRST_UNUSED_BIT() se define aquí .

PPS Puede que no sea una buena idea llamar a alloca () en un bucle: la memoria puede no liberarse hasta que la función regrese.