Come catturare l’errore di segmentazione in Linux?

Devo rilevare l’errore di segmentazione nelle operazioni di pulizia delle librerie di terze parti. Ciò accade a volte poco prima che il mio programma venga chiuso e non riesco a risolvere il vero motivo di ciò. Nella programmazione di Windows ho potuto farlo con __try – __catch. Esiste un modo multipiattaforma o specifico per la piattaforma di fare lo stesso? Ho bisogno di questo in Linux, gcc.

Su Linux possiamo avere anche queste come eccezioni.

Normalmente, quando il programma esegue un errore di segmentazione, viene inviato un segnale SIGSEGV . È ansible impostare il proprio gestore per questo segnale e attenuarne le conseguenze. Certo dovresti davvero essere sicuro di poterti recuperare dalla situazione. Nel tuo caso, penso, dovresti eseguire il debug del tuo codice.

Torna all’argomento. Recentemente ho incontrato una libreria (un breve manuale ) che trasforma tali segnali in eccezioni, quindi puoi scrivere un codice come questo:

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

Non ho controllato, però. Funziona con la mia scatola Gentoo x86-64. Ha un back-end specifico per la piattaforma (preso in prestito dall'implementazione java di gcc), quindi può funzionare su molte piattaforms. Supporta appena x86 e x86-64, ma puoi ottenere backend da libjava, che risiede nei sorgenti di gcc.

Ecco un esempio di come farlo in 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; } 

Soluzione C ++ trovata qui ( 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 volte vogliamo prendere un SIGSEGV per scoprire se un puntatore è valido, cioè se fa riferimento a un indirizzo di memoria valido. (O controlla anche se qualche valore arbitrario può essere un puntatore.)

Un’opzione è di verificarla con isValidPtr() (funzionante su 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); } 

Un'altra opzione è leggere gli attributi di protezione della memoria, che è un po 'più complicato (funziona su 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() è printf() nel log di Android. FIRST_UNUSED_BIT() è definito qui .

PPS Potrebbe non essere una buona idea chiamare alloca () in un ciclo: la memoria potrebbe non essere liberata fino a quando la funzione non ritorna.