Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ private protected override void EmitRelocations(int sectionIndex, List<SymbolicR
IMAGE_REL_BASED_ARM64_BRANCH26 => 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ private void EmitRelocationsARM64(int sectionIndex, List<SymbolicRelocation> 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -629,7 +630,7 @@ private void EmitRelocationsArm64(int sectionIndex, List<SymbolicRelocation> 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)
{
Expand All @@ -649,6 +650,7 @@ private void EmitRelocationsArm64(int sectionIndex, List<SymbolicRelocation> 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
};

Expand All @@ -660,7 +662,7 @@ private void EmitRelocationsArm64(int sectionIndex, List<SymbolicRelocation> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,7 @@ private unsafe void ResolveRelocations(int sectionIndex, List<SymbolicRelocation
break;
}
case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A:
case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L:
if (addend != 0)
{
throw new NotSupportedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ public ImportThunk(NodeFactory factory, ReadyToRunHelper helperId, ImportSection
}

if (_thunkKind != Kind.Eager
&& factory.Target.Architecture is Internal.TypeSystem.TargetArchitecture.ARM64
or Internal.TypeSystem.TargetArchitecture.LoongArch64
&& factory.Target.Architecture is Internal.TypeSystem.TargetArchitecture.LoongArch64
or Internal.TypeSystem.TargetArchitecture.RiscV64)
{
// We stuff the reloc to the module import pointer before the start of the thunk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi
return;
}

instructionEncoder.Builder.RequireInitialPointerAlignment();
Debug.Assert(instructionEncoder.Builder.CountBytes == 0);

instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);

Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset);

if (relocsOnly)
{
// When doing relocs only, we don't need to generate the actual instructions
Expand All @@ -50,21 +43,21 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi
instructionEncoder.EmitMOV(Register.X9, checked((ushort)index));

// Move Module* -> 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:
Expand Down
Loading