Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2019-10

034881: Request debug info for SEH (finally/except) to prevent regression when win32 switches to SEH

2 messages · 2 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin Frb

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.

Sven Barth
to Martin Frb

Martin Frb schrieb am Di., 29. Okt. 2019, 20:08:

Show quoted text 14 lines
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".

When implementing the patch I saw a bug report on GDB regarding the exception blocks (that's how I learned that ICC generates them), so it seems that it does handle them in some case... (at least to get the C++ exception variable that has been caught).

*** Other OS ??

Are there any plans for such exception handling?

FPC trunk now supports PSABIEH on i386 and x86_64 linux. You need to recompile the compiler with -dPSABIEH as well as the library path set to wherever libgcc.a resides (of course RTL and packages need to be recompiled as well).

Show quoted text 22 lines
====================== 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

Yes, I didn't test nested blocks, because I wasn't yet sure whether there debug information should be siblings or nested as well.

Show quoted text 10 lines
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.

Yeah, I noticed that as well, but at least it could be used to set a breakpoint at the called function inside that range...

Show quoted text 38 lines
============ 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.

For the compiler the finally code is generated essentially as a nested function. It does not know the concept of a function following immediately afterwards.

So that would be a rather complex undertaking that I don't believe is worth the effort.

Regards,
Sven