如何在Linux中捕捉分段错误?

我需要捕获第三方库清理操作中的分段错误。 有时候会在我的程序退出之前发生,而我无法解决这个问题的真正原因。 在Windows编程中,我可以用__try – __catch来做到这一点。 是否有跨平台或平台特定的方式来做同样的事情? 我需要在Linux,gcc。

在Linux上,我们也可以将这些作为例外。

通常,当程序执行分段错误时,会发送一个SIGSEGV信号。 您可以为此信号设置自己的处理程序,并减轻后果。 当然,你应该确定你可以从情况中恢复过来。 在你的情况下,我认为,你应该debugging你的代码。

回到主题。 我最近遇到了一个库 ( 简短手册 ),将这些信号转换为exception,因此您可以编写如下代码:

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

虽然没有检查。 适用于我的x86-64 Gentoo盒子。 它有一个特定于平台的后端(从gcc的java实现中借用),所以它可以在很多平台上工作。 它只是支持x86和x86-64开箱即用,但是你可以从libjava获得后端,它驻留在gcc源代码中。

这是一个如何在C中执行的例子

 #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> 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; } 

在这里findC ++解决scheme( http://www.cplusplus.com/forum/unices/16430/

 #include <signal.h> #include <stdio.h> #include <unistd.h> 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); } } 

有时我们想要捕获一个SIGSEGV来查明一个指针是否有效,也就是说,如果它引用了一个有效的内存地址。 (或者甚至检查一些任意值是否可能是一个指针。)

一个select是用isValidPtr() (在Android上工作isValidPtr()检查它:

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

另一个select是读取内存保护属性,这有点棘手(在Android上工作):

re_mprot.c:

 #include <errno.h> #include <malloc.h> //#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 <alloca.h> #include "re_bits.h" #include <sys/mman.h> 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()到Android日志。 这里定义FIRST_UNUSED_BIT()

PPS在循环中调用alloca()可能不是一个好主意 – 内存可能不会被释放,直到函数返回。