[Lazarus] Making fpdebug more cross-platform friendly

Martin Frb lazarus at mfriebe.de
Thu Dec 16 23:26:57 CET 2021

Ok, for now limiting this to a smaller audience, to get some 
brainstorming done.
=> Original mail at the bottom.

Some initial thoughts.

*** First, I would suggest, that all calls go to T[Foo]DbgProccess ***
T[Foo]DbgProccess can then forward them.
Outside classes should not have knowledge, nor access to the delegates.
(Better start restricted, so we have more flexibility for future changes)


I can currently see the following areas, that could be separated as needed.

API:  Calls to the OS => start/stop/mem-access/register/access
ABI:  Conventions about register usage, and stack layout
         (registers are known by either name or dwarf-index)
CPU: Identifying specific asm instructions  (Breakpoint / ret / call ....)

- API remains hardcoded in T[Foo]DbgProccess.
- The others are private delegate classes.

- Your idea about WOW64_CONTEXT / _ARM64_NT_CONTEXT is imho API.
    It can not be CPU or ABI. If it were, we would need to subclass them 
for each API.
- Identifying breakpoint is CPU (not necessarily the current asm class / 
could be a new class)
- unwinding stack => could be either. (there may be an API of the OS, 
otherwise ABI, maybe with help of CPU)

Only, question is, if we need the ABI?
- Do win64 and linux64 share the same ABI? (or win32 <> linux32)?
   Afaik SEH is win specific (and probably would be best of in the ABI?).
   But there could still be a base class that is shared between Win/Linux.

Similar about ABI vs CPU.
Only here we definitely have a shared base class for inter 32/64bit 
(currently it's only one class / no subclasses for the bitness).
So IMHO there is a point in splitting them.

Mind that, FPC specific units could provide specialized sub-classes of 
any of the delegate classes.
E.g. dealing with $fin$MyProcedure subroutines. (Win-SEH)
For some purposes, finally blocks need to look like they are part of the 
main-procedure, not sub-routines of their own.


With the above some of the issues can be solved by combining different 
subclasses of each.

And then we would end up with combinations like this:
(I am not sure, if the ABI for Win and Linux are actually different..., 
but there would be win-seh)

TDbgWinProcess with:    x64-win ABI     /  x64 CPU
TDbgWinProcess with:    i386-win-ABI    /  x32 CPU
TDbgWinProcess with:    arm-ABI    /  arm CPU
TDbgLinuxProcess with:    x64-linux-ABI    /  x64 CPU
TDbgLinuxProcess with:    i386-linux-ABI    /  x32 CPU

There is potential for some re-usable code there.

Since all access would be through T[Foo]DbgProccess code can be moved 


However, setting Breakpoints for example may be a bit more complex.
For example:
- if there is a limit to the amount, or only certain locations can be set.
- If breakpoints are not set to memory, but like watchpoints on intel.

In those cases, it needs to be decided how much power to give to the CPU 

Should the CPU class be able to make calls to read/write mem and/or 
change registers?
(That is the point, where it would be a separate class from the asm 
class, which at most reads mem)

Even, if the CPU class can handle it all by itself, it may still end up 
needing support in the main class (T[Foo]DbgProccess).
- There can be errors due to restrictions (user watch points have been 
set to the register needed for the breakpoint).
- There may be different requirement on the relative timing of those 
actions and other actions outside the CPU class.
   Those timings are not known by the CPU class. So we end up with a 
tighter coupling than desired.



On 16/12/2021 12:10, Christo Crause via lazarus wrote:
> Currently there are a few instances where x86 specific 
> assumptions/code are used [1] in what I consider as the target 
> agnostic level of fpdebug.  My proposal is to move these instances to 
> a hardware target specific unit so that these target specific handling 
> is clearly separated.  One location for the target specific code is 
> perhaps the fpdbgdisas* units, since it already provides a number of 
> hardware specific concepts such as identifying call & return 
> instructions and some stack frame analysis.  Adding for example 
> IsSoftwareBreakInstruction seems like a logical addition.
> These changes are hindering some functionality on AVR and Xtensa 
> targets, e.g. some instructions are incorrectly disassembled because 
> they start with $CC, which is incorrectly  interpreted as a software 
> break.  Also the StepOut functionality currently assumes the x86 ABI 
> to locate the return address, which obviously fails for other targets. 
> While these methods could in principle be hidden by new target 
> specific overrides, it is not aligned with proper OOP principles.  I 
> will start with some concepts to address these soon.
> Similarly, but possibly a bit more tricky, is to separate the OS code 
> from the OS view of the hardware.  This would imply for example that 
> the register handling in fpdbglinuxclasses needs to be separated from 
> the rest of the code so that one could re-use the Linux layer, but 
> swap out the hardware specific layer x86_Linux.  Similarly for 
> Windows, the WOW64_CONTEXT could be part of an x86_Windows unit, so 
> that it is possible to also define and use _ARM64_NT_CONTEXT for 
> Windows on ARM64.
> Any thoughts, critique or suggestions are welcome.
> Best wishes,
> Christo
> [1] Examples of x86 specific code not in a x86 specific unit
> fpdbgclasses.pas: checking for int3 and its x86 encoding in 
> TDbgProcess, TDbgThread and TBreakLocationMap
> fpdbgcontroller.pas: TDbgControllerCallRoutineCmd uses x86 call 
> specific encoding in InsertCallInstructionCode and 
> RestoreInstructionPointer, StoreRoutineResult using x86 register 
> specific assumptions, 
> TDbgControllerStepOutCmd.SetReturnAdressBreakpoint assumes return 
> address is stored according to x86 stack frame layout.

More information about the lazarus mailing list