Skip to content

Commit

Permalink
Handle edge cases, add comments
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Jan 28, 2024
1 parent 6fd5383 commit ce9fe57
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ILCompiler.ObjectWriter
{
/// <summary>
/// JIT enum used in the CFI code blob.
/// </summary>
internal enum CFI_OPCODE
{
CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one.
CFI_DEF_CFA_REGISTER, // New register is used to compute CFA
CFI_REL_OFFSET, // Register is saved at offset from the current CFA
CFI_DEF_CFA // Take address from register and add offset to it.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,6 @@ public DwarfFde(
PersonalitySymbolName = personalitySymbolName;
}

private enum CFI_OPCODE
{
CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one.
CFI_DEF_CFA_REGISTER, // New register is used to compute CFA
CFI_REL_OFFSET, // Register is saved at offset from the current CFA
CFI_DEF_CFA // Take address from register and add offset to it.
}

/// <summary>
/// Convert JIT version of CFI blob into the the DWARF byte code form.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

namespace ILCompiler.ObjectWriter
{
/// <summary>
/// Builder class for constructing the .ARM.attributes table that
/// describes the parameters of the emitted ARM code for use by the
/// linker or debugger.
/// </summary>
internal sealed class EabiAttributesBuilder
{
private readonly SectionWriter _sectionWriter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ namespace ILCompiler.ObjectWriter
{
internal static class EabiUnwindConverter
{
private enum CFI_OPCODE
{
CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one.
CFI_DEF_CFA_REGISTER, // New register is used to compute CFA
CFI_REL_OFFSET, // Register is saved at offset from the current CFA
CFI_DEF_CFA // Take address from register and add offset to it.
}

/// <summary>
/// Convert from the DWARF CFI opcodes produced by JIT into the ARM EHABI
/// opcodes for exception unwinding.
/// </summary>
/// <param name="blobData">DWARF CFI blob from JIT</param>
/// <returns>
/// ARM EHABI unwind code, as specified by Exception Handling ABI for the Arm
/// Architecture, 2023Q3, section 10.3.
/// </returns>
public static byte[] ConvertCFIToEabi(byte[] blobData)
{
if (blobData == null || blobData.Length == 0)
Expand All @@ -37,18 +38,51 @@ public static byte[] ConvertCFIToEabi(byte[] blobData)
// bytes.
byte[] unwindData = new byte[1024];
int unwindDataOffset = 0;

// The DWARF CFI data produced by the JIT describe the method prolog that
// saves registers, adjusts the stack, and optionally set ups the frame
// register. In contrast, the ARM EHABI unwind code describes how the epilog
// would do the unwinding. It lacks the code offsets so it cannot unwind
// inside either the prolog, or the epilog. The runtime code detects these
// cases when doing the asynchronous unwinding.
//
// In order to convert between the two formats we thus need to reverse
// the order of operatations. The EHABI unwind codes closely mirror the
// ARM instructions and they efficiently describe the POP/VPOP operation on
// multiple registers. In order to get the most compact representation we
// record the pending opertations at each code offset and only flush the
// unwind code when necessary.

// Adjustment to VSP made by VPOP instruction relative to the DWARF CFI
// which uses an explicit CFI_ADJUST_CFA_OFFSET opcode.
int popOffset = 0;

// Mask of pending Rn registers popped at current code offset.
uint pendingPopMask = 0;

// Mask of pending Dn vector registers popped at current code offset.
uint pendingVPopMask = 0;

// Stack adjustment by add/sub sp, X instructions
int pendingSpAdjustment = 0;

int lastCodeOffset = blobData[blobData.Length - 8];

// Walk the CFI data backwards
for (int offset = blobData.Length - 8; offset >= 0; offset -= 8)
{
byte codeOffset = blobData[offset];
CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset + 1];
short dwarfReg = BinaryPrimitives.ReadInt16LittleEndian(blobData.AsSpan(offset + 2));
int cfiOffset = BinaryPrimitives.ReadInt32LittleEndian(blobData.AsSpan(offset + 4));

if (lastCodeOffset != codeOffset)
{
Debug.Assert(popOffset == 0);
FlushPendingOperation();
lastCodeOffset = codeOffset;
}

switch (opcode)
{
case CFI_OPCODE.CFI_DEF_CFA_REGISTER:
Expand Down Expand Up @@ -117,9 +151,8 @@ void EmitSpAdjustment(int spAdjustment)

void FlushPendingOperation()
{
if (pendingSpAdjustment != 0)
if (pendingSpAdjustment > 0)
{
Debug.Assert(pendingSpAdjustment > 0);
Debug.Assert((pendingSpAdjustment & 3) == 0);
if (pendingSpAdjustment <= 0x100)
{
Expand All @@ -144,6 +177,22 @@ void FlushPendingOperation()
unwindData[unwindDataOffset++] = (byte)0xb2;
unwindDataOffset += DwarfHelper.WriteULEB128(unwindData.AsSpan(unwindDataOffset), (uint)((pendingSpAdjustment - 0x204) >> 2));
}

pendingSpAdjustment = 0;
}
else if (pendingSpAdjustment < 0)
{
while (pendingSpAdjustment < -0x100)
{
// vsp = vsp - (0x3f << 2) - 4.
// 01111111
unwindData[unwindDataOffset++] = 0x7f;
pendingSpAdjustment += 0x100;
}
// vsp = vsp - (xxxxxx << 2) - 4.
// 01xxxxxx
unwindData[unwindDataOffset++] = (byte)(0x40 | ((-pendingSpAdjustment >> 2) - 1));

pendingSpAdjustment = 0;
}
else if (pendingPopMask != 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ private protected override void EmitSectionsAndLayout()

private protected override void CreateEhSections()
{
// ARM creates the EHABI sections lazily in EmitUnwindInfo
if (_machine is not EM_ARM)
{
base.CreateEhSections();
Expand Down Expand Up @@ -544,10 +545,17 @@ private protected override void EmitUnwindInfo(
armUnwindInfo = armUnwindInfo.Slice(2);
}

// Emit unwind code and dummy relocation to the personality routine
extabSectionWriter.EmitAlignment(4);
extabSectionWriter.EmitSymbolDefinition(extabSymbolName);

// ARM EHABI requires emitting a dummy relocation to the personality routine
// to tell the linker to preserve it.
extabSectionWriter.EmitRelocation(0, unwindWord, IMAGE_REL_BASED_ABSOLUTE, personalitySymbolName, 0);

// Emit the unwinding code. First word specifies the personality routine,
// format and first few bytes of the unwind code. For longer unwind codes
// the other words follow. They are padded with the "finish" instruction
// (0xB0).
extabSectionWriter.Write(unwindWord);
while (armUnwindInfo.Length > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64UnboxingStubNode.cs" />
<Compile Include="Compiler\ObjectWriter\Dwarf\DwarfAbbrev.cs" />
<Compile Include="Compiler\ObjectWriter\Dwarf\DwarfBuilder.cs" />
<Compile Include="Compiler\ObjectWriter\Dwarf\DwarfCfiOpcode.cs" />
<Compile Include="Compiler\ObjectWriter\Dwarf\DwarfCie.cs" />
<Compile Include="Compiler\ObjectWriter\Dwarf\DwarfEhFrame.cs" />
<Compile Include="Compiler\ObjectWriter\Dwarf\DwarfExpressionBuilder.cs" />
Expand Down

0 comments on commit ce9fe57

Please sign in to comment.