From 1a985d9d113b3873f337cead6c3a136e1876cd2c Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 17 Jan 2024 15:34:24 +0100 Subject: [PATCH] [NativeAOT] 32-bit platform ObjWriter and bit rot fixes (#96890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use PC-relative relocations for ARMEmitter.EmitMOV(dest, symbol) * Fix handling of signed offsets in IMAGE_REL_BASED_THUMB_BRANCH24 calculations * Generate position independent code in NativeAOT on linux-arm * Handle ARM32 in DwarfCie * Fix relocations emitted in 32-bit ELF for x86/arm32; apply correct addends to account for R2R ABI differences; use inline addend for 32-bit platforms; mark function symbols as Thumb code on ARM * ELF/ARM32: Emit thumb mapping symbols for executable sections * Try to revert ARMEmitter.EmitMOV * Convert IMAGE_REL_BASED_THUMB_MOV32_PCREL to ELF with the same offset as R2R * Unoptimal, but working, version of INLINE_GET_TLS_VAR for ARM32 * Use PC-relative relocations for ARMEmitter.EmitMOV(dest, symbol) * Fat function pointers are not function symbols as far as ELF is concerned; the should not get the symbol size or the Thumb bit * Fix some bits and pieces of the ARM unwinding code * Don't try to use ObjWriter package on unsupported platforms * Generate valid ARM32 DWARF unwind info in JIT * Handle negative offsets in CFI_REL_OFFSET conversion (unused at the moment) * Add linux-arm support to cross-compile targets * Update src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc Co-authored-by: Jan Kotas * Update ObjectWriter.cs Co-authored-by: Michal Strehovský * Fix the order of register push in CFI unwind codes on ARM. * Make jit-format happy without making the code look ugly --------- Co-authored-by: Jan Kotas Co-authored-by: Michal Strehovský --- src/coreclr/jit/unwind.cpp | 33 ++--- src/coreclr/jit/unwindarmarch.cpp | 4 - .../Microsoft.NETCore.Native.Unix.targets | 6 +- .../Runtime/unix/UnixNativeCodeManager.cpp | 4 - .../nativeaot/Runtime/unix/UnwindHelpers.cpp | 140 ++++++++++-------- .../Runtime/unix/unixasmmacrosarm.inc | 17 ++- .../DependencyAnalysis/ObjectDataBuilder.cs | 1 + .../Compiler/DependencyAnalysis/Relocation.cs | 20 +-- .../Target_ARM/ARMEmitter.cs | 11 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 - .../Compiler/ObjectWriter/Dwarf/DwarfCie.cs | 13 ++ .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 12 +- .../Compiler/ObjectWriter/ElfNative.cs | 2 +- .../Compiler/ObjectWriter/ElfObjectWriter.cs | 97 +++++++----- .../Compiler/ObjectWriter/ObjectWriter.cs | 2 +- .../tools/aot/ILCompiler/ILCompiler.props | 2 +- 16 files changed, 210 insertions(+), 156 deletions(-) diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index e035b1188a22f..7151aeaa8638b 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -165,17 +165,11 @@ void Compiler::unwindPushPopCFI(regNumber reg) #endif ; + createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES); if (relOffsetMask & genRegMask(reg)) { -#ifndef TARGET_ARM - createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES); -#endif createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg)); } - else - { - createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES); - } } typedef jitstd::vector CFICodeVector; @@ -203,19 +197,20 @@ void Compiler::unwindBegPrologCFI() void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat) { - regMaskTP regBit = isFloat ? genRegMask(REG_FP_FIRST) : 1; +#if TARGET_ARM + regNumber regNum = isFloat ? REG_PREV(REG_FP_LAST) : REG_INT_LAST; + regMaskTP regBit = isFloat ? genRegMask(regNum) | genRegMask(REG_NEXT(regNum)) : genRegMask(regNum); +#else + regNumber regNum = isFloat ? REG_FP_LAST : REG_INT_LAST; + regMaskTP regBit = genRegMask(regNum); +#endif - regNumber regNum = isFloat ? REG_FP_FIRST : REG_FIRST; - for (; regNum < REG_COUNT;) + for (; regMask != 0 && regBit != RBM_NONE;) { - if (regBit > regMask) - { - break; - } - if (regBit & regMask) { unwindPushPopCFI(regNum); + regMask &= ~regBit; } #if TARGET_ARM @@ -224,11 +219,11 @@ void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat) // because LLVM only know about D0-D31. // As such pairs Sx,Sx+1 are referenced as D0-D15 registers in DWARF // For that we process registers in pairs. - regNum = isFloat ? REG_NEXT(REG_NEXT(regNum)) : REG_NEXT(regNum); - regBit <<= isFloat ? 2 : 1; + regBit >>= isFloat ? 2 : 1; + regNum = isFloat ? REG_PREV(REG_PREV(regNum)) : REG_PREV(regNum); #else - regNum = REG_NEXT(regNum); - regBit <<= 1; + regBit >>= 1; + regNum = REG_PREV(regNum); #endif } } diff --git a/src/coreclr/jit/unwindarmarch.cpp b/src/coreclr/jit/unwindarmarch.cpp index e53917ffda9c2..445b2581ca0ab 100644 --- a/src/coreclr/jit/unwindarmarch.cpp +++ b/src/coreclr/jit/unwindarmarch.cpp @@ -329,10 +329,6 @@ void Compiler::unwindPushMaskInt(regMaskTP maskInt) if (generateCFIUnwindCodes()) { // If we are pushing LR, we should give unwind codes in terms of caller's PC - if (maskInt & RBM_LR) - { - maskInt = (maskInt & ~RBM_LR) | RBM_PC; - } unwindPushPopMaskCFI(maskInt, false); return; } diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index ec91635d8fb1d..19485ec753df5 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -39,9 +39,13 @@ The .NET Foundation licenses this file to you under the MIT license. x86_64 aarch64 arm64 + arm + + gnu + gnueabihf - $(CrossCompileArch)-linux-gnu + $(CrossCompileArch)-linux-$(CrossCompileAbi) $(CrossCompileArch)-alpine-linux-musl $(CrossCompileArch)-linux-android21 $(CrossCompileArch)-unknown-freebsd12 diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 68f58f77b4119..9407499e329ca 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -99,10 +99,6 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, pMethodInfo->unwind_info = procInfo.unwind_info; uintptr_t lsda = procInfo.lsda; -#if defined(HOST_ARM) - // libunwind fills by reference not by value for ARM - lsda = *((uintptr_t *)lsda); -#endif PTR_UInt8 p = dac_cast(lsda); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 120cb68d1811f..6fd0df0c63546 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -339,136 +339,155 @@ struct Registers_REGDISPLAY : REGDISPLAY #endif // TARGET_X86 #if defined(TARGET_ARM) -class Registers_arm_rt: public libunwind::Registers_arm { -public: - Registers_arm_rt() { abort(); }; - Registers_arm_rt(void *registers) { regs = (REGDISPLAY *)registers; }; - uint32_t getRegister(int num); +struct Registers_REGDISPLAY : REGDISPLAY +{ + inline static int getArch() { return libunwind::REGISTERS_ARM; } + inline static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; } + + bool validRegister(int num) const; + bool validFloatRegister(int num) { return false; }; + bool validVectorRegister(int num) const { return false; }; + + uint32_t getRegister(int num) const; void setRegister(int num, uint32_t value, uint32_t location); - uint32_t getRegisterLocation(int regNum) const { abort();} - unw_fpreg_t getFloatRegister(int num) { abort();} - void setFloatRegister(int num, unw_fpreg_t value) {abort();} - bool validVectorRegister(int num) const { abort();} - uint32_t getVectorRegister(int num) const {abort();}; - void setVectorRegister(int num, uint32_t value) {abort();}; - void jumpto() { abort();}; - uint32_t getSP() const { return regs->SP;} - void setSP(uint32_t value, uint32_t location) { regs->SP = value;} - uint32_t getIP() const { return regs->IP;} + + double getFloatRegister(int num) const {abort();} + void setFloatRegister(int num, double value) {abort();} + + libunwind::v128 getVectorRegister(int num) const {abort();} + void setVectorRegister(int num, libunwind::v128 value) {abort();} + + uint32_t getSP() const { return SP;} + void setSP(uint32_t value, uint32_t location) { SP = value;} + uint32_t getIP() const { return IP;} void setIP(uint32_t value, uint32_t location) - { regs->IP = value; regs->pIP = (PTR_UIntNative)location; } - void saveVFPAsX() {abort();}; -private: - REGDISPLAY *regs; + { IP = value; pIP = (PTR_UIntNative)location; } + uint32_t getFP() const { return *pR11;} + void setFP(uint32_t value, uint32_t location) { pR11 = (PTR_UIntNative)location;} }; -inline uint32_t Registers_arm_rt::getRegister(int regNum) { +inline bool Registers_REGDISPLAY::validRegister(int num) const { + if (num == UNW_REG_SP || num == UNW_ARM_SP) + return true; + + if (num == UNW_ARM_LR) + return true; + + if (num == UNW_REG_IP || num == UNW_ARM_IP) + return true; + + if (num >= UNW_ARM_R0 && num <= UNW_ARM_R12) + return true; + + return false; +} + +inline uint32_t Registers_REGDISPLAY::getRegister(int regNum) const { if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) - return regs->SP; + return SP; if (regNum == UNW_ARM_LR) - return *regs->pLR; + return *pLR; if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) - return regs->IP; + return IP; switch (regNum) { case (UNW_ARM_R0): - return *regs->pR0; + return *pR0; case (UNW_ARM_R1): - return *regs->pR1; + return *pR1; case (UNW_ARM_R2): - return *regs->pR2; + return *pR2; case (UNW_ARM_R3): - return *regs->pR3; + return *pR3; case (UNW_ARM_R4): - return *regs->pR4; + return *pR4; case (UNW_ARM_R5): - return *regs->pR5; + return *pR5; case (UNW_ARM_R6): - return *regs->pR6; + return *pR6; case (UNW_ARM_R7): - return *regs->pR7; + return *pR7; case (UNW_ARM_R8): - return *regs->pR8; + return *pR8; case (UNW_ARM_R9): - return *regs->pR9; + return *pR9; case (UNW_ARM_R10): - return *regs->pR10; + return *pR10; case (UNW_ARM_R11): - return *regs->pR11; + return *pR11; case (UNW_ARM_R12): - return *regs->pR12; + return *pR12; } PORTABILITY_ASSERT("unsupported arm register"); } -void Registers_arm_rt::setRegister(int num, uint32_t value, uint32_t location) +void Registers_REGDISPLAY::setRegister(int num, uint32_t value, uint32_t location) { - if (num == UNW_REG_SP || num == UNW_ARM_SP) { - regs->SP = (uintptr_t )value; + SP = (uintptr_t )value; return; } if (num == UNW_ARM_LR) { - regs->pLR = (PTR_UIntNative)location; + pLR = (PTR_UIntNative)location; return; } if (num == UNW_REG_IP || num == UNW_ARM_IP) { - regs->IP = value; + IP = value; /* the location could be NULL, we could try to recover pointer to value in stack from pLR */ - if ((!location) && (regs->pLR) && (*regs->pLR == value)) - regs->pIP = regs->pLR; + if ((!location) && pLR && (*pLR == value)) + pIP = pLR; else - regs->pIP = (PTR_UIntNative)location; + pIP = (PTR_UIntNative)location; return; } switch (num) { case (UNW_ARM_R0): - regs->pR0 = (PTR_UIntNative)location; + pR0 = (PTR_UIntNative)location; break; case (UNW_ARM_R1): - regs->pR1 = (PTR_UIntNative)location; + pR1 = (PTR_UIntNative)location; break; case (UNW_ARM_R2): - regs->pR2 = (PTR_UIntNative)location; + pR2 = (PTR_UIntNative)location; break; case (UNW_ARM_R3): - regs->pR3 = (PTR_UIntNative)location; + pR3 = (PTR_UIntNative)location; break; case (UNW_ARM_R4): - regs->pR4 = (PTR_UIntNative)location; + pR4 = (PTR_UIntNative)location; break; case (UNW_ARM_R5): - regs->pR5 = (PTR_UIntNative)location; + pR5 = (PTR_UIntNative)location; break; case (UNW_ARM_R6): - regs->pR6 = (PTR_UIntNative)location; + pR6 = (PTR_UIntNative)location; break; case (UNW_ARM_R7): - regs->pR7 = (PTR_UIntNative)location; + pR7 = (PTR_UIntNative)location; break; case (UNW_ARM_R8): - regs->pR8 = (PTR_UIntNative)location; + pR8 = (PTR_UIntNative)location; break; case (UNW_ARM_R9): - regs->pR9 = (PTR_UIntNative)location; + pR9 = (PTR_UIntNative)location; break; case (UNW_ARM_R10): - regs->pR10 = (PTR_UIntNative)location; + pR10 = (PTR_UIntNative)location; break; case (UNW_ARM_R11): - regs->pR11 = (PTR_UIntNative)location; + pR11 = (PTR_UIntNative)location; break; case (UNW_ARM_R12): - regs->pR12 = (PTR_UIntNative)location; + pR12 = (PTR_UIntNative)location; break; default: PORTABILITY_ASSERT("unsupported arm register"); @@ -788,13 +807,8 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t fo uintptr_t pc = regs->GetIP(); bool isSignalFrame = false; -#if defined(TARGET_ARM) - DwarfInstructions dwarfInst; - int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, unwind_info, *(Registers_arm_rt*)regs, isSignalFrame, /* stage2 */ false); -#else DwarfInstructions dwarfInst; int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, unwind_info, *(Registers_REGDISPLAY*)regs, isSignalFrame, /* stage2 */ false); -#endif if (stepRet != UNW_STEP_SUCCESS) { @@ -819,7 +833,7 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio #if defined(TARGET_AMD64) libunwind::UnwindCursor uc(_addressSpace); #elif defined(TARGET_ARM) - libunwind::UnwindCursor uc(_addressSpace); + libunwind::UnwindCursor uc(_addressSpace); #elif defined(TARGET_ARM64) libunwind::UnwindCursor uc(_addressSpace); #elif defined(HOST_X86) diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc index fbfc6c84ff098..9f1d17a46188a 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc @@ -259,13 +259,18 @@ C_FUNC(\Name): ldr r0, 2f 1: add r0, pc, r0 - bl __tls_get_addr(PLT) - // push data at the end of text section - .pushsection .text.tlsvar, "aM", %progbits, 4 - .balign 4 + bl __tls_get_addr + b 3f + + // Inline data + // LLVM assembler has no concept of subsections and this is not expressible as + // cross-section relocation. + .p2align 2 2: - .4byte \Var@TLSGD + (. - 1b - 4) - .popsection + .extern \Var + .type \Var, tls_object + .long \Var(TLSGD) + (2b - 1b - 4) +3: .endm .macro INLINE_GETTHREAD diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index 19f367e82518c..1181b642a7df7 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -306,6 +306,7 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: case RelocType.IMAGE_REL_BASED_THUMB_MOV32: + case RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL: case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index bbe1ce26784b8..337811103e35f 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -154,13 +154,13 @@ private static unsafe int GetThumb2BlRel24(ushort* p) ((Opcode1 << 1) & 0x0000FFE); // Sign-extend and return - return (int)((ret << 7) >> 7); + return (int)(ret << 7) >> 7; } //***************************************************************************** // Returns whether the offset fits into bl instruction //***************************************************************************** - private static bool FitsInThumb2BlRel24(uint imm24) + private static bool FitsInThumb2BlRel24(int imm24) { return ((imm24 << 7) >> 7) == imm24; } @@ -168,7 +168,7 @@ private static bool FitsInThumb2BlRel24(uint imm24) //***************************************************************************** // Deposit the 24-bit rel offset into bl instruction //***************************************************************************** - private static unsafe void PutThumb2BlRel24(ushort* p, uint imm24) + private static unsafe void PutThumb2BlRel24(ushort* p, int imm24) { // Verify that we got a valid offset Debug.Assert(FitsInThumb2BlRel24(imm24)); @@ -182,17 +182,17 @@ private static unsafe void PutThumb2BlRel24(ushort* p, uint imm24) Opcode0 &= 0xF800; Opcode1 &= 0xD000; - uint S = (imm24 & 0x1000000) >> 24; - uint J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1; - uint J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1; + uint S = ((uint)imm24 & 0x1000000) >> 24; + uint J1 = (((uint)imm24 & 0x0800000) >> 23) ^ S ^ 1; + uint J2 = (((uint)imm24 & 0x0400000) >> 22) ^ S ^ 1; - Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10); - Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11); + Opcode0 |= (((uint)imm24 & 0x03FF000) >> 12) | (S << 10); + Opcode1 |= (((uint)imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11); p[0] = (ushort)Opcode0; p[1] = (ushort)Opcode1; - Debug.Assert((uint)GetThumb2BlRel24(p) == imm24); + Debug.Assert(GetThumb2BlRel24(p) == imm24); } //***************************************************************************** @@ -485,7 +485,7 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v PutThumb2Mov32((ushort*)location, (uint)value); break; case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: - PutThumb2BlRel24((ushort*)location, (uint)value); + PutThumb2BlRel24((ushort*)location, (int)value); break; case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: PutArm64Rel28((uint*)location, value); diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs index dd41f891552a1..822a351610b9b 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs @@ -139,15 +139,24 @@ public void EmitLDR(Register destination, Register source, int offset) // movw reg, [reloc] & 0x0000FFFF // movt reg, [reloc] & 0xFFFF0000 + // add reg, pc // reg range: [0..12, LR] public void EmitMOV(Register destination, ISymbolNode symbol) { Debug.Assert(destination >= Register.R0 && (destination <= Register.R12 || destination == TargetRegister.LR)); - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_THUMB_MOV32); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL); // 12-byte offset is part of the relocation Builder.EmitShort(unchecked((short)0xf240)); Builder.EmitShort((short)((byte)destination << 8)); Builder.EmitShort(unchecked((short)0xf2c0)); Builder.EmitShort((short)((byte)destination << 8)); + if (destination <= Register.R7) + { + Builder.EmitShort(unchecked((short)(0x4478u + (byte)destination))); + } + else + { + Builder.EmitShort(unchecked((short)(0x44f0u + (byte)destination))); + } } // b.w symbol diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 19faf31a20867..cadc58a701451 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4081,10 +4081,8 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) break; } -#if READYTORUN if (targetArchitecture == TargetArchitecture.ARM && !_compilation.TypeSystemContext.Target.IsWindows) flags.Set(CorJitFlag.CORJIT_FLAG_RELATIVE_CODE_RELOCS); -#endif if (this.MethodBeingCompiled.IsUnmanagedCallersOnly) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs index f0bc413e2c6e7..7497734283314 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs @@ -39,6 +39,19 @@ public DwarfCie(TargetArchitecture targetArchitecture) switch (targetArchitecture) { + case TargetArchitecture.ARM: + CodeAlignFactor = 1; + DataAlignFactor = -4; + ReturnAddressRegister = 14; // LR + Instructions = new byte[] + { + DW_CFA_def_cfa, + 13, // SP + 0, // Offset from SP + }; + InitialCFAOffset = 0; + break; + case TargetArchitecture.ARM64: CodeAlignFactor = 1; DataAlignFactor = -4; 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 ea3bcdc740ec0..b3ea6ac201286 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 @@ -89,16 +89,24 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) break; case CFI_OPCODE.CFI_REL_OFFSET: - if (dwarfReg <= 0x3F) + int absOffset = ((cfiOffset - cfaOffset) / cie.DataAlignFactor); + if (absOffset < 0) + { + cfiCode[cfiCodeOffset++] = DW_CFA_offset_extended_sf; + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)dwarfReg); + cfiCodeOffset += DwarfHelper.WriteSLEB128(cfiCode.AsSpan(cfiCodeOffset), absOffset); + } + else if (dwarfReg <= 0x3F) { cfiCode[cfiCodeOffset++] = (byte)(DW_CFA_offset | (byte)dwarfReg); + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)absOffset); } else { cfiCode[cfiCodeOffset++] = DW_CFA_offset_extended; cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)dwarfReg); + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)absOffset); } - cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)((cfiOffset - cfaOffset) / cie.DataAlignFactor)); break; case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs index b39253a02e854..625bfabef36fe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs @@ -182,7 +182,7 @@ internal static class ElfNative public const uint R_ARM_THM_ABS5 = 7; public const uint R_ARM_ABS8 = 8; public const uint R_ARM_SBREL32 = 9; - public const uint R_ARM_THM_PC22 = 10; + public const uint R_ARM_THM_CALL = 10; public const uint R_ARM_THM_PC8 = 11; public const uint R_ARM_AMP_VCALL9 = 12; public const uint R_ARM_SWI24 = 13; 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 00648d5d7b897..17fa66b7c7a3f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -32,6 +32,7 @@ namespace ILCompiler.ObjectWriter /// internal sealed class ElfObjectWriter : UnixObjectWriter { + private readonly bool _useInlineRelocationAddends; private readonly ushort _machine; private readonly List _sections = new(); private readonly List _symbols = new(); @@ -52,6 +53,7 @@ public ElfObjectWriter(NodeFactory factory, ObjectWritingOptions options) TargetArchitecture.ARM64 => EM_AARCH64, _ => throw new NotSupportedException("Unsupported architecture") }; + _useInlineRelocationAddends = _machine is EM_386 or EM_ARM; // By convention the symbol table starts with empty symbol _symbols.Add(new ElfSymbol {}); @@ -138,6 +140,16 @@ private protected override void CreateSection(ObjectNodeSection section, string }); } + if (_machine is EM_ARM && section.Type is SectionType.Executable) + { + _symbols.Add(new ElfSymbol + { + Section = _sections[sectionIndex], + Info = STT_NOTYPE, + Name = $"$t.{sectionIndex}" + }); + } + base.CreateSection(section, comdatName, symbolName ?? sectionName, sectionStream); } @@ -155,16 +167,41 @@ protected internal override unsafe void EmitRelocation( string symbolName, long addend) { - // We read the addend from the data and clear it. This is necessary - // to produce correct addends in the `.rela` sections which override - // the destination with the addend from relocation table. fixed (byte *pData = data) { - long inlineValue = Relocation.ReadValue(relocType, (void*)pData); - if (inlineValue != 0) + if (relocType is IMAGE_REL_BASED_REL32 && _machine is EM_386 or EM_X86_64) + { + addend -= 4; + } + else if (relocType is IMAGE_REL_BASED_THUMB_BRANCH24) + { + addend -= 4; + } + else if (relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL) + { + addend -= 12; + } + + if (!_useInlineRelocationAddends) { - addend += inlineValue; - Relocation.WriteValue(relocType, (void*)pData, 0); + // We read the addend from the data and clear it. This is necessary + // to produce correct addends in the `.rela` sections which override + // the destination with the addend from relocation table. + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + if (inlineValue != 0) + { + addend += inlineValue; + Relocation.WriteValue(relocType, (void*)pData, 0); + } + } + else + { + if (addend != 0) + { + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + Relocation.WriteValue(relocType, (void*)pData, inlineValue + addend); + addend = 0; + } } } @@ -182,10 +219,11 @@ private protected override void EmitSymbolTable( var type = (section.SectionHeader.Flags & SHF_TLS) == SHF_TLS ? STT_TLS : definition.Size > 0 ? STT_FUNC : STT_NOTYPE; + ulong thumbBit = _machine == EM_ARM && type == STT_FUNC ? 1u : 0u; sortedSymbols.Add(new ElfSymbol { Name = name, - Value = (ulong)definition.Value, + Value = (ulong)definition.Value | thumbBit, Size = (ulong)definition.Size, Section = _sections[definition.SectionIndex], Info = (byte)(type | (STB_GLOBAL << 4)), @@ -246,13 +284,10 @@ private protected override void EmitRelocations(int sectionIndex, List relocationList) { - // TODO: We are emitting .rela sections on x86 which is technically wrong. We should be - // using .rel sections with the addend embedded in the data. Since x86 is not an officially - // supported platform this is left for future enhancement. if (relocationList.Count > 0) { - Span relocationEntry = stackalloc byte[12]; - var relocationStream = new MemoryStream(12 * relocationList.Count); + Span relocationEntry = stackalloc byte[8]; + var relocationStream = new MemoryStream(8 * relocationList.Count); _sections[sectionIndex].RelocationStream = relocationStream; foreach (SymbolicRelocation symbolicRelocation in relocationList) { @@ -267,15 +302,8 @@ private void EmitRelocationsX86(int sectionIndex, List reloc _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; - long addend = symbolicRelocation.Addend; - if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) - { - addend -= 4; - } - BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)symbolicRelocation.Offset); BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | type); - BinaryPrimitives.WriteInt32LittleEndian(relocationEntry.Slice(8), (int)addend); relocationStream.Write(relocationEntry); } } @@ -302,15 +330,9 @@ private void EmitRelocationsX64(int sectionIndex, List reloc _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; - long addend = symbolicRelocation.Addend; - if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) - { - addend -= 4; - } - BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)symbolicRelocation.Offset); BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); - BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), addend); + BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); relocationStream.Write(relocationEntry); } } @@ -320,8 +342,8 @@ private void EmitRelocationsARM(int sectionIndex, List reloc { if (relocationList.Count > 0) { - Span relocationEntry = stackalloc byte[12]; - var relocationStream = new MemoryStream(12 * relocationList.Count); + Span relocationEntry = stackalloc byte[8]; + var relocationStream = new MemoryStream(8 * relocationList.Count); _sections[sectionIndex].RelocationStream = relocationStream; foreach (SymbolicRelocation symbolicRelocation in relocationList) { @@ -333,19 +355,12 @@ private void EmitRelocationsARM(int sectionIndex, List reloc IMAGE_REL_BASED_REL32 => R_ARM_REL32, IMAGE_REL_BASED_THUMB_MOV32 => R_ARM_THM_MOVW_ABS_NC, IMAGE_REL_BASED_THUMB_MOV32_PCREL => R_ARM_THM_MOVW_PREL_NC, - IMAGE_REL_BASED_THUMB_BRANCH24 => R_ARM_THM_PC22, + IMAGE_REL_BASED_THUMB_BRANCH24 => R_ARM_THM_CALL, _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; - long addend = symbolicRelocation.Addend; - if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) - { - addend -= 4; - } - BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)symbolicRelocation.Offset); BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | type); - BinaryPrimitives.WriteInt32LittleEndian(relocationEntry.Slice(8), (int)addend); relocationStream.Write(relocationEntry); if (symbolicRelocation.Type is IMAGE_REL_BASED_THUMB_MOV32 or IMAGE_REL_BASED_THUMB_MOV32_PCREL) @@ -461,7 +476,7 @@ private void EmitObjectFile(FileStream outputFileStream) if (section.RelocationStream != Stream.Null) { - _stringTable.ReserveString(".rela" + section.Name); + _stringTable.ReserveString((_useInlineRelocationAddends ? ".rel" : ".rela") + section.Name); sectionCount++; } @@ -582,8 +597,8 @@ private void EmitObjectFile(FileStream outputFileStream) { ElfSectionHeader relaSectionHeader = new ElfSectionHeader { - NameIndex = _stringTable.GetStringOffset(".rela" + section.Name), - Type = SHT_RELA, + NameIndex = _stringTable.GetStringOffset((_useInlineRelocationAddends ? ".rel" : ".rela") + section.Name), + Type = _useInlineRelocationAddends ? SHT_REL : SHT_RELA, Flags = (section.GroupSection is not null ? SHF_GROUP : 0u) | SHF_INFO_LINK, Address = 0u, Offset = section.SectionHeader.Offset + section.SectionHeader.Size, @@ -591,7 +606,7 @@ private void EmitObjectFile(FileStream outputFileStream) Link = symTabSectionIndex, Info = section.SectionIndex, Alignment = 8u, - EntrySize = (ulong)default(TSize).GetByteCount() * 3u, + EntrySize = (ulong)default(TSize).GetByteCount() * (_useInlineRelocationAddends ? 2u : 3u), }; relaSectionHeader.Write(outputFileStream); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs index 9699c9551a2f1..fde0bb8a53d01 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs @@ -388,7 +388,7 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection true - true + true