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