[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 

*** 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 

program Project1;

project1.lpr(2,6) Error: Internal error 2019102101

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;
   call b
   jmp a

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