mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-12-03 06:17:30 +01:00
FpDebug: Refactor disassembler into class
git-svn-id: trunk@62746 -
This commit is contained in:
parent
f6c39c019d
commit
676c17f1cf
@ -145,7 +145,8 @@ begin
|
||||
end;
|
||||
p := @CodeBin;
|
||||
|
||||
Disassemble(p, GController.CurrentProcess.Mode=dm64, CodeBytes, Code);
|
||||
GController.CurrentProcess.Disassembler
|
||||
.Disassemble(p, CodeBytes, Code);
|
||||
|
||||
WriteLN(' ', CodeBytes:20, ' ', Code);
|
||||
Inc(a, PtrUInt(p) - PtrUInt(@CodeBin));
|
||||
|
||||
@ -450,7 +450,8 @@ function TFpDebugThreadDisassembleCommand.Execute(AController: TFpServerDbgContr
|
||||
else
|
||||
begin
|
||||
p := @CodeBin;
|
||||
FpDbgDisasX86.Disassemble(p, {$ifndef disassemblernestedproc}FController{$else}AController{$endif}.CurrentProcess.Mode=dm64, ADump, AStatement);
|
||||
AController.CurrentProcess.Disassembler
|
||||
.Disassemble(p, ADump, AStatement);
|
||||
|
||||
Sym := {$ifndef disassemblernestedproc}FController{$else}AController{$endif}.CurrentProcess.FindProcSymbol(AnAddr);
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ uses
|
||||
DbgIntfDebuggerBase,
|
||||
FpPascalBuilder,
|
||||
fpDbgSymTableContext,
|
||||
FpDbgDwarfDataClasses, FpDbgDisasX86;
|
||||
FpDbgDwarfDataClasses;
|
||||
|
||||
type
|
||||
TFPDEvent = (deExitProcess, deFinishedStep, deBreakpoint, deException, deCreateProcess, deLoadLibrary, deUnloadLibrary, deInternalContinue);
|
||||
@ -394,12 +394,40 @@ type
|
||||
TStartInstanceFlag = (siRediretOutput, siForceNewConsole);
|
||||
TStartInstanceFlags = set of TStartInstanceFlag;
|
||||
|
||||
{ TDbgDisassemblerInstruction }
|
||||
|
||||
TDbgDisassemblerInstruction = class(TRefCountedObject)
|
||||
public
|
||||
// returns byte len of call instruction at AAddress // 0 if not a call intruction
|
||||
function IsCallInstruction: boolean; virtual;
|
||||
function IsReturnInstruction: boolean; virtual;
|
||||
function IsLeaveStackFrame: boolean; virtual;
|
||||
function InstructionLength: Integer; virtual;
|
||||
end;
|
||||
|
||||
{ TDbgDisassembler }
|
||||
|
||||
TDbgDisassembler = class
|
||||
protected
|
||||
function GetLastErrorWasMemReadErr: Boolean; virtual;
|
||||
public
|
||||
constructor Create(AProcess: TDbgProcess); virtual; abstract;
|
||||
|
||||
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); virtual; abstract;
|
||||
function GetInstructionInfo(AnAddress: TDBGPtr): TDbgDisassemblerInstruction; virtual; abstract;
|
||||
function GetFunctionFrameInfo(AnAddress: TDBGPtr; out AnIsOutsideFrame: Boolean): Boolean; virtual;
|
||||
|
||||
property LastErrorWasMemReadErr: Boolean read GetLastErrorWasMemReadErr;
|
||||
end;
|
||||
TDbgDisassemblerClass = class of TDbgDisassembler;
|
||||
|
||||
{ TDbgProcess }
|
||||
|
||||
TDbgProcess = class(TDbgInstance)
|
||||
protected const
|
||||
Int3: Byte = $CC;
|
||||
private
|
||||
FDisassembler: TDbgDisassembler;
|
||||
FExceptionClass: string;
|
||||
FExceptionMessage: string;
|
||||
FExitCode: DWord;
|
||||
@ -409,6 +437,7 @@ type
|
||||
FThreadID: Integer;
|
||||
FWatchPointData: TFpWatchPointData;
|
||||
|
||||
function GetDisassembler: TDbgDisassembler;
|
||||
function GetLastLibraryLoaded: TDbgLibrary;
|
||||
function GetPauseRequested: boolean;
|
||||
procedure SetPauseRequested(AValue: boolean);
|
||||
@ -533,6 +562,7 @@ public
|
||||
property LastEventProcessIdentifier: THandle read GetLastEventProcessIdentifier;
|
||||
property MainThread: TDbgThread read FMainThread;
|
||||
property GotExitProcess: Boolean read FGotExitProcess write FGotExitProcess;
|
||||
property Disassembler: TDbgDisassembler read GetDisassembler;
|
||||
end;
|
||||
TDbgProcessClass = class of TDbgProcess;
|
||||
|
||||
@ -571,6 +601,7 @@ public
|
||||
DbgBreakpointClass : TFpInternalBreakpointClass;
|
||||
DbgWatchpointClass : TFpInternalWatchpointClass;
|
||||
DbgProcessClass : TDbgProcessClass;
|
||||
DbgDisassemblerClass : TDbgDisassemblerClass;
|
||||
end;
|
||||
|
||||
var
|
||||
@ -1168,6 +1199,41 @@ begin
|
||||
SetValue(ANumValue, trim(FlagS),4,Cardinal(-1));
|
||||
end;
|
||||
|
||||
{ TDbgDisassemblerInstruction }
|
||||
|
||||
function TDbgDisassemblerInstruction.IsCallInstruction: boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TDbgDisassemblerInstruction.IsReturnInstruction: boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TDbgDisassemblerInstruction.IsLeaveStackFrame: boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TDbgDisassemblerInstruction.InstructionLength: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
{ TDbgDisassembler }
|
||||
|
||||
function TDbgDisassembler.GetLastErrorWasMemReadErr: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TDbgDisassembler.GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
||||
AnIsOutsideFrame: Boolean): Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
{ TDbgInstance }
|
||||
|
||||
function TDbgInstance.AddBreak(const AFileName: String; ALine: Cardinal;
|
||||
@ -1398,6 +1464,7 @@ begin
|
||||
FreeAndNil(FThreadMap);
|
||||
FreeAndNil(FLibMap);
|
||||
FreeAndNil(FSymInstances);
|
||||
FreeAndNil(FDisassembler);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
@ -1766,6 +1833,13 @@ begin
|
||||
Result := FLibMap.LastLibraryAdded;
|
||||
end;
|
||||
|
||||
function TDbgProcess.GetDisassembler: TDbgDisassembler;
|
||||
begin
|
||||
if FDisassembler = nil then
|
||||
FDisassembler := OSDbgClasses.DbgDisassemblerClass.Create(Self);
|
||||
Result := FDisassembler;
|
||||
end;
|
||||
|
||||
function TDbgProcess.GetAndClearPauseRequested: Boolean;
|
||||
begin
|
||||
Result := Boolean(InterLockedExchange(FPauseRequested, ord(False)));
|
||||
@ -2195,7 +2269,6 @@ procedure TDbgThread.PrepareCallStackEntryList(AFrameRequired: Integer);
|
||||
const
|
||||
MAX_FRAMES = 50000; // safety net
|
||||
var
|
||||
CodeBin: array[0..50] of byte;
|
||||
Address, FrameBase, LastFrameBase: QWord;
|
||||
Size, CountNeeded, IP, BP, CodeReadErrCnt, SP: integer;
|
||||
AnEntry: TDbgCallstackEntry;
|
||||
@ -2271,12 +2344,11 @@ begin
|
||||
CodeReadErrCnt := 0;
|
||||
while (CountNeeded > 0) and (FrameBase <> 0) and (FrameBase > LastFrameBase) do
|
||||
begin
|
||||
if not Process.ReadData(Address, sizeof(CodeBin), CodeBin, AReadSize) then begin
|
||||
if not Process.Disassembler.GetFunctionFrameInfo(Address, OutSideFrame) then begin
|
||||
if Process.Disassembler.LastErrorWasMemReadErr then begin
|
||||
inc(CodeReadErrCnt);
|
||||
if CodeReadErrCnt > 5 then break; // If the code cannot be read the stack pointer is wrong.
|
||||
end
|
||||
else begin
|
||||
if not GetFunctionFrameInfo(@CodeBin[0], AReadSize, FProcess.Mode=dm64, OutSideFrame) then
|
||||
end;
|
||||
OutSideFrame := False;
|
||||
end;
|
||||
LastFrameBase := FrameBase;
|
||||
|
||||
@ -9,7 +9,7 @@ uses
|
||||
Classes,
|
||||
SysUtils,
|
||||
Maps,
|
||||
LazLoggerBase,
|
||||
LazLoggerBase, LazClasses,
|
||||
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
||||
FpDbgDisasX86,
|
||||
FpDbgClasses, FpDbgInfo;
|
||||
@ -39,15 +39,16 @@ type
|
||||
FProcess: TDbgProcess;
|
||||
FThreadRemoved: boolean;
|
||||
FIsInitialized: Boolean;
|
||||
FNextInstruction: TDbgDisassemblerInstruction;
|
||||
procedure Init; virtual;
|
||||
function IsAtCallInstruction: Integer;
|
||||
function GetAsmInstruction(var AnInstr: TInstruction): Integer;
|
||||
procedure DoResolveEvent(var AnEvent: TFPDEvent; AnEventThread: TDbgThread; out Finished: boolean); virtual; abstract;
|
||||
public
|
||||
constructor Create(AController: TDbgController); virtual;
|
||||
destructor Destroy; override;
|
||||
procedure DoBeforeLoopStart;
|
||||
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); virtual; abstract;
|
||||
procedure ResolveEvent(var AnEvent: TFPDEvent; AnEventThread: TDbgThread; out Finished: boolean); virtual;
|
||||
procedure ResolveEvent(var AnEvent: TFPDEvent; AnEventThread: TDbgThread; out Finished: boolean);
|
||||
function NextInstruction: TDbgDisassemblerInstruction; inline;
|
||||
property Thread: TDbgThread read FThread write SetThread;
|
||||
end;
|
||||
|
||||
@ -78,11 +79,7 @@ type
|
||||
FIsSteppedOut: Boolean;
|
||||
FHiddenBreakpoint: TFpInternalBreakpoint;
|
||||
FHiddenBreakAddr, FHiddenBreakInstrPtr, FHiddenBreakFrameAddr, FHiddenBreakStackPtrAddr: TDBGPtr;
|
||||
FNextInstruction: TInstruction;
|
||||
FNextInstructionLen: Integer;
|
||||
function GetIsSteppedOut: Boolean;
|
||||
function GetNextInstructionLen: integer; inline;
|
||||
function GetNextOpCode: TOpCode; inline;
|
||||
protected
|
||||
function IsAtHiddenBreak: Boolean; inline;
|
||||
function HasHiddenBreak: Boolean; inline;
|
||||
@ -90,8 +87,6 @@ type
|
||||
function IsAtOrOutOfHiddenBreakFrame: Boolean; inline; // Stopped in/out-of the origin frame, maybe by a breakpoint after an exception
|
||||
procedure SetHiddenBreak(AnAddr: TDBGPtr);
|
||||
procedure RemoveHiddenBreak;
|
||||
property NextInstruction: TInstruction read FNextInstruction;
|
||||
|
||||
function CheckForCallAndSetBreak: boolean; // True, if break is newly set
|
||||
|
||||
procedure Init; override;
|
||||
@ -102,8 +97,6 @@ type
|
||||
|
||||
property StoredStackFrame: TDBGPtr read FStoredStackFrame;
|
||||
property IsSteppedOut: Boolean read GetIsSteppedOut;
|
||||
property NextInstructionLen: integer read GetNextInstructionLen;
|
||||
property NextOpCode: TOpCode read GetNextOpCode;
|
||||
end;
|
||||
|
||||
{ TDbgControllerStepOverInstructionCmd }
|
||||
@ -297,30 +290,6 @@ begin
|
||||
//
|
||||
end;
|
||||
|
||||
function TDbgControllerCmd.IsAtCallInstruction: Integer;
|
||||
var
|
||||
CodeBin: array[0..20] of byte;
|
||||
begin
|
||||
Result := 0;
|
||||
if FProcess.ReadData(FThread.GetInstructionPointerRegisterValue, sizeof(CodeBin), CodeBin) then
|
||||
Result := IsCallInstruction(@CodeBin, FProcess.Mode=dm64);
|
||||
end;
|
||||
|
||||
function TDbgControllerCmd.GetAsmInstruction(var AnInstr: TInstruction
|
||||
): Integer;
|
||||
var
|
||||
CodeBin: array[0..20] of byte;
|
||||
p: Pointer;
|
||||
begin
|
||||
Result := 0;
|
||||
AnInstr.OpCode := OPX_Invalid;
|
||||
if FProcess.ReadData(FThread.GetInstructionPointerRegisterValue, sizeof(CodeBin), CodeBin) then begin
|
||||
p := @CodeBin;
|
||||
Disassemble(p, FProcess.Mode=dm64, AnInstr);
|
||||
Result := p - @CodeBin[0];
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TDbgControllerCmd.Create(AController: TDbgController);
|
||||
begin
|
||||
FController := AController;
|
||||
@ -328,6 +297,12 @@ begin
|
||||
FThread := FController.CurrentThread;
|
||||
end;
|
||||
|
||||
destructor TDbgControllerCmd.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
ReleaseRefAndNil(FNextInstruction);
|
||||
end;
|
||||
|
||||
procedure TDbgControllerCmd.DoBeforeLoopStart;
|
||||
begin
|
||||
if not FIsInitialized then
|
||||
@ -340,6 +315,7 @@ procedure TDbgControllerCmd.ResolveEvent(var AnEvent: TFPDEvent;
|
||||
var
|
||||
dummy: TDbgThread;
|
||||
begin
|
||||
ReleaseRefAndNil(FNextInstruction); // instruction from last pause
|
||||
Finished := FThreadRemoved;
|
||||
if Finished then
|
||||
exit;
|
||||
@ -359,6 +335,15 @@ begin
|
||||
DoResolveEvent(AnEvent, AnEventThread, Finished);
|
||||
end;
|
||||
|
||||
function TDbgControllerCmd.NextInstruction: TDbgDisassemblerInstruction;
|
||||
begin
|
||||
if FNextInstruction = nil then begin
|
||||
FNextInstruction := FProcess.Disassembler.GetInstructionInfo(FThread.GetInstructionPointerRegisterValue);
|
||||
FNextInstruction.AddReference;
|
||||
end;
|
||||
Result := FNextInstruction;
|
||||
end;
|
||||
|
||||
{ TDbgControllerContinueCmd }
|
||||
|
||||
procedure TDbgControllerContinueCmd.Init;
|
||||
@ -398,20 +383,6 @@ end;
|
||||
|
||||
{ TDbgControllerHiddenBreakStepBaseCmd }
|
||||
|
||||
function TDbgControllerHiddenBreakStepBaseCmd.GetNextInstructionLen: integer;
|
||||
begin
|
||||
if FNextInstruction.OpCode = OPX_InternalUnknown then
|
||||
FNextInstructionLen := GetAsmInstruction(FNextInstruction);
|
||||
Result := FNextInstructionLen;
|
||||
end;
|
||||
|
||||
function TDbgControllerHiddenBreakStepBaseCmd.GetNextOpCode: TOpCode;
|
||||
begin
|
||||
if FNextInstruction.OpCode = OPX_InternalUnknown then
|
||||
FNextInstructionLen := GetAsmInstruction(FNextInstruction);
|
||||
Result := FNextInstruction.OpCode;
|
||||
end;
|
||||
|
||||
function TDbgControllerHiddenBreakStepBaseCmd.GetIsSteppedOut: Boolean;
|
||||
var
|
||||
CurBp, CurSp: TDBGPtr;
|
||||
@ -492,10 +463,10 @@ begin
|
||||
Result := FHiddenBreakpoint = nil;
|
||||
if not Result then
|
||||
exit;
|
||||
Result := NextOpCode = OPcall;
|
||||
Result := NextInstruction.IsCallInstruction;
|
||||
if Result then
|
||||
{$PUSH}{$Q-}{$R-}
|
||||
SetHiddenBreak(FThread.GetInstructionPointerRegisterValue + NextInstructionLen);
|
||||
SetHiddenBreak(FThread.GetInstructionPointerRegisterValue + NextInstruction.InstructionLength);
|
||||
{$POP}
|
||||
end;
|
||||
|
||||
@ -514,17 +485,19 @@ end;
|
||||
|
||||
procedure TDbgControllerHiddenBreakStepBaseCmd.DoContinue(AProcess: TDbgProcess;
|
||||
AThread: TDbgThread);
|
||||
var
|
||||
r: Boolean;
|
||||
begin
|
||||
if (AThread = FThread) then
|
||||
r := NextInstruction.IsReturnInstruction
|
||||
else
|
||||
r := False;
|
||||
InternalContinue(AProcess, AThread);
|
||||
if (AThread = FThread) then begin
|
||||
if ((FNextInstruction.OpCode = OPret) or (FNextInstruction.OpCode = OPretf)) and
|
||||
if r and
|
||||
(FHiddenBreakpoint = nil)
|
||||
then
|
||||
FIsSteppedOut := True;
|
||||
end;
|
||||
FNextInstruction.OpCode := OPX_InternalUnknown;
|
||||
FNextInstructionLen := 0;
|
||||
end;
|
||||
|
||||
{ TDbgControllerStepOverInstructionCmd }
|
||||
|
||||
@ -690,7 +663,7 @@ begin
|
||||
end;
|
||||
|
||||
inc(FStepCount);
|
||||
if IsAtCallInstruction > 0 then
|
||||
if NextInstruction.IsCallInstruction then
|
||||
inc(FNestDepth);
|
||||
|
||||
// FNestDepth = 2 => About to step into 3rd level nested
|
||||
@ -746,12 +719,8 @@ end;
|
||||
|
||||
function TDbgControllerStepOutCmd.GetOutsideFrame(var AnOutside: Boolean
|
||||
): Boolean;
|
||||
var
|
||||
CodeBin: array[0..30] of byte;
|
||||
begin
|
||||
Result := (FProcess.ReadData(FThread.GetInstructionPointerRegisterValue, sizeof(CodeBin), CodeBin) and
|
||||
GetFunctionFrameInfo(@CodeBin[0], sizeof(CodeBin), FProcess.Mode=dm64, AnOutside)
|
||||
);
|
||||
Result := FProcess.Disassembler.GetFunctionFrameInfo(FThread.GetInstructionPointerRegisterValue, AnOutside);
|
||||
end;
|
||||
|
||||
procedure TDbgControllerStepOutCmd.SetReturnAdressBreakpoint(
|
||||
@ -780,7 +749,6 @@ end;
|
||||
procedure TDbgControllerStepOutCmd.InternalContinue(AProcess: TDbgProcess;
|
||||
AThread: TDbgThread);
|
||||
var
|
||||
Opc: TOpCode;
|
||||
Outside: Boolean;
|
||||
begin
|
||||
assert(FProcess=AProcess, 'TDbgControllerStepOutCmd.DoContinue: FProcess=AProcess');
|
||||
@ -801,13 +769,12 @@ begin
|
||||
// setup already. To avoid problems in these cases, start with a few (max
|
||||
// 12) single steps.
|
||||
Inc(FStepCount);
|
||||
Opc := NextOpCode;
|
||||
if (Opc = OPcall) or (Opc = OPleave) then // asm "call" // set break before "leave" or the frame becomes unavail
|
||||
if NextInstruction.IsCallInstruction or NextInstruction.IsLeaveStackFrame then // asm "call" // set break before "leave" or the frame becomes unavail
|
||||
begin
|
||||
SetReturnAdressBreakpoint(AProcess, False);
|
||||
end
|
||||
else
|
||||
if (Opc = OPret) or (Opc = OPretf) then // asm "ret"
|
||||
if NextInstruction.IsReturnInstruction then // asm "ret"
|
||||
begin
|
||||
FStepCount := MaxInt; // Do one more single-step, and we're finished.
|
||||
FProcess.Continue(FProcess, FThread, True);
|
||||
|
||||
@ -230,6 +230,7 @@ procedure RegisterDbgClasses;
|
||||
begin
|
||||
OSDbgClasses.DbgProcessClass:=TDbgDarwinProcess;
|
||||
OSDbgClasses.DbgThreadClass:=TDbgDarwinThread;
|
||||
OSDbgClasses.DbgDisassemblerClass := TX86Disassembler;
|
||||
end;
|
||||
|
||||
Function WIFSTOPPED(Status: Integer): Boolean;
|
||||
|
||||
@ -39,8 +39,8 @@ interface
|
||||
{.$define verbose_string_instructions}
|
||||
|
||||
uses
|
||||
SysUtils,
|
||||
FpDbgUtil, FpDbgInfo, DbgIntfBaseTypes, FpdMemoryTools, LazLoggerBase;
|
||||
SysUtils, FpDbgUtil, FpDbgInfo, DbgIntfBaseTypes, FpdMemoryTools,
|
||||
FpDbgClasses, LazLoggerBase, LazClasses;
|
||||
|
||||
{
|
||||
The function Disassemble decodes the instruction at the given address.
|
||||
@ -201,15 +201,58 @@ type
|
||||
ParseFlags: TFlags;
|
||||
end;
|
||||
|
||||
procedure Disassemble(var AAddress: Pointer; const A64Bit: Boolean; out ACodeBytes: String; out ACode: String);
|
||||
procedure Disassemble(var AAddress: Pointer; const A64Bit: Boolean; out AnInstruction: TInstruction);
|
||||
TX86Disassembler = class;
|
||||
|
||||
// returns byte len of call instruction at AAddress // 0 if not a call intruction
|
||||
function IsCallInstruction(AAddress: Pointer; const A64Bit: Boolean): Integer;
|
||||
function IsReturnInstruction(AAddress: Pointer; const A64Bit: Boolean): Integer;
|
||||
{ TX86DisassemblerInstruction }
|
||||
|
||||
function GetFunctionFrameInfo(AData: PByte; ADataLen: Cardinal; const A64Bit: Boolean;
|
||||
out AnIsOutsideFrame: Boolean): Boolean;
|
||||
TX86DisassemblerInstruction = class(TDbgDisassemblerInstruction)
|
||||
private
|
||||
FDisassembler: TX86Disassembler;
|
||||
FAddress: TDBGPtr;
|
||||
FCodeBin: array[0..16] of byte;
|
||||
FInstruction: TInstruction;
|
||||
FInstrLen: Integer;
|
||||
FFlags: set of (diCodeRead, diCodeReadError, diDisAss);
|
||||
const
|
||||
INSTR_CODEBIN_LEN = 16;
|
||||
protected
|
||||
procedure ReadCode; inline;
|
||||
procedure Disassemble; inline;
|
||||
public
|
||||
constructor Create(ADisassembler: TX86Disassembler);
|
||||
procedure SetAddress(AnAddress: TDBGPtr);
|
||||
function IsCallInstruction: boolean; override;
|
||||
function IsReturnInstruction: boolean; override;
|
||||
function IsLeaveStackFrame: boolean; override;
|
||||
function InstructionLength: Integer; override;
|
||||
function X86OpCode: TOpCode;
|
||||
property X86Instruction: TInstruction read FInstruction; // only valid after call to X86OpCode
|
||||
end;
|
||||
|
||||
{ TX86Disassembler }
|
||||
|
||||
TX86Disassembler = class(TDBGDisassembler)
|
||||
private
|
||||
FProcess: TDbgProcess;
|
||||
FLastErrWasMem: Boolean;
|
||||
FCodeBin: array[0..50] of byte;
|
||||
FLastInstr: TX86DisassemblerInstruction;
|
||||
const
|
||||
MAX_CODEBIN_LEN = 50;
|
||||
protected
|
||||
function GetLastErrorWasMemReadErr: Boolean; override;
|
||||
function ReadCodeAt(AnAddress: TDBGPtr; var ALen: Cardinal): Boolean; inline;
|
||||
procedure Disassemble(var AAddress: Pointer; out AnInstruction: TInstruction);
|
||||
public
|
||||
constructor Create(AProcess: TDbgProcess); override;
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); override;
|
||||
function GetInstructionInfo(AnAddress: TDBGPtr): TDbgDisassemblerInstruction; override;
|
||||
|
||||
function GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
||||
AnIsOutsideFrame: Boolean): Boolean; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
var
|
||||
@ -324,7 +367,93 @@ const
|
||||
'o', 'no', 'b', 'nb', 'z', 'nz', 'be', 'nbe', 's', 'ns', 'p', 'np', 'l', 'nl', 'le', 'nle'
|
||||
);
|
||||
|
||||
procedure Disassemble(var AAddress: Pointer; const A64Bit: Boolean; out AnInstruction: TInstruction);
|
||||
{ TX86DisassemblerInstruction }
|
||||
|
||||
procedure TX86DisassemblerInstruction.ReadCode;
|
||||
begin
|
||||
if not (diCodeRead in FFlags) then begin
|
||||
if not FDisassembler.FProcess.ReadData(FAddress, INSTR_CODEBIN_LEN, FCodeBin) then
|
||||
Include(FFlags, diCodeReadError);
|
||||
Include(FFlags, diCodeRead);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TX86DisassemblerInstruction.Disassemble;
|
||||
var
|
||||
a: PByte;
|
||||
begin
|
||||
if not (diDisAss in FFlags) then begin
|
||||
ReadCode;
|
||||
a := @FCodeBin[0];
|
||||
FDisassembler.Disassemble(a, FInstruction);
|
||||
FInstrLen := a - @FCodeBin[0];
|
||||
Include(FFlags, diDisAss);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TX86DisassemblerInstruction.Create(ADisassembler: TX86Disassembler);
|
||||
begin
|
||||
FDisassembler := ADisassembler;
|
||||
inherited Create;
|
||||
AddReference;
|
||||
end;
|
||||
|
||||
procedure TX86DisassemblerInstruction.SetAddress(AnAddress: TDBGPtr);
|
||||
begin
|
||||
FAddress := AnAddress;
|
||||
FFlags := [];
|
||||
end;
|
||||
|
||||
function TX86DisassemblerInstruction.IsCallInstruction: boolean;
|
||||
var
|
||||
a: PByte;
|
||||
begin
|
||||
Result := False;
|
||||
ReadCode;
|
||||
a := @FCodeBin[0];
|
||||
|
||||
if (FDisassembler.FProcess.Mode = dm64) then begin
|
||||
while (a < @FCodeBin[0] + INSTR_CODEBIN_LEN) and (a^ in [$40..$4F, $64..$67]) do
|
||||
inc(a);
|
||||
if not (a^ in [$E8, $FF]) then
|
||||
exit;
|
||||
end
|
||||
else begin
|
||||
while (a < @FCodeBin[0] + INSTR_CODEBIN_LEN) and (a^ in [$26, $2E, $36, $3E, $64..$67]) do
|
||||
inc(a);
|
||||
if not (a^ in [$9A, $E8, $FF]) then
|
||||
exit;
|
||||
end;
|
||||
|
||||
Disassemble;
|
||||
Result := FInstruction.OpCode = OPcall;
|
||||
end;
|
||||
|
||||
function TX86DisassemblerInstruction.IsReturnInstruction: boolean;
|
||||
begin
|
||||
Disassemble;
|
||||
Result := (FInstruction.OpCode = OPret) or (FInstruction.OpCode = OPretf);
|
||||
end;
|
||||
|
||||
function TX86DisassemblerInstruction.IsLeaveStackFrame: boolean;
|
||||
begin
|
||||
Disassemble;
|
||||
Result := (FInstruction.OpCode = OPleave);
|
||||
end;
|
||||
|
||||
function TX86DisassemblerInstruction.InstructionLength: Integer;
|
||||
begin
|
||||
Disassemble;
|
||||
Result := FInstrLen;
|
||||
end;
|
||||
|
||||
function TX86DisassemblerInstruction.X86OpCode: TOpCode;
|
||||
begin
|
||||
Disassemble;
|
||||
Result := FInstruction.OpCode;
|
||||
end;
|
||||
|
||||
procedure TX86Disassembler.Disassemble(var AAddress: Pointer; out AnInstruction: TInstruction);
|
||||
var
|
||||
Code: PByte;
|
||||
CodeIdx: Byte;
|
||||
@ -336,21 +465,21 @@ var
|
||||
procedure Check32;
|
||||
begin
|
||||
// only valid in 32-bit mode
|
||||
if A64Bit then
|
||||
if (FProcess.Mode = dm64) then
|
||||
Include(AnInstruction.Flags, ifOnly32);
|
||||
end;
|
||||
|
||||
procedure Check64;
|
||||
begin
|
||||
// only valid in 64-bit mode
|
||||
if A64Bit then
|
||||
if (FProcess.Mode = dm64) then
|
||||
Include(AnInstruction.Flags, ifOnly64);
|
||||
end;
|
||||
|
||||
function Ignore64(s: String): String;
|
||||
begin
|
||||
// ignored in 64-bit mode
|
||||
if A64Bit then
|
||||
if (FProcess.Mode = dm64) then
|
||||
Result := '('+s+')'
|
||||
else
|
||||
Result := s;
|
||||
@ -436,7 +565,7 @@ var
|
||||
function AddressSize32: TAddressSize;
|
||||
begin
|
||||
// effective address size for default 32 AnInstruction.operand size
|
||||
if A64Bit
|
||||
if (FProcess.Mode = dm64)
|
||||
then begin
|
||||
if preAdr in Flags
|
||||
then Result := as32
|
||||
@ -574,7 +703,7 @@ var
|
||||
{64}(os8, os16, os32, os64, os64, os128, os16, os64, os64, os80)
|
||||
);
|
||||
begin
|
||||
AddOperand(StdReg(AIndex, AType, AExtReg), REGSIZE[A64Bit, AType]);
|
||||
AddOperand(StdReg(AIndex, AType, AExtReg), REGSIZE[(FProcess.Mode = dm64), AType]);
|
||||
end;
|
||||
|
||||
procedure AddStdReg(AIndex: Byte);
|
||||
@ -907,7 +1036,7 @@ var
|
||||
|
||||
procedure AddMs;
|
||||
begin
|
||||
if A64Bit
|
||||
if (FProcess.Mode = dm64)
|
||||
then AddModRM([modMem], os80, reg0 {do not care})
|
||||
else AddModRM([modMem], os48, reg0 {do not care});
|
||||
end;
|
||||
@ -958,7 +1087,7 @@ var
|
||||
|
||||
procedure AddRd_q;
|
||||
begin
|
||||
if A64Bit
|
||||
if (FProcess.Mode = dm64)
|
||||
then AddModRM([modReg], os64, reg64)
|
||||
else AddModRM([modReg], os32, reg32);
|
||||
end;
|
||||
@ -2706,7 +2835,7 @@ var
|
||||
end;
|
||||
//---
|
||||
$40..$4F: begin
|
||||
if A64Bit
|
||||
if (FProcess.Mode = dm64)
|
||||
then begin
|
||||
if (Code[CodeIdx] and 1) <> 0 then Include(Flags, rexB);
|
||||
if (Code[CodeIdx] and 2) <> 0 then Include(Flags, rexX);
|
||||
@ -2749,7 +2878,7 @@ var
|
||||
AddGv; AddMa;
|
||||
end;
|
||||
$63: begin
|
||||
if A64Bit
|
||||
if (FProcess.Mode = dm64)
|
||||
then begin
|
||||
Opcode := (OPmovsxd);
|
||||
AddGv; AddEd;
|
||||
@ -3293,7 +3422,8 @@ begin
|
||||
Inc(AAddress, CodeIdx);
|
||||
end;
|
||||
|
||||
procedure Disassemble(var AAddress: Pointer; const A64Bit: Boolean; out ACodeBytes: String; out ACode: String);
|
||||
procedure TX86Disassembler.Disassemble(var AAddress: Pointer;
|
||||
out ACodeBytes: String; out ACode: String);
|
||||
const
|
||||
MEMPTR: array[TOperandSize] of string = ('byte ptr ', 'word ptr ', 'dword ptr ', 'qword ptr ', '', 'tbyte ptr ', '16byte ptr ');
|
||||
{$ifdef debug_OperandSize}
|
||||
@ -3308,7 +3438,7 @@ var
|
||||
Code: PByte;
|
||||
begin
|
||||
Code := AAddress;
|
||||
Disassemble(AAddress, A64Bit, Instr);
|
||||
Disassemble(AAddress, Instr);
|
||||
|
||||
Soper := '';
|
||||
HasMem := False;
|
||||
@ -3374,52 +3504,55 @@ begin
|
||||
ACodeBytes := S;
|
||||
end;
|
||||
|
||||
function IsCallInstruction(AAddress: Pointer; const A64Bit: Boolean): Integer;
|
||||
function TX86Disassembler.GetInstructionInfo(AnAddress: TDBGPtr
|
||||
): TDbgDisassemblerInstruction;
|
||||
begin
|
||||
if (FLastInstr = nil) or (FLastInstr.RefCount > 1) then begin
|
||||
ReleaseRefAndNil(FLastInstr);
|
||||
FLastInstr := TX86DisassemblerInstruction.Create(Self);
|
||||
end;
|
||||
|
||||
FLastInstr.SetAddress(AnAddress);
|
||||
Result := FLastInstr;
|
||||
end;
|
||||
|
||||
{ TX86Disassembler }
|
||||
|
||||
function TX86Disassembler.GetLastErrorWasMemReadErr: Boolean;
|
||||
begin
|
||||
Result := FLastErrWasMem;
|
||||
end;
|
||||
|
||||
function TX86Disassembler.ReadCodeAt(AnAddress: TDBGPtr; var ALen: Cardinal
|
||||
): Boolean;
|
||||
begin
|
||||
FLastErrWasMem := not FProcess.ReadData(AnAddress, ALen, FCodeBin[0], ALen);
|
||||
Result := FLastErrWasMem;
|
||||
end;
|
||||
|
||||
constructor TX86Disassembler.Create(AProcess: TDbgProcess);
|
||||
begin
|
||||
FProcess := AProcess;
|
||||
end;
|
||||
|
||||
destructor TX86Disassembler.Destroy;
|
||||
begin
|
||||
ReleaseRefAndNil(FLastInstr);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TX86Disassembler.GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
||||
AnIsOutsideFrame: Boolean): Boolean;
|
||||
var
|
||||
Instr: TInstruction;
|
||||
a: PByte;
|
||||
ADataLen: Cardinal;
|
||||
AData: PByte;
|
||||
begin
|
||||
Result := 0;
|
||||
a := AAddress;
|
||||
|
||||
if A64Bit then begin
|
||||
while (a < AAddress + 16) and (a^ in [$40..$4F, $64..$67]) do
|
||||
inc(a);
|
||||
if not (a^ in [$E8, $FF]) then
|
||||
ADataLen := MAX_CODEBIN_LEN;
|
||||
Result := False;
|
||||
if not ReadCodeAt(AnAddress, ADataLen) then
|
||||
exit;
|
||||
end
|
||||
else begin
|
||||
while (a < AAddress + 16) and (a^ in [$26, $2E, $36, $3E, $64..$67]) do
|
||||
inc(a);
|
||||
if not (a^ in [$9A, $E8, $FF]) then
|
||||
exit;
|
||||
end;
|
||||
AData := @FCodeBin[0];
|
||||
|
||||
a := AAddress;
|
||||
Disassemble(AAddress, A64Bit, Instr);
|
||||
if Instr.OpCode <> OPcall
|
||||
then
|
||||
exit;
|
||||
Result := AAddress - a;
|
||||
end;
|
||||
|
||||
function IsReturnInstruction(AAddress: Pointer; const A64Bit: Boolean): Integer;
|
||||
var
|
||||
Instr: TInstruction;
|
||||
a: PByte;
|
||||
begin
|
||||
Result := 0;
|
||||
a := AAddress;
|
||||
Disassemble(AAddress, A64Bit, Instr);
|
||||
if (Instr.OpCode <> OPret) and (Instr.OpCode <> OPretf)
|
||||
then
|
||||
exit;
|
||||
Result := AAddress - a;
|
||||
end;
|
||||
|
||||
function GetFunctionFrameInfo(AData: PByte; ADataLen: Cardinal;
|
||||
const A64Bit: Boolean; out AnIsOutsideFrame: Boolean): Boolean;
|
||||
begin
|
||||
while (ADataLen > 0) and (AData^ = $90) do begin // nop
|
||||
inc(AData);
|
||||
dec(ADataLen);
|
||||
|
||||
@ -326,6 +326,7 @@ procedure RegisterDbgClasses;
|
||||
begin
|
||||
OSDbgClasses.DbgProcessClass:=TDbgLinuxProcess;
|
||||
OSDbgClasses.DbgThreadClass:=TDbgLinuxThread;
|
||||
OSDbgClasses.DbgDisassemblerClass := TX86Disassembler;
|
||||
end;
|
||||
|
||||
Function WIFSTOPPED(Status: Integer): Boolean;
|
||||
|
||||
@ -116,7 +116,7 @@ uses
|
||||
FpDbgWinExtra,
|
||||
strutils,
|
||||
FpDbgInfo,
|
||||
FpDbgLoader,
|
||||
FpDbgLoader, FpDbgDisasX86,
|
||||
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
||||
LazLoggerBase, UTF8Process;
|
||||
|
||||
@ -341,6 +341,7 @@ begin
|
||||
OSDbgClasses.DbgThreadClass:=TDbgWinThread;
|
||||
OSDbgClasses.DbgBreakpointClass:=TFpInternalBreakpoint;
|
||||
OSDbgClasses.DbgProcessClass:=TDbgWinProcess;
|
||||
OSDbgClasses.DbgDisassemblerClass := TX86Disassembler;
|
||||
end;
|
||||
|
||||
procedure TDbgWinProcess.LogLastError;
|
||||
|
||||
@ -103,7 +103,7 @@ type
|
||||
procedure DoResolveEvent(var AnEvent: TFPDEvent; AnEventThread: TDbgThread; out Finished: boolean); override;
|
||||
procedure InternalContinue(AProcess: TDbgProcess; AThread: TDbgThread); override;
|
||||
public
|
||||
constructor Create(AController: TDbgController; AnAfterFinCallAddr: TDbgPtr);
|
||||
constructor Create(AController: TDbgController; AnAfterFinCallAddr: TDbgPtr); reintroduce;
|
||||
end;
|
||||
|
||||
{ TFpDebugExceptionStepping }
|
||||
@ -510,7 +510,7 @@ procedure TDbgControllerStepOverFirstFinallyLineCmd.DoResolveEvent(
|
||||
var AnEvent: TFPDEvent; AnEventThread: TDbgThread; out Finished: boolean);
|
||||
begin
|
||||
Finished := (FThread.CompareStepInfo(0, True) <> dcsiSameLine) or
|
||||
(NextOpCode = OPret) or IsSteppedOut;
|
||||
(NextInstruction.IsReturnInstruction) or IsSteppedOut;
|
||||
|
||||
if Finished then
|
||||
AnEvent := deFinishedStep
|
||||
@ -523,6 +523,8 @@ end;
|
||||
|
||||
procedure TDbgControllerStepOverOrFinallyCmd.InternalContinue(
|
||||
AProcess: TDbgProcess; AThread: TDbgThread);
|
||||
var
|
||||
Instr: TDbgDisassemblerInstruction;
|
||||
begin
|
||||
{
|
||||
00000001000374AE 4889C1 mov rcx,rax
|
||||
@ -531,10 +533,12 @@ begin
|
||||
00000001000374BB E89022FEFF call -$0001DD70
|
||||
|
||||
}
|
||||
if (AThread = FThread) then
|
||||
case NextOpCode of
|
||||
if (AThread = FThread) then begin
|
||||
Instr := NextInstruction;
|
||||
if Instr is TX86DisassemblerInstruction then begin
|
||||
case TX86DisassemblerInstruction(Instr).X86OpCode of
|
||||
OPmov:
|
||||
if UpperCase(NextInstruction.Operand[2].Value) = 'RBP' then
|
||||
if UpperCase(TX86DisassemblerInstruction(Instr).X86Instruction.Operand[2].Value) = 'RBP' then
|
||||
FFinState := fsMov;
|
||||
OPcall:
|
||||
if FFinState = fsMov then begin
|
||||
@ -546,6 +550,8 @@ begin
|
||||
else
|
||||
FFinState := fsNone;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
inherited InternalContinue(AProcess, AThread);
|
||||
end;
|
||||
|
||||
@ -582,7 +588,7 @@ begin
|
||||
if IsAtOrOutOfHiddenBreakFrame then
|
||||
RemoveHiddenBreak;
|
||||
|
||||
Finished := IsSteppedOut or FDone or ((not HasHiddenBreak) and (NextOpCode = OPret));
|
||||
Finished := IsSteppedOut or FDone or ((not HasHiddenBreak) and (NextInstruction.IsReturnInstruction));
|
||||
if Finished then
|
||||
AnEvent := deFinishedStep
|
||||
else
|
||||
@ -594,8 +600,9 @@ procedure TDbgControllerStepThroughFpcSpecialHandler.InternalContinue(
|
||||
AProcess: TDbgProcess; AThread: TDbgThread);
|
||||
begin
|
||||
{$PUSH}{$Q-}{$R-}
|
||||
if (AThread = FThread) and (NextOpCode = OPcall) and
|
||||
(FThread.GetInstructionPointerRegisterValue + NextInstructionLen = FAfterFinCallAddr)
|
||||
if (AThread = FThread) and
|
||||
(NextInstruction.IsCallInstruction) and
|
||||
(FThread.GetInstructionPointerRegisterValue + NextInstruction.InstructionLength = FAfterFinCallAddr)
|
||||
then begin
|
||||
RemoveHiddenBreak;
|
||||
FProcess.Continue(FProcess, FThread, True);
|
||||
@ -1331,7 +1338,8 @@ begin
|
||||
else
|
||||
begin
|
||||
p := @CodeBin;
|
||||
FpDbgDisasX86.Disassemble(p, TFpDebugDebugger(Debugger).FDbgController.CurrentProcess.Mode=dm64, ADump, AStatement);
|
||||
TFpDebugDebugger(Debugger).FDbgController.CurrentProcess
|
||||
.Disassembler.Disassemble(p, ADump, AStatement);
|
||||
|
||||
Sym := TFpDebugDebugger(Debugger).FDbgController.CurrentProcess.FindProcSymbol(AnAddr);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user