mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-07-25 21:36:15 +02:00
Tweak PrepareCallStackEntryList to cater for avr-gcc ABI. Added TAvrAsmDecoder.GetFunctionFrameReturnAddress to scan prologue and epilogue for frame information.
This commit is contained in:
parent
02c76188e3
commit
7f341cbe68
@ -167,7 +167,7 @@ var
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
FpDbgDisasAvr;
|
FpDbgDisasAvr, FpDbgDwarfDataClasses, FpDbgInfo;
|
||||||
|
|
||||||
var
|
var
|
||||||
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
|
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
|
||||||
@ -499,21 +499,24 @@ const
|
|||||||
MAX_FRAMES = 50000; // safety net
|
MAX_FRAMES = 50000; // safety net
|
||||||
// Use fake dwarf indexes, these seem to be for lookup in RegisterValueList only?
|
// Use fake dwarf indexes, these seem to be for lookup in RegisterValueList only?
|
||||||
PC = 0;
|
PC = 0;
|
||||||
BP = 1;
|
FP = 1;
|
||||||
SP = 2;
|
SP = 2;
|
||||||
nPC = 'PC';
|
nPC = 'PC';
|
||||||
nBP = 'BP';
|
nFP = 'FP';
|
||||||
nSP = 'SP';
|
nSP = 'SP';
|
||||||
|
// To read RAM, add data space offset to address
|
||||||
DataOffset = $800000;
|
DataOffset = $800000;
|
||||||
Size = 2;
|
Size = 2;
|
||||||
var
|
var
|
||||||
Address, FrameBase, LastFrameBase: QWord;
|
Address, FrameBase, LastFrameBase: TDBGPtr;
|
||||||
CountNeeded, CodeReadErrCnt: integer;
|
CountNeeded, CodeReadErrCnt: integer;
|
||||||
AnEntry: TDbgCallstackEntry;
|
AnEntry: TDbgCallstackEntry;
|
||||||
R: TDbgRegisterValue;
|
R: TDbgRegisterValue;
|
||||||
NextIdx: LongInt;
|
NextIdx: LongInt;
|
||||||
OutSideFrame: Boolean;
|
OutSideFrame: Boolean;
|
||||||
StackPtr: TDBGPtr;
|
StackPtr: TDBGPtr;
|
||||||
|
startPC, endPC: TDBGPtr;
|
||||||
|
returnAddrStackOffset: word;
|
||||||
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
|
||||||
@ -530,7 +533,7 @@ begin
|
|||||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(PC);
|
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(PC);
|
||||||
if R = nil then exit;
|
if R = nil then exit;
|
||||||
Address := R.NumValue;
|
Address := R.NumValue;
|
||||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(BP);
|
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(FP);
|
||||||
if R = nil then exit;
|
if R = nil then exit;
|
||||||
FrameBase := R.NumValue;
|
FrameBase := R.NumValue;
|
||||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(SP);
|
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(SP);
|
||||||
@ -544,7 +547,7 @@ begin
|
|||||||
AnEntry := TDbgCallstackEntry.create(Self, 0, FrameBase, Address);
|
AnEntry := TDbgCallstackEntry.create(Self, 0, FrameBase, Address);
|
||||||
// Top level could be without entry in registerlist / same as GetRegisterValueList / but some code tries to find it here ....
|
// Top level could be without entry in registerlist / same as GetRegisterValueList / but some code tries to find it here ....
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PC);
|
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PC);
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nFP].SetValue(FrameBase, IntToStr(FrameBase),Size, FP);
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
||||||
FCallStackEntryList.Add(AnEntry);
|
FCallStackEntryList.Add(AnEntry);
|
||||||
end;
|
end;
|
||||||
@ -557,48 +560,45 @@ begin
|
|||||||
CodeReadErrCnt := 0;
|
CodeReadErrCnt := 0;
|
||||||
while (CountNeeded > 0) and (FrameBase <> 0) and (FrameBase > LastFrameBase) do
|
while (CountNeeded > 0) and (FrameBase <> 0) and (FrameBase > LastFrameBase) do
|
||||||
begin
|
begin
|
||||||
if not Process.Disassembler.GetFunctionFrameInfo(Address, OutSideFrame) then begin
|
// Get start/end PC of proc from debug info
|
||||||
if Process.Disassembler.LastErrorWasMemReadErr then begin
|
if not Self.Process.FindProcStartEndPC(Address, startPC, endPC) then
|
||||||
inc(CodeReadErrCnt);
|
begin
|
||||||
if CodeReadErrCnt > 5 then break; // If the code cannot be read the stack pointer is wrong.
|
startPC := 0;
|
||||||
|
endPC := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if not TAvrAsmDecoder(Process.Disassembler).GetFunctionFrameReturnAddress(Address, startPC, endPC, returnAddrStackOffset, OutSideFrame) then
|
||||||
|
begin
|
||||||
OutSideFrame := False;
|
OutSideFrame := False;
|
||||||
end;
|
end;
|
||||||
LastFrameBase := FrameBase;
|
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 OutSideFrame then begin
|
||||||
// at start of prologue, before pushing starts, so return PC should be at current SP+1
|
// Before adjustment of frame pointer, or after restoration of frame pointer,
|
||||||
// To read SRAM this needs to be masked by $800000
|
// return PC should be located by offset from SP
|
||||||
if not Process.ReadData(DataOffset or (StackPtr + Size), Size, Address) or (Address = 0) then Break;
|
if not Process.ReadData(DataOffset or (StackPtr + returnAddrStackOffset), Size, Address) or (Address = 0) then Break;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// Inside stack frame, return PC should be located by offset from FP
|
||||||
|
if not Process.ReadData(DataOffset or (FrameBase + returnAddrStackOffset), Size, Address) or (Address = 0) then Break;
|
||||||
|
end;
|
||||||
// Convert return address from BE to LE, shl 1 to get byte address
|
// Convert return address from BE to LE, shl 1 to get byte address
|
||||||
Address := BEtoN(word(Address)) shl 1;
|
Address := BEtoN(word(Address)) shl 1;
|
||||||
{$PUSH}{$R-}{$Q-}
|
{$PUSH}{$R-}{$Q-}
|
||||||
StackPtr := StackPtr + 1 * Size + 1; // After popping return-addr from "StackPtr"
|
StackPtr := FrameBase + returnAddrStackOffset + Size; // After popping return-addr from "StackPtr"
|
||||||
LastFrameBase := LastFrameBase - 1; // Make the loop think thas LastFrameBase was smaller
|
FrameBase := StackPtr; // Estimate of previous FP
|
||||||
|
LastFrameBase := LastFrameBase - 1; // Make the loop think that LastFrameBase was smaller
|
||||||
{$POP}
|
{$POP}
|
||||||
end
|
|
||||||
else begin
|
|
||||||
// Need to add localsize and saved register count to frame pointer (Y) to locate saved return PC
|
|
||||||
break;
|
|
||||||
//{$PUSH}{$R-}{$Q-}
|
|
||||||
//StackPtr := FrameBase + 2 * Size; // After popping return-addr from "FrameBase + Size"
|
|
||||||
//{$POP}
|
|
||||||
//if not Process.ReadData(DataOffset or (FrameBase + Size), Size, Address) or (Address = 0) then Break;
|
|
||||||
//if not Process.ReadData(DataOffset or FrameBase, Size, FrameBase) then Break;
|
|
||||||
end;
|
|
||||||
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PC);
|
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PC);
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nFP].SetValue(FrameBase, IntToStr(FrameBase),Size, FP);
|
||||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
||||||
FCallStackEntryList.Add(AnEntry);
|
FCallStackEntryList.Add(AnEntry);
|
||||||
Dec(CountNeeded);
|
Dec(CountNeeded);
|
||||||
inc(NextIdx);
|
inc(NextIdx);
|
||||||
CodeReadErrCnt := 0;
|
CodeReadErrCnt := 0;
|
||||||
If (NextIdx > MAX_FRAMES) then
|
if (NextIdx > MAX_FRAMES) then
|
||||||
break;
|
break;
|
||||||
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
|
||||||
|
@ -74,11 +74,17 @@ type
|
|||||||
TAvrAsmDecoder = class(TDbgAsmDecoder)
|
TAvrAsmDecoder = class(TDbgAsmDecoder)
|
||||||
private const
|
private const
|
||||||
MAX_CODEBIN_LEN = 20*2; // About 20 instructions
|
MAX_CODEBIN_LEN = 20*2; // About 20 instructions
|
||||||
|
MaxPrologueSize = 64; // Bytes, so ~32 instructions
|
||||||
|
MaxEpilogueSize = MaxPrologueSize; // Perhaps a bit smaller, since the locals/parameters do not have to be initialized
|
||||||
private
|
private
|
||||||
FProcess: TDbgProcess;
|
FProcess: TDbgProcess;
|
||||||
FLastErrWasMem: Boolean;
|
FLastErrWasMem: Boolean;
|
||||||
FCodeBin: array[0..MAX_CODEBIN_LEN-1] of byte;
|
FCodeBin: array[0..MAX_CODEBIN_LEN-1] of byte;
|
||||||
FLastInstr: TAvrAsmInstruction;
|
FLastInstr: TAvrAsmInstruction;
|
||||||
|
function FParsePrologue(AnAddress, AStartPC, AEndPC: TDBGPtr; out
|
||||||
|
returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||||
|
function FParseEpilogue(AnAddress, AStartPC, AEndPC: TDBGPtr; out
|
||||||
|
returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||||
protected
|
protected
|
||||||
function GetLastErrorWasMemReadErr: Boolean; override;
|
function GetLastErrorWasMemReadErr: Boolean; override;
|
||||||
function GetMaxInstrSize: integer; override;
|
function GetMaxInstrSize: integer; override;
|
||||||
@ -90,10 +96,17 @@ type
|
|||||||
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); override;
|
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); override;
|
||||||
function GetInstructionInfo(AnAddress: TDBGPtr): TDbgAsmInstruction; override;
|
function GetInstructionInfo(AnAddress: TDBGPtr): TDbgAsmInstruction; override;
|
||||||
|
|
||||||
// returns byte len of call instruction at AAddress // 0 if not a call intruction
|
// Don't use, ot really suited to AVR ABI
|
||||||
function GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
function GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
||||||
AnIsOutsideFrame: Boolean): Boolean; override;
|
AnIsOutsideFrame: Boolean): Boolean; override;
|
||||||
|
|
||||||
|
// Rather use the next function to locate the call return address.
|
||||||
|
// AStartPC & AEndPC indicates proc limits to help with scanning for prologue/epilogue
|
||||||
|
// returnAddressOffset gives the offset to return address relative to Y pointer (r28:r29) inside frame
|
||||||
|
// else returnAddressOffset gives the offset to return address relative to SP
|
||||||
|
function GetFunctionFrameReturnAddress(AnAddress, AStartPC, AEndPC: TDBGPtr; out
|
||||||
|
returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||||
|
|
||||||
constructor Create(AProcess: TDbgProcess); override;
|
constructor Create(AProcess: TDbgProcess); override;
|
||||||
destructor Destroy;
|
destructor Destroy;
|
||||||
end;
|
end;
|
||||||
@ -102,7 +115,10 @@ type
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
StrUtils, LazClasses;
|
StrUtils, LazClasses, Math;
|
||||||
|
|
||||||
|
var
|
||||||
|
DBG_WARNINGS: PLazLoggerLogGroup;
|
||||||
|
|
||||||
const
|
const
|
||||||
opRegReg = '%s r%d, r%d';
|
opRegReg = '%s r%d, r%d';
|
||||||
@ -128,6 +144,28 @@ const
|
|||||||
branchIfSetNames: array[0..7] of string = ('brcs', 'breq', 'brmi', 'brvs', 'brlt', 'brhs', 'brts', 'brie');
|
branchIfSetNames: array[0..7] of string = ('brcs', 'breq', 'brmi', 'brvs', 'brlt', 'brhs', 'brts', 'brie');
|
||||||
branchIfClrNames: array[0..7] of string = ('brcc', 'brne', 'brpl', 'brvc', 'brge', 'brhc', 'brtc', 'brid');
|
branchIfClrNames: array[0..7] of string = ('brcc', 'brne', 'brpl', 'brvc', 'brge', 'brhc', 'brtc', 'brid');
|
||||||
|
|
||||||
|
OpCodeNOP = $0000;
|
||||||
|
OpCodeLoadSPL = $B7CD; // in r28, 0x3d
|
||||||
|
OpCodeLoadSPH = $B7DE; // in r29, 0x3e
|
||||||
|
OpCodeLoadSRegR0 = $B60F; // in r0, 0x3f
|
||||||
|
OpCodeLoadSRegR16 = $B70F; // in r16, 0x3f, avrtiny subarch
|
||||||
|
OpCodeCli = $94F8; // cli
|
||||||
|
OpCodeSetSPH = $BFDE; // out 0x3e, r29
|
||||||
|
OpCodeSetSPL = $BFCD; // out 0x3d, r28
|
||||||
|
OpCodeSetSregR0 = $BE0F; // out 0x3f, r0
|
||||||
|
OpCodeSetSregR16 = $BF0F; // out 0x3f, r16
|
||||||
|
OpCodeRet = $9508; // ret
|
||||||
|
OpCodeReti = $9518; // reti
|
||||||
|
|
||||||
|
OpCodePushMask = $920F; // PUSH 1001 001d dddd 1111
|
||||||
|
OpCodePopMask = $900F; // POP 1001 000d dddd 1111
|
||||||
|
// Frame pointer is r28:r29, so fix register index in opcode
|
||||||
|
OpCodeAdjustFrameLMask = $50C0; // subi r28, k, 0101 kkkk dddd kkkk, rd = 16 + d
|
||||||
|
OpCodeAdjustFrameHMask = $40D0; // sbci r28, k, 0100 kkkk dddd kkkk, rd = 16 + d
|
||||||
|
// Not yet implemented in FPC, but more compact option
|
||||||
|
OpCodeAdjustFrameMask = $9720; // sbiw r28, k, 1001 0111 kkdd kkkk, rd = 24 + d*2
|
||||||
|
OpCodeStoreOnStackMask = $8208; // std Y+2, r24 10q0 qq1r rrrr 1qqq
|
||||||
|
|
||||||
procedure get_r_d_10(o: word; out r, d: byte);
|
procedure get_r_d_10(o: word; out r, d: byte);
|
||||||
begin
|
begin
|
||||||
r := ((o shr 5) and $10) or (o and $f);
|
r := ((o shr 5) and $10) or (o and $f);
|
||||||
@ -214,6 +252,272 @@ begin
|
|||||||
Result := 4;
|
Result := 4;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
type
|
||||||
|
TPrologueState = (psStart, psPush, psLoadSPL, psLoadSPH, psLoadSreg,
|
||||||
|
psModifySPL, psModifySPH, psWriteSPH, psWriteSreg, psWriteSPL, psCopyParams);
|
||||||
|
|
||||||
|
function TAvrAsmDecoder.FParsePrologue(AnAddress, AStartPC, AEndPC: TDBGPtr;
|
||||||
|
out returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||||
|
var
|
||||||
|
ADataLen: Cardinal;
|
||||||
|
AData: PByte;
|
||||||
|
opcode, frameOffset: word;
|
||||||
|
d, k: byte;
|
||||||
|
stackState: TPrologueState;
|
||||||
|
begin
|
||||||
|
{ AVR function entry example
|
||||||
|
3e: df 93 push r29
|
||||||
|
40: cf 93 push r28
|
||||||
|
42: 3f 92 push r3
|
||||||
|
44: 2f 92 push r2
|
||||||
|
|
||||||
|
// Load SP and calculate BP
|
||||||
|
46: cd b7 in r28, 0x3d ; 61 // SPL
|
||||||
|
48: de b7 in r29, 0x3e ; 62 // SPH
|
||||||
|
4a: c6 50 subi r28, 0x06 ; 6
|
||||||
|
4c: d0 40 sbci r29, 0x00 ; 0
|
||||||
|
// Disable interrupts
|
||||||
|
4e: 0f b6 in r0, 0x3f ; 63
|
||||||
|
50: f8 94 cli
|
||||||
|
// Then write out new SP
|
||||||
|
52: de bf out 0x3e, r29 ; 62
|
||||||
|
// Interleaved with restoring SREG
|
||||||
|
54: 0f be out 0x3f, r0 ; 63
|
||||||
|
56: cd bf out 0x3d, r28 ; 61
|
||||||
|
// Copy param to stack
|
||||||
|
58: 8a 83 std Y+2, r24 ; 0x02
|
||||||
|
5a: 9b 83 std Y+3, r25 ; 0x03
|
||||||
|
}
|
||||||
|
|
||||||
|
ADataLen := Min(MaxPrologueSize, AnAddress - AStartPC);
|
||||||
|
Result := ReadCodeAt(AStartPC, ADataLen);
|
||||||
|
if not Result then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
AData := @FCodeBin[0];
|
||||||
|
// SP points to next empty slot, previous data is before current SP
|
||||||
|
returnAddressOffset := 1;
|
||||||
|
AnIsOutsideFrame := true;
|
||||||
|
stackState := psStart;
|
||||||
|
frameOffset := 0;
|
||||||
|
|
||||||
|
// Loop until prologue buffer is empty, or stepped inside stack frame
|
||||||
|
while (ADataLen > 1) and AnIsOutsideFrame do
|
||||||
|
begin
|
||||||
|
opcode := AData[0] or (AData[1] shl 8);
|
||||||
|
inc(AData, 2);
|
||||||
|
dec(ADataLen, 2);
|
||||||
|
|
||||||
|
case opcode of
|
||||||
|
OpCodeNOP: ;
|
||||||
|
OpCodeLoadSPL: stackState := psLoadSPL;
|
||||||
|
OpCodeLoadSPH: stackState := psLoadSPH;
|
||||||
|
OpCodeLoadSRegR0, OpCodeLoadSRegR16: stackState := psLoadSreg;
|
||||||
|
OpCodeCli: ;
|
||||||
|
OpCodeSetSPH: stackState := psWriteSPH;
|
||||||
|
OpCodeSetSregR0, OpCodeSetSregR16: stackState := psWriteSreg;
|
||||||
|
OpCodeSetSPL: stackState := psWriteSPL;
|
||||||
|
else
|
||||||
|
// Push a register onto stack
|
||||||
|
if (opcode and OpCodePushMask) = OpCodePushMask then
|
||||||
|
begin
|
||||||
|
if stackState <= psPush then
|
||||||
|
begin
|
||||||
|
inc(returnAddressOffset);
|
||||||
|
stackState := psPush;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for PUSH opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else if (opcode and OpCodeAdjustFrameLMask) = OpCodeAdjustFrameLMask then
|
||||||
|
begin
|
||||||
|
if stackState >= psLoadSPH then
|
||||||
|
begin
|
||||||
|
stackState := psModifySPL;
|
||||||
|
// Decode register and constant
|
||||||
|
get_k_r16(opcode, d, k);
|
||||||
|
frameOffset := frameOffset + k;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for OpCodeAdjustFrameLMask opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else if (opcode and OpCodeAdjustFrameHMask) = OpCodeAdjustFrameHMask then
|
||||||
|
begin
|
||||||
|
if stackState >= psLoadSPH then
|
||||||
|
begin
|
||||||
|
stackState := psModifySPH;
|
||||||
|
// Decode register and constant
|
||||||
|
get_k_r16(opcode, d, k);
|
||||||
|
frameOffset := frameOffset + (k shl 8);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for OpCodeAdjustFrameHMask opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else if (opcode and OpCodeAdjustFrameMask) = OpCodeAdjustFrameMask then
|
||||||
|
begin
|
||||||
|
if stackState >= psLoadSPH then
|
||||||
|
begin
|
||||||
|
stackState := psModifySPH;
|
||||||
|
// Decode constant in SBIW opcode
|
||||||
|
k := ((opcode and $00c0) shr 2) or (opcode and $f);
|
||||||
|
frameOffset := frameOffset + k;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for OpCodeAdjustFrameMask opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else if (opcode and OpCodeStoreOnStackMask) = OpCodeStoreOnStackMask then
|
||||||
|
begin
|
||||||
|
if stackState >= psWriteSPL then
|
||||||
|
begin
|
||||||
|
stackState := psCopyParams;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for OpCodeStoreOnStackMask opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else // Any other opcode isn't part of prologue
|
||||||
|
begin
|
||||||
|
AnIsOutsideFrame := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Check if frame pointer was updated
|
||||||
|
if (stackState >= psWriteSPL) and (frameOffset > 0) then
|
||||||
|
begin
|
||||||
|
AnIsOutsideFrame := false;
|
||||||
|
returnAddressOffset := returnAddressOffset + frameOffset;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
AnIsOutsideFrame := true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
type
|
||||||
|
// State sequence runs in reverse direction
|
||||||
|
TEpilogueState = (esStart, esRet, esPop, esWriteSPH, esWriteSreg, esWriteSPL,
|
||||||
|
esLoadSreg, esModifyFPH, esModifyFPL);
|
||||||
|
|
||||||
|
function TAvrAsmDecoder.FParseEpilogue(AnAddress, AStartPC, AEndPC: TDBGPtr;
|
||||||
|
out returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||||
|
var
|
||||||
|
ADataLen: Cardinal;
|
||||||
|
AData: PByte;
|
||||||
|
opcode, frameOffset: word;
|
||||||
|
d, k: byte;
|
||||||
|
stackState: TEpilogueState;
|
||||||
|
begin
|
||||||
|
{ AVR function epilogue example
|
||||||
|
// Move FP to previous frame
|
||||||
|
ba: cd 5f subi r28, 0xFD ; 253
|
||||||
|
bc: df 4f sbci r29, 0xFF ; 255
|
||||||
|
|
||||||
|
// Update SP
|
||||||
|
be: 0f b6 in r0, 0x3f ; 63
|
||||||
|
c0: f8 94 cli
|
||||||
|
c2: de bf out 0x3e, r29 ; 62
|
||||||
|
c4: 0f be out 0x3f, r0 ; 63
|
||||||
|
c6: cd bf out 0x3d, r28 ; 61
|
||||||
|
|
||||||
|
// Restore saved regs
|
||||||
|
c8: cf 91 pop r28
|
||||||
|
ca: df 91 pop r29
|
||||||
|
cc: 08 95 ret
|
||||||
|
}
|
||||||
|
|
||||||
|
ADataLen := Min(MaxEpilogueSize, AEndPC - AnAddress);
|
||||||
|
Result := ReadCodeAt(AEndPC - ADataLen, ADataLen);
|
||||||
|
if not Result then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
AData := @FCodeBin[ADataLen - 2];
|
||||||
|
// SP points to next empty slot, previous data is before current SP
|
||||||
|
returnAddressOffset := 1;
|
||||||
|
AnIsOutsideFrame := true;
|
||||||
|
stackState := esStart;
|
||||||
|
frameOffset := 0;
|
||||||
|
|
||||||
|
// Loop until epilogue buffer is empty, or stepped inside stack frame
|
||||||
|
while (ADataLen > 1) and AnIsOutsideFrame do
|
||||||
|
begin
|
||||||
|
opcode := AData[0] or (AData[1] shl 8);
|
||||||
|
dec(AData, 2);
|
||||||
|
dec(ADataLen, 2);
|
||||||
|
|
||||||
|
case opcode of
|
||||||
|
OpCodeNOP: ;
|
||||||
|
OpCodeLoadSRegR0, OpCodeLoadSRegR16: stackState := esLoadSreg;
|
||||||
|
OpCodeCli: ;
|
||||||
|
OpCodeSetSPH: stackState := esWriteSPH;
|
||||||
|
OpCodeSetSregR0, OpCodeSetSregR16: stackState := esWriteSreg;
|
||||||
|
OpCodeSetSPL: stackState := esWriteSPL;
|
||||||
|
OpCodeRet, OpCodeReti: stackState := esRet;
|
||||||
|
else
|
||||||
|
// Push a register onto stack
|
||||||
|
if (opcode and OpCodePopMask) = OpCodePopMask then
|
||||||
|
begin
|
||||||
|
if stackState <= esPop then
|
||||||
|
begin
|
||||||
|
inc(returnAddressOffset);
|
||||||
|
stackState := esPop;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for POP opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else if (opcode and OpCodeAdjustFrameLMask) = OpCodeAdjustFrameLMask then
|
||||||
|
begin
|
||||||
|
if stackState < esModifyFPL then
|
||||||
|
begin
|
||||||
|
stackState := esModifyFPL;
|
||||||
|
// Decode register and constant
|
||||||
|
get_k_r16(opcode, d, k);
|
||||||
|
// Normally subtract negative values to increase FP
|
||||||
|
k := (256 - word(k));
|
||||||
|
frameOffset := frameOffset + k;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for OpCodeAdjustFrameLMask opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else if (opcode and OpCodeAdjustFrameHMask) = OpCodeAdjustFrameHMask then
|
||||||
|
begin
|
||||||
|
if stackState < esModifyFPH then
|
||||||
|
begin
|
||||||
|
stackState := esModifyFPH;
|
||||||
|
// Decode register and constant
|
||||||
|
get_k_r16(opcode, d, k);
|
||||||
|
// Normally subtract negative values to increase FP, + carry bit
|
||||||
|
k := (256 - word(k) - 1);
|
||||||
|
frameOffset := frameOffset + (k shl 8);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DebugLn(DBG_WARNINGS, ['Invalid stack state for OpCodeAdjustFrameHMask opcode: ', stackState]);
|
||||||
|
end
|
||||||
|
else // Any other opcode isn't part of prologue
|
||||||
|
begin
|
||||||
|
AnIsOutsideFrame := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Check before frame pointer gets adjusted
|
||||||
|
if (stackState >= esModifyFPL) and (frameOffset > 0) then
|
||||||
|
begin
|
||||||
|
AnIsOutsideFrame := false;
|
||||||
|
returnAddressOffset := returnAddressOffset + frameOffset;
|
||||||
|
end
|
||||||
|
// Frame pointer inconsistent, so work relative to SP
|
||||||
|
// Unreliable, SP can be adjusted inside frame
|
||||||
|
//else if (stackState = esModifyFPH) then
|
||||||
|
//begin
|
||||||
|
// AnIsOutsideFrame := true;
|
||||||
|
// returnAddressOffset := returnAddressOffset + frameOffset;
|
||||||
|
//end
|
||||||
|
//else if (stackState > esPop) then
|
||||||
|
//begin
|
||||||
|
//
|
||||||
|
//end
|
||||||
|
else
|
||||||
|
AnIsOutsideFrame := true;
|
||||||
|
end;
|
||||||
|
|
||||||
function TAvrAsmDecoder.GetLastErrorWasMemReadErr: Boolean;
|
function TAvrAsmDecoder.GetLastErrorWasMemReadErr: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := FLastErrWasMem;
|
Result := FLastErrWasMem;
|
||||||
@ -373,12 +677,12 @@ begin
|
|||||||
ACode := format(opRegConstHex8, ['cpi', d, k]);
|
ACode := format(opRegConstHex8, ['cpi', d, k]);
|
||||||
end;
|
end;
|
||||||
$4000:
|
$4000:
|
||||||
begin // SBCI Subtract Immediate With Carry 0101 10 kkkk dddd kkkk
|
begin // SBCI Subtract Immediate With Carry 0100 kkkk dddd kkkk
|
||||||
get_k_r16(code, d, k);
|
get_k_r16(code, d, k);
|
||||||
ACode := format(opRegConstHex8, ['sbci', d, k]);
|
ACode := format(opRegConstHex8, ['sbci', d, k]);
|
||||||
end;
|
end;
|
||||||
$5000:
|
$5000:
|
||||||
begin // SUB Subtract Immediate 0101 10 kkkk dddd kkkk
|
begin // SUB Subtract Immediate 0101 kkkk dddd kkkk
|
||||||
get_k_r16(code, d, k);
|
get_k_r16(code, d, k);
|
||||||
ACode := format(opRegConstHex8, ['subi', d, k]);
|
ACode := format(opRegConstHex8, ['subi', d, k]);
|
||||||
end;
|
end;
|
||||||
@ -858,75 +1162,46 @@ end;
|
|||||||
|
|
||||||
function TAvrAsmDecoder.GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
function TAvrAsmDecoder.GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
||||||
AnIsOutsideFrame: Boolean): Boolean;
|
AnIsOutsideFrame: Boolean): Boolean;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAvrAsmDecoder.GetFunctionFrameReturnAddress(AnAddress, AStartPC,
|
||||||
|
AEndPC: TDBGPtr; out returnAddressOffset: word; out AnIsOutsideFrame: Boolean
|
||||||
|
): Boolean;
|
||||||
var
|
var
|
||||||
ADataLen: Cardinal;
|
ADataLen: Cardinal;
|
||||||
AData: PByte;
|
AData: PByte;
|
||||||
begin
|
begin
|
||||||
{ AVR function entry example
|
{ Cases to consider:
|
||||||
3e: df 93 push r29
|
A - if (AStartPC + MaxPrologueSize < AnAddress) and (AnAddress + MaxEpilogueSize < AEndPC)
|
||||||
40: cf 93 push r28
|
then currently inside stack frame. Parse prologue to figure out
|
||||||
42: 3f 92 push r3
|
offset from frame pointer to return address.
|
||||||
44: 2f 92 push r2
|
|
||||||
|
|
||||||
// Load SP and calculate BP
|
B - if (AStartPC + MaxPrologueSize < AnAddress)
|
||||||
46: cd b7 in r28, 0x3d ; 61 // SPL
|
then possibly before final stack frame. Need to parse prologue up to AnAddress
|
||||||
48: de b7 in r29, 0x3e ; 62 // SPH
|
to figure out how far the stack has moved since entry to calculate offset
|
||||||
4a: c6 50 subi r28, 0x06 ; 6
|
from SP to return address. If frame pointer has been configured before
|
||||||
4c: d0 40 sbci r29, 0x00 ; 0
|
AnAddress, assume inside frame and return offset relative to frame pointer.
|
||||||
// Disable interrupts
|
|
||||||
4e: 0f b6 in r0, 0x3f ; 63
|
|
||||||
50: f8 94 cli
|
|
||||||
// Then write out new SP
|
|
||||||
52: de bf out 0x3e, r29 ; 62
|
|
||||||
// Interleaved with restoring SREG
|
|
||||||
54: 0f be out 0x3f, r0 ; 63
|
|
||||||
56: cd bf out 0x3d, r28 ; 61
|
|
||||||
// Copy param to stack
|
|
||||||
58: 8a 83 std Y+2, r24 ; 0x02
|
|
||||||
5a: 9b 83 std Y+3, r25 ; 0x03
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should be an even size
|
C - if (AnAddress + MaxEpilogueSize < AEndPC)
|
||||||
ADataLen := MAX_CODEBIN_LEN; // Not sure how much data to process??
|
then possibly inside frame teardown. Need to reverse parse epilogue up to AnAddress
|
||||||
Result := False;
|
to figure out how much frame will unwind to calculate offset to return address.
|
||||||
if not ReadCodeAt(AnAddress, ADataLen) then
|
If frame pointer has been restored before AnAddress then ouside frame.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AnAddress = AStartPC) or (AnAddress = AEndPC) then
|
||||||
|
begin
|
||||||
|
// Frame not yet constructed, so return address is located via SP + offset
|
||||||
|
returnAddressOffset := 1;
|
||||||
|
AnIsOutsideFrame := true;
|
||||||
exit;
|
exit;
|
||||||
AData := @FCodeBin[0];
|
end
|
||||||
|
//else if (AStartPC + MaxPrologueSize > AnAddress) then
|
||||||
while (ADataLen > 0) and (AData[0] = $00) and (AData[1] = $00) do begin // nop
|
else if (AnAddress - AStartPC) < (AEndPC - AnAddress) then
|
||||||
inc(AData, 2);
|
result := FParsePrologue(AnAddress, AStartPC, AEndPC, returnAddressOffset, AnIsOutsideFrame)
|
||||||
dec(ADataLen, 2);
|
else
|
||||||
end;
|
result := FParseEpilogue(AnAddress, AStartPC, AEndPC, returnAddressOffset, AnIsOutsideFrame);
|
||||||
Result := ADataLen > 0;
|
|
||||||
if not Result then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
AnIsOutsideFrame := False;
|
|
||||||
// in r28, 0x3d = cd b7
|
|
||||||
if (AData[0] = $cd) and (AData[1] = $b7) then begin
|
|
||||||
AnIsOutsideFrame := True;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// ret / reti $9508 / $9518
|
|
||||||
if (AData[0] in [$08, $18]) and (AData[1] = $95) then begin
|
|
||||||
AnIsOutsideFrame := True;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Swallow push instructions until the loading of SPL
|
|
||||||
// // PUSH 1001 001d dddd 1111
|
|
||||||
if ((AData[0] and $0f) = $0f) and ((Adata[1] and $92) = $92) then begin // push
|
|
||||||
while (ADataLen > 1) and ((AData[0] and $0f) = $0f) and ((Adata[1] and $92) = $92) do begin
|
|
||||||
inc(AData, 2);
|
|
||||||
dec(ADataLen, 2);
|
|
||||||
end;
|
|
||||||
// in r28, 0x3d = cd b7
|
|
||||||
if (AData[0] = $cd) and (AData[1] = $b7) then begin
|
|
||||||
AnIsOutsideFrame := True;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TAvrAsmDecoder.Create(AProcess: TDbgProcess);
|
constructor TAvrAsmDecoder.Create(AProcess: TDbgProcess);
|
||||||
@ -940,9 +1215,7 @@ begin
|
|||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//var DBG_WARNINGS: PLazLoggerLogGroup;
|
|
||||||
initialization
|
initialization
|
||||||
//DBG_WARNINGS :=
|
|
||||||
DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
Loading…
Reference in New Issue
Block a user