diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs
new file mode 100644
index 0000000000000..25d81deb39aab
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs
@@ -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
+{
+ ///
+ /// JIT enum used in the CFI code blob.
+ ///
+ 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.
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs
index b3ea6ac201286..b14a770f3feae 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs
@@ -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.
- }
-
///
/// Convert JIT version of CFI blob into the the DWARF byte code form.
///
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs
index d2ebf9f820238..a4e56fc1d2b2f 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs
@@ -13,6 +13,11 @@
namespace ILCompiler.ObjectWriter
{
+ ///
+ /// Builder class for constructing the .ARM.attributes table that
+ /// describes the parameters of the emitted ARM code for use by the
+ /// linker or debugger.
+ ///
internal sealed class EabiAttributesBuilder
{
private readonly SectionWriter _sectionWriter;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs
index a0d0b6c95a10d..e3f8337dc5e25 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs
@@ -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.
- }
-
+ ///
+ /// Convert from the DWARF CFI opcodes produced by JIT into the ARM EHABI
+ /// opcodes for exception unwinding.
+ ///
+ /// DWARF CFI blob from JIT
+ ///
+ /// ARM EHABI unwind code, as specified by Exception Handling ABI for the Arm
+ /// Architecture, 2023Q3, section 10.3.
+ ///
public static byte[] ConvertCFIToEabi(byte[] blobData)
{
if (blobData == null || blobData.Length == 0)
@@ -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:
@@ -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)
{
@@ -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)
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs
index a4bf86200bbf3..7601974561a73 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs
@@ -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();
@@ -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)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index 93d364012b83f..bae527c9de4d1 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
@@ -593,6 +593,7 @@
+