FpDebug: Refactor disassembler into class

git-svn-id: trunk@62746 -
This commit is contained in:
martin 2020-03-12 14:09:14 +00:00
parent f6c39c019d
commit 676c17f1cf
9 changed files with 346 additions and 161 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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,13 +2344,12 @@ 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
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
OutSideFrame := False;
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;
OutSideFrame := False;
end;
LastFrameBase := FrameBase;
if OutSideFrame then begin

View File

@ -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,16 +485,18 @@ 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
(FHiddenBreakpoint = nil)
then
FIsSteppedOut := True;
end;
FNextInstruction.OpCode := OPX_InternalUnknown;
FNextInstructionLen := 0;
if r and
(FHiddenBreakpoint = nil)
then
FIsSteppedOut := True;
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);

View File

@ -230,6 +230,7 @@ procedure RegisterDbgClasses;
begin
OSDbgClasses.DbgProcessClass:=TDbgDarwinProcess;
OSDbgClasses.DbgThreadClass:=TDbgDarwinThread;
OSDbgClasses.DbgDisassemblerClass := TX86Disassembler;
end;
Function WIFSTOPPED(Status: Integer): Boolean;

View File

@ -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;
var
Instr: TInstruction;
a: PByte;
function TX86Disassembler.GetInstructionInfo(AnAddress: TDBGPtr
): TDbgDisassemblerInstruction;
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
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;
if (FLastInstr = nil) or (FLastInstr.RefCount > 1) then begin
ReleaseRefAndNil(FLastInstr);
FLastInstr := TX86DisassemblerInstruction.Create(Self);
end;
a := AAddress;
Disassemble(AAddress, A64Bit, Instr);
if Instr.OpCode <> OPcall
then
exit;
Result := AAddress - a;
FLastInstr.SetAddress(AnAddress);
Result := FLastInstr;
end;
function IsReturnInstruction(AAddress: Pointer; const A64Bit: Boolean): Integer;
{ 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;
Disassemble(AAddress, A64Bit, Instr);
if (Instr.OpCode <> OPret) and (Instr.OpCode <> OPretf)
then
exit;
Result := AAddress - a;
end;
ADataLen := MAX_CODEBIN_LEN;
Result := False;
if not ReadCodeAt(AnAddress, ADataLen) then
exit;
AData := @FCodeBin[0];
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);

View File

@ -326,6 +326,7 @@ procedure RegisterDbgClasses;
begin
OSDbgClasses.DbgProcessClass:=TDbgLinuxProcess;
OSDbgClasses.DbgThreadClass:=TDbgLinuxThread;
OSDbgClasses.DbgDisassemblerClass := TX86Disassembler;
end;
Function WIFSTOPPED(Status: Integer): Boolean;

View File

@ -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;

View File

@ -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,21 +533,25 @@ begin
00000001000374BB E89022FEFF call -$0001DD70
}
if (AThread = FThread) then
case NextOpCode of
OPmov:
if UpperCase(NextInstruction.Operand[2].Value) = 'RBP' then
FFinState := fsMov;
OPcall:
if FFinState = fsMov then begin
CheckForCallAndSetBreak;
FProcess.Continue(FProcess, FThread, True); // Step into
FFinState := fsCall;
exit;
end;
else
FFinState := fsNone;
if (AThread = FThread) then begin
Instr := NextInstruction;
if Instr is TX86DisassemblerInstruction then begin
case TX86DisassemblerInstruction(Instr).X86OpCode of
OPmov:
if UpperCase(TX86DisassemblerInstruction(Instr).X86Instruction.Operand[2].Value) = 'RBP' then
FFinState := fsMov;
OPcall:
if FFinState = fsMov then begin
CheckForCallAndSetBreak;
FProcess.Continue(FProcess, FThread, True); // Step into
FFinState := fsCall;
exit;
end;
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);