Tweak PrepareCallStackEntryList to cater for avr-gcc ABI. Added TAvrAsmDecoder.GetFunctionFrameReturnAddress to scan prologue and epilogue for frame information.

This commit is contained in:
ccrause 2021-07-04 08:42:34 +02:00
parent 02c76188e3
commit 7f341cbe68
2 changed files with 374 additions and 101 deletions

View File

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

View File

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