mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-30 23:23:39 +02: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.
|
|
|