mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-01 00:09:38 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			764 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			764 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| unit FpdMemoryTools;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| (* Tools to read data from Target or Own memory.
 | |
| 
 | |
|    This is to deal in one place with all conversion of data from target format
 | |
|    to debugger format.
 | |
| 
 | |
|    A typical read would involve the following steps:
 | |
| 
 | |
|    The code calls MemoryManager with an Address, a Size and a space for the data.
 | |
| 
 | |
|    If the data needs to be extended (SizeOf(data) > TargetSize), then
 | |
|    MemConvertor is called to decide where in the provided space the read data
 | |
|    should initially be stored.
 | |
| 
 | |
|    Then the data is read using MemReader.
 | |
| 
 | |
|    And finally MemConvertor is given a chance to extend or convert the data.
 | |
| 
 | |
| *)
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, SysUtils, math, DbgIntfBaseTypes, FpErrorMessages, LazClasses;
 | |
| 
 | |
| type
 | |
| 
 | |
|   TFpDbgAddressContext = class(TRefCountedObject)
 | |
|   protected
 | |
|     function GetAddress: TDbgPtr; virtual; abstract;
 | |
|     function GetStackFrame: Integer; virtual; abstract;
 | |
|     function GetThreadId: Integer; virtual; abstract;
 | |
|     function GetSizeOfAddress: Integer; virtual; abstract;
 | |
|   public
 | |
|     property Address: TDbgPtr read GetAddress;
 | |
|     property ThreadId: Integer read GetThreadId;
 | |
|     property StackFrame: Integer read GetStackFrame;
 | |
|     property SizeOfAddress: Integer read GetSizeOfAddress;
 | |
|   end;
 | |
| 
 | |
| 
 | |
|   TFpDbgMemReaderBase = class
 | |
|   public
 | |
|     function ReadMemory(AnAddress: TDbgPtr; ASize: Cardinal; ADest: Pointer): Boolean; virtual; abstract;
 | |
|     function ReadMemoryEx(AnAddress, AnAddressSpace: TDbgPtr; ASize: Cardinal; ADest: Pointer): Boolean; virtual; abstract;
 | |
|     // ReadRegister may need TargetMemConvertor
 | |
|     // Register with reduced size are treated as unsigned
 | |
|     // TODO: ReadRegister should only take THREAD-ID, not context
 | |
|     function ReadRegister(ARegNum: Cardinal; out AValue: TDbgPtr; AContext: TFpDbgAddressContext): Boolean; virtual; abstract;
 | |
|     function RegisterSize(ARegNum: Cardinal): Integer; virtual; abstract;
 | |
|     // Registernum from name
 | |
|   end;
 | |
| 
 | |
|   (* Options for all read operation // TODO *)
 | |
| 
 | |
|   TFpDbgMemReadOptions = record
 | |
|     (* potential flags
 | |
|          target bit start/shift (method to map dwarf2/3 bit offse to dwarf 4
 | |
|          target bit size
 | |
|          target assume big/little endian
 | |
|          float precisson
 | |
|          AddressSpace
 | |
| 
 | |
|          ( rmBigEndian, rmLittleEndian, rmUseAddressSpace, UseBitSize)
 | |
|          AddressSpace, BitOffset, precission
 | |
|     *)
 | |
|   end;
 | |
| 
 | |
| 
 | |
|   TFpDbgMemReadDataType = (
 | |
|     rdtAddress, rdtSignedInt, rdtUnsignedInt, rdtfloat,
 | |
|     rdtEnum, rdtSet
 | |
|   );
 | |
| 
 | |
|   TFpDbgMemConvData = record
 | |
|     NewTargetAddress: TDbgPtr;
 | |
|     NewDestAddress: Pointer;
 | |
|     NewReadSize: Cardinal;
 | |
|     PrivData1, PrivData2: Pointer;
 | |
|   end;
 | |
| 
 | |
|   // Todo, cpu/language specific operations, endianess, sign extend, float .... default int value for bool
 | |
|   // convert from debugge format to debuger format and back
 | |
|   // TODO: currently it assumes target and own mem are in the same format
 | |
|   TFpDbgMemConvertor = class
 | |
|   public
 | |
|     (* PrepareTargetRead
 | |
|        called before every Read operation.
 | |
|        In case of reading from a bit-offset more memory may be needed, and must be allocated here
 | |
|     *)
 | |
|     function PrepareTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|                                ATargetPointer: TDbgPtr; ADestPointer: Pointer;
 | |
|                                ATargetSize, ADestSize: Cardinal;
 | |
|                                out AConvertorData: TFpDbgMemConvData
 | |
|                               ): boolean; virtual; abstract;
 | |
|     {function PrepareTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|                                ATargetPointer: TDbgPtr; ADestPointer: Pointer;
 | |
|                                ATargetSize, ADestSize: Cardinal;
 | |
|                                AnOpts: TFpDbgMemReadOptions;
 | |
|                                out AConvertorData: TFpDbgMemConvData
 | |
|                               ): boolean; virtual; abstract;}
 | |
| 
 | |
|     (* FinishTargetRead
 | |
|        called after every Read operation.
 | |
|     *)
 | |
|     function FinishTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|                               ATargetPointer: TDbgPtr; ADestPointer: Pointer;
 | |
|                               ATargetSize, ADestSize: Cardinal;
 | |
|                               AConvertorData: TFpDbgMemConvData
 | |
|                              ): boolean; virtual; abstract;
 | |
|     {function FinishTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|                               ATargetPointer: TDbgPtr; ADestPointer: Pointer;
 | |
|                               ATargetSize, ADestSize: Cardinal;
 | |
|                               AnOpts: TFpDbgMemReadOptions;
 | |
|                               AConvertorData: TFpDbgMemConvData
 | |
|                              ): boolean; virtual; abstract;}
 | |
| 
 | |
|     // just to free any data
 | |
|     procedure FailedTargetRead(AConvertorData: TFpDbgMemConvData); virtual; abstract;
 | |
| 
 | |
|     (* AdjustIntPointer:
 | |
|        To copy a smaller int/cardinal (e.g. word) into a bigger (e.g. dword),
 | |
|        adjust ADestPointer so it points to the low value part of the dest
 | |
|        No conversion
 | |
|     *)
 | |
|     procedure AdjustIntPointer(var ADataPointer: Pointer; ADataSize, ANewSize: Cardinal); virtual; abstract;
 | |
|     //(* SignExtend:
 | |
|     //   Expects a signed integer value of ASourceSize bytes in the low value end
 | |
|     //   of the memory (total memory ADataPointer, ADestSize)
 | |
|     //   Does zero fill the memory, if no sign extend is needed
 | |
|     //*)
 | |
|     //procedure SignExtend(ADataPointer: Pointer; ASourceSize, ADestSize: Cardinal); virtual; abstract;
 | |
|     //(* Expects an unsigned integer value of ASourceSize bytes in the low value end
 | |
|     //   of the memory (total memory ADataPointer, ADestSize)
 | |
|     //   Basically zero fill the memory
 | |
|     //*)
 | |
|     //procedure UnsignedExtend(ADataPointer: Pointer; ASourceSize, ADestSize: Cardinal); virtual; abstract;
 | |
|   end;
 | |
| 
 | |
|   { TFpDbgMemConvertorLittleEndian }
 | |
| 
 | |
|   TFpDbgMemConvertorLittleEndian = class(TFpDbgMemConvertor)
 | |
|   public
 | |
|     function PrepareTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|                                ATargetPointer: TDbgPtr; ADestPointer: Pointer;
 | |
|                                ATargetSize, ADestSize: Cardinal;
 | |
|                                out AConvertorData: TFpDbgMemConvData
 | |
|                               ): boolean; override;
 | |
| 
 | |
|     function FinishTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|                               {%H-}ATargetPointer: TDbgPtr; ADestPointer: Pointer;
 | |
|                               ATargetSize, ADestSize: Cardinal;
 | |
|                               {%H-}AConvertorData: TFpDbgMemConvData
 | |
|                              ): boolean; override;
 | |
|     procedure FailedTargetRead({%H-}AConvertorData: TFpDbgMemConvData); override;
 | |
| 
 | |
|     procedure AdjustIntPointer(var {%H-}ADataPointer: Pointer; ADataSize, ANewSize: Cardinal); override;
 | |
| 
 | |
| 
 | |
|     //procedure SignExtend(ADataPointer: Pointer; ASourceSize, ADestSize: Cardinal); override;
 | |
|     //procedure UnsignedExtend(ADataPointer: Pointer; ASourceSize, ADestSize: Cardinal); override;
 | |
|   end;
 | |
| 
 | |
|   (* TFpDbgMemManager
 | |
|    * allows to to pretend reading from the target, by using its own memory, or
 | |
|        a constant.
 | |
|      This is useful if an object expects to read data from the target, but the
 | |
|        caller needs to "fake" another value.
 | |
|      E.g. A TObject variable has the address of the (internal) pointer to the
 | |
|        object data:
 | |
|        SomeMethod expects "Address of variable". At that address in the target
 | |
|        memory it expects another address, the pointer to the object data.
 | |
|      But when doing "TObject(1234)" then 1234 is the pointer to the object data.
 | |
|        1234 can not be read from the target memory. MemManager will pretend.
 | |
|     * Provides access to TFpDbgMemConvertor
 | |
|     * TODO: allow to pre-read and cache Target mem (e.g. before reading all fields of a record
 | |
|   *)
 | |
|   TFpDbgMemLocationType = (
 | |
|     mlfUninitialized := 0,   // like invalid, but not known // 0 means objet fields will start wint this
 | |
|     mlfInvalid,
 | |
|     mlfTargetMem,            // an address in the target (debuggee) process
 | |
|     mlfSelfMem,              // an address in this(the debuggers) process memory; the data is  in TARGET format (endian, ...)
 | |
|     // the below will be mapped (and extended) according to endianess
 | |
|     mlfTargetRegister,       // reads from the register
 | |
|     mlfConstant              // an (up to) SizeOf(TDbgPtr) (=8) Bytes Value (endian in format of debug process)
 | |
|   );
 | |
| 
 | |
|   TFpDbgMemLocation = record
 | |
|     Address: TDbgPtr;
 | |
|     MType: TFpDbgMemLocationType;
 | |
|   end;
 | |
| 
 | |
| 
 | |
|   { TFpDbgMemManager }
 | |
| 
 | |
|   TFpDbgMemManager = class
 | |
|   private
 | |
|     FDefaultContext: TFpDbgAddressContext;
 | |
|     FLastError: TFpError;
 | |
|     FMemReader: TFpDbgMemReaderBase;
 | |
|     FTargetMemConvertor: TFpDbgMemConvertor;
 | |
|     FSelfMemConvertor: TFpDbgMemConvertor; // used when resizing constants (or register values, which are already in self format)
 | |
|   protected
 | |
|     function ReadMemory(AReadDataType: TFpDbgMemReadDataType;
 | |
|                         const ALocation: TFpDbgMemLocation; ATargetSize: Cardinal;
 | |
|                         ADest: Pointer; ADestSize: Cardinal;
 | |
|                         AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|   public
 | |
|     constructor Create(AMemReader: TFpDbgMemReaderBase; AMemConvertor: TFpDbgMemConvertor);
 | |
|     constructor Create(AMemReader: TFpDbgMemReaderBase; ATargenMemConvertor, ASelfMemConvertor: TFpDbgMemConvertor);
 | |
|     procedure ClearLastError;
 | |
| 
 | |
|     function ReadMemory(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                         ADest: Pointer; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     function ReadMemoryEx(const ALocation: TFpDbgMemLocation; AnAddressSpace: TDbgPtr; ASize: Cardinal; ADest: Pointer; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     (* ReadRegister needs a Context, to get the thread/stackframe
 | |
|     *)
 | |
|     function ReadRegister(ARegNum: Cardinal; out AValue: TDbgPtr; AContext: TFpDbgAddressContext {= nil}): Boolean;
 | |
| 
 | |
|     // location will be invalid, if read failed
 | |
|     function ReadAddress(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                          AContext: TFpDbgAddressContext = nil): TFpDbgMemLocation;
 | |
|     function ReadAddressEx(const ALocation: TFpDbgMemLocation;  AnAddressSpace: TDbgPtr;
 | |
|                            ASize: Cardinal; AContext: TFpDbgAddressContext = nil): TFpDbgMemLocation;
 | |
| 
 | |
|     // ALocation and AnAddress MUST NOT be the same variable on the callers side
 | |
|     function ReadAddress    (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                              out AnAddress: TFpDbgMemLocation;
 | |
|                              AContext: TFpDbgAddressContext = nil): Boolean; inline;
 | |
|     //function ReadAddress    (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|     //                         out AnAddress: TFpDbgMemLocation;
 | |
|     //                         AnOpts: TFpDbgMemReadOptions; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     function ReadUnsignedInt(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                              out AValue: QWord; AContext: TFpDbgAddressContext = nil): Boolean; inline;
 | |
|     //function ReadUnsignedInt(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|     //                         out AValue: QWord;
 | |
|     //                         AnOpts: TFpDbgMemReadOptions; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     function ReadSignedInt  (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                              out AValue: Int64; AContext: TFpDbgAddressContext = nil): Boolean; inline;
 | |
|     //function ReadSignedInt  (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|     //                         out AValue: Int64;
 | |
|     //                         AnOpts: TFpDbgMemReadOptions; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     // //enum/set: may need bitorder swapped
 | |
|     function ReadEnum       (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                              out AValue: QWord; AContext: TFpDbgAddressContext = nil): Boolean; inline;
 | |
|     //function ReadEnum       (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|     //                         out AValue: QWord;
 | |
|     //                         AnOpts: TFpDbgMemReadOptions; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     function ReadSet        (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                              out AValue: TBytes; AContext: TFpDbgAddressContext = nil): Boolean; inline;
 | |
|     //function ReadSet        (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|     //                         out AValue: TBytes;
 | |
|     //                         AnOpts: TFpDbgMemReadOptions; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
|     function ReadFloat      (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|                              out AValue: Extended; AContext: TFpDbgAddressContext = nil): Boolean; inline;
 | |
|     //function ReadFloat      (const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|     //                         out AValue: Extended;
 | |
|     //                         AnOpts: TFpDbgMemReadOptions; AContext: TFpDbgAddressContext = nil): Boolean;
 | |
| 
 | |
|     property TargetMemConvertor: TFpDbgMemConvertor read FTargetMemConvertor;
 | |
|     property SelfMemConvertor: TFpDbgMemConvertor read FSelfMemConvertor;
 | |
|     property LastError: TFpError read FLastError;
 | |
|     property DefaultContext: TFpDbgAddressContext read FDefaultContext write FDefaultContext;
 | |
|   end;
 | |
| 
 | |
| function NilLoc: TFpDbgMemLocation; inline;
 | |
| function InvalidLoc: TFpDbgMemLocation; inline;
 | |
| function UnInitializedLoc: TFpDbgMemLocation; inline;
 | |
| function TargetLoc(AnAddress: TDbgPtr): TFpDbgMemLocation; inline;
 | |
| function RegisterLoc(ARegNum: Cardinal): TFpDbgMemLocation; inline;
 | |
| function SelfLoc(AnAddress: TDbgPtr): TFpDbgMemLocation; inline;
 | |
| function SelfLoc(AnAddress: Pointer): TFpDbgMemLocation; inline;
 | |
| function ConstLoc(AValue: QWord): TFpDbgMemLocation; inline;
 | |
| 
 | |
| function IsTargetAddr(ALocation: TFpDbgMemLocation): Boolean; inline;
 | |
| function IsInitializedLoc(ALocation: TFpDbgMemLocation): Boolean; inline;
 | |
| function IsValidLoc(ALocation: TFpDbgMemLocation): Boolean; inline;     // Valid, Nil allowed
 | |
| function IsReadableLoc(ALocation: TFpDbgMemLocation): Boolean; inline;  // Valid and not Nil // can be const or reg
 | |
| function IsReadableMem(ALocation: TFpDbgMemLocation): Boolean; inline;  // Valid and target or sel <> nil
 | |
| function IsTargetNil(ALocation: TFpDbgMemLocation): Boolean; inline;    // valid targed = nil
 | |
| function IsTargetNotNil(ALocation: TFpDbgMemLocation): Boolean; inline; // valid targed <> nil
 | |
| 
 | |
| function LocToAddr(ALocation: TFpDbgMemLocation): TDbgPtr; inline;      // does not check valid
 | |
| function LocToAddrOrNil(ALocation: TFpDbgMemLocation): TDbgPtr; inline; // save version
 | |
| 
 | |
| function EmptyMemReadOpts:TFpDbgMemReadOptions;
 | |
| 
 | |
| function dbgs(ALocation: TFpDbgMemLocation): String; overload;
 | |
| 
 | |
| implementation
 | |
| 
 | |
| function NilLoc: TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := 0;
 | |
|   Result.MType := mlfTargetMem;
 | |
| end;
 | |
| 
 | |
| function InvalidLoc: TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := 0;
 | |
|   Result.MType := mlfInvalid;
 | |
| end;
 | |
| 
 | |
| function UnInitializedLoc: TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := 0;
 | |
|   Result.MType := mlfUninitialized;
 | |
| end;
 | |
| 
 | |
| function TargetLoc(AnAddress: TDbgPtr): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := AnAddress;
 | |
|   Result.MType := mlfTargetMem;
 | |
| end;
 | |
| 
 | |
| function RegisterLoc(ARegNum: Cardinal): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := ARegNum;
 | |
|   Result.MType := mlfTargetRegister;
 | |
| end;
 | |
| 
 | |
| function SelfLoc(AnAddress: TDbgPtr): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := AnAddress;
 | |
|   Result.MType := mlfSelfMem;
 | |
| end;
 | |
| 
 | |
| function SelfLoc(AnAddress: Pointer): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := TDbgPtr(AnAddress);
 | |
|   Result.MType := mlfSelfMem;
 | |
| end;
 | |
| 
 | |
| function ConstLoc(AValue: QWord): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.Address := AValue;
 | |
|   Result.MType := mlfConstant;
 | |
| end;
 | |
| 
 | |
| function IsTargetAddr(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := ALocation.MType = mlfTargetMem;
 | |
| end;
 | |
| 
 | |
| function IsInitializedLoc(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := ALocation.MType <> mlfUninitialized;
 | |
| end;
 | |
| 
 | |
| function IsValidLoc(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := not(ALocation.MType in [mlfInvalid, mlfUninitialized]);
 | |
| end;
 | |
| 
 | |
| function IsReadableLoc(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := (not(ALocation.MType in [mlfInvalid, mlfUninitialized])) and
 | |
|             ( (not(ALocation.MType in [mlfTargetMem, mlfSelfMem])) or
 | |
|               (ALocation.Address <> 0)
 | |
|             );
 | |
| end;
 | |
| 
 | |
| function IsReadableMem(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := (ALocation.MType in [mlfTargetMem, mlfSelfMem]) and
 | |
|             (ALocation.Address <> 0);
 | |
| end;
 | |
| 
 | |
| function IsTargetNil(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := (ALocation.MType = mlfTargetMem) and (ALocation.Address = 0);
 | |
| end;
 | |
| 
 | |
| function IsTargetNotNil(ALocation: TFpDbgMemLocation): Boolean;
 | |
| begin
 | |
|   Result := (ALocation.MType = mlfTargetMem) and (ALocation.Address <> 0);
 | |
| end;
 | |
| 
 | |
| function LocToAddr(ALocation: TFpDbgMemLocation): TDbgPtr;
 | |
| begin
 | |
|   assert(ALocation.MType = mlfTargetMem, 'LocToAddr for other than mlfTargetMem');
 | |
|   Result := ALocation.Address;
 | |
| end;
 | |
| 
 | |
| function LocToAddrOrNil(ALocation: TFpDbgMemLocation): TDbgPtr;
 | |
| begin
 | |
|   if (ALocation.MType = mlfTargetMem) then
 | |
|     Result := ALocation.Address
 | |
|   else
 | |
|     Result := 0;
 | |
| end;
 | |
| 
 | |
| function {%H-}EmptyMemReadOpts: TFpDbgMemReadOptions;
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| function dbgs(ALocation: TFpDbgMemLocation): String;
 | |
| begin
 | |
|   Result := '';
 | |
|   if not (ALocation.MType in [low(TFpDbgMemLocationType)..high(TFpDbgMemLocationType)]) then
 | |
|     Result := 'Location=out-of-range'
 | |
|   else
 | |
|     WriteStr(Result, 'Location=', ALocation.Address, ',', ALocation.MType)
 | |
| end;
 | |
| 
 | |
| { TFpDbgMemConvertorLittleEndian }
 | |
| 
 | |
| function TFpDbgMemConvertorLittleEndian.PrepareTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|   ATargetPointer: TDbgPtr; ADestPointer: Pointer; ATargetSize, ADestSize: Cardinal; out
 | |
|   AConvertorData: TFpDbgMemConvData): boolean;
 | |
| begin
 | |
|   Result := ATargetSize <= ADestSize;
 | |
|   if not Result then
 | |
|     exit;
 | |
|   // just read to begin of data
 | |
|   AConvertorData.NewTargetAddress := ATargetPointer;
 | |
|   AConvertorData.NewDestAddress   := ADestPointer;
 | |
|   AConvertorData.NewReadSize      := Min(ATargetSize, ADestSize);
 | |
|   case AReadDataType of
 | |
|     rdtAddress, rdtSignedInt, rdtUnsignedInt,
 | |
|     rdtEnum, rdtSet: ;
 | |
|     rdtfloat:
 | |
|       Result := (ATargetSize = AConvertorData.NewReadSize) and
 | |
|                 (ADestSize = SizeOf(Extended)) and // only can read to extended... TODO (if need more)
 | |
|                 ( (ATargetSize = SizeOf(Extended)) or
 | |
|                   (ATargetSize = SizeOf(Double)) or
 | |
|                   (ATargetSize = SizeOf(Single)) or
 | |
|                   (ATargetSize = SizeOf(real48))
 | |
|                 )
 | |
|     else begin
 | |
|       Assert(False, 'TFpDbgMemConvertorLittleEndian.PrepareTargetRead');
 | |
|       Result := False;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemConvertorLittleEndian.FinishTargetRead(AReadDataType: TFpDbgMemReadDataType;
 | |
|   ATargetPointer: TDbgPtr; ADestPointer: Pointer; ATargetSize, ADestSize: Cardinal;
 | |
|   AConvertorData: TFpDbgMemConvData): boolean;
 | |
| type
 | |
|   Preal48 = ^real48;
 | |
| begin
 | |
|   Result := True;
 | |
|   case AReadDataType of
 | |
|     rdtAddress, rdtUnsignedInt, rdtEnum, rdtSet: begin
 | |
|         if ATargetSize < ADestSize then
 | |
|           FillByte((ADestPointer + ATargetSize)^, ADestSize-ATargetSize, $00)
 | |
|       end;
 | |
|     rdtSignedInt: begin
 | |
|         if ATargetSize < ADestSize then
 | |
|           if (ATargetSize > 0) and ((PByte(ADestPointer + ATargetSize - 1)^ and $80) <> 0)
 | |
|           then
 | |
|             FillByte((ADestPointer + ATargetSize)^, ADestSize-ATargetSize, $FF)
 | |
|           else
 | |
|             FillByte((ADestPointer + ATargetSize)^, ADestSize-ATargetSize, $00);
 | |
|       end;
 | |
|     rdtfloat: begin
 | |
|       assert((ADestSize = SizeOf(Extended)));
 | |
|       if (ATargetSize = SizeOf(Extended)) then
 | |
|         //
 | |
|       else
 | |
|       if (ATargetSize = SizeOf(Double)) then
 | |
|         PExtended(ADestPointer)^ := PDouble(ADestPointer)^
 | |
|       else
 | |
|       if (ATargetSize = SizeOf(real48)) then
 | |
|         PExtended(ADestPointer)^ := Preal48(ADestPointer)^
 | |
|       else
 | |
|       if (ATargetSize = SizeOf(Single)) then
 | |
|         PExtended(ADestPointer)^ := PSingle(ADestPointer)^
 | |
|       else
 | |
|         Result := False;
 | |
|     end;
 | |
|     else begin
 | |
|       Assert(False, 'TFpDbgMemConvertorLittleEndian.FailedTargetRead');
 | |
|       Result := False;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TFpDbgMemConvertorLittleEndian.FailedTargetRead(AConvertorData: TFpDbgMemConvData);
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TFpDbgMemConvertorLittleEndian.AdjustIntPointer(var ADataPointer: Pointer;
 | |
|   ADataSize, ANewSize: Cardinal);
 | |
| begin
 | |
|   Assert(ANewSize <= ADataSize, 'TFpDbgMemConvertorLittleEndian.AdjustIntPointer');
 | |
|   // no adjustment needed
 | |
| end;
 | |
| 
 | |
| //procedure TFpDbgMemConvertorLittleEndian.SignExtend(ADataPointer: Pointer; ASourceSize,
 | |
| //  ADestSize: Cardinal);
 | |
| //begin
 | |
| //  Assert(ASourceSize > 0, 'TFpDbgMemConvertorLittleEndian.SignExtend');
 | |
| //  if ASourceSize >= ADestSize then
 | |
| //    exit;
 | |
| //
 | |
| //  if (PByte(ADataPointer + ASourceSize - 1)^ and $80) <> 0 then
 | |
| //    FillByte((ADataPointer + ASourceSize)^, ADestSize-ASourceSize, $ff)
 | |
| //  else
 | |
| //    FillByte((ADataPointer + ASourceSize)^, ADestSize-ASourceSize, $00)
 | |
| //end;
 | |
| //
 | |
| //procedure TFpDbgMemConvertorLittleEndian.UnsignedExtend(ADataPointer: Pointer;
 | |
| //  ASourceSize, ADestSize: Cardinal);
 | |
| //begin
 | |
| //  Assert(ASourceSize > 0, 'TFpDbgMemConvertorLittleEndian.SignExtend');
 | |
| //  if ASourceSize >= ADestSize then
 | |
| //    exit;
 | |
| //
 | |
| //  FillByte((ADataPointer + ASourceSize)^, ADestSize-ASourceSize, $00)
 | |
| //end;
 | |
| 
 | |
| { TFpDbgMemManager }
 | |
| 
 | |
| function TFpDbgMemManager.ReadMemory(AReadDataType: TFpDbgMemReadDataType;
 | |
|   const ALocation: TFpDbgMemLocation; ATargetSize: Cardinal; ADest: Pointer;
 | |
|   ADestSize: Cardinal; AContext: TFpDbgAddressContext): Boolean;
 | |
| var
 | |
|   Addr2: Pointer;
 | |
|   i: Integer;
 | |
|   TmpVal: TDbgPtr;
 | |
|   ConvData: TFpDbgMemConvData;
 | |
| begin
 | |
|   FLastError := NoError;
 | |
|   Result := False;
 | |
|   if AContext = nil then
 | |
|     AContext := FDefaultContext;
 | |
|   case ALocation.MType of
 | |
|     mlfInvalid, mlfUninitialized:
 | |
|       FLastError := CreateError(fpErrCanNotReadInvalidMem);
 | |
|     mlfTargetMem, mlfSelfMem: begin
 | |
|       Result := TargetMemConvertor.PrepareTargetRead(AReadDataType, ALocation.Address,
 | |
|         ADest, ATargetSize, ADestSize, ConvData);
 | |
|       if not Result then exit;
 | |
| 
 | |
|       if ALocation.MType = mlfTargetMem then begin
 | |
|         Result := FMemReader.ReadMemory(ConvData.NewTargetAddress, ConvData.NewReadSize, ConvData.NewDestAddress);
 | |
|         if not Result then
 | |
|           FLastError := CreateError(fpErrCanNotReadMemAtAddr, [ALocation.Address]);
 | |
|       end
 | |
|       else
 | |
|       begin
 | |
|         try
 | |
|           move(Pointer(ConvData.NewTargetAddress)^, ConvData.NewDestAddress^, ConvData.NewReadSize);
 | |
|           Result := True;
 | |
|         except
 | |
|           Result := False;
 | |
|         end;
 | |
|       end;
 | |
| 
 | |
|       if Result then
 | |
|         Result := TargetMemConvertor.FinishTargetRead(AReadDataType, ALocation.Address,
 | |
|           ADest, ATargetSize, ADestSize, ConvData)
 | |
|       else
 | |
|         TargetMemConvertor.FailedTargetRead(ConvData);
 | |
|     end;
 | |
|     mlfConstant, mlfTargetRegister:
 | |
|       begin
 | |
|         case ALocation.MType of
 | |
|           mlfConstant: begin
 | |
|               TmpVal := ALocation.Address;
 | |
|               i := SizeOf(ALocation.Address);
 | |
|             end;
 | |
|           mlfTargetRegister: begin
 | |
|               i := FMemReader.RegisterSize(Cardinal(ALocation.Address));
 | |
|               if i = 0 then
 | |
|                 exit; // failed
 | |
|               if not FMemReader.ReadRegister(Cardinal(ALocation.Address), TmpVal, AContext) then
 | |
|                 exit; // failed
 | |
|             end;
 | |
|         end;
 | |
|         if i > ATargetSize then
 | |
|           i := ATargetSize;
 | |
| 
 | |
|         Addr2 := @TmpVal;
 | |
|         if SizeOf(TmpVal) <> i then
 | |
|           FSelfMemConvertor.AdjustIntPointer(Addr2, SizeOf(TmpVal), i);
 | |
| 
 | |
|         Result := FSelfMemConvertor.PrepareTargetRead(AReadDataType, TDbgPtr(Addr2),
 | |
|           ADest, i, ADestSize, ConvData);
 | |
|         if not Result then exit;
 | |
| 
 | |
|         move(Pointer(ConvData.NewTargetAddress)^, ConvData.NewDestAddress^, ConvData.NewReadSize);
 | |
| 
 | |
|         Result := TargetMemConvertor.FinishTargetRead(AReadDataType, TDbgPtr(Addr2),
 | |
|           ADest, i, ADestSize, ConvData);
 | |
|         Result := True;
 | |
|       end;
 | |
|   end;
 | |
|   if (not Result) and (not IsError(FLastError)) then
 | |
|     FLastError := CreateError(fpErrFailedReadMem);
 | |
| end;
 | |
| 
 | |
| constructor TFpDbgMemManager.Create(AMemReader: TFpDbgMemReaderBase;
 | |
|   AMemConvertor: TFpDbgMemConvertor);
 | |
| begin
 | |
|   FMemReader := AMemReader;
 | |
|   FTargetMemConvertor := AMemConvertor;
 | |
|   FSelfMemConvertor := AMemConvertor;
 | |
| end;
 | |
| 
 | |
| constructor TFpDbgMemManager.Create(AMemReader: TFpDbgMemReaderBase; ATargenMemConvertor,
 | |
|   ASelfMemConvertor: TFpDbgMemConvertor);
 | |
| begin
 | |
|   FMemReader := AMemReader;
 | |
|   FTargetMemConvertor := ATargenMemConvertor;
 | |
|   FSelfMemConvertor := ASelfMemConvertor;
 | |
| end;
 | |
| 
 | |
| procedure TFpDbgMemManager.ClearLastError;
 | |
| begin
 | |
|   FLastError := NoError;
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadMemory(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|   ADest: Pointer; AContext: TFpDbgAddressContext): Boolean;
 | |
| var
 | |
|   Addr2: Pointer;
 | |
|   i: Integer;
 | |
|   TmpVal: TDbgPtr;
 | |
|   ConvData: TFpDbgMemConvData;
 | |
| begin
 | |
|   FLastError := NoError;
 | |
|   Result := False;
 | |
|   if AContext = nil then
 | |
|     AContext := FDefaultContext;
 | |
|   case ALocation.MType of
 | |
|     mlfInvalid, mlfUninitialized: ;
 | |
|     mlfTargetMem:
 | |
|       begin
 | |
|         Result := FMemReader.ReadMemory(ALocation.Address, ASize, ADest);
 | |
|         if not Result then
 | |
|           FLastError := CreateError(fpErrCanNotReadMemAtAddr, [ALocation.Address]);
 | |
|       end;
 | |
|     mlfSelfMem:
 | |
|       begin
 | |
|         move(Pointer(ALocation.Address)^, ADest^, ASize);
 | |
|         Result := True;
 | |
|       end;
 | |
|     mlfConstant, mlfTargetRegister:
 | |
|       begin
 | |
|         case ALocation.MType of
 | |
|           mlfConstant: begin
 | |
|               TmpVal := ALocation.Address;
 | |
|               i := SizeOf(ALocation.Address);
 | |
|             end;
 | |
|           mlfTargetRegister: begin
 | |
|               i := FMemReader.RegisterSize(Cardinal(ALocation.Address));
 | |
|               if i = 0 then
 | |
|                 exit; // failed
 | |
|               if not FMemReader.ReadRegister(Cardinal(ALocation.Address), TmpVal, AContext) then
 | |
|                 exit; // failed
 | |
|             end;
 | |
|         end;
 | |
| 
 | |
|         Addr2 := @TmpVal;
 | |
|         if SizeOf(TmpVal) <> i then
 | |
|           FSelfMemConvertor.AdjustIntPointer(Addr2, SizeOf(TmpVal), i);
 | |
| 
 | |
|         Result := FSelfMemConvertor.PrepareTargetRead(rdtUnsignedInt, TDbgPtr(Addr2),
 | |
|           ADest, i, ASize, ConvData);
 | |
|         if not Result then exit;
 | |
| 
 | |
|         move(Pointer(ConvData.NewTargetAddress)^, ConvData.NewDestAddress^, ConvData.NewReadSize);
 | |
| 
 | |
|         Result := TargetMemConvertor.FinishTargetRead(rdtUnsignedInt, TDbgPtr(Addr2),
 | |
|           ADest, i, ASize, ConvData);
 | |
|         Result := True;
 | |
|       end;
 | |
|   end;
 | |
|   if (not Result) and (not IsError(FLastError)) then
 | |
|     FLastError := CreateError(fpErrFailedReadMem);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadMemoryEx(const ALocation: TFpDbgMemLocation;
 | |
|   AnAddressSpace: TDbgPtr; ASize: Cardinal; ADest: Pointer;
 | |
|   AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   FLastError := NoError;
 | |
|   // AnAddressSpace is ignored, when not actually reading from target address
 | |
|   case ALocation.MType of
 | |
|     mlfTargetMem: Result := FMemReader.ReadMemoryEx(ALocation.Address, AnAddressSpace, ASize, ADest);
 | |
|     else
 | |
|       Result := ReadMemory(ALocation, ASize, ADest, AContext);
 | |
|   end;
 | |
|   if (not Result) and (not IsError(FLastError)) then
 | |
|     FLastError := CreateError(fpErrFailedReadMem);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadRegister(ARegNum: Cardinal; out AValue: TDbgPtr;
 | |
|   AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   FLastError := NoError;
 | |
|   // TODO: If stackframe <> 0 then get the register internally (unroll stack)
 | |
|   if AContext = nil then
 | |
|     AContext := FDefaultContext;
 | |
|   Result := FMemReader.ReadRegister(ARegNum, AValue, AContext);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadAddress(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|   AContext: TFpDbgAddressContext): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result.MType := mlfTargetMem;
 | |
|   if not ReadMemory(rdtAddress, ALocation, ASize, @Result.Address, SizeOf(Result.Address), AContext) then
 | |
|     Result := InvalidLoc;
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadAddressEx(const ALocation: TFpDbgMemLocation;
 | |
|   AnAddressSpace: TDbgPtr; ASize: Cardinal; AContext: TFpDbgAddressContext): TFpDbgMemLocation;
 | |
| begin
 | |
|   Result := InvalidLoc;
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadAddress(const ALocation: TFpDbgMemLocation; ASize: Cardinal; out
 | |
|   AnAddress: TFpDbgMemLocation; AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   Result := ReadMemory(rdtAddress, ALocation, ASize, @AnAddress.Address, SizeOf(AnAddress.Address), AContext);
 | |
|   if Result
 | |
|   then AnAddress.MType := mlfTargetMem
 | |
|   else AnAddress.MType := mlfInvalid;
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadUnsignedInt(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|   out AValue: QWord; AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   Result := ReadMemory(rdtUnsignedInt, ALocation, ASize, @AValue, SizeOf(AValue), AContext);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadSignedInt(const ALocation: TFpDbgMemLocation; ASize: Cardinal;
 | |
|   out AValue: Int64; AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   Result := ReadMemory(rdtSignedInt, ALocation, ASize, @AValue, SizeOf(AValue), AContext);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadEnum(const ALocation: TFpDbgMemLocation; ASize: Cardinal; out
 | |
|   AValue: QWord; AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   Result := ReadMemory(rdtEnum, ALocation, ASize, @AValue, SizeOf(AValue), AContext);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadSet(const ALocation: TFpDbgMemLocation; ASize: Cardinal; out
 | |
|   AValue: TBytes; AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   SetLength(AValue, ASize);
 | |
|   Result := ASize > 0;
 | |
|   if Result then
 | |
|     Result := ReadMemory(rdtSet, ALocation, ASize, @AValue[0], ASize, AContext);
 | |
| end;
 | |
| 
 | |
| function TFpDbgMemManager.ReadFloat(const ALocation: TFpDbgMemLocation; ASize: Cardinal; out
 | |
|   AValue: Extended; AContext: TFpDbgAddressContext): Boolean;
 | |
| begin
 | |
|   Result := ReadMemory(rdtfloat, ALocation, ASize, @AValue, SizeOf(AValue), AContext);
 | |
| end;
 | |
| 
 | |
| end.
 | |
| 
 | 
