サードパーティのライブラリクリーンアップ操作でセグメンテーション違反をキャッチする必要があります。これは、プログラムが終了する直前に発生することがあり、この本当の理由を修正することはできません。Windowsプログラミングでは、これは__try --__ catchで実行できます。同じことを行うためのクロスプラットフォームまたはプラットフォーム固有の方法はありますか?Linux、gccでこれが必要です。
サードパーティのライブラリクリーンアップ操作でセグメンテーション違反をキャッチする必要があります。これは、プログラムが終了する直前に発生することがあり、この本当の理由を修正することはできません。Windowsプログラミングでは、これは__try --__ catchで実行できます。同じことを行うためのクロスプラットフォームまたはプラットフォーム固有の方法はありますか?Linux、gccでこれが必要です。
回答:
Linuxでは、これらを例外として使用することもできます。
通常、プログラムがセグメンテーション違反を実行すると、SIGSEGV
シグナルが送信されます。このシグナルに対して独自のハンドラーを設定し、結果を軽減することができます。もちろん、あなたは本当にあなたが状況から回復できることを確信するべきです。あなたの場合、代わりにコードをデバッグする必要があると思います。
トピックに戻ります。最近、このようなシグナルを例外に変換するライブラリ (短いマニュアル)に遭遇したので、次のようなコードを記述できます。
try
{
*(int*) 0 = 0;
}
catch (std::exception& e)
{
std::cerr << "Exception caught : " << e.what() << std::endl;
}
しかし、それをチェックしませんでした。x86-64Gentooボックスで動作します。プラットフォーム固有のバックエンド(gccのJava実装から借用)があるため、多くのプラットフォームで動作できます。そのままでx86とx86-64をサポートしますが、gccソースにあるlibjavaからバックエンドを取得できます。
-fnon-call-exceptions
それが機能することを確認する必要があります。それにはパフォーマンスコストがかかります。また、例外サポートなしの関数(C関数など)からスローされ、後でリーク/クラッシュする危険性もあります。
./build_gcc_linux_release
いくつかのエラーが発生します。
これは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;
}
signal(7)
比較的注意を払わずに使用できるすべての非同期シグナルセーフ関数をリストします。上記の例では、プログラム内の他の何も触れてstdout
いないのでprintf
、ハンドラー内の呼び出し以外も完全に安全です。
ここにある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);
}
}
移植性のために、おそらくstd::signal
標準C ++ライブラリから使用する必要がありますが、シグナルハンドラが実行できることには多くの制限があります。残念ながら、仕様には次のように記載されているため、未定義の動作を導入せずにC ++プログラム内からSIGSEGVをキャッチすることはできません。
abort
、exit
いくつかの原子の機能は、現在のシグナルハンドラを再インストールし、memcpy
、memmove
、タイプの特徴、 `のstd ::移動, std::forward
、およびいくつかのより多くの)。throw
式を使用する場合、これは未定義の動作です。これは、厳密に標準で移植可能なC ++を使用してプログラム内からSIGSEGVをキャッチすることは不可能であることを証明しています。SIGSEGVは引き続きオペレーティングシステムによってキャッチされ、通常、待機時に親プロセスに報告されます。ファミリ関数が呼び出されたれます。
2.4.3シグナルアクションに次のような句があるため、POSIXシグナルを使用すると同じ種類の問題が発生する可能性があります。
それによって生成されなかったSIGBUS、SIGFPE、SIGILL、またはSIGSEGV信号の信号捕捉関数から正常に戻った後、プロセスの挙動が定義されていない
kill()
、sigqueue()
またはraise()
。
について一言longjump
。POSIXシグナルを使用longjump
していると仮定すると、スタックの巻き戻しをシミュレートするためにを使用しても役に立ちません。
けれども
longjmp()
、それが(例えばするための処理と等価の非非同期シグナルセーフ機能または同等の中断シグナルハンドラから呼び出された場合、非同期シグナル安全関数でexit()
の最初の呼び出しから復帰した後に行うとmain()
)、非同期シグナルセーフでない関数または同等の関数への後続の呼び出しの動作は定義されていません。
つまり、longjumpの呼び出しによって呼び出された継続は、などの通常は有用なライブラリ関数を確実に呼び出すことはできませんprintf
。malloc
またはexit
、未定義の動作を引き起こさずにmainから戻ることはできません。そのため、継続は制限された操作のみを実行でき、何らかの異常な終了メカニズムを介してのみ終了する可能性があります。
簡単に言うと、SIGSEGVをキャッチして、ポータブルでプログラムの実行を再開することは、UBを導入しないとおそらく実行不可能です。構造化例外処理にアクセスできるWindowsプラットフォームで作業している場合でも、MSDNがハードウェア例外の処理を決して試みないことを提案していることに言及する価値があります。ハードウェア例外
時々私たちはキャッチしたい SIGSEGV
ポインタが有効かどうか、つまり、有効なメモリアドレスを参照しているかどうかを確認場合があります。(または、任意の値がポインターであるかどうかを確認することもできます。)
1つのオプションはそれをチェックすることです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);
}
もう1つのオプションは、メモリ保護属性を読み取ることです。これは少し注意が必要です(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);
PSDLOG()
はprintf()
Androidログにあります。ここFIRST_UNUSED_BIT()
で定義されています。
PPSループ内でalloca()を呼び出すのは良い考えではないかもしれません-関数が戻るまでメモリが解放されないかもしれません。