Skip to content

Commit

Permalink
Initial implementation of ARM EHABI
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Jan 27, 2024
1 parent a228a16 commit 14b2bf3
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 56 deletions.
36 changes: 34 additions & 2 deletions src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,28 @@ struct Registers_REGDISPLAY : REGDISPLAY
void setFP(uint32_t value, uint32_t location) { pR11 = (PTR_UIntNative)location;}
};

struct ArmUnwindCursor : public libunwind::AbstractUnwindCursor
{
Registers_REGDISPLAY *_registers;
public:
ArmUnwindCursor(Registers_REGDISPLAY *registers) : _registers(registers) {}
virtual bool validReg(int num) { return _registers->validRegister(num); }
virtual unw_word_t getReg(int num) { return _registers->getRegister(num); }
virtual void setReg(int num, unw_word_t value, unw_word_t location) { _registers->setRegister(num, value, location); }
virtual unw_word_t getRegLocation(int num) {abort();}
virtual bool validFloatReg(int num) { return _registers->validFloatRegister(num); }
virtual unw_fpreg_t getFloatReg(int num) { return _registers->getFloatRegister(num); }
virtual void setFloatReg(int num, unw_fpreg_t value) { _registers->setFloatRegister(num, value); }
virtual int step(bool stage2 = false) {abort();}
virtual void getInfo(unw_proc_info_t *) {abort();}
virtual void jumpto() {abort();}
virtual bool isSignalFrame() { return false; }
virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off) {abort();}
virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false) {abort();}
virtual const char *getRegisterName(int num) {abort();}
virtual void saveVFPAsX() {abort();}
};

inline bool Registers_REGDISPLAY::validRegister(int num) const {
if (num == UNW_REG_SP || num == UNW_ARM_SP)
return true;
Expand Down Expand Up @@ -798,7 +820,14 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t fo
}

#elif defined(_LIBUNWIND_ARM_EHABI)
PORTABILITY_ASSERT("StepFrame");
size_t len = 0;
size_t off = 0;
const uint32_t *ehtp = decode_eht_entry(reinterpret_cast<const uint32_t *>(unwind_info), &off, &len);
ArmUnwindCursor unwindCursor((Registers_REGDISPLAY*)regs);
if (_Unwind_VRS_Interpret((_Unwind_Context *)&unwindCursor, ehtp, off, len) != _URC_CONTINUE_UNWIND)
{
return false;
}
#else
PORTABILITY_ASSERT("StepFrame");
#endif
Expand Down Expand Up @@ -853,7 +882,10 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio
}

#elif defined(_LIBUNWIND_ARM_EHABI)
PORTABILITY_ASSERT("GetUnwindProcInfo");
if (uwInfoSections.arm_section == 0 || !uc.getInfoFromEHABISection(pc, uwInfoSections))
{
return false;
}
#else
PORTABILITY_ASSERT("GetUnwindProcInfo");
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum SectionType
Executable,
Uninitialized,
Debug,
UnwindData,
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ public enum RelocType
IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12 = 0x10B,
IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 0x10C,

// Linux arm32
IMAGE_REL_ARM_PREL31 = 0x10D,

//
// Relocations for R2R image production
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// 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.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

using static ILCompiler.ObjectWriter.EabiNative;

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

public static byte[] ConvertCFIToEabi(byte[] blobData)
{
if (blobData == null || blobData.Length == 0)
{
return blobData;
}

Debug.Assert(blobData.Length % 8 == 0);

// The maximum sequence length of the ARM EHABI unwinding code is 1024
// bytes.
byte[] unwindData = new byte[1024];
int unwindDataOffset = 0;
int popOffset = 0;
uint pendingPopMask = 0;
uint pendingVPopMask = 0;
int pendingSpAdjustment = 0;

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

switch (opcode)
{
case CFI_OPCODE.CFI_DEF_CFA_REGISTER:
Debug.Assert(dwarfReg != 13); // SP
Debug.Assert(dwarfReg < 15);

FlushPendingOperation();
// Set vsp = r[nnnn]
unwindData[unwindDataOffset++] = (byte)(0x90 | dwarfReg);
break;

case CFI_OPCODE.CFI_REL_OFFSET:
Debug.Assert(cfiOffset == popOffset);
if (dwarfReg >= 0 && dwarfReg <= 15)
{
EmitPop((uint)(1u << dwarfReg));
popOffset += 4;
}
else if (dwarfReg >= 256 && dwarfReg <= 271)
{
dwarfReg -= 256;
EmitVPop((uint)(3u << (dwarfReg << 1)));
popOffset += 8;
}
else
{
Debug.Fail("Unknown register");
}
break;

case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET:
cfiOffset -= popOffset;
popOffset = 0;
if (cfiOffset != 0)
{
EmitSpAdjustment(cfiOffset);
}
break;
}
}

FlushPendingOperation();

return unwindData[..unwindDataOffset];

void EmitPop(uint popMask)
{
if (pendingPopMask == 0)
FlushPendingOperation();
pendingPopMask |= popMask;
}

void EmitVPop(uint vpopMask)
{
if (pendingVPopMask == 0)
FlushPendingOperation();
pendingVPopMask |= vpopMask;
}

void EmitSpAdjustment(int spAdjustment)
{
if (pendingSpAdjustment == 0)
FlushPendingOperation();
pendingSpAdjustment += spAdjustment;
}

void FlushPendingOperation()
{
if (pendingSpAdjustment != 0)
{
Debug.Assert(pendingSpAdjustment > 0);
if (pendingSpAdjustment <= 0x100)
{
// vsp = vsp + (xxxxxx << 2) + 4.
unwindData[unwindDataOffset++] = (byte)((pendingSpAdjustment >> 2) - 1);
}
else if (pendingSpAdjustment <= 0x200)
{
// vsp = vsp + (0x3f << 2) + 4.
unwindData[unwindDataOffset++] = (byte)0x3f;
pendingSpAdjustment -= 0x100;
// vsp = vsp + (xxxxxx << 2) + 4.
unwindData[unwindDataOffset++] = (byte)((pendingSpAdjustment >> 2) - 1);
}
else
{
// vsp = vsp + 0x204 + (uleb128 << 2)
unwindData[unwindDataOffset++] = (byte)0xb2;
unwindDataOffset += DwarfHelper.WriteULEB128(unwindData.AsSpan(unwindDataOffset), (uint)((pendingSpAdjustment - 0x204) >> 2));
}
pendingSpAdjustment = 0;
}
else if (pendingPopMask != 0)
{
// TODO: Efficient encodings!

if ((pendingPopMask & 0xFFF0) != 0)
{
ushort ins = (ushort)(0x8000u | (pendingPopMask >> 4));
unwindData[unwindDataOffset++] = (byte)(ins >> 8);
unwindData[unwindDataOffset++] = (byte)(ins & 0xff);
}

if ((pendingPopMask & 0xF) != 0)
{
ushort ins = (ushort)(0xB100u | (pendingPopMask & 0xf));
unwindData[unwindDataOffset++] = (byte)(ins >> 8);
unwindData[unwindDataOffset++] = (byte)(ins & 0xff);
}

pendingPopMask = 0;
}
else if (pendingVPopMask != 0)
{
Debug.Fail("VPOP unwinding not implemented");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ internal static class ElfNative
public const uint SHT_GROUP = 17;
public const uint SHT_SYMTAB_SHNDX = 18;
public const uint SHT_IA_64_UNWIND = 0x70000001;
public const uint SHT_ARM_EXIDX = 0x70000001;
public const uint SHT_ARM_ATTRIBUTES = 0x70000003;

// Section header flags
Expand Down
Loading

0 comments on commit 14b2bf3

Please # to comment.