PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Invalid Operation fangen *ohne* SEH


zeckensack
2005-02-20, 09:54:16
Ich habe diesen Code, der auch wunderprächtig funktioniert:
;*** execute an SSE instruction that will raise an exception if the OS doesn't support SSE
;prototype:
;void __stdcall SSE_try_OS_support()
SSE_try_OS_support@0:
ORPS xmm0,xmm0
RETN
bool
Cpu::got_SSE()
{
if (!sse_verified)
{
__try
{
SSE_try_OS_support();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
feature_mask&=~8;
}
sse_verified=true;
}

if (feature_mask&8) return(true);
else return(false);
}Das ist allerdings "structured exception handling". Ich würde das ganze gerne mit C++-Exceptions machen. Nun weiß ich nicht ob das mit CPU-generierten Exceptions funktioniert, und wenn ja, weiß ich nicht wie die passende Exception-Klasse heißt.

Grund für den Umwandlungswunsch ist iÜ dass ich Kompilierbarkeit auf MinGW anstrebe. Wenn's da noch eine einfachere Lösung geben sollte, immer her damit.

Demirug
2005-02-20, 11:02:50
Man kann SEH Exceptions in normale C++ Exceptions wandeln. Man braucht dafür eine Translator Funktion mit der man dann selbst bestimmt welcher C++ Exception eine SEH Exception entsprechen soll. Das ganze ist aber eine Funktion der der Runtime und damit nicht direkt von einem Compiler zum nächsten übertragbar.

In Verbindung mit Managed C++ ist das alles schon hinterlegt und es wird eine SEHException geworfen. Das bringt dir aber hier nichts weil Managed C++ derezeit ja nur von den MS Compilern unterstützt wird.

Man kann SEH auch ohne Compilerunterstützung betreiben und direkt die Funktionen des Betriebssystems dafür nutzen aber das ist ganz übel.

Ich könnte aber notfalls die ganzen Links zu den entsprechenden Artikeln raussuchen die das mal beschrieben haben.

zeckensack
2005-02-20, 11:11:29
Man kann SEH auch ohne Compilerunterstützung betreiben und direkt die Funktionen des Betriebssystems dafür nutzen aber das ist ganz übel.Meinst du SetUnhandledExceptionFilter?
Das habe ich auch schonmal in Benutzung gehabt, allerdings für etwas anderes. Ich hatte damit auch so meine Probleme, weil im Kontext einer Bibliothek kommt's ja leider auch immer darauf an ob der Host-Prozess sauber programmiert ist.

Einmalig in einer Initialisierungsfunktion müsste es aber eigentlich noch gehen. Werde ich mal testen :)

Demirug
2005-02-20, 11:25:13
Meinst du SetUnhandledExceptionFilter?
Das habe ich auch schonmal in Benutzung gehabt, allerdings für etwas anderes. Ich hatte damit auch so meine Probleme, weil im Kontext einer Bibliothek kommt's ja leider auch immer darauf an ob der Host-Prozess sauber programmiert ist.

Einmalig in einer Initialisierungsfunktion müsste es aber eigentlich noch gehen. Werde ich mal testen :)

SetUnhandledExceptionFilter sollte man wirklich nur einmal in einer Initialisierungsfunktion benutzen. Was ich meinte sind Sachen die im offiziellen SDK nicht dokumentiert sind. Nämlich wie __try, __catch use implementiert sind.

Wenn es dir aber primär erst mal nur darum geht die SEH Teile aus dem Code zu bekommen ist wie gesagt der Translator eine gute Lösung. Die Funktion um diesen zu nutzen ist _set_se_translator. Dort gibt es auch ein Beispiel wie man das genau macht.

zeckensack
2005-02-20, 11:39:58
SetUnhandledExceptionFilter sollte man wirklich nur einmal in einer Initialisierungsfunktion benutzen.Oooch ...
Wenn der Host-Prozess sauber ist, kann man das auch viele tausend mal machen :)
Man muss natürlich den vorherigen Handler sichern, bei Exceptions die einen selbst nicht interessieren delegieren, und ihn später, wenn man seinen EH nicht mehr braucht, wiederherstellen.
Das sind btw auch die Anforderungen die ein sauberer Host-Prozess erfüllen muss.
Was ich meinte sind Sachen die im offiziellen SDK nicht dokumentiert sind. Nämlich wie __try, __catch use implementiert sind.Das interessiert mich so langsam. Denn mein kleines Experiment gerade mit SUHEF ist recht unerwartet gescheitert. Mit Speicherfehlern dagegen (ich habe exzessiv guard pages genutzt) klappt's :conf2:
(ich habe eine SSE3-Operation ausprobiert, weil meine CPU und mein OS ja definitiv SSE und SSE2 unterstützen)

Wenn es dir aber primär erst mal nur darum geht die SEH Teile aus dem Code zu bekommen ist wie gesagt der Translator eine gute Lösung. Die Funktion um diesen zu nutzen ist _set_se_translator. Dort gibt es auch ein Beispiel wie man das genau macht.Eine kurze Suche im "include"-Verzeichnis ergab, dass MinGW _set_se_translator nicht kennt :(

edit:
Mein Experiment sollte in dieser Form (auskommentiertes Zeug) eigentlich alle Exceptions fangen können ...
#ifdef PEDESTRIAN_SSE_CHECK

static LPTOP_LEVEL_EXCEPTION_FILTER app_exception_handler;
static bool SSE_failure=false;

static LONG __stdcall
SSE_exception_handler(LPEXCEPTION_POINTERS ep)
{
// if (ep->ExceptionRecord->ExceptionCode==EXCEPTION_ILLEGAL_INSTRUCTION)
{
SSE_failure=true;
return(EXCEPTION_CONTINUE_EXECUTION);
}
/* else
{
if (app_exception_handler!=NULL) return(app_exception_handler(ep));
else return(EXCEPTION_CONTINUE_SEARCH);
}*/
}
#endif

Cpu::Cpu():
sse_verified(false)
{
//delegate to assembly module (see misc.asm)
feature_mask=detect_cpu_features();
#ifdef PEDESTRIAN_SSE_CHECK
if (feature_mask&8)
{
app_exception_handler=SetUnhandledExceptionFilter(SSE_exception_handler);
SSE_try_OS_support();
SetUnhandledExceptionFilter(app_exception_handler);
if (SSE_failure) feature_mask&=~8;
}
sse_verified=true;
#else
if (feature_mask&8) sse_verified=false;
else sse_verified=true;
#endif

SYSTEM_INFO si;
GetSystemInfo(&si);
_page_size=si.dwPageSize;
//HACK: emulate behaviour on 2MB page machines
// _page_size=1<<21;
}

zeckensack
2005-02-20, 12:44:19
Hmmm (http://msdn.microsoft.com/library/en-us/vclib/html/_CRT_signal.asp) =)
Mal ausprobieren.

zeckensack
2005-02-20, 17:20:28
Hier meine Lösung des Problems in NASM
;*** a stab at implementing the SSE-trial with structured exception handling
;done in assembler to avoid MinGW's SEH support mess

;some global data (don't know if registers can be returned from an EH)
SECTION .data
exception_fired: dd 0

SECTION .code

;the exception handler
;follows the form
;LONG __cdecl handler(EXCEPTION_RECORD*,void*,_CONTEXT*,void*)
SSE_SEH_handler:
MOV EAX,[ESP+12] ;get pointer to execution context
;check if it was our code that raised the exception
CMP dword [EAX+184],SSE_trial.pre_death ;note: EAX+184 points to the evil EIP
JNZ .not_ours

INC dword [exception_fired]
;mangle EIP to point past the invalid instruction
MOV dword [EAX+184],SSE_trial.past_death

;now we need a return value
XOR EAX,EAX ;0=EXCEPTION_CONTINUE_EXECUTION
RETN

.not_ours:
MOV EAX,1 ;1=EXCEPTION_CONTINUE_SEARCH
RETN

;the actual trial function
;takes no arguments, returns 1 if SSE support is working, 0 if not
SSE_trial:
XOR EAX,EAX
MOV [exception_fired],EAX

;set up SEH (should be equivalent to a __try {} block)
PUSH SSE_SEH_handler ;push handler address
PUSH dword [FS:0] ;chain old exception frame (if any, or, whatever)
MOV [FS:0],ESP ;put pointer to new exception frame to TLS[0]

;do the trial
;*only* *one* *instruction* please
;to not interfere with other exceptions, the handler will check
;if EIP==pre_death
;this won't work if the exception is caused somewhere after the label
.pre_death:
ORPS xmm0,xmm0 ;SSE instruction (non-destructive)
.past_death:
;clean up our mess
MOV EAX,[ESP]
MOV dword [FS:0],EAX
ADD ESP,8

MOV EAX,[exception_fired]
;we want to return 1 if no exception fired, 0 if an exception fired
;so we'll leave the "inverted" value of exception_filtered in EAX
XOR EAX,1
RETN