mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-19 00:59:25 +02:00
FpDebug: refactor stack unwinding. Introduce TDbgStackUnwinder and new classes for X86
This commit is contained in:
parent
b5db1bf52f
commit
476cfd4484
@ -192,6 +192,50 @@ type
|
|||||||
property StoredStackFrame: TDBGPtr read FStoredStackFrame;
|
property StoredStackFrame: TDBGPtr read FStoredStackFrame;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TTDbgStackUnwindResult = (suSuccess, suFailed,
|
||||||
|
suFailedAtEOS, // this is the End Of Stack
|
||||||
|
suGuessed // Got a frame, but may be wrong
|
||||||
|
);
|
||||||
|
|
||||||
|
TDbgStackUnwinder = class
|
||||||
|
public
|
||||||
|
procedure InitForThread(AThread: TDbgThread); virtual; abstract;
|
||||||
|
// FrameBasePointer is optional
|
||||||
|
procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
out ANewFrame: TDbgCallstackEntry); virtual; abstract;
|
||||||
|
procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry;
|
||||||
|
out CodePointer, StackPointer, FrameBasePointer: TDBGPtr); virtual; abstract;
|
||||||
|
// AFrameIndex: The frame-index to be read. Starts at 1 (since 0 is top-lever, and handled by GetTopFrame)
|
||||||
|
function Unwind(AFrameIndex: integer;
|
||||||
|
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
ACurrentFrame: TDbgCallstackEntry; // nil for top frame
|
||||||
|
out ANewFrame: TDbgCallstackEntry
|
||||||
|
): TTDbgStackUnwindResult; virtual; abstract;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86Base }
|
||||||
|
// Avoid circular unit refs
|
||||||
|
|
||||||
|
TDbgStackUnwinderX86Base = class(TDbgStackUnwinder)
|
||||||
|
private
|
||||||
|
FThread: TDbgThread;
|
||||||
|
FProcess: TDbgProcess;
|
||||||
|
FAddressSize: Integer;
|
||||||
|
protected
|
||||||
|
FDwarfNumIP, FDwarfNumBP, FDwarfNumSP: integer;
|
||||||
|
FNameIP, FNameBP, FNameSP: String;
|
||||||
|
property Process: TDbgProcess read FProcess;
|
||||||
|
property Thread: TDbgThread read FThread;
|
||||||
|
property AddressSize: Integer read FAddressSize;
|
||||||
|
public
|
||||||
|
constructor Create(AProcess: TDbgProcess);
|
||||||
|
procedure InitForThread(AThread: TDbgThread); override;
|
||||||
|
procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry; out CodePointer,
|
||||||
|
StackPointer, FrameBasePointer: TDBGPtr); override;
|
||||||
|
procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
out ANewFrame: TDbgCallstackEntry); override;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TDbgThread }
|
{ TDbgThread }
|
||||||
TFpInternalBreakpoint = class;
|
TFpInternalBreakpoint = class;
|
||||||
|
|
||||||
@ -223,6 +267,7 @@ type
|
|||||||
procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change
|
procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change
|
||||||
procedure ValidateRemovedBreakPointInfo;
|
procedure ValidateRemovedBreakPointInfo;
|
||||||
function GetName: String; virtual;
|
function GetName: String; virtual;
|
||||||
|
function GetStackUnwinder: TDbgStackUnwinder; virtual; abstract;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual;
|
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual;
|
||||||
@ -3051,6 +3096,74 @@ begin
|
|||||||
FHasSteppedOut := True;
|
FHasSteppedOut := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86Base }
|
||||||
|
|
||||||
|
constructor TDbgStackUnwinderX86Base.Create(AProcess: TDbgProcess);
|
||||||
|
begin
|
||||||
|
FProcess := AProcess;
|
||||||
|
case AProcess.Mode of
|
||||||
|
dm32: begin
|
||||||
|
FAddressSize := 4;
|
||||||
|
FDwarfNumIP := 8; // Dwarf Reg Num EIP
|
||||||
|
FDwarfNumBP := 5; // EBP
|
||||||
|
FDwarfNumSP := 4; // ESP
|
||||||
|
FNameIP := 'eip';
|
||||||
|
FNameBP := 'ebp';
|
||||||
|
FNameSP := 'esp';
|
||||||
|
end;
|
||||||
|
dm64: begin
|
||||||
|
FAddressSize := 8;
|
||||||
|
FDwarfNumIP := 16; // Dwarf Reg Num RIP
|
||||||
|
FDwarfNumBP := 6; // RBP
|
||||||
|
FDwarfNumSP := 7; // RSP
|
||||||
|
FNameIP := 'rip';
|
||||||
|
FNameBP := 'rbp';
|
||||||
|
FNameSP := 'rsp';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDbgStackUnwinderX86Base.InitForThread(AThread: TDbgThread);
|
||||||
|
begin
|
||||||
|
FThread := AThread;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDbgStackUnwinderX86Base.InitForFrame(
|
||||||
|
ACurrentFrame: TDbgCallstackEntry; out CodePointer, StackPointer,
|
||||||
|
FrameBasePointer: TDBGPtr);
|
||||||
|
var
|
||||||
|
R: TDbgRegisterValue;
|
||||||
|
begin
|
||||||
|
CodePointer := ACurrentFrame.AnAddress;
|
||||||
|
FrameBasePointer := ACurrentFrame.FrameAdress;
|
||||||
|
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumBP);
|
||||||
|
if R <> nil then
|
||||||
|
FrameBasePointer := R.NumValue;
|
||||||
|
StackPointer := 0;
|
||||||
|
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumSP);
|
||||||
|
if R = nil then exit;
|
||||||
|
StackPointer := R.NumValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDbgStackUnwinderX86Base.GetTopFrame(out CodePointer, StackPointer,
|
||||||
|
FrameBasePointer: TDBGPtr; out ANewFrame: TDbgCallstackEntry);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
R: TDbgRegisterValue;
|
||||||
|
begin
|
||||||
|
CodePointer := Thread.GetInstructionPointerRegisterValue;
|
||||||
|
StackPointer := Thread.GetStackPointerRegisterValue;
|
||||||
|
FrameBasePointer := Thread.GetStackBasePointerRegisterValue;
|
||||||
|
ANewFrame := TDbgCallstackEntry.create(Thread, 0, FrameBasePointer, CodePointer);
|
||||||
|
|
||||||
|
i := Thread.RegisterValueList.Count;
|
||||||
|
while i > 0 do begin
|
||||||
|
dec(i);
|
||||||
|
R := Thread.RegisterValueList[i];
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TDbgThread }
|
{ TDbgThread }
|
||||||
|
|
||||||
function TDbgThread.GetRegisterValueList: TDbgRegisterValueList;
|
function TDbgThread.GetRegisterValueList: TDbgRegisterValueList;
|
||||||
@ -3315,254 +3428,50 @@ procedure TDbgThread.PrepareCallStackEntryList(AFrameRequired: Integer);
|
|||||||
const
|
const
|
||||||
MAX_FRAMES = 150000; // safety net
|
MAX_FRAMES = 150000; // safety net
|
||||||
var
|
var
|
||||||
Address, FrameBase, LastFrameBase, Dummy: QWord;
|
Address, FrameBase, StackPtr: TDBGPtr;
|
||||||
PrevAddress, PrevFrameBase, PrevStackPtr: QWord;
|
CountNeeded, i: integer;
|
||||||
Size, CountNeeded, IP, BP, CodeReadErrCnt, SP, i,
|
AnEntry: TDbgCallstackEntry;
|
||||||
PrevStmtAddressOffs: integer;
|
|
||||||
AnEntry, NewEntry: TDbgCallstackEntry;
|
|
||||||
R, StackReg, FrameReg: TDbgRegisterValue;
|
|
||||||
nIP, nBP, nSP: String;
|
|
||||||
NextIdx: LongInt;
|
NextIdx: LongInt;
|
||||||
OutSideFrame: Boolean;
|
Unwinder: TDbgStackUnwinder;
|
||||||
StackPtr: TDBGPtr;
|
Res: TTDbgStackUnwindResult;
|
||||||
Row: TDwarfCallFrameInformationRow;
|
|
||||||
CIE: TDwarfCIE;
|
|
||||||
CU: TDwarfCompilationUnit;
|
|
||||||
|
|
||||||
procedure CheckFrame(var NextEntry: TDbgCallstackEntry; AForce: boolean = False);
|
|
||||||
begin
|
|
||||||
if (not AForce) and (NextEntry <> nil) and Process.Disassembler.IsAfterCallInstruction(Address) then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
if not Process.Disassembler.UnwindFrame(PrevAddress, PrevStackPtr, PrevFrameBase, False) then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
if (NextEntry <> nil) and
|
|
||||||
(Address = PrevAddress) and (FrameBase = PrevFrameBase) and (StackPtr = PrevStackPtr)
|
|
||||||
then
|
|
||||||
exit;
|
|
||||||
if not Process.Disassembler.IsAfterCallInstruction(PrevAddress) then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
Address := PrevAddress;
|
|
||||||
FrameBase := PrevFrameBase;
|
|
||||||
StackPtr := PrevStackPtr;
|
|
||||||
NextEntry.Free;
|
|
||||||
NextEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
|
||||||
NextEntry.RegisterValueList.DbgRegisterAutoCreate[nIP].SetValue(Address, IntToStr(Address),Size, IP);
|
|
||||||
NextEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
|
||||||
NextEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
|
||||||
LastFrameBase := FrameBase;
|
|
||||||
{$PUSH}{$R-}{$Q-}
|
|
||||||
if LastFrameBase > 0 then
|
|
||||||
LastFrameBase := LastFrameBase - 1;
|
|
||||||
{$POP}
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
// TODO: use AFrameRequired // check if already partly done
|
// TODO: use AFrameRequired // check if already partly done
|
||||||
if FCallStackEntryList = nil then
|
if FCallStackEntryList = nil then begin
|
||||||
FCallStackEntryList := TDbgCallstackEntryList.Create;
|
FCallStackEntryList := TDbgCallstackEntryList.Create;
|
||||||
|
FCallStackEntryList.FreeObjects:=true;
|
||||||
|
end;
|
||||||
if AFrameRequired = -2 then
|
if AFrameRequired = -2 then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
if (AFrameRequired >= 0) and (AFrameRequired < FCallStackEntryList.Count) then
|
if (AFrameRequired >= 0) and (AFrameRequired < FCallStackEntryList.Count) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
case FProcess.Mode of
|
Unwinder := GetStackUnwinder;
|
||||||
dm32: begin
|
Unwinder.InitForThread(Self);
|
||||||
Size := 4;
|
|
||||||
IP := 8; // Dwarf Reg Num EIP
|
|
||||||
BP := 5; // EBP
|
|
||||||
SP := 4; // ESP
|
|
||||||
nIP := 'eip';
|
|
||||||
nBP := 'ebp';
|
|
||||||
nSP := 'esp';
|
|
||||||
end;
|
|
||||||
dm64: begin
|
|
||||||
Size := 8;
|
|
||||||
IP := 16; // Dwarf Reg Num RIP
|
|
||||||
BP := 6; // RBP
|
|
||||||
SP := 7; // RSP
|
|
||||||
nIP := 'rip';
|
|
||||||
nBP := 'rbp';
|
|
||||||
nSP := 'rsp';
|
|
||||||
end;
|
|
||||||
else begin
|
|
||||||
assert(False, 'unknown address size for stack');
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
FCallStackEntryList.FreeObjects:=true;
|
if FCallStackEntryList.Count = 0 then begin
|
||||||
|
Unwinder.GetTopFrame(Address, StackPtr, FrameBase, AnEntry);
|
||||||
PrevStmtAddressOffs := 1;
|
FCallStackEntryList.Add(AnEntry);
|
||||||
if FCallStackEntryList.Count > 0 then begin
|
|
||||||
AnEntry := FCallStackEntryList[FCallStackEntryList.Count - 1];
|
|
||||||
Address:=AnEntry.AnAddress;
|
|
||||||
FrameBase:=AnEntry.FrameAdress;
|
|
||||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(SP);
|
|
||||||
if R = nil then exit;
|
|
||||||
StackPtr := R.NumValue;
|
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
PrevStmtAddressOffs := 0;
|
AnEntry := FCallStackEntryList[FCallStackEntryList.Count - 1];
|
||||||
Address := GetInstructionPointerRegisterValue;
|
Unwinder.InitForFrame(AnEntry, Address, StackPtr, FrameBase);
|
||||||
FrameBase := GetStackBasePointerRegisterValue;
|
|
||||||
StackPtr := GetStackPointerRegisterValue;
|
|
||||||
AnEntry := TDbgCallstackEntry.create(Self, 0, FrameBase, Address);
|
|
||||||
|
|
||||||
// Initialize register values
|
|
||||||
// Top level could do without entry in registerlist, but this way the
|
|
||||||
// same code can handle both cases.
|
|
||||||
i := 0;
|
|
||||||
R := RegisterValueList.FindRegisterByDwarfIndex(i);
|
|
||||||
while Assigned(R) do
|
|
||||||
begin
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx);
|
|
||||||
inc(i);
|
|
||||||
R := RegisterValueList.FindRegisterByDwarfIndex(i);
|
|
||||||
end;
|
|
||||||
FCallStackEntryList.Add(AnEntry);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
NextIdx := FCallStackEntryList.Count;
|
NextIdx := FCallStackEntryList.Count;
|
||||||
|
|
||||||
if AFrameRequired < 0 then
|
if AFrameRequired < 0 then
|
||||||
AFrameRequired := MaxInt;
|
AFrameRequired := MaxInt;
|
||||||
CountNeeded := AFrameRequired - FCallStackEntryList.Count;
|
CountNeeded := AFrameRequired - FCallStackEntryList.Count;
|
||||||
LastFrameBase := 0;
|
|
||||||
CodeReadErrCnt := 0;
|
|
||||||
while (CountNeeded > 0) do
|
while (CountNeeded > 0) do
|
||||||
begin
|
begin
|
||||||
PrevAddress := Address;
|
Res := Unwinder.Unwind(NextIdx, Address, StackPtr, FrameBase, AnEntry, AnEntry);
|
||||||
PrevFrameBase := FrameBase;
|
if not (Res in [suSuccess, suGuessed]) then
|
||||||
PrevStackPtr := StackPtr;
|
break;
|
||||||
{$PUSH}{$R-}{$Q-}
|
|
||||||
if (Process.DbgInfo as TFpDwarfInfo).FindCallFrameInfo(Address - PrevStmtAddressOffs, CIE, Row) and
|
|
||||||
TDwarfCallFrameInformation.TryObtainNextCallFrame(AnEntry, CIE, Size, NextIdx, Self, Row, Process, NewEntry)
|
|
||||||
{$POP}
|
|
||||||
then begin
|
|
||||||
PrevStmtAddressOffs := 1;
|
|
||||||
if not Assigned(NewEntry) then begin
|
|
||||||
CU := (Process.DbgInfo as TFpDwarfInfo).CompilationUnitForAddr(Address);
|
|
||||||
if (CU = nil) or (CU.DwarfSymbolClassMap = nil) or (not CU.DwarfSymbolClassMap.IgnoreCfiStackEnd) then
|
|
||||||
// Done.
|
|
||||||
Break;
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
Address := NewEntry.AnAddress;
|
|
||||||
StackReg := NewEntry.RegisterValueList.FindRegisterByDwarfIndex(SP);
|
|
||||||
FrameReg := NewEntry.RegisterValueList.FindRegisterByDwarfIndex(BP);
|
|
||||||
StackPtr := 0;
|
|
||||||
if (StackReg <> nil) and (FrameReg <> nil) then begin
|
|
||||||
StackPtr := StackReg.FNumValue;
|
|
||||||
FrameBase := FrameReg.FNumValue;
|
|
||||||
end;
|
|
||||||
AnEntry := NewEntry;
|
|
||||||
CheckFrame(NewEntry);
|
|
||||||
FCallStackEntryList.Add(NewEntry);
|
|
||||||
Dec(CountNeeded);
|
|
||||||
inc(NextIdx);
|
|
||||||
If (NextIdx > MAX_FRAMES) then
|
|
||||||
Break;
|
|
||||||
|
|
||||||
Continue;
|
FCallStackEntryList.Add(AnEntry);
|
||||||
end;
|
dec(CountNeeded);
|
||||||
end;
|
inc(NextIdx);
|
||||||
PrevStmtAddressOffs := 1;
|
|
||||||
|
|
||||||
if Process.Disassembler.UnwindFrame(PrevAddress, PrevStackPtr, PrevFrameBase, True) and
|
|
||||||
Process.Disassembler.IsAfterCallInstruction(PrevAddress)
|
|
||||||
then begin
|
|
||||||
Address := PrevAddress;
|
|
||||||
FrameBase := PrevFrameBase;
|
|
||||||
StackPtr := PrevStackPtr;
|
|
||||||
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nIP].SetValue(Address, IntToStr(Address),Size, IP);
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
|
||||||
FCallStackEntryList.Add(AnEntry);
|
|
||||||
Dec(CountNeeded);
|
|
||||||
inc(NextIdx);
|
|
||||||
CodeReadErrCnt := 0;
|
|
||||||
If (NextIdx > MAX_FRAMES) then
|
|
||||||
break;
|
|
||||||
LastFrameBase := FrameBase;
|
|
||||||
{$PUSH}{$R-}{$Q-}
|
|
||||||
if LastFrameBase > 0 then
|
|
||||||
LastFrameBase := LastFrameBase - 1;
|
|
||||||
{$POP}
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Address := PrevAddress;
|
|
||||||
FrameBase := PrevFrameBase;
|
|
||||||
StackPtr := PrevStackPtr;
|
|
||||||
|
|
||||||
if (FrameBase <> 0) and (FrameBase > LastFrameBase)
|
|
||||||
then begin
|
|
||||||
if StackPtr = 0 then
|
|
||||||
break;
|
|
||||||
// CFI not available or contains unsupported structures. Fallback to
|
|
||||||
// old fashioned stack-tracing.
|
|
||||||
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.
|
|
||||||
if NextIdx <= 1 then
|
|
||||||
OutSideFrame := True; // Maybe after "TProc(nil)();" call, then no frame could have been set up
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
LastFrameBase := FrameBase;
|
|
||||||
|
|
||||||
if (not OutSideFrame) and (NextIdx = 1) and (AnEntry.ProcSymbol <> nil) then begin
|
|
||||||
OutSideFrame := Address = LocToAddrOrNil(AnEntry.ProcSymbol.Address); // the top frame must be outside frame, if it is at entrypoint / needed for exceptions
|
|
||||||
end;
|
|
||||||
|
|
||||||
if OutSideFrame then begin
|
|
||||||
if not Process.ReadData(StackPtr, Size, Address) or (Address = 0) then Break;
|
|
||||||
|
|
||||||
if (not Process.ReadData(Address, 1, Dummy) or (Address = 0)) then begin
|
|
||||||
OutSideFrame := False;
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
{$PUSH}{$R-}{$Q-}
|
|
||||||
StackPtr := StackPtr + 1 * Size; // After popping return-addr from "StackPtr"
|
|
||||||
if LastFrameBase > 0 then
|
|
||||||
LastFrameBase := LastFrameBase - 1; // Make the loop think thas LastFrameBase was smaller
|
|
||||||
{$POP}
|
|
||||||
// last stack has no frame
|
|
||||||
//AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(0, '0',Size, BP);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not OutSideFrame then begin
|
|
||||||
{$PUSH}{$R-}{$Q-}
|
|
||||||
StackPtr := FrameBase + 2 * Size; // After popping return-addr from "FrameBase + Size"
|
|
||||||
{$POP}
|
|
||||||
if not Process.ReadData(FrameBase + Size, Size, Address) or (Address = 0) then Break;
|
|
||||||
if not Process.ReadData(FrameBase, Size, FrameBase) then Break;
|
|
||||||
end;
|
|
||||||
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nIP].SetValue(Address, IntToStr(Address),Size, IP);
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
|
||||||
CheckFrame(AnEntry, FrameBase < StackPtr);
|
|
||||||
FCallStackEntryList.Add(AnEntry);
|
|
||||||
Dec(CountNeeded);
|
|
||||||
inc(NextIdx);
|
|
||||||
CodeReadErrCnt := 0;
|
|
||||||
If (NextIdx > MAX_FRAMES) then
|
|
||||||
break;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
AnEntry := nil;
|
|
||||||
CheckFrame(AnEntry);
|
|
||||||
if AnEntry <> nil then
|
|
||||||
FCallStackEntryList.Add(AnEntry)
|
|
||||||
else
|
|
||||||
Break;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
if CountNeeded > 0 then // there was an error / not possible to read more frames
|
if CountNeeded > 0 then // there was an error / not possible to read more frames
|
||||||
FCallStackEntryList.SetHasReadAllAvailableFrames;
|
FCallStackEntryList.SetHasReadAllAvailableFrames;
|
||||||
|
307
components/fpdebug/fpdbgcpux86.pas
Normal file
307
components/fpdebug/fpdbgcpux86.pas
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
unit FpDbgCpuX86;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, DbgIntfBaseTypes, FpDbgClasses, FpdMemoryTools,
|
||||||
|
FpDbgDwarfCFI, FpDbgDwarfDataClasses, FpDbgDisasX86;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86FramePointer }
|
||||||
|
|
||||||
|
TDbgStackUnwinderX86FramePointer = class(TDbgStackUnwinderX86Base)
|
||||||
|
private
|
||||||
|
FLastFrameBaseIncreased: Boolean;
|
||||||
|
FCodeReadErrCnt: integer;
|
||||||
|
public
|
||||||
|
procedure InitForThread(AThread: TDbgThread); override;
|
||||||
|
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||||
|
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||||
|
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86DwarfCfi }
|
||||||
|
|
||||||
|
TDbgStackUnwinderX86DwarfCfi = class(TDbgStackUnwinderX86Base)
|
||||||
|
// Maybe have TDbgStackUnwinderX86Base as delegate instead of base
|
||||||
|
private
|
||||||
|
public
|
||||||
|
//procedure InitForThread(AThread: TDbgThread); override;
|
||||||
|
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||||
|
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||||
|
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86MultiMethod }
|
||||||
|
|
||||||
|
TDbgStackUnwinderX86MultiMethod = class(TDbgStackUnwinderX86Base)
|
||||||
|
// Base is for GetTopFrame // Alternatively forward to any of the sub-unwinder
|
||||||
|
private
|
||||||
|
FDwarfUnwinder: TDbgStackUnwinderX86DwarfCfi;
|
||||||
|
FFrameUnwinder: TDbgStackUnwinderX86FramePointer;
|
||||||
|
FAsmUnwinder: TDbgStackUnwinderIntelDisAssembler;
|
||||||
|
public
|
||||||
|
constructor Create(AProcess: TDbgProcess);
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure InitForThread(AThread: TDbgThread); override;
|
||||||
|
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||||
|
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||||
|
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86FramePointer }
|
||||||
|
|
||||||
|
procedure TDbgStackUnwinderX86FramePointer.InitForThread(AThread: TDbgThread);
|
||||||
|
begin
|
||||||
|
inherited InitForThread(AThread);
|
||||||
|
FLastFrameBaseIncreased := True;
|
||||||
|
FCodeReadErrCnt := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDbgStackUnwinderX86FramePointer.Unwind(AFrameIndex: integer;
|
||||||
|
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||||
|
): TTDbgStackUnwindResult;
|
||||||
|
var
|
||||||
|
OutSideFrame: Boolean;
|
||||||
|
Dummy: QWord;
|
||||||
|
LastFrameBase: TDBGPtr;
|
||||||
|
NewRes: TTDbgStackUnwindResult;
|
||||||
|
begin
|
||||||
|
ANewFrame := nil;
|
||||||
|
Result := suFailed;
|
||||||
|
NewRes := suSuccess;
|
||||||
|
|
||||||
|
if StackPointer = 0 then
|
||||||
|
exit;
|
||||||
|
if not FLastFrameBaseIncreased then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
OutSideFrame := False;
|
||||||
|
LastFrameBase := FrameBasePointer;
|
||||||
|
|
||||||
|
if not Process.Disassembler.GetFunctionFrameInfo(CodePointer, OutSideFrame) then begin
|
||||||
|
if Process.Disassembler.LastErrorWasMemReadErr then begin
|
||||||
|
inc(FCodeReadErrCnt);
|
||||||
|
if FCodeReadErrCnt > 5 then // If the code cannot be read the stack pointer is wrong.
|
||||||
|
exit;
|
||||||
|
if AFrameIndex <= 1 then
|
||||||
|
OutSideFrame := True; // Maybe after "TProc(nil)();" call, then no frame could have been set up
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if (not OutSideFrame) {and (AFrameIndex = 1)} and (ACurrentFrame.ProcSymbol <> nil) then begin
|
||||||
|
// the frame must be outside frame, if it is at entrypoint / needed for exceptions
|
||||||
|
OutSideFrame := CodePointer = LocToAddrOrNil(ACurrentFrame.ProcSymbol.Address);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if OutSideFrame then begin
|
||||||
|
if not Process.ReadData(StackPointer, AddressSize, CodePointer) or (CodePointer = 0) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
if (not Process.ReadData(CodePointer, 1, Dummy) or (CodePointer = 0)) then begin
|
||||||
|
OutSideFrame := False;
|
||||||
|
NewRes := suGuessed;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
StackPointer := StackPointer + 1 * AddressSize; // After popping return-addr from "StackPointer"
|
||||||
|
if LastFrameBase > 0 then
|
||||||
|
LastFrameBase := LastFrameBase - 1; // Make the loop think thas LastFrameBase was smaller
|
||||||
|
{$POP}
|
||||||
|
// last stack has no frame
|
||||||
|
//ACurrentFrame.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(0, '0',AddressSize, BP);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not OutSideFrame then begin
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
StackPointer := FrameBasePointer + 2 * AddressSize; // After popping return-addr from "FrameBasePointer + AddressSize"
|
||||||
|
{$POP}
|
||||||
|
if not Process.ReadData(FrameBasePointer + AddressSize, AddressSize, CodePointer) or (CodePointer = 0) then
|
||||||
|
exit;
|
||||||
|
if not Process.ReadData(FrameBasePointer, AddressSize, FrameBasePointer) then
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
FLastFrameBaseIncreased := (FrameBasePointer <> 0) and (FrameBasePointer > LastFrameBase);
|
||||||
|
if not FLastFrameBaseIncreased then
|
||||||
|
NewRes := suGuessed;
|
||||||
|
|
||||||
|
|
||||||
|
ANewFrame := TDbgCallstackEntry.create(Thread, AFrameIndex, FrameBasePointer, CodePointer);
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameIP].SetValue(CodePointer, IntToStr(CodePointer),AddressSize, FDwarfNumIP);
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameBP].SetValue(FrameBasePointer, IntToStr(FrameBasePointer),AddressSize, FDwarfNumBP);
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameSP].SetValue(StackPointer, IntToStr(StackPointer),AddressSize, FDwarfNumSP);
|
||||||
|
FCodeReadErrCnt := 0;
|
||||||
|
Result := NewRes;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86DwarfCfi }
|
||||||
|
|
||||||
|
function TDbgStackUnwinderX86DwarfCfi.Unwind(AFrameIndex: integer;
|
||||||
|
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||||
|
): TTDbgStackUnwindResult;
|
||||||
|
var
|
||||||
|
PrevStmtAddressOffs: Integer;
|
||||||
|
Row: TDwarfCallFrameInformationRow;
|
||||||
|
CIE: TDwarfCIE;
|
||||||
|
CU: TDwarfCompilationUnit;
|
||||||
|
StackReg, FrameReg: TDbgRegisterValue;
|
||||||
|
begin
|
||||||
|
ANewFrame := nil;
|
||||||
|
Result := suFailed;
|
||||||
|
|
||||||
|
PrevStmtAddressOffs := 1;
|
||||||
|
if AFrameIndex <= 1 then
|
||||||
|
PrevStmtAddressOffs := 0;
|
||||||
|
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
if (Process.DbgInfo as TFpDwarfInfo).FindCallFrameInfo(CodePointer - PrevStmtAddressOffs, CIE, Row) and
|
||||||
|
TDwarfCallFrameInformation.TryObtainNextCallFrame(
|
||||||
|
ACurrentFrame, CIE, AddressSize, AFrameIndex, Thread, Row, Process, ANewFrame
|
||||||
|
)
|
||||||
|
{$POP}
|
||||||
|
then begin
|
||||||
|
if not Assigned(ANewFrame) then begin
|
||||||
|
CU := (Process.DbgInfo as TFpDwarfInfo).CompilationUnitForAddr(CodePointer);
|
||||||
|
if (CU = nil) or (CU.DwarfSymbolClassMap = nil) or (not CU.DwarfSymbolClassMap.IgnoreCfiStackEnd) then begin
|
||||||
|
// Done.
|
||||||
|
Result := suFailedAtEOS;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
Result := suSuccess;
|
||||||
|
CodePointer := ANewFrame.AnAddress;
|
||||||
|
StackReg := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumSP);
|
||||||
|
FrameReg := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumBP);
|
||||||
|
StackPointer := 0;
|
||||||
|
FrameBasePointer := 0;
|
||||||
|
if (StackReg <> nil) and (FrameReg <> nil) then begin
|
||||||
|
StackPointer := StackReg.NumValue;
|
||||||
|
FrameBasePointer := FrameReg.NumValue;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderX86MultiMethod }
|
||||||
|
|
||||||
|
constructor TDbgStackUnwinderX86MultiMethod.Create(AProcess: TDbgProcess);
|
||||||
|
begin
|
||||||
|
FDwarfUnwinder := TDbgStackUnwinderX86DwarfCfi.Create(AProcess);
|
||||||
|
FFrameUnwinder := TDbgStackUnwinderX86FramePointer.Create(AProcess);
|
||||||
|
FAsmUnwinder := TDbgStackUnwinderIntelDisAssembler.Create(AProcess);
|
||||||
|
inherited Create(AProcess);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TDbgStackUnwinderX86MultiMethod.Destroy;
|
||||||
|
begin
|
||||||
|
inherited Destroy;
|
||||||
|
FDwarfUnwinder.Free;
|
||||||
|
FFrameUnwinder.Free;
|
||||||
|
FAsmUnwinder.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDbgStackUnwinderX86MultiMethod.InitForThread(AThread: TDbgThread);
|
||||||
|
begin
|
||||||
|
inherited InitForThread(AThread);
|
||||||
|
FDwarfUnwinder.InitForThread(AThread);
|
||||||
|
FFrameUnwinder.InitForThread(AThread);
|
||||||
|
FAsmUnwinder.InitForThread(AThread);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDbgStackUnwinderX86MultiMethod.Unwind(AFrameIndex: integer;
|
||||||
|
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||||
|
): TTDbgStackUnwindResult;
|
||||||
|
var
|
||||||
|
OrigCodePointer, OrigFrameBasePointer, OrigStackPointer: TDBGPtr;
|
||||||
|
CodePointer2, FrameBasePointer2, StackPointer2: TDBGPtr;
|
||||||
|
ANewFrame2: TDbgCallstackEntry;
|
||||||
|
ResAsm: TTDbgStackUnwindResult;
|
||||||
|
begin
|
||||||
|
OrigCodePointer := CodePointer;
|
||||||
|
OrigFrameBasePointer := FrameBasePointer;
|
||||||
|
OrigStackPointer := StackPointer;
|
||||||
|
|
||||||
|
Result := FDwarfUnwinder.Unwind(AFrameIndex,
|
||||||
|
CodePointer, StackPointer, FrameBasePointer, ACurrentFrame, ANewFrame);
|
||||||
|
|
||||||
|
if (Result = suSuccess) then begin
|
||||||
|
FFrameUnwinder.FLastFrameBaseIncreased := True;
|
||||||
|
if Process.Disassembler.IsAfterCallInstruction(CodePointer) then
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
if Result = suFailedAtEOS then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// Get Asm unwind
|
||||||
|
CodePointer2 := OrigCodePointer;
|
||||||
|
FrameBasePointer2 := OrigFrameBasePointer;
|
||||||
|
StackPointer2 := OrigStackPointer;
|
||||||
|
ResAsm := FAsmUnwinder.Unwind(AFrameIndex,
|
||||||
|
CodePointer2, StackPointer2, FrameBasePointer2, ACurrentFrame, ANewFrame2);
|
||||||
|
|
||||||
|
if (ResAsm = suSuccess) then begin
|
||||||
|
// prefer Asm result over DwarfCfi
|
||||||
|
FFrameUnwinder.FLastFrameBaseIncreased := True;
|
||||||
|
if Process.Disassembler.IsAfterCallInstruction(CodePointer2) then begin
|
||||||
|
ANewFrame.Free;
|
||||||
|
CodePointer := CodePointer2;
|
||||||
|
FrameBasePointer := FrameBasePointer2;
|
||||||
|
StackPointer := StackPointer2;
|
||||||
|
ANewFrame := ANewFrame2;
|
||||||
|
Result := suSuccess;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
if (ResAsm in [suSuccess, suGuessed]) and
|
||||||
|
(Result in [suSuccess, suGuessed]) and
|
||||||
|
(CodePointer = CodePointer2) and (StackPointer = StackPointer2) and (FrameBasePointer = FrameBasePointer2)
|
||||||
|
then begin
|
||||||
|
// Both results where unsure => but both equal
|
||||||
|
FFrameUnwinder.FLastFrameBaseIncreased := True;
|
||||||
|
ANewFrame2.Free;
|
||||||
|
Result := suSuccess;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
ANewFrame.Free;
|
||||||
|
// get frame-unwind
|
||||||
|
CodePointer := OrigCodePointer;
|
||||||
|
FrameBasePointer := OrigFrameBasePointer;
|
||||||
|
StackPointer := OrigStackPointer;
|
||||||
|
Result := FFrameUnwinder.Unwind(AFrameIndex,
|
||||||
|
CodePointer, StackPointer, FrameBasePointer, ACurrentFrame, ANewFrame);
|
||||||
|
|
||||||
|
if (Result = suSuccess) then begin
|
||||||
|
if Process.Disassembler.IsAfterCallInstruction(CodePointer2) then
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (ResAsm in [suSuccess, suGuessed]) then begin
|
||||||
|
ANewFrame.Free;
|
||||||
|
CodePointer := CodePointer2;
|
||||||
|
FrameBasePointer := FrameBasePointer2;
|
||||||
|
StackPointer := StackPointer2;
|
||||||
|
ANewFrame := ANewFrame2;
|
||||||
|
Result := suSuccess;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
@ -114,6 +114,7 @@ type
|
|||||||
protected
|
protected
|
||||||
function ReadThreadState: boolean;
|
function ReadThreadState: boolean;
|
||||||
function ReadDebugState: boolean;
|
function ReadDebugState: boolean;
|
||||||
|
// function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||||
public
|
public
|
||||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||||
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
||||||
|
@ -287,21 +287,23 @@ type
|
|||||||
TOperandFlag = (ofMemory);
|
TOperandFlag = (ofMemory);
|
||||||
TOperandFlags = set of TOperandFlag;
|
TOperandFlags = set of TOperandFlag;
|
||||||
|
|
||||||
|
TInstructionOperand = record
|
||||||
|
CodeIndex: integer;
|
||||||
|
Value: String;
|
||||||
|
Size: TOperandSize;
|
||||||
|
ByteCount: Word;
|
||||||
|
ByteCount2: Byte;
|
||||||
|
FormatFlags: THexValueFormatFlags;
|
||||||
|
Flags: TOperandFlags;
|
||||||
|
end;
|
||||||
|
|
||||||
TInstruction = record
|
TInstruction = record
|
||||||
OpCode: TFullOpcode;
|
OpCode: TFullOpcode;
|
||||||
Flags: set of TInstructionFlag;
|
Flags: set of TInstructionFlag;
|
||||||
Segment: String;
|
Segment: String;
|
||||||
MaskIndex: Byte;
|
MaskIndex: Byte;
|
||||||
|
|
||||||
Operand: array[1..4] of record
|
Operand: array[1..4] of TInstructionOperand;
|
||||||
CodeIndex: integer;
|
|
||||||
Value: String;
|
|
||||||
Size: TOperandSize;
|
|
||||||
ByteCount: Word;
|
|
||||||
ByteCount2: Byte;
|
|
||||||
FormatFlags: THexValueFormatFlags;
|
|
||||||
Flags: TOperandFlags;
|
|
||||||
end;
|
|
||||||
OperCnt: Integer;
|
OperCnt: Integer;
|
||||||
|
|
||||||
ParseFlags: TFlags;
|
ParseFlags: TFlags;
|
||||||
@ -575,6 +577,18 @@ type
|
|||||||
function UnwindFrame(var AnAddress, AStackPtr, AFramePtr: TDBGPtr; AQuick: boolean): boolean; override;
|
function UnwindFrame(var AnAddress, AStackPtr, AFramePtr: TDBGPtr; AQuick: boolean): boolean; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderIntelDisAssembler }
|
||||||
|
|
||||||
|
TDbgStackUnwinderIntelDisAssembler = class(TDbgStackUnwinderX86Base)
|
||||||
|
private
|
||||||
|
//FCodeReadErrCnt: integer;
|
||||||
|
public
|
||||||
|
//procedure InitForThread(AThread: TDbgThread); override;
|
||||||
|
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||||
|
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||||
|
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -5272,17 +5286,74 @@ function TX86AsmDecoder.UnwindFrame(var AnAddress, AStackPtr,
|
|||||||
Result := 8;
|
Result := 8;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
NewAddr, NewStack, NewFrame: TDBGPtr;
|
||||||
|
CurAddr: PByte;
|
||||||
|
|
||||||
|
function ValueFromOperand(constref Oper: TInstructionOperand; out AVal: TDBGPtr): boolean;
|
||||||
|
var
|
||||||
|
OpVal: Int64;
|
||||||
|
Src: TDBGPtr;
|
||||||
|
RSize: Cardinal;
|
||||||
|
begin
|
||||||
|
AVal := 0;
|
||||||
|
Result := True;
|
||||||
|
if (Oper.ByteCount2 <> 0) then
|
||||||
|
exit(False);
|
||||||
|
|
||||||
|
if (Oper.ByteCount = 0)
|
||||||
|
then begin
|
||||||
|
if (IsRegister(Oper.Value, 'bp')) then
|
||||||
|
AVal := NewFrame
|
||||||
|
else
|
||||||
|
if (IsRegister(Oper.Value, 'sp')) then
|
||||||
|
AVal := NewStack
|
||||||
|
else
|
||||||
|
exit(False);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (Oper.ByteCount <> 0)
|
||||||
|
then begin
|
||||||
|
OpVal := ValueFromMem(CurAddr[Oper.CodeIndex], Oper.ByteCount, Oper.FormatFlags);
|
||||||
|
|
||||||
|
if (IsRegister(Oper.Value, 'bp%s')) then
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
AVal := NewFrame + OpVal
|
||||||
|
{$POP}
|
||||||
|
else
|
||||||
|
if (IsRegister(Oper.Value, 'sp%s')) then
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
AVal := NewStack + OpVal
|
||||||
|
{$POP}
|
||||||
|
else
|
||||||
|
if (Oper.Value = '%s') and (not(ofMemory in Oper.Flags))
|
||||||
|
then
|
||||||
|
AVal := TDBGPtr(OpVal) // constant
|
||||||
|
else
|
||||||
|
exit(False);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
exit(False);
|
||||||
|
|
||||||
|
if (ofMemory in Oper.Flags) then begin
|
||||||
|
Src := AVal;
|
||||||
|
AVal := 0;
|
||||||
|
RSize := RegisterSize(Oper.Value);
|
||||||
|
if not FProcess.ReadData(Src, RSize, AVal, RSize) then
|
||||||
|
exit(False);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
const
|
const
|
||||||
MAX_SEARCH_ADDR = 8000;
|
MAX_SEARCH_ADDR = 8000;
|
||||||
MAX_SEARCH_CNT = 400;
|
MAX_SEARCH_CNT = 400;
|
||||||
var
|
var
|
||||||
NewAddr, NewStack, NewFrame, MaxAddr, MaxAddr2, StartAddr, StartStack, Tmp: TDBGPtr;
|
MaxAddr, MaxAddr2, StartAddr, StartStack, Tmp: TDBGPtr;
|
||||||
ConditionalForwardAddr, BackwardJumpAddress: TDBGPtr;
|
ConditionalForwardAddr, BackwardJumpAddress: TDBGPtr;
|
||||||
Cnt: Integer;
|
Cnt: Integer;
|
||||||
instr: TX86AsmInstruction;
|
instr: TX86AsmInstruction;
|
||||||
RSize: Cardinal;
|
RSize: Cardinal;
|
||||||
Val: Int64;
|
Val: Int64;
|
||||||
CurAddr: PByte;
|
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
NewAddr := AnAddress;
|
NewAddr := AnAddress;
|
||||||
@ -5324,8 +5395,9 @@ begin
|
|||||||
case instr.X86OpCode of
|
case instr.X86OpCode of
|
||||||
OPret:
|
OPret:
|
||||||
begin
|
begin
|
||||||
if instr.X86Instruction.OperCnt > 1 then
|
if instr.X86Instruction.OperCnt > 1 then begin
|
||||||
exit;
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
Val := 0;
|
Val := 0;
|
||||||
if instr.X86Instruction.OperCnt = 1 then
|
if instr.X86Instruction.OperCnt = 1 then
|
||||||
@ -5415,20 +5487,19 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
|
|
||||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp') and
|
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp') and
|
||||||
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||||
then begin
|
then begin
|
||||||
if (not IsRegister(instr.X86Instruction.Operand[2].Value, 'bp')) or
|
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||||
(Instr.X86Instruction.Operand[2].ByteCount <> 0) or
|
exit;
|
||||||
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
NewStack := Tmp;
|
||||||
(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
|
||||||
then
|
|
||||||
exit; // TODO: check if source is known part of stack
|
|
||||||
NewStack := NewFrame;
|
|
||||||
end;
|
end;
|
||||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp') and
|
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp') and
|
||||||
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||||
then
|
then begin
|
||||||
exit; // TODO: check if source is SP or known part of stack
|
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||||
|
exit;
|
||||||
|
NewFrame := Tmp;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
OPlea:
|
OPlea:
|
||||||
begin
|
begin
|
||||||
@ -5436,17 +5507,24 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp')
|
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp')
|
||||||
then begin
|
then begin
|
||||||
if (not IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) or
|
if (Instr.X86Instruction.Operand[2].ByteCount = 0) or
|
||||||
(Instr.X86Instruction.Operand[2].ByteCount = 0) or
|
|
||||||
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
||||||
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
||||||
then
|
then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||||
{$PUSH}{$R-}{$Q-}
|
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'sp%s')) then begin
|
||||||
NewFrame := NewFrame + Val;
|
{$PUSH}{$R-}{$Q-}
|
||||||
{$POP}
|
NewFrame := NewStack + Val;
|
||||||
|
{$POP}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) then begin
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
NewFrame := NewFrame + Val;
|
||||||
|
{$POP}
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp')
|
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp')
|
||||||
then begin
|
then begin
|
||||||
@ -5456,15 +5534,14 @@ begin
|
|||||||
then
|
then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||||
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'sp%s')) then begin
|
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'sp%s')) then begin
|
||||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
|
||||||
{$PUSH}{$R-}{$Q-}
|
{$PUSH}{$R-}{$Q-}
|
||||||
NewStack := NewStack + Val;
|
NewStack := NewStack + Val;
|
||||||
{$POP}
|
{$POP}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) then begin
|
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) then begin
|
||||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
|
||||||
{$PUSH}{$R-}{$Q-}
|
{$PUSH}{$R-}{$Q-}
|
||||||
NewStack := NewFrame + Val;
|
NewStack := NewFrame + Val;
|
||||||
{$POP}
|
{$POP}
|
||||||
@ -5477,18 +5554,23 @@ begin
|
|||||||
begin
|
begin
|
||||||
if instr.X86Instruction.OperCnt <> 2 then
|
if instr.X86Instruction.OperCnt <> 2 then
|
||||||
exit;
|
exit;
|
||||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp')
|
|
||||||
then begin
|
|
||||||
if (instr.X86Instruction.Operand[2].Value <> '%s') or
|
|
||||||
(Instr.X86Instruction.Operand[2].ByteCount = 0) or
|
|
||||||
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
|
||||||
(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
|
||||||
then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp') and
|
||||||
|
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||||
|
then begin
|
||||||
|
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||||
|
exit;
|
||||||
{$PUSH}{$R-}{$Q-}
|
{$PUSH}{$R-}{$Q-}
|
||||||
NewStack := NewStack + Val;
|
NewStack := NewStack + int64(Tmp);
|
||||||
|
{$POP}
|
||||||
|
end;
|
||||||
|
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp') and
|
||||||
|
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||||
|
then begin
|
||||||
|
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||||
|
exit;
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
NewFrame := NewFrame + int64(Tmp);
|
||||||
{$POP}
|
{$POP}
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -5557,6 +5639,26 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TDbgStackUnwinderIntelDisAssembler }
|
||||||
|
|
||||||
|
function TDbgStackUnwinderIntelDisAssembler.Unwind(AFrameIndex: integer;
|
||||||
|
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||||
|
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||||
|
): TTDbgStackUnwindResult;
|
||||||
|
begin
|
||||||
|
ANewFrame := nil;
|
||||||
|
Result := suFailed;
|
||||||
|
|
||||||
|
if Process.Disassembler.UnwindFrame(CodePointer, StackPointer, FrameBasePointer, False)
|
||||||
|
then begin
|
||||||
|
ANewFrame := TDbgCallstackEntry.Create(Thread, AFrameIndex, FrameBasePointer, CodePointer);
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameIP].SetValue(CodePointer, IntToStr(CodePointer),AddressSize, FDwarfNumIP);
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameBP].SetValue(FrameBasePointer, IntToStr(FrameBasePointer),AddressSize, FDwarfNumBP);
|
||||||
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameSP].SetValue(StackPointer, IntToStr(StackPointer),AddressSize, FDwarfNumSP);
|
||||||
|
Result := suSuccess;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
||||||
|
@ -27,7 +27,8 @@ uses
|
|||||||
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, Maps,
|
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, Maps,
|
||||||
FpDbgCommon, FpdMemoryTools,
|
FpDbgCommon, FpdMemoryTools,
|
||||||
FpErrorMessages,
|
FpErrorMessages,
|
||||||
FpImgReaderBase;
|
FpImgReaderBase,
|
||||||
|
FpDbgCpuX86;
|
||||||
|
|
||||||
type
|
type
|
||||||
user_regs_struct64 = record
|
user_regs_struct64 = record
|
||||||
@ -274,6 +275,7 @@ type
|
|||||||
FIsSteppingBreakPoint: boolean;
|
FIsSteppingBreakPoint: boolean;
|
||||||
FDidResetInstructionPointer: Boolean;
|
FDidResetInstructionPointer: Boolean;
|
||||||
FHasThreadState: boolean;
|
FHasThreadState: boolean;
|
||||||
|
FUnwinder: TDbgStackUnwinderX86MultiMethod;
|
||||||
function GetDebugRegOffset(ind: byte): pointer;
|
function GetDebugRegOffset(ind: byte): pointer;
|
||||||
function ReadDebugReg(ind: byte; out AVal: PtrUInt): boolean;
|
function ReadDebugReg(ind: byte; out AVal: PtrUInt): boolean;
|
||||||
function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||||
@ -284,7 +286,9 @@ type
|
|||||||
function RequestInternalPause: Boolean;
|
function RequestInternalPause: Boolean;
|
||||||
function CheckSignalForPostponing(AWaitedStatus: cint): Boolean;
|
function CheckSignalForPostponing(AWaitedStatus: cint): Boolean;
|
||||||
procedure ResetPauseStates;
|
procedure ResetPauseStates;
|
||||||
|
function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||||
public
|
public
|
||||||
|
destructor Destroy; override;
|
||||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||||
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
||||||
function DetectHardwareWatchpoint: Pointer; override;
|
function DetectHardwareWatchpoint: Pointer; override;
|
||||||
@ -666,6 +670,19 @@ begin
|
|||||||
FDidResetInstructionPointer := False;
|
FDidResetInstructionPointer := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDbgLinuxThread.GetStackUnwinder: TDbgStackUnwinder;
|
||||||
|
begin
|
||||||
|
if FUnwinder = nil then
|
||||||
|
FUnwinder := TDbgStackUnwinderX86MultiMethod.Create(Process);
|
||||||
|
Result := FUnwinder;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TDbgLinuxThread.Destroy;
|
||||||
|
begin
|
||||||
|
FUnwinder.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
function TDbgLinuxThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
function TDbgLinuxThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
||||||
begin
|
begin
|
||||||
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgLinuxThread.ResetInstructionPointerAfterBreakpoint');{$ENDIF}
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgLinuxThread.ResetInstructionPointerAfterBreakpoint');{$ENDIF}
|
||||||
|
@ -120,7 +120,7 @@ uses
|
|||||||
FpDbgLoader, FpDbgDisasX86,
|
FpDbgLoader, FpDbgDisasX86,
|
||||||
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
||||||
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, UTF8Process,
|
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, UTF8Process,
|
||||||
FpDbgCommon, FpdMemoryTools, FpErrorMessages;
|
FpDbgCommon, FpdMemoryTools, FpErrorMessages, FpDbgCpuX86;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -140,6 +140,7 @@ type
|
|||||||
FBreakPointState: TBreakPointState;
|
FBreakPointState: TBreakPointState;
|
||||||
FDoNotPollName: Boolean;
|
FDoNotPollName: Boolean;
|
||||||
FName: String;
|
FName: String;
|
||||||
|
FUnwinder: TDbgStackUnwinderX86MultiMethod;
|
||||||
protected
|
protected
|
||||||
FThreadContextChanged: boolean;
|
FThreadContextChanged: boolean;
|
||||||
FThreadContextChangeFlags: TFpContextChangeFlags;
|
FThreadContextChangeFlags: TFpContextChangeFlags;
|
||||||
@ -150,7 +151,9 @@ type
|
|||||||
function GetFpThreadContext(var AStorage: TFpContext; out ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags): Boolean;
|
function GetFpThreadContext(var AStorage: TFpContext; out ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags): Boolean;
|
||||||
function SetFpThreadContext(ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags = cfSkip): Boolean;
|
function SetFpThreadContext(ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags = cfSkip): Boolean;
|
||||||
function GetName: String; override;
|
function GetName: String; override;
|
||||||
|
function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||||
public
|
public
|
||||||
|
destructor Destroy; override;
|
||||||
procedure Suspend;
|
procedure Suspend;
|
||||||
procedure SuspendForStepOverBreakPoint;
|
procedure SuspendForStepOverBreakPoint;
|
||||||
procedure Resume;
|
procedure Resume;
|
||||||
@ -1673,6 +1676,19 @@ begin
|
|||||||
Result := inherited GetName;
|
Result := inherited GetName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDbgWinThread.GetStackUnwinder: TDbgStackUnwinder;
|
||||||
|
begin
|
||||||
|
if FUnwinder = nil then
|
||||||
|
FUnwinder := TDbgStackUnwinderX86MultiMethod.Create(Process);
|
||||||
|
Result := FUnwinder;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TDbgWinThread.Destroy;
|
||||||
|
begin
|
||||||
|
FUnwinder.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TDbgWinThread.Suspend;
|
procedure TDbgWinThread.Suspend;
|
||||||
var
|
var
|
||||||
r: DWORD;
|
r: DWORD;
|
||||||
|
@ -229,6 +229,10 @@ File(s) with other licenses (see also header in file(s):
|
|||||||
<Filename Value="fpdbgdwarfcfi.pas"/>
|
<Filename Value="fpdbgdwarfcfi.pas"/>
|
||||||
<UnitName Value="FpDbgDwarfCFI"/>
|
<UnitName Value="FpDbgDwarfCFI"/>
|
||||||
</Item>
|
</Item>
|
||||||
|
<Item>
|
||||||
|
<Filename Value="fpdbgcpux86.pas"/>
|
||||||
|
<UnitName Value="fpdbgcpux86"/>
|
||||||
|
</Item>
|
||||||
</Files>
|
</Files>
|
||||||
<i18n>
|
<i18n>
|
||||||
<EnableI18N Value="True"/>
|
<EnableI18N Value="True"/>
|
||||||
|
@ -16,7 +16,7 @@ uses
|
|||||||
FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, fpDbgSymTableContext,
|
FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, fpDbgSymTableContext,
|
||||||
fpDbgSymTable, FpDbgAvrClasses, FpDbgDisasAvr, FpDbgRsp, FpDbgCommon,
|
fpDbgSymTable, FpDbgAvrClasses, FpDbgDisasAvr, FpDbgRsp, FpDbgCommon,
|
||||||
FpImgReaderWinPETypes, FpDbgHardcodedFreepascalInfo, FpDbgCallContextInfo,
|
FpImgReaderWinPETypes, FpDbgHardcodedFreepascalInfo, FpDbgCallContextInfo,
|
||||||
FpWatchResultData, FpDbgDwarfCFI;
|
FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user