Как поймать ошибку сегментации в Linux?
Мне нужно поймать ошибку сегментации в операциях очистки сторонних библиотек. Это происходит иногда непосредственно перед выходом моей программы, и я не могу исправить реальную причину этого. В программировании Windows я мог бы сделать это с __try - __catch. Есть ли кросс-платформенный или платформенный способ сделать то же самое? Мне это нужно в Linux, gcc.
4 ответов:
в Linux мы также можем использовать их в качестве исключений.
обычно, когда ваша программа выполняет ошибку сегментации, она отправляется
SIGSEGVсигнал. Вы можете создать свой собственный обработчик для этого сигнала и смягчить последствия. Конечно, вы должны быть уверены, что вы можете выйти из ситуации. В вашем случае, я думаю, вы должны отлаживать свой код вместо этого.вернемся к теме. Я недавно столкнулся библиотека (краткое руководство), который преобразует такие сигналы в исключения, поэтому вы можете написать такой код:
try { *(int*) 0 = 0; } catch (std::exception& e) { std::cerr << "Exception catched : " << e.what() << std::endl; }
не проверять его.работает на моем x86-64 Gentoo box. Он имеет бэкэнд для конкретной платформы (заимствованный из реализации java gcc), поэтому он может работать на многих платформах. Он просто поддерживает x86 и x86-64 из коробки, но вы можете получить backends из 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; }
C++ решение найдено здесь (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чтобы узнать, является ли указатель допустимым, то есть если он ссылается на допустимый адрес памяти. (Или даже проверить, если какое-то произвольное значение может быть указателем.)один из вариантов, чтобы проверить его с
isValidPtr()(работал на 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); }другой вариант-прочитать атрибуты защиты памяти, что немного сложнее (работает 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 () в цикле -- память может быть не освобождена до тех пор, пока функция не вернется.
Comments