如何捕获 Linux 中的分段错误?
我需要在第三方库清理操作中捕获分段错误。这种情况有时会在我的程序退出之前发生,我无法解决其真正原因。在 Windows 编程中,我可以使用 __try - __catch 来做到这一点。是否有跨平台或特定于平台的方法可以做到这一点?我在 Linux 中需要这个,gcc。
I need to catch segmentation fault in third party library cleanup operations. This happens sometimes just before my program exits, and I cannot fix the real reason of this. In Windows programming I could do this with __try - __catch. Is there cross-platform or platform-specific way to do the same? I need this in Linux, gcc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在 Linux 上,我们也可以将这些作为例外。
通常,当您的程序执行分段错误时,会发送一个
SIGSEGV
信号。您可以为此信号设置自己的处理程序并减轻后果。当然,您应该真正确定自己可以从这种情况中恢复过来。对于你的情况,我认为你应该调试你的代码。回到主题。我最近遇到了一个库 (简短手册)将此类信号转换为异常,因此您可以编写如下代码:
但没有检查它。适用于我的 x86-64 Gentoo 盒子。它有一个特定于平台的后端(借用了 gcc 的 java 实现),因此它可以在许多平台上工作。它只支持开箱即用的 x86 和 x86-64,但您可以从 libjava 获取后端,它驻留在 gcc 源代码中。On Linux we can have these as exceptions, too.
Normally, when your program performs a segmentation fault, it is sent a
SIGSEGV
signal. You can set up your own handler for this signal and mitigate the consequences. Of course you should really be sure that you can recover from the situation. In your case, I think, you should debug your code instead.Back to the topic. I recently encountered a library (short manual) that transforms such signals to exceptions, so you can write code like this:
Didn't check it, though.Works on my x86-64 Gentoo box. It has a platform-specific backend (borrowed from gcc's java implementation), so it can work on many platforms. It just supports x86 and x86-64 out of the box, but you can get backends from libjava, which resides in gcc sources.下面是如何在 C 中执行此操作的示例。
Here's an example of how to do it in C.
为了可移植性,人们可能应该使用标准 C++ 库中的
std::signal
,但是信号处理程序的功能有很多限制。不幸的是,在不引入未定义行为的情况下,不可能从 C++ 程序中捕获 SIGSEGV,因为规范指出:abort
、exit
、一些原子函数、重新安装当前信号处理程序、memcpy
、memmove
、类型特征、std::move
、std::forward
等等)。throw
表达式,则这是未定义的行为。这证明使用严格标准和可移植的 C++ 不可能从程序内捕获 SIGSEGV。 SIGSEGV 仍然被操作系统捕获,并且通常在调用 wait 系列函数时报告给父进程。
使用 POSIX 信号您可能会遇到同样的麻烦,因为有一个子句在 2.4.3 信号操作:
关于
跳远
的一句话。假设我们使用 POSIX 信号,使用 longjump 来模拟堆栈展开不会有帮助:这意味着调用 longjump 所调用的延续无法可靠地调用通常有用的库函数,例如 printf、malloc 或 exit 或从 main 返回不会引起未定义的行为。因此,延续只能执行受限操作,并且只能通过某种异常终止机制退出。
简而言之,在不引入未定义行为的情况下,捕获 SIGSEGV 并在便携式设备中恢复程序的执行可能是不可行的。即使您在可以访问结构化异常处理的 Windows 平台上工作,值得一提的是,MSDN 建议永远不要尝试处理硬件异常:硬件异常。
最后但并非最不重要的一点是,在取消引用空值指针(或无效值指针)时是否会引发任何 SIGSEGV 不是标准的要求。因为通过空值指针或任何无效值指针进行间接访问是未定义行为,这意味着编译器假设您的代码在运行时永远不会尝试这样的事情,编译器可以自由地进行代码转换,从而消除这种未定义的行为。例如,来自 cppreference,
这里
if
的真实路径可以被编译器完全忽略作为优化;只能保留else
部分。换句话说,编译器推断 foo() 在运行时永远不会收到空值指针,因为它会导致未定义的行为。使用空值指针调用它,您可能会观察到打印到标准输出的值0
并且没有崩溃,您可能会观察到 SIGSEG 崩溃,事实上您可以观察到任何东西,因为没有强加合理的要求存在未定义行为的程序。For portability, one should probably use
std::signal
from the standard C++ library, but there is a lot of restriction on what a signal handler can do. Unfortunately, it is not possible to catch a SIGSEGV from within a C++ program without introducing undefined behavior because the specification says:abort
,exit
, some atomic functions, reinstall current signal handler,memcpy
,memmove
, type traits,std::move
,std::forward
, and some more).throw
expression.This proves that it is impossible to catch SIGSEGV from within a program using strictly standard and portable C++. SIGSEGV is still caught by the operating system and is normally reported to the parent process when a wait family function is called.
You will probably run into the same kind of trouble using POSIX signal because there is a clause that says in 2.4.3 Signal Actions:
A word about the
longjump
s. Assuming we are using POSIX signals, usinglongjump
to simulate stack unwinding won't help:This means that the continuation invoked by the call to longjump cannot reliably call usually useful library function such as
printf
,malloc
orexit
or return from main without inducing undefined behavior. As such, the continuation can only do a restricted operations and may only exit through some abnormal termination mechanism.To put things short, catching a SIGSEGV and resuming execution of the program in a portable is probably infeasible without introducing undefined behavior. Even if you are working on a Windows platform for which you have access to Structured exception handling, it is worth mentioning that MSDN suggest to never attempt to handle hardware exceptions: Hardware Exceptions.
At last but not least, whether any SIGSEGV would be raised when dereferencing a null valued pointer (or invalid valued pointer) is not a requirement from the standard. Because indirection through a null valued pointer or any invalid valued pointer is an undefined behaviour, which means the compiler assumes your code will never attempt such a thing at runtime, the compiler is free to make code transformation that would elide such undefined behavior. For example, from cppreference,
Here the true path of the
if
could be completely elided by the compiler as an optimization; only theelse
part could be kept. Said otherwise, the compiler infersfoo()
will never receive a null valued pointer at runtime since it would lead to an undefined behaviour. Invoking it with a null valued pointer, you may observe the value0
printed to standard output and no crash, you may observe a crash with SIGSEG, in fact you could observe anything since no sensible requirements are imposed on programs that are not free of undefined behaviors.在这里找到C++解决方案(http://www.cplusplus.com/forum/unices/16430/< /a>)
C++ solution found here (http://www.cplusplus.com/forum/unices/16430/)
有时我们想要捕获 SIGSEGV 来确定指针是否有效,即它是否引用了有效的内存地址。 (或者甚至检查某个任意值是否可能是指针。)
一种选择是使用 isValidPtr() 进行检查(适用于 Android):
另一种选择是读取内存保护属性,这是一个有点棘手(适用于 Android):
re_mrot.c:
re_mrot.h:
PS
DLOG()
是printf()< /code> 到 Android 日志。
FIRST_UNUSED_BIT()
定义为 此处。PPS 在循环中调用alloca()可能不是一个好主意——在函数返回之前内存可能不会被释放。
Sometimes we want to catch a
SIGSEGV
to find out if a pointer is valid, that is, if it references a valid memory address. (Or even check if some arbitrary value may be a pointer.)One option is to check it with
isValidPtr()
(worked on Android):Another option is to read the memory protection attributes, which is a bit more tricky (worked on Android):
re_mprot.c:
re_mprot.h:
PS
DLOG()
isprintf()
to the Android log.FIRST_UNUSED_BIT()
is defined here.PPS It may not be a good idea to call alloca() in a loop -- the memory may be not freed until the function returns.