Structured Exception Handler and Delphi
I am trying to set SEH
without using try except
T开发者_如何学运维he following code doesn't work
type
TSeh = packed record
OldSeh:DWORD;
NewSeh:DWORD;
end;
procedure test;
begin
WriteLn('Hello from seh');
end;
var
eu:TSeh;
old_seh:DWORD;
begin
asm
mov eax,fs:[0]
mov old_seh,eax
end;
eu.OldSeh := old_seh;
eu.NewSeh := DWORD(@test);
asm
mov eax,offset eu
mov fs:[0],eax
ret //This will cause an exception because jumps on an invalid memory address
end;
end.
But this does
procedure test;
begin
WriteLn('Hello from seh');
end;
begin
asm
push offset test
push fs:[0]
mov fs:[0],esp
ret //This will cause an exception because jumps on an invalid memory address
end;
end.
What am I doing wrong? What is the difference between the first code and the second one?
Windows requires all stack frames to be inside the stack allocated by the system. It also requires the stack frames to be in sequential order on the stack. Furthermore, for exception handling, it requires all 'exception records' to be on the stack, and for them to chain in a sequential order through stack memory.
I figured this out/read this somewhere years ago while writing a micro-thread library (http://www.eternallines.com/microthreads).
You can't use test
procedure as exception callback function because exception callback function have different prototype. Read Matt Pietrek article, IMO the best source of information about Win32 SEH.
Update
For the further investigations I would recommend the following changes in the code to make the problem a bit more clean:
function test: Integer;
begin
WriteLn('Hello from seh');
Result:= 0;
end;
(because exception callback should return integer value in EAX)
And for the first code snippet
begin
asm
mov eax,fs:[0]
mov old_seh,eax
end;
eu.OldSeh := old_seh;
eu.NewSeh := Cardinal(@test);
asm
lea eax, eu
mov fs:[0],eax
mov ds:[0],eax //This will cause an AV exception
end;
end.
Now you see that the exception is handled correctly as:
---------------------------
Debugger Fault Notification
---------------------------
Project C:\Users\Serg\Documents\RAD Studio\Projects\Project13.exe faulted with
message: 'access violation at 0x004050f5: write of address 0x00000000'. Process
Stopped. Use Step or Run to continue.
---------------------------
but not by your exception handler. Probably OS ignores exception registration records that are not stack-based (OS can easily do it because it knows minimum and maximum stack values)
For the first code, the TSeh
is on the DATA global section of the executable, whereas the 2nd code stores it on the Stack.
This is IMHO where the difference is. The _EXCEPTION_REGISTRATION_RECORD structure should probably be on stack. Don't know why, honestly (some low-level SS register trick?).
To raise an exception, you should better try something like a division per zero or an access to a nil absolute address:
PInteger(nil)^ := 0; // will always raise an exception
asm
xor eax,eax
mov [eax],eax // will always raise an exception
end;
About how to intercept exceptions in Delphi, take a look at this article. In fact, Delphi add some custom layer over SEH over Windows.
And note also that the exception handling changes in Win64 mode. Worth reading when moving to up to come Delphi XE2.
精彩评论