mirror of https://github.com/dotnet/runtime
Merge 32f945fa09
into 02596ba8d9
This commit is contained in:
commit
deb3e1b231
|
@ -657,8 +657,6 @@
|
|||
<_BuildMonoRuntimePack Condition="'$(RuntimeFlavor)' == 'Mono' and '$(MonoSupported)' == 'true'">true</_BuildMonoRuntimePack>
|
||||
<_BuildHostPack Condition="'$(RuntimeFlavor)' == '$(PrimaryRuntimeFlavor)' and '$(TargetsMobile)' != 'true'">true</_BuildHostPack>
|
||||
<_BuildCdacPack Condition="'$(_CDacToolsBuilt)' == 'true' and '$(RuntimeFlavor)' == 'CoreCLR' and '$(TargetsMobile)' != 'true' and ('$(TargetOS)' == 'windows' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'linux')">true</_BuildCdacPack>
|
||||
<!-- Some source build configurations and musl need to work out packaging errors before they can be enabled -->
|
||||
<_BuildCdacPack Condition="'$(DotNetBuildSourceOnly)' == 'true' or '$(TargetsLinuxMusl)' == 'true'">false</_BuildCdacPack>
|
||||
<_BuildBundle Condition="'$(RuntimeFlavor)' == '$(PrimaryRuntimeFlavor)' and '$(TargetsMobile)' != 'true'">true</_BuildBundle>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -521,7 +521,7 @@ extends:
|
|||
jobParameters:
|
||||
testScope: innerloop
|
||||
nameSuffix: CoreCLR_Bootstrapped
|
||||
buildArgs: -s clr+libs+host+packs -c $(_BuildConfig) -rc Checked --bootstrap
|
||||
buildArgs: -s clr+libs+host+packs+tools.cdac -c $(_BuildConfig) -rc Checked --bootstrap
|
||||
timeoutInMinutes: 120
|
||||
condition: >-
|
||||
or(
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<Project>
|
||||
<Import Project=".\native-library.targets" Condition="'$(IsSourceProject)' == 'true' and '$(Language)' == 'C#'" />
|
||||
<Import Project="..\..\..\Directory.Build.targets" />
|
||||
<Import Project="$(RepositoryEngineeringDir)liveILLink.targets" Condition="'$(UseBootstrapLayout)' == 'true'" />
|
||||
<Import Project="$(RepositoryEngineeringDir)targetingpacks.targets" Condition="'$(UseBootstrapLayout)' == 'true'" />
|
||||
</Project>
|
||||
|
|
|
@ -32,6 +32,7 @@ public interface IPlatformAgnosticContext
|
|||
RuntimeInfoArchitecture.X64 => new ContextHolder<AMD64Context>(),
|
||||
RuntimeInfoArchitecture.Arm => new ContextHolder<ARMContext>(),
|
||||
RuntimeInfoArchitecture.Arm64 => new ContextHolder<ARM64Context>(),
|
||||
RuntimeInfoArchitecture.RiscV64 => new ContextHolder<RISCV64Context>(),
|
||||
RuntimeInfoArchitecture.Unknown => throw new InvalidOperationException($"Processor architecture is required for creating a platform specific context and is not provided by the target"),
|
||||
_ => throw new InvalidOperationException($"Unsupported architecture {runtimeInfo.GetTargetArchitecture()}"),
|
||||
};
|
||||
|
|
|
@ -0,0 +1,698 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.RISCV64Context;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.RISCV64;
|
||||
|
||||
internal class RISCV64Unwinder(Target target)
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// This table describes the size of each unwind code, in bytes.
|
||||
/// </summary>
|
||||
private static ReadOnlySpan<byte> UnwindCodeSizeTable =>
|
||||
[
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00-0F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10-1F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20-2F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30-3F
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 40-4F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50-5F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60-6F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70-7F
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 80-8F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90-9F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0-AF
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0-BF
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, // C0-CF
|
||||
3, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 1, // D0-DF
|
||||
4, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E0-EF
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F0-FF
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly Target _target = target;
|
||||
private readonly IExecutionManager _eman = target.Contracts.ExecutionManager;
|
||||
|
||||
#region Entrypoint
|
||||
|
||||
public bool Unwind(ref RISCV64Context context)
|
||||
{
|
||||
if (_eman.GetCodeBlockHandle(context.InstructionPointer.Value) is not CodeBlockHandle cbh)
|
||||
return false;
|
||||
|
||||
TargetPointer imageBase = _eman.GetUnwindInfoBaseAddress(cbh);
|
||||
Data.RuntimeFunction functionEntry = _target.ProcessedData.GetOrAdd<Data.RuntimeFunction>(_eman.GetUnwindInfo(cbh));
|
||||
|
||||
ulong startingPc = context.Pc;
|
||||
ulong startingSp = context.Sp;
|
||||
|
||||
bool status = VirtualUnwind(ref context, imageBase, functionEntry);
|
||||
|
||||
//
|
||||
// If we fail the unwind, clear the PC to 0. This is recognized by
|
||||
// many callers as a failure, given that VirtualUnwind does not
|
||||
// return a status code.
|
||||
//
|
||||
|
||||
if (!status)
|
||||
{
|
||||
context.Pc = 0;
|
||||
}
|
||||
|
||||
// PC == 0 means unwinding is finished.
|
||||
// Same if no forward progress is made
|
||||
if (context.Pc == 0 || (startingPc == context.Pc && startingSp == context.Sp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Unwinder
|
||||
|
||||
private bool VirtualUnwind(ref RISCV64Context context, TargetPointer imageBase, Data.RuntimeFunction functionEntry)
|
||||
{
|
||||
// FunctionEntry could be null if the function is a pure leaf/trivial function.
|
||||
// This is not a valid case for managed code and not handled here.
|
||||
|
||||
uint controlPcRva = (uint)(context.Pc - imageBase.Value);
|
||||
|
||||
//
|
||||
// Identify the compact .pdata format versus the full .pdata+.xdata format.
|
||||
//
|
||||
// RISC-V64 does not have a compact format like ARM64, so we only handle the full format.
|
||||
//
|
||||
|
||||
return VirtualUnwindFull(ref context, controlPcRva, imageBase, functionEntry);
|
||||
}
|
||||
|
||||
private bool VirtualUnwindFull(
|
||||
ref RISCV64Context context,
|
||||
uint controlPcRva,
|
||||
TargetPointer imageBase,
|
||||
Data.RuntimeFunction functionEntry)
|
||||
{
|
||||
//
|
||||
// Unless a special frame is encountered, assume that any unwinding
|
||||
// will return us to the return address of a call and set the flag
|
||||
// appropriately (it will be cleared again if the special cases apply).
|
||||
//
|
||||
context.ContextFlags |= (uint)ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL;
|
||||
|
||||
//
|
||||
// By default, unwinding is done by popping to the RA, then copying
|
||||
// that RA to the PC. However, some special opcodes require different
|
||||
// behavior.
|
||||
//
|
||||
bool finalPcFromRa = true;
|
||||
|
||||
//
|
||||
// Fetch the header word from the .xdata blob
|
||||
//
|
||||
|
||||
TargetPointer unwindDataPtr = imageBase + functionEntry.UnwindData;
|
||||
uint headerWord = _target.Read<uint>(unwindDataPtr);
|
||||
unwindDataPtr += 4;
|
||||
|
||||
//
|
||||
// Verify the version before we do anything else
|
||||
//
|
||||
|
||||
if (((headerWord >> 18) & 3) != 0)
|
||||
{
|
||||
// unsupported version
|
||||
return false;
|
||||
}
|
||||
|
||||
uint functionLength = headerWord & 0x3ffffu;
|
||||
uint offsetInFunction = (controlPcRva - functionEntry.BeginAddress) / 4;
|
||||
|
||||
//
|
||||
// Determine the number of epilog scope records and the maximum number
|
||||
// of unwind codes.
|
||||
//
|
||||
uint unwindWords = (headerWord >> 27) & 31;
|
||||
uint epilogScopeCount = (headerWord >> 22) & 31;
|
||||
if (epilogScopeCount == 0 && unwindWords == 0)
|
||||
{
|
||||
epilogScopeCount = _target.Read<uint>(unwindDataPtr);
|
||||
unwindDataPtr += 4;
|
||||
unwindWords = (epilogScopeCount >> 16) & 0xff;
|
||||
epilogScopeCount &= 0xffff;
|
||||
}
|
||||
|
||||
uint unwindIndex = 0;
|
||||
if ((headerWord & (1 << 21)) != 0)
|
||||
{
|
||||
unwindIndex = epilogScopeCount;
|
||||
epilogScopeCount = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Exception data is not supported in this implementation and is not used by managed code.
|
||||
// If it were, it should be extracted here.
|
||||
//
|
||||
|
||||
//
|
||||
// Unless we are in a prolog/epilog, we execute the unwind codes
|
||||
// that immediately follow the epilog scope list.
|
||||
//
|
||||
|
||||
TargetPointer unwindCodePtr = unwindDataPtr + 4 * epilogScopeCount;
|
||||
TargetPointer unwindCodesEndPtr = unwindCodePtr + 4 * unwindWords;
|
||||
uint skipWords = 0;
|
||||
|
||||
//
|
||||
// If we're near the start of the function, and this function has a prolog,
|
||||
// compute the size of the prolog from the unwind codes. If we're in the
|
||||
// midst of it, we still execute starting at unwind code index 0, but we may
|
||||
// need to skip some to account for partial execution of the prolog.
|
||||
//
|
||||
// N.B. As an optimization here, note that each byte of unwind codes can
|
||||
// describe at most one 32-bit instruction. Thus, the largest prologue
|
||||
// that could possibly be described by UnwindWords (which is 4 * the
|
||||
// number of unwind code bytes) is 4 * UnwindWords words. If
|
||||
// OffsetInFunction is larger than this value, it is guaranteed to be
|
||||
// in the body of the function.
|
||||
//
|
||||
uint scopeSize;
|
||||
if (offsetInFunction < 4 * unwindWords)
|
||||
{
|
||||
scopeSize = ComputeScopeSize(unwindCodePtr, unwindCodesEndPtr);
|
||||
|
||||
if (offsetInFunction < scopeSize)
|
||||
{
|
||||
skipWords = scopeSize - offsetInFunction;
|
||||
}
|
||||
}
|
||||
|
||||
if (skipWords > 0)
|
||||
{
|
||||
// Found that we are in the middle of a prolog, no need to check for epilog scopes
|
||||
}
|
||||
|
||||
//
|
||||
// We're not in the prolog, now check to see if we are in the epilog.
|
||||
// In the simple case, the 'E' bit is set indicating there is a single
|
||||
// epilog that lives at the end of the function. If we're near the end
|
||||
// of the function, compute the actual size of the epilog from the
|
||||
// unwind codes. If we're in the midst of it, adjust the unwind code
|
||||
// pointer to the start of the codes and determine how many we need to skip.
|
||||
//
|
||||
// N.B. Similar to the prolog case above, the maximum number of halfwords
|
||||
// that an epilog can cover is limited by UnwindWords. In the epilog
|
||||
// case, however, the starting index within the unwind code table is
|
||||
// non-zero, and so the maximum number of unwind codes that can pertain
|
||||
// to an epilog is (UnwindWords * 4 - UnwindIndex), thus further
|
||||
// constraining the bounds of the epilog.
|
||||
//
|
||||
else if ((headerWord & (1 << 21)) != 0)
|
||||
{
|
||||
if (offsetInFunction + (4 * unwindWords - unwindIndex) >= functionLength)
|
||||
{
|
||||
scopeSize = ComputeScopeSize(unwindCodePtr + unwindIndex, unwindCodesEndPtr);
|
||||
uint scopeStart = functionLength - scopeSize;
|
||||
|
||||
//
|
||||
// N.B. This code assumes that no handleable exceptions can occur in
|
||||
// the prolog or in a chained shrink-wrapping prolog region.
|
||||
//
|
||||
if (offsetInFunction >= scopeStart)
|
||||
{
|
||||
unwindCodePtr += unwindIndex;
|
||||
skipWords = offsetInFunction - scopeStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// In the multiple-epilog case, we scan forward to see if we are within
|
||||
// shooting distance of any of the epilogs. If we are, we compute the
|
||||
// actual size of the epilog from the unwind codes and proceed like the
|
||||
// simple case above.
|
||||
//
|
||||
else
|
||||
{
|
||||
for (uint scopeNum = 0; scopeNum < epilogScopeCount; scopeNum++)
|
||||
{
|
||||
headerWord = _target.Read<uint>(unwindDataPtr);
|
||||
unwindDataPtr += 4;
|
||||
|
||||
//
|
||||
// The scope records are stored in order. If we hit a record that
|
||||
// starts after our current position, we must not be in an epilog.
|
||||
//
|
||||
uint scopeStart = headerWord & 0x3ffffu;
|
||||
if (offsetInFunction < scopeStart)
|
||||
break;
|
||||
|
||||
unwindIndex = headerWord >> 22;
|
||||
if (offsetInFunction < scopeStart + (4 * unwindWords - unwindIndex))
|
||||
{
|
||||
scopeSize = ComputeScopeSize(unwindCodePtr + unwindIndex, unwindCodesEndPtr);
|
||||
|
||||
if (offsetInFunction < scopeStart + scopeSize)
|
||||
{
|
||||
unwindCodePtr += unwindIndex;
|
||||
skipWords = offsetInFunction - scopeStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Skip over unwind codes until we account for the number of halfwords
|
||||
// to skip.
|
||||
//
|
||||
|
||||
while (unwindCodePtr < unwindCodesEndPtr && skipWords > 0)
|
||||
{
|
||||
byte curCode = _target.Read<byte>(unwindCodePtr);
|
||||
if (OpcodeIsEnd(curCode))
|
||||
{
|
||||
break;
|
||||
}
|
||||
unwindCodePtr += UnwindCodeSizeTable[curCode];
|
||||
skipWords--;
|
||||
}
|
||||
|
||||
//
|
||||
// Now execute codes until we hit the end.
|
||||
//
|
||||
|
||||
uint accumulatedSaveNexts = 0;
|
||||
while (unwindCodePtr < unwindCodesEndPtr)
|
||||
{
|
||||
byte curCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
|
||||
bool isEndCode = OpcodeIsEnd(curCode);
|
||||
if (!ProcessUnwindCode(ref context, curCode, ref unwindCodePtr, unwindCodesEndPtr, ref accumulatedSaveNexts))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isEndCode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If we succeeded, post-process the final state.
|
||||
//
|
||||
|
||||
if (finalPcFromRa)
|
||||
{
|
||||
context.Pc = context.Ra;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private uint ComputeScopeSize(TargetPointer unwindCodePtr, TargetPointer unwindCodesEndPtr)
|
||||
{
|
||||
//
|
||||
// Iterate through the unwind codes until we hit an end marker.
|
||||
// While iterating, accumulate the total scope size.
|
||||
//
|
||||
|
||||
uint scopeSize = 0;
|
||||
while (unwindCodePtr < unwindCodesEndPtr)
|
||||
{
|
||||
byte opcode = _target.Read<byte>(unwindCodePtr);
|
||||
if (OpcodeIsEnd(opcode))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
unwindCodePtr += UnwindCodeSizeTable[opcode];
|
||||
scopeSize++;
|
||||
}
|
||||
|
||||
return scopeSize;
|
||||
}
|
||||
|
||||
private static bool OpcodeIsEnd(byte opcode)
|
||||
{
|
||||
return (opcode & 0xfe) == 0xe4;
|
||||
}
|
||||
|
||||
private bool ProcessUnwindCode(ref RISCV64Context context, byte curCode, ref TargetPointer unwindCodePtr, TargetPointer unwindCodesEndPtr, ref uint accumulatedSaveNexts)
|
||||
{
|
||||
// Note: finalPcFromRa parameter is reserved for future use when different final PC handling is needed
|
||||
|
||||
try
|
||||
{
|
||||
//
|
||||
// alloc_s (000xxxxx): allocate small stack with size < 1024 (2^5 * 16)
|
||||
//
|
||||
|
||||
if (curCode <= 0x1f)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
context.Sp += 16u * (uint)(curCode & 0x1f);
|
||||
}
|
||||
|
||||
//
|
||||
// save_r19r20_x (001zzzzz): save register pair r19,r20 at [sp-#Z*8]!, pre-indexed offset >= -248
|
||||
//
|
||||
|
||||
else if (curCode <= 0x3f)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint offset = (uint)(curCode & 0x1f) * 8;
|
||||
context.S3 = _target.ReadPointer(context.Sp + offset).Value;
|
||||
context.S4 = _target.ReadPointer(context.Sp + offset + 8).Value;
|
||||
}
|
||||
|
||||
//
|
||||
// save_fplr (01zzzzzz): save <r29,r30> pair at [sp+#Z*8], offset <= 504
|
||||
//
|
||||
|
||||
else if (curCode <= 0x7f)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint offset = (uint)(curCode & 0x3f) * 8;
|
||||
context.Fp = _target.ReadPointer(context.Sp + offset).Value;
|
||||
context.Ra = _target.ReadPointer(context.Sp + offset + 8).Value;
|
||||
}
|
||||
|
||||
//
|
||||
// save_fplr_x (10zzzzzz): save <r29,r30> pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
|
||||
//
|
||||
|
||||
else if (curCode <= 0xbf)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint offset = (uint)((curCode & 0x3f) + 1) * 8;
|
||||
context.Sp += offset;
|
||||
context.Fp = _target.ReadPointer(context.Sp - offset).Value;
|
||||
context.Ra = _target.ReadPointer(context.Sp - offset + 8).Value;
|
||||
}
|
||||
|
||||
//
|
||||
// alloc_m (11000xxx|xxxxxxxx): allocate large stack with size < 32k (2^11 * 16)
|
||||
//
|
||||
|
||||
else if (curCode <= 0xc7)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr >= unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
uint offset = (uint)(((curCode & 0x07) << 8) | nextCode) * 16;
|
||||
context.Sp += offset;
|
||||
}
|
||||
|
||||
//
|
||||
// save_regp (110010xx|xxzzzzzz): save r(19+#X) pair at [sp+#Z*8], offset <= 504
|
||||
//
|
||||
|
||||
else if (curCode <= 0xcb)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr >= unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
uint regPair = (uint)((curCode & 0x03) << 2) | (uint)((nextCode & 0xc0) >> 6);
|
||||
uint offset = (uint)(nextCode & 0x3f) * 8;
|
||||
|
||||
SetRegisterFromOffset(ref context, 19 + regPair, context.Sp + offset);
|
||||
SetRegisterFromOffset(ref context, 20 + regPair, context.Sp + offset + 8);
|
||||
}
|
||||
|
||||
//
|
||||
// save_regp_x (110011xx|xxzzzzzz): save pair r(19+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
|
||||
//
|
||||
|
||||
else if (curCode <= 0xcf)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr >= unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
uint regPair = (uint)((curCode & 0x03) << 2) | (uint)((nextCode & 0xc0) >> 6);
|
||||
uint offset = (uint)((nextCode & 0x3f) + 1) * 8;
|
||||
|
||||
context.Sp += offset;
|
||||
SetRegisterFromOffset(ref context, 19 + regPair, context.Sp - offset);
|
||||
SetRegisterFromOffset(ref context, 20 + regPair, context.Sp - offset + 8);
|
||||
}
|
||||
|
||||
//
|
||||
// save_reg (1101xxxx|xxzzzzzz): save reg r(19+#X) at [sp+#Z*8], offset <= 504
|
||||
//
|
||||
|
||||
else if (curCode <= 0xd7)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr >= unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
uint reg = (uint)((curCode & 0x0f) << 2) | (uint)((nextCode & 0xc0) >> 6);
|
||||
uint offset = (uint)(nextCode & 0x3f) * 8;
|
||||
|
||||
SetRegisterFromOffset(ref context, 19 + reg, context.Sp + offset);
|
||||
}
|
||||
|
||||
//
|
||||
// save_reg_x (1101xxxx|xxzzzzzz): save reg r(19+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
|
||||
//
|
||||
|
||||
else if (curCode <= 0xdf)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr >= unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
uint reg = (uint)((curCode & 0x0f) << 2) | (uint)((nextCode & 0xc0) >> 6);
|
||||
uint offset = (uint)((nextCode & 0x3f) + 1) * 8;
|
||||
|
||||
context.Sp += offset;
|
||||
SetRegisterFromOffset(ref context, 19 + reg, context.Sp - offset);
|
||||
}
|
||||
|
||||
//
|
||||
// alloc_l (11100000|xxxxxxxx|xxxxxxxx|xxxxxxxx): allocate large stack with size < 256M
|
||||
//
|
||||
|
||||
else if (curCode == 0xe0)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr + 3 > unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode1 = _target.Read<byte>(unwindCodePtr);
|
||||
byte nextCode2 = _target.Read<byte>(unwindCodePtr + 1);
|
||||
byte nextCode3 = _target.Read<byte>(unwindCodePtr + 2);
|
||||
unwindCodePtr += 3;
|
||||
uint size = (uint)((nextCode1 << 16) | (nextCode2 << 8) | nextCode3);
|
||||
context.Sp += size * 16;
|
||||
}
|
||||
|
||||
//
|
||||
// set_fp (11100001): set up r29: with: mov r29, sp
|
||||
//
|
||||
|
||||
else if (curCode == 0xe1)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// No action needed for unwind
|
||||
}
|
||||
|
||||
//
|
||||
// add_fp (11100010|xxxxxxxx): set up r29 with: add r29, sp, #x*8
|
||||
//
|
||||
|
||||
else if (curCode == 0xe2)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (unwindCodePtr >= unwindCodesEndPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte nextCode = _target.Read<byte>(unwindCodePtr);
|
||||
unwindCodePtr += 1;
|
||||
// For unwinding, we need to adjust SP relative to FP
|
||||
// This is a nop in terms of register restoration
|
||||
}
|
||||
|
||||
//
|
||||
// nop (11100011): no unwind operation is required
|
||||
//
|
||||
|
||||
else if (curCode == 0xe3)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// No operation
|
||||
}
|
||||
|
||||
//
|
||||
// end (11100100): end of unwind code
|
||||
//
|
||||
|
||||
else if (curCode == 0xe4)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true; // End marker found
|
||||
}
|
||||
|
||||
//
|
||||
// end_c (11100101): end of unwind code in current chained scope
|
||||
//
|
||||
|
||||
else if (curCode == 0xe5)
|
||||
{
|
||||
if (accumulatedSaveNexts != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true; // End marker found
|
||||
}
|
||||
|
||||
//
|
||||
// save_next (11100110): save next non-volatile Int or FP register pair
|
||||
//
|
||||
|
||||
else if (curCode == 0xe6)
|
||||
{
|
||||
accumulatedSaveNexts++;
|
||||
}
|
||||
|
||||
//
|
||||
// For unsupported opcodes, just fail
|
||||
//
|
||||
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetRegisterFromOffset(ref RISCV64Context context, uint regNum, ulong address)
|
||||
{
|
||||
try
|
||||
{
|
||||
ulong value = _target.ReadPointer(address).Value;
|
||||
SetRegisterValue(ref context, regNum, value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore read failures
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetRegisterValue(ref RISCV64Context context, uint regNum, ulong value)
|
||||
{
|
||||
switch (regNum)
|
||||
{
|
||||
case 1: context.Ra = value; break;
|
||||
case 2: context.Sp = value; break;
|
||||
case 3: context.Gp = value; break;
|
||||
case 4: context.Tp = value; break;
|
||||
case 5: context.T0 = value; break;
|
||||
case 6: context.T1 = value; break;
|
||||
case 7: context.T2 = value; break;
|
||||
case 8: context.Fp = value; break;
|
||||
case 9: context.S1 = value; break;
|
||||
case 10: context.A0 = value; break;
|
||||
case 11: context.A1 = value; break;
|
||||
case 12: context.A2 = value; break;
|
||||
case 13: context.A3 = value; break;
|
||||
case 14: context.A4 = value; break;
|
||||
case 15: context.A5 = value; break;
|
||||
case 16: context.A6 = value; break;
|
||||
case 17: context.A7 = value; break;
|
||||
case 18: context.S2 = value; break;
|
||||
case 19: context.S3 = value; break;
|
||||
case 20: context.S4 = value; break;
|
||||
case 21: context.S5 = value; break;
|
||||
case 22: context.S6 = value; break;
|
||||
case 23: context.S7 = value; break;
|
||||
case 24: context.S8 = value; break;
|
||||
case 25: context.S9 = value; break;
|
||||
case 26: context.S10 = value; break;
|
||||
case 27: context.S11 = value; break;
|
||||
case 28: context.T3 = value; break;
|
||||
case 29: context.T4 = value; break;
|
||||
case 30: context.T5 = value; break;
|
||||
case 31: context.T6 = value; break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.RISCV64;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
|
||||
|
||||
/// <summary>
|
||||
/// RISC-V 64-bit specific thread context.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 1)]
|
||||
internal struct RISCV64Context : IPlatformContext
|
||||
{
|
||||
[Flags]
|
||||
public enum ContextFlagsValues : uint
|
||||
{
|
||||
CONTEXT_RISCV64 = 0x01000000,
|
||||
CONTEXT_CONTROL = CONTEXT_RISCV64 | 0x1,
|
||||
CONTEXT_INTEGER = CONTEXT_RISCV64 | 0x2,
|
||||
CONTEXT_FLOATING_POINT = CONTEXT_RISCV64 | 0x4,
|
||||
CONTEXT_DEBUG_REGISTERS = CONTEXT_RISCV64 | 0x8,
|
||||
CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT,
|
||||
CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS,
|
||||
|
||||
//
|
||||
// This flag is set by the unwinder if it has unwound to a call
|
||||
// site, and cleared whenever it unwinds through a trap frame.
|
||||
// It is used by language-specific exception handlers to help
|
||||
// differentiate exception scopes during dispatching.
|
||||
//
|
||||
CONTEXT_UNWOUND_TO_CALL = 0x20000000,
|
||||
CONTEXT_AREA_MASK = 0xFFFF,
|
||||
}
|
||||
|
||||
public readonly uint Size => 0x220;
|
||||
|
||||
public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL;
|
||||
|
||||
public TargetPointer StackPointer
|
||||
{
|
||||
readonly get => new(Sp);
|
||||
set => Sp = value.Value;
|
||||
}
|
||||
public TargetPointer InstructionPointer
|
||||
{
|
||||
readonly get => new(Pc);
|
||||
set => Pc = value.Value;
|
||||
}
|
||||
public TargetPointer FramePointer
|
||||
{
|
||||
readonly get => new(Fp);
|
||||
set => Fp = value.Value;
|
||||
}
|
||||
|
||||
public void Unwind(Target target)
|
||||
{
|
||||
RISCV64Unwinder unwinder = new(target);
|
||||
unwinder.Unwind(ref this);
|
||||
}
|
||||
|
||||
// Control flags
|
||||
|
||||
[FieldOffset(0x0)]
|
||||
public uint ContextFlags;
|
||||
|
||||
#region General registers
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x8)]
|
||||
public ulong Ra;
|
||||
|
||||
[Register(RegisterType.General | RegisterType.StackPointer)]
|
||||
[FieldOffset(0x10)]
|
||||
public ulong Sp;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x18)]
|
||||
public ulong Gp;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x20)]
|
||||
public ulong Tp;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x28)]
|
||||
public ulong T0;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x30)]
|
||||
public ulong T1;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x38)]
|
||||
public ulong T2;
|
||||
|
||||
[Register(RegisterType.General | RegisterType.FramePointer)]
|
||||
[FieldOffset(0x40)]
|
||||
public ulong Fp;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x48)]
|
||||
public ulong S1;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x50)]
|
||||
public ulong A0;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x58)]
|
||||
public ulong A1;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x60)]
|
||||
public ulong A2;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x68)]
|
||||
public ulong A3;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x70)]
|
||||
public ulong A4;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x78)]
|
||||
public ulong A5;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x80)]
|
||||
public ulong A6;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x88)]
|
||||
public ulong A7;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x90)]
|
||||
public ulong S2;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0x98)]
|
||||
public ulong S3;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xa0)]
|
||||
public ulong S4;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xa8)]
|
||||
public ulong S5;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xb0)]
|
||||
public ulong S6;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xb8)]
|
||||
public ulong S7;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xc0)]
|
||||
public ulong S8;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xc8)]
|
||||
public ulong S9;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xd0)]
|
||||
public ulong S10;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xd8)]
|
||||
public ulong S11;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xe0)]
|
||||
public ulong T3;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xe8)]
|
||||
public ulong T4;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xf0)]
|
||||
public ulong T5;
|
||||
|
||||
[Register(RegisterType.General)]
|
||||
[FieldOffset(0xf8)]
|
||||
public ulong T6;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Control Registers
|
||||
|
||||
[Register(RegisterType.Control | RegisterType.ProgramCounter)]
|
||||
[FieldOffset(0x100)]
|
||||
public ulong Pc;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Floating Point Registers
|
||||
|
||||
[Register(RegisterType.FloatingPoint)]
|
||||
[FieldOffset(0x110)]
|
||||
public unsafe fixed ulong F[32];
|
||||
|
||||
[Register(RegisterType.FloatingPoint)]
|
||||
[FieldOffset(0x210)]
|
||||
public uint Fcsr;
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Diagnostics.DataContractReader.Data;
|
||||
using static Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.RISCV64Context;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
|
||||
|
||||
internal class RISCV64FrameHandler(Target target, ContextHolder<RISCV64Context> contextHolder) : BaseFrameHandler(target, contextHolder), IPlatformFrameHandler
|
||||
{
|
||||
private readonly ContextHolder<RISCV64Context> _holder = contextHolder;
|
||||
|
||||
public void HandleHijackFrame(HijackFrame frame)
|
||||
{
|
||||
HijackArgs args = _target.ProcessedData.GetOrAdd<Data.HijackArgs>(frame.HijackArgsPtr);
|
||||
|
||||
_holder.InstructionPointer = frame.ReturnAddress;
|
||||
|
||||
// The stack pointer is the address immediately following HijackArgs
|
||||
uint hijackArgsSize = _target.GetTypeInfo(DataType.HijackArgs).Size ?? throw new InvalidOperationException("HijackArgs size is not set");
|
||||
Debug.Assert(hijackArgsSize % 8 == 0, "HijackArgs contains register values and should be a multiple of the pointer size (8 bytes for RISC-V64)");
|
||||
// The stack must be multiple of 16. So if hijackArgsSize is not multiple of 16 then there must be padding of 8 bytes
|
||||
hijackArgsSize += hijackArgsSize % 16;
|
||||
_holder.StackPointer = frame.HijackArgsPtr + hijackArgsSize;
|
||||
|
||||
UpdateFromRegisterDict(args.Registers);
|
||||
}
|
||||
}
|
|
@ -117,6 +117,16 @@ public class PrecodeStubsTests
|
|||
StubPrecodeSize = 12,
|
||||
};
|
||||
|
||||
internal static PrecodeTestDescriptor RiscV64TestDescriptor = new PrecodeTestDescriptor("RiscV64") {
|
||||
Arch = new MockTarget.Architecture { IsLittleEndian = true, Is64Bit = true },
|
||||
ReadWidthOfPrecodeType = 1,
|
||||
ShiftOfPrecodeType = 0,
|
||||
OffsetOfPrecodeType = 0,
|
||||
StubCodePageSize = 0x4000u, // 16KiB
|
||||
StubPrecode = 0x17,
|
||||
StubPrecodeSize = 24,
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> PrecodeTestDescriptorData()
|
||||
{
|
||||
var arch32le = new MockTarget.Architecture { IsLittleEndian = true, Is64Bit = false };
|
||||
|
@ -125,6 +135,7 @@ public class PrecodeStubsTests
|
|||
|
||||
yield return new object[] { X64TestDescriptor };
|
||||
yield return new object[] { Arm64TestDescriptor };
|
||||
yield return new object[] { RiscV64TestDescriptor };
|
||||
yield return new object[] { LoongArch64TestDescriptor };
|
||||
yield return new object[] { Arm32Thumb };
|
||||
// FIXME: maybe make these a little more exotic
|
||||
|
@ -329,11 +340,11 @@ public class PrecodeStubsTests
|
|||
Builder.AddHeapFragment(stubCodeFragment);
|
||||
|
||||
Span<byte> thisPtrStubData = Builder.BorrowAddressRange(thisPtrRetBufStubDataFragment.Address, (int)thisPtrRetBufDataTypeInfo.Size);
|
||||
|
||||
|
||||
Builder.TargetTestHelpers.WritePointer(thisPtrStubData.Slice(thisPtrRetBufDataTypeInfo.Fields[nameof(Data.ThisPtrRetBufPrecodeData.MethodDesc)].Offset, Builder.TargetTestHelpers.PointerSize), methodDesc);
|
||||
|
||||
Span<byte> stubData = Builder.BorrowAddressRange(stubDataFragment.Address, (int)stubDataTypeInfo.Size);
|
||||
|
||||
|
||||
Builder.TargetTestHelpers.Write(stubData.Slice(stubDataTypeInfo.Fields[nameof(Data.StubPrecodeData_2.Type)].Offset, sizeof(byte)), test.ThisPtrRetBufPrecode);
|
||||
Builder.TargetTestHelpers.WritePointer(stubData.Slice(stubDataTypeInfo.Fields[nameof(Data.StubPrecodeData_2.SecretParam)].Offset, Builder.TargetTestHelpers.PointerSize), thisPtrRetBufStubDataFragment.Address);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="..\Directory.Build.targets" />
|
||||
<Import Project="$(RepositoryEngineeringDir)targetingpacks.targets" Condition="'$(UseBootstrapLayout)' == 'true'" />
|
||||
</Project>
|
||||
|
|
|
@ -19,13 +19,8 @@
|
|||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and '$(RuntimeFlavor)' == 'Mono'">false</SupportsNativeAotComponents>
|
||||
<!-- disable on linux-bionic, for now -->
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and '$(TargetsLinuxBionic)' == 'true'">false</SupportsNativeAotComponents>
|
||||
<!-- NativeAOT doesn't support cross-OS compilation. disable for crossdac-->
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and '$(HostOS)' != '$(TargetOS)'">false</SupportsNativeAotComponents>
|
||||
<!-- unsupported targets -->
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and '$(DotNetBuildSourceOnly)' == 'true'">false</SupportsNativeAotComponents>
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and ('$(TargetArchitecture)' == 'armel' or '$(TargetArchitecture)' == 'riscv64')">false</SupportsNativeAotComponents>
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and ('$(TargetsWindows)' == 'true' or '$(TargetsOSX)' == 'true' or ('$(TargetsLinux)' == 'true' and '$(TargetsAndroid)' != 'true' and '$(TargetsLinuxMusl)' != 'true'))">true</SupportsNativeAotComponents>
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == ''">false</SupportsNativeAotComponents>
|
||||
<!-- use global property for the rest -->
|
||||
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == ''">$(NativeAotSupported)</SupportsNativeAotComponents>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<SubprojectProps Include="Configuration" Value="$(Configuration)" />
|
||||
<SubprojectProps Include="RuntimeConfiguration" Value="$(RuntimeConfiguration)" />
|
||||
<SubprojectProps Include="LibrariesConfiguration" Value="$(LibrariesConfiguration)" />
|
||||
<SubprojectProps Include="RuntimeIdentifier" Value="$(TargetRid)" />
|
||||
<SubprojectProps Include="RuntimeIdentifier" Value="$(PortableTargetRid)" />
|
||||
<SubprojectProps Include="UseBootstrapLayout" Value="$(UseBootstrap)" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
Loading…
Reference in New Issue