Ale*_*x F 67 c++ try-catch segmentation-fault
我需要在第三方库清理操作中捕获分段错误.这有时会在我的程序退出之前发生,我无法解决这个问题的真正原因.在Windows编程中,我可以使用__try - __catch执行此操作.是否有跨平台或平台特定的方式来做同样的事情?我需要在Linux,gcc中使用它.
P S*_*ved 65
在Linux上,我们也可以将这些作为例外.
通常,当程序执行分段故障时,会发送一个SIGSEGV
信号.您可以为此信号设置自己的处理程序并减轻后果.当然,你应该确定你可以从这种情况中恢复过来.在您的情况下,我认为,您应该调试代码.
回到主题.我最近遇到了一个库 (简短手册),它将这些信号转换为异常,因此您可以编写如下代码:
try
{
*(int*) 0 = 0;
}
catch (std::exception& e)
{
std::cerr << "Exception caught : " << e.what() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
但是没有检查.适用于我的x86-64 Gentoo盒子.它有一个特定于平台的后端(借用gcc的java实现),因此它可以在许多平台上运行.它只支持开箱即用的x86和x86-64,但你可以从libjava获得后端,它位于gcc源代码中.
Jay*_*ayM 35
这是一个如何在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;
}
Run Code Online (Sandbox Code Playgroud)
Jul*_*tte 12
为了可移植性,可能应该使用std::signal
标准的 C++ 库,但是信号处理程序可以做什么有很多限制。不幸的是,在不引入未定义行为的情况下从 C++ 程序中捕获 SIGSEGV 是不可能的,因为规范说:
abort
, exit
,一些原子函数,重新安装当前信号处理程序,memcpy
,memmove
,类型特征,std::move
,std::forward
等等)之外,从处理程序中调用任何库函数都是未定义的行为。throw
表达式,则这是未定义的行为。这证明使用严格标准和可移植的 C++从程序中捕获 SIGSEGV 是不可能的。SIGSEGV 仍然被操作系统捕获,并且通常在调用等待系列函数时报告给父进程。
您可能会在使用 POSIX 信号时遇到同样的问题,因为在2.4.3 Signal Actions 中有一个子句:
它从一个信号捕获函数通常返回为不是由产生的SIGBUS,SIGFPE,SIGILL或SIGSEGV信号之后的过程的行为是未定义的
kill()
,sigqueue()
或raise()
。
关于longjump
s的一句话。假设我们使用 POSIX 信号,longjump
用于模拟堆栈展开将无济于事:
虽然
longjmp()
是异步信号安全函数,但如果它是从中断非异步信号安全函数或等效函数的信号处理程序调用的(例如exit()
从初始调用返回后执行的等效处理main()
),则对非异步信号安全函数或等效函数的任何后续调用的行为未定义。
这意味着对 longjump 的调用所调用的延续不能可靠地调用通常有用的库函数,例如printf
,malloc
或exit
从 main 返回,而不会引起未定义的行为。因此,continuation 只能进行受限操作,并且只能通过某种异常终止机制退出。
简而言之,在不引入 UB 的情况下,捕获 SIGSEGV并在便携式中恢复程序的执行可能是不可行的。即使您在 Windows 平台上工作,您可以访问结构化异常处理,值得一提的是 MSDN 建议永远不要尝试处理硬件异常:Hardware Exceptions。
最后但并非最不重要的是,在取消引用空值指针(或无效值指针)时是否会引发任何 SIGSEGV 不是标准的要求。因为通过空值指针或任何无效值指针的间接性是未定义行为,这意味着编译器假定您的代码永远不会在运行时尝试这样的事情,编译器可以自由地进行代码转换以消除此类未定义行为。例如,从 cppreference,
int foo(int* p) {
int x = *p;
if(!p)
return x; // Either UB above or this branch is never taken
else
return 0;
}
int main() {
int* p = nullptr;
std::cout << foo(p);
}
Run Code Online (Sandbox Code Playgroud)
在这里,if
编译器可以完全忽略的真实路径作为优化;只能else
保留一部分。否则,编译器推断foo()
永远不会在运行时收到空值指针,因为它会导致未定义的行为。使用空值指针调用它,您可能会观察到0
打印到标准输出的值并且没有崩溃,您可能会观察到 SIGSEG 的崩溃,实际上您可以观察到任何事情,因为没有对没有未定义行为的程序强加合理的要求.
这里找到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);
}
}
Run Code Online (Sandbox Code Playgroud)
有时我们想抓住一个 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);
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是读取内存保护属性,这有点棘手(适用于 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;
}
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
PSDLOG()
是printf()
到Android日志。在这里FIRST_UNUSED_BIT()
定义。
PPS在循环中调用alloca()可能不是一个好主意——在函数返回之前可能不会释放内存。
归档时间: |
|
查看次数: |
97477 次 |
最近记录: |