[fpc-devel] 034881: Request debug info for SEH (finally/except) to prevent regression when win32 switches to SEH
Martin Frb
lazarus at mfriebe.de
Tue Oct 29 20:08:06 CET 2019
About https://bugs.freepascal.org/view.php?id=34881
First of all, big thanks to Sven for the patch.
I had a look at it, I also looked through the alternatives again.
First of all the patch would need some tweaking (but that is to be
expected), but I am not sure it is the best way to go.
Under gdb the issue is that there seems no way to access the data (added
by the patch). And gdb itself does not seem to use it either (it does
read the entries, but does not seem to further access them).
Gdb actually does the same as the IDE does in none seh code. gdb sets
breakpoints at "__cxa_begin_catch".
FpDebug of course could access the data, but I am not sure it is worth
it (At least in the immediate future).
To access the data it is needed to unroll the stack, including through
kernel and 3rd party code. Normal stack unrolling can fail in such cases.
Of course it can be done with the help of the seh data itself. And that
may be a path worth pursuing, even if only to get better stack traces.
(Not sure yet what priority that may get...)
On win64 the unroll data can actually be captured by breaking on
__FPC_specific_handler and using dispatch.ControlPc / So that might be
easy (in fpdebug).
On win32 this is not available. There is some dispatch argument, but I
found no info what it contains.
------------------
I found some alternatives. (Comments welcome)
They do depend on implementation details, but so does having a
breakpoint at fpc_raiseexception, fpc_catch...., or even
__FPC_specific_handler.
*** Detecting on Win64
** Except
On win64, the address of except handlers can already be detected, by
breaking on the kernel's RtlUnwindEx
** Finally
Breaking on __FPC_specific_handler the debugger can access "HandlerData"
(a table with finally addresses for the frame (on each call))
That works as long as those tables do not change in format.
"HandlerData" is the only bit, that is compiler specific. The other
structures are given by the OS.
The detected addresses can be verified, as they must have line info, and
for finally blocks the function name must match .*fin\$.*
All this should work through gdb.
Not sure if it is worth writing dwarf info for __FPC_specific_handler
and its arguments. Then of course the arguments format could be checked,
if they have the correct format.
At the moment, I am quite happy to access them without dbg info.
Alternative it is possible to use dispatch.ControlPc to find the
function in which to look for finally handlers. FpDebug could use that
to combine it with the dwarf from the patch.
But since FpDebug knows the version of fpc that created the file, it
also knows if it can trust the "HandlerData".
*** Detecting on Win32
The debugger can break on __FPC_finally_handler and __FPC_except_handler
Both have frame.HandlerArg as argument which (depending on the other
args) is the address of the next finally/except handler.
Again that relies on fpc not changing what HandlerArg contains.
But since it can be checked to be an address with line info, it should
be use-able. (and should work through gdb)
*** Other OS ??
Are there any plans for such exception handling?
======================
About the patch. ...
The need to address the issues below, depends on the outcome of the
above....
1)
program Project1;
begin
try
try
writeln;
except
writeln;
end;
except
writeln;
end;
end.
gives
project1.lpr(2,6) Error: Internal error 2019102101
2)
The tag for a finally block point to the code in the function containing
the try/finally. That is the asm instruction " call fin$1"
However, in case of an exception this code is not executed. fin$1 is
called from __FPC_specific_handler. So those addresses do not actually help.
The dwarf spec is not too specific, but I am not sure how good an idea
it is to have the "catch" address range in a different function.
============
Something else / Stepping
Because finally is a subroutine, "step over / F8" does not enter it.
That is not what the user expects.
In FpDebug that can be solved.
With GDB, that would require a lot of work, and probably slow down
stepping quite a bit....
However gdb does check the function after a call statement.
program Project1;
label a,b;
begin
writeln;
asm
call b
jmp a
b:
nop
nop
ret
a:
end;
writeln;
end.
And F8 will step into b (gdb 8.x). Because b is the same function. (and
apparently gdb does some checks to distinguish this from a recursive call)
So if fpc would write dwarf info where the function includes the finally
code, then stepping would work.
Though with the finally code currently being in front of the function
body, it would need an entry point. (not tested...). Or the finally code
could be moved to the end.
Only tested with 64 bit.
I can do more tests, if it is considered worth the effort.
More information about the fpc-devel
mailing list