diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 7cff8eef4b5ecb..2be41335d6b152 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -323,6 +323,49 @@ private static unsafe void PutArm64TlsRel12(uint* pCode, int imm12) Debug.Assert(GetArm64Rel12(pCode) == imm12); } + //***************************************************************************** + // Extract the 12-bit page offset from an LDR instruction (unsigned immediate) + // For 64-bit LDR, the immediate is scaled by 8 bytes + //***************************************************************************** + private static unsafe int GetArm64Rel12Ldr(uint* pCode) + { + uint ldrInstr = *pCode; + + // 0x003FFC00: Mask for bits 21-10 of the 32-bit ARM64 LDR instruction + // which contain the scaled immediate value + int scaledImm12 = (int)(ldrInstr & 0x003FFC00) >> 10; + + // Scale back to byte offset (multiply by 8) + return scaledImm12 << 3; + } + + //***************************************************************************** + // Deposit the 12-bit page offset 'imm12' into an LDR instruction (unsigned immediate) + // For 64-bit LDR, the immediate represents offset/8 (scaled by 8 bytes) + //***************************************************************************** + private static unsafe void PutArm64Rel12Ldr(uint* pCode, int imm12) + { + // Verify that we got a valid offset and that it's aligned to 8 bytes + Debug.Assert(FitsInRel12(imm12)); + Debug.Assert((imm12 & 7) == 0, "LDR offset must be 8-byte aligned"); + + uint ldrInstr = *pCode; + // Check ldr opcode: 0b11111001010000000000000000000000 (LDR 64-bit register, unsigned immediate) + Debug.Assert((ldrInstr & 0xFFC00000) == 0xF9400000); + + // Scale the offset by dividing by 8 for the instruction encoding + int scaledImm12 = imm12 >> 3; + + // 0xFFC003FF: Mask to preserve bits 31-22 (opcode) and bits 9-0 (registers) + // Clear bits 21-10 which will hold the scaled immediate value + ldrInstr &= 0xFFC003FF; + ldrInstr |= (uint)(scaledImm12 << 10); // Set bits 21-10 with scaled offset + + *pCode = ldrInstr; // write the assembled instruction + + Debug.Assert(GetArm64Rel12Ldr(pCode) == imm12); + } + private static unsafe int GetArm64Rel28(uint* pCode) { uint branchInstr = *pCode; @@ -548,6 +591,9 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_ARM64_TLS_SECREL_LOW12A: PutArm64Rel12((uint*)location, (int)value); break; + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: + PutArm64Rel12Ldr((uint*)location, (int)value); + break; case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: PutLoongArch64PC12((uint*)location, value); break; @@ -578,6 +624,7 @@ public static int GetSize(RelocType relocType) // a span of this many bytes. RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => 4, RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => 4, + RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => 4, RelocType.IMAGE_REL_BASED_THUMB_MOV32 => 8, RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL => 8, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 16, @@ -617,6 +664,8 @@ public static unsafe long ReadValue(RelocType relocType, void* location) case RelocType.IMAGE_REL_ARM64_TLS_SECREL_HIGH12A: case RelocType.IMAGE_REL_ARM64_TLS_SECREL_LOW12A: return GetArm64Rel12((uint*)location); + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: + return GetArm64Rel12Ldr((uint*)location); case RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12: case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12: case RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12: diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs index d1bd8b917f1f82..39f733d3804c91 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs @@ -49,6 +49,14 @@ public void EmitMOV(Register regDst, ISymbolNode symbol) Builder.EmitUInt((uint)(0b1_0_0_100010_0_000000000000_00000_00000 | ((byte)regDst << 5) | (byte)regDst)); } + // adrp regDst, symbol + public void EmitADRP(Register regDst, ISymbolNode symbol) + { + Debug.Assert((uint)regDst <= 0x1f); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21); + Builder.EmitUInt(0x90000000u | (uint)regDst); + } + // ldr regDst, [PC + imm19] public void EmitLDR(Register regDst, short offset) { @@ -90,6 +98,15 @@ public void EmitLDR(Register regDst, Register regSrc, int offset) } } + // ldr regDst, [regAddr, symbol page offset] + public void EmitLDR(Register regDst, Register regAddr, ISymbolNode symbol) + { + Debug.Assert((uint)regDst <= 0x1f); + Debug.Assert((uint)regAddr <= 0x1f); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L); + Builder.EmitUInt(0xf9400000 | ((uint)regAddr << 5) | (uint)regDst); + } + // ldar regDst, [regAddr] public void EmitLDAR(Register regDst, Register regAddr) { @@ -150,24 +167,15 @@ public void EmitJMP(ISymbolNode symbol) { if (symbol.RepresentsIndirectionCell) { - Builder.RequireInitialPointerAlignment(); + // Use ADRP/LDR pair to load the indirection cell address + // adrp x12, symbol + EmitADRP(Register.X12, symbol); - if (Builder.CountBytes % Builder.TargetPointerSize == 0) - { - // Emit a NOP instruction to align the 64-bit reloc below. - EmitNOP(); - } - - // ldr x12, [PC+0xc] - EmitLDR(Register.X12, 0xc); - - // ldr x12, [x12] - EmitLDR(Register.X12, Register.X12); + // ldr x12, [x12, symbol page offset] + EmitLDR(Register.X12, Register.X12, symbol); // br x12 EmitJMP(Register.X12); - - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); } else { diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs index 74e999317eec9e..11bc8b65729570 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs @@ -356,6 +356,7 @@ private protected override void EmitRelocations(int sectionIndex, List IMAGE_REL_ARM64_BRANCH26, IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => IMAGE_REL_ARM64_PAGEBASE_REL21, IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => IMAGE_REL_ARM64_PAGEOFFSET_12A, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => IMAGE_REL_ARM64_PAGEOFFSET_12L, IMAGE_REL_ARM64_TLS_SECREL_HIGH12A => IMAGE_REL_ARM64_SECREL_HIGH12A, IMAGE_REL_ARM64_TLS_SECREL_LOW12A => IMAGE_REL_ARM64_SECREL_LOW12A, IMAGE_REL_SECREL => IMAGE_REL_ARM64_SECREL, diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs index d2c3b99efbd77b..c9ffd85b08f785 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -487,6 +487,7 @@ private void EmitRelocationsARM64(int sectionIndex, List rel IMAGE_REL_BASED_ARM64_BRANCH26 => R_AARCH64_CALL26, IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => R_AARCH64_ADR_PREL_PG_HI21, IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => R_AARCH64_ADD_ABS_LO12_NC, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => R_AARCH64_LDST64_ABS_LO12_NC, IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12 => R_AARCH64_TLSLE_ADD_TPREL_HI12, IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC => R_AARCH64_TLSLE_ADD_TPREL_LO12_NC, IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 => R_AARCH64_TLSDESC_ADR_PAGE21, diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index fec3165308290d..d826a0a6248142 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -417,6 +417,7 @@ protected internal override unsafe void EmitRelocation( { case IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: case IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + case IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: // Addend is handled through ARM64_RELOC_ADDEND break; @@ -629,7 +630,7 @@ private void EmitRelocationsArm64(int sectionIndex, List rel IsPCRelative = true, }); } - else if (symbolicRelocation.Type is IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A) + else if (symbolicRelocation.Type is IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L) { if (symbolicRelocation.Addend != 0) { @@ -649,6 +650,7 @@ private void EmitRelocationsArm64(int sectionIndex, List rel { IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => ARM64_RELOC_PAGE21, IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => ARM64_RELOC_PAGEOFF12, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L => ARM64_RELOC_PAGEOFF12, _ => 0 }; @@ -660,7 +662,7 @@ private void EmitRelocationsArm64(int sectionIndex, List rel Length = 4, RelocationType = type, IsExternal = true, - IsPCRelative = symbolicRelocation.Type != IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A, + IsPCRelative = symbolicRelocation.Type == IMAGE_REL_BASED_ARM64_PAGEBASE_REL21, }); } else if (symbolicRelocation.Type == IMAGE_REL_BASED_DIR64) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 6e28b753d2ce88..c9cd75cd327cde 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -874,6 +874,7 @@ private unsafe void ResolveRelocations(int sectionIndex, List x10 - // ldr x10, [PC-0xc] - instructionEncoder.EmitLDR(Register.X10, -0xc); + // adrp x10, ModuleImport + instructionEncoder.EmitADRP(Register.X10, factory.ModuleImport); - // ldr x10, [x10] - instructionEncoder.EmitLDR(Register.X10, Register.X10); + // ldr x10, [x10, ModuleImport page offset] + instructionEncoder.EmitLDR(Register.X10, Register.X10, factory.ModuleImport); break; case Kind.Lazy: // Move Module* -> x1 - // ldr x1, [PC-0x8] - instructionEncoder.EmitLDR(Register.X1, -0x8); + // adrp x1, ModuleImport + instructionEncoder.EmitADRP(Register.X1, factory.ModuleImport); - // ldr x1, [x1] - instructionEncoder.EmitLDR(Register.X1, Register.X1); + // ldr x1, [x1, ModuleImport page offset] + instructionEncoder.EmitLDR(Register.X1, Register.X1, factory.ModuleImport); break; default: