### 12/08/06 17:03:16 kovserg:
Доброго времени суток.
Возник вопрос досих пор немогу найти решения.
Что нужно сказать gcc что бы следующий код заработал
try {
int *p=NULL;
struct A { ~A() { printf("a.dtor\n"); } } a;
*p=*p;
} catch(...) {
printf("segmentation fault\n");
}
При возникновении исключения типа access violation или деление на 0
в блок catch(...) программа не попадает, а сразу завершается. При
яем a.dtor не вызывается.
Под windows в VC это решалось ключем компилятора /EHa и кодом вида:
#include <windows.h>
#include <eh.h>
struct MyException {
MyException(int code) : code {}
int code;
};
void trans_Exception( unsigned int u, EXCEPTION_POINTERS* exc) {
throw MyException(u);
}
int init_Exceptions() {
_set_se_translator(trans_Exception); // compile with: /EHa
return 1;
}
int auto_init = init_Exceptions();
В если linux обрабатывать сигналы то не получается перевести сигнал
в exception. Главная неясность как при возникновении сигнала SIGSEGV
сделать так что бы вызвались деструкторы для созданных объектов.
Поставил ряд экспериментов с gcc. Суть такая что при возникновении сигнала
access violation просто вызвать throw и дальше программа должна поити по
отлаженной схеме пройтись по unwind tables и грохнуть созданные объекты.
Но как оказалось gcc очень странно генерить кадры исключений. Вообще
непонятно как. Но повсей видимости привязывается к месту возникновения
исключения. Решение подобной проблеммы видел только для Tru64
там есть встроенная функция преобразования сигналов в exception
exc_raise_signal_exception
http://www.camk.edu.pl/doc/ccc/Programmers_Guide/XCPTCHPX.HTM
Проделать такое в SuSe, Derbian и RedHat неудалось там такого
даже близко нету
. Но ведь неможет такого быть что бы подобная проблемма
не возникала до меня.
Вот код для проведения экспериментов по преобразованию signal -> exception
#include <signal.h>
#include <iostream>
#include <stdexcept>
using namespace std;
static void av_throw() {
//cout << "prepare to die\n";
throw 0xDEAD;
}
void emu_call(sigcontext *regs,int subr,int ret_addr) {
regs->esp-=sizeof(int);
*(int*)regs->esp=ret_addr;
regs->eip=subr;
}
static void sig_action(int signum, siginfo_t *si, void* unk) {
sigcontext *regs=(sigcontext*)((int*)unk+5); // экспериментально подобрано
//printf("sig action %d\n",signum);
//printf("old: eip=%08X esp=%08X\n",regs->eip,regs->esp);
//regs->eip+=7; // 1. это работает (просто пропускаем место аварии)
//regs->eip=(int)av_throw; // 2. симулим jmp -- это падает -> signal_abort
{ // 3. симулим call
emu_call(regs,(int)av_throw,regs->eip+12); // 3a. с этим адресом возврата работает!!!
//emu_call(regs,(int)av_throw,regs->eip+7); // 3b. с этим вызывает abort_signal
// 3b. если добавить ключ -fnon-call-exceptions то тоже работает. правда почему непонятно
//emu_call(regs,(int)av_throw,regs->eip); // 3с. с этим вызывает abort_signal
// видимо его exception_frames как то связаны с адресом возрата
}
//throw 0x33; // 4. просто приводит к abort_signal
//printf("new: eip=%08X esp=%08X\n",regs->eip,regs->esp);
//sigreturn(regs); // warning: sigreturn is not implemented and will always fail
}
void __throw() {
throw 0x12378;
}
void test() {
struct A {
~A() { cout<<"a is dead\n"; }
} a;
int *p=NULL;
*p=*p;
__throw();
}
void sig_test() {
struct sigaction sa, old_sa;
sa.sa_sigaction = &sig_action;
sa.sa_flags = SA_SIGINFO;
cout<<"install signal action\n";
if (sigaction(SIGSEGV, &sa, &old_sa) == -1) throw runtime_error("can't set signal handler");
try {
test();
} catch(int x) {
cout<<"catch.int="<<x<<endl;
} catch(...) {
cout<<"catch.something\n";
}
}
Вариант 3a. работает. Вариант 3b может работать а может и падать
в зависимости от ключа компиляции, вариант 3c всёгда падает.
3a.
install signal action
a is dead
catch.int=57005
Press Enter to continue!
3c.
install signal action
terminate called after throwing an instance of 'int'
/bin/sh: line 1: 7628 Aborted ./threads_tst
Press Enter to continue!
Вот что генерит gcc для void test() собственно осюда смещения +7 и +12
Dump of assembler code for function _Z4testv:
0x0804bb7a <test()+0>: push %ebp
0x0804bb7b <test()+1>: mov %esp,%ebp
0x0804bb7d <test()+3>: push %ebx //
0x0804bb7e <test()+4>: sub $0x14,%esp //
0x0804bb81 <test()+7>: movl $0x0,0xfffffff8(%ebp) //
0x0804bb88 <test()+14>: mov 0xfffffff8(%ebp),%eax //
0x0804bb8b <test()+17>: mov (%eax),%edx // +0 -- page fault point
0x0804bb8d <test()+19>: mov 0xfffffff8(%ebp),%eax //
0x0804bb90 <test()+22>: mov %edx,(%eax) //
0x0804bb92 <test()+24>: call 0x804bb2c <__throw()> // +7 -- point1
0x0804bb97 <test()+29>: lea 0xfffffff7(%ebp),%eax // +12 -- point2
0x0804bb9a <test()+32>: call 0x804bb5a <test()::A::~A()> //
0x0804bb9f <test()+37>: jmp 0x804bbbd <test()+67> //
0x0804bba1 <test()+39>: mov %eax,0xffffffe8(%ebp) // повидимому unwind handler
0x0804bba4 <test()+42>: mov 0xffffffe8(%ebp),%ebx // правда как он его находит
0x0804bba7 <test()+45>: lea 0xfffffff7(%ebp),%eax // пока остаётся загадкой :)
0x0804bbaa <test()+48>: call 0x804bb5a <test()::A::~A()> // a.dtor()
0x0804bbaf <test()+53>: mov %ebx,0xffffffe8(%ebp) //
0x0804bbb2 <test()+56>: sub $0xc,%esp //
0x0804bbb5 <test()+59>: pushl 0xffffffe8(%ebp) //
0x0804bbb8 <test()+62>: call 0x80495c8 <std::runtime_error::~runtime_error()+384> // передача управления дальше
0x0804bbbd <test()+67>: mov 0xfffffffc(%ebp),%ebx
0x0804bbc0 <test()+70>: leave
0x0804bbc1 <test()+71>: ret
End of assembler dump.
Может кто знает как gcc строит свои unwind tables (как он привязан к стеку).
Или може есть какой ключ компиляции который позволяет сменить способ постраения unwind table,
Что бы при возникновении access violation или деления на ноль код обрабатывался по схеме как в 3a ??
Люди help ! очень надо! незнаю что делать.
30.08.06 15:30: Перенесено модератором из 'C/C++' — Павел Кузнецов