Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NativeAOT] 32-bit platform ObjWriter and bit rot fixes #96890

Merged
merged 21 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f4974b7
Use PC-relative relocations for ARMEmitter.EmitMOV(dest, symbol)
filipnavara Jan 12, 2024
0e5eae4
Fix handling of signed offsets in IMAGE_REL_BASED_THUMB_BRANCH24 calc…
filipnavara Jan 12, 2024
47eaebc
Generate position independent code in NativeAOT on linux-arm
filipnavara Jan 12, 2024
24c2a5d
Handle ARM32 in DwarfCie
filipnavara Jan 12, 2024
5340bb6
Fix relocations emitted in 32-bit ELF for x86/arm32; apply correct ad…
filipnavara Jan 12, 2024
1d3e590
ELF/ARM32: Emit thumb mapping symbols for executable sections
filipnavara Jan 12, 2024
35164cc
Try to revert ARMEmitter.EmitMOV
filipnavara Jan 12, 2024
f17709b
Convert IMAGE_REL_BASED_THUMB_MOV32_PCREL to ELF with the same offset…
filipnavara Jan 13, 2024
f880ca6
Unoptimal, but working, version of INLINE_GET_TLS_VAR for ARM32
filipnavara Jan 13, 2024
ad0b3d2
Use PC-relative relocations for ARMEmitter.EmitMOV(dest, symbol)
filipnavara Jan 13, 2024
c7b10be
Fat function pointers are not function symbols as far as ELF is conce…
filipnavara Jan 13, 2024
58d9f8c
Fix some bits and pieces of the ARM unwinding code
filipnavara Jan 13, 2024
0e6ff46
Don't try to use ObjWriter package on unsupported platforms
filipnavara Jan 13, 2024
fca6f2f
Generate valid ARM32 DWARF unwind info in JIT
filipnavara Jan 13, 2024
69e97ab
Handle negative offsets in CFI_REL_OFFSET conversion (unused at the m…
filipnavara Jan 13, 2024
1908816
Add linux-arm support to cross-compile targets
filipnavara Jan 13, 2024
391f070
Update src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc
filipnavara Jan 14, 2024
54fa66c
Update ObjectWriter.cs
filipnavara Jan 15, 2024
a7ec26a
Fix the order of register push in CFI unwind codes on ARM.
filipnavara Jan 15, 2024
78b9eee
Make jit-format happy without making the code look ugly
filipnavara Jan 15, 2024
17ae730
Merge branch 'main' into naot32
jkotas Jan 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/coreclr/jit/unwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,7 @@ void Compiler::unwindPushPopCFI(regNumber reg)

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
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/jit/unwindarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ The .NET Foundation licenses this file to you under the MIT license.
<CrossCompileArch Condition="$(CrossCompileRid.EndsWith('-x64'))">x86_64</CrossCompileArch>
<CrossCompileArch Condition="$(CrossCompileRid.EndsWith('-arm64')) and '$(_IsApplePlatform)' != 'true'">aarch64</CrossCompileArch>
<CrossCompileArch Condition="$(CrossCompileRid.EndsWith('-arm64')) and '$(_IsApplePlatform)' == 'true'">arm64</CrossCompileArch>
<CrossCompileArch Condition="$(CrossCompileRid.EndsWith('-arm'))">arm</CrossCompileArch>

<CrossCompileAbi>gnu</CrossCompileAbi>
<CrossCompileAbi Condition="$(CrossCompileRid.EndsWith('-arm'))">gnueabihf</CrossCompileAbi>

<TargetTriple />
<TargetTriple Condition="'$(CrossCompileArch)' != ''">$(CrossCompileArch)-linux-gnu</TargetTriple>
<TargetTriple Condition="'$(CrossCompileArch)' != ''">$(CrossCompileArch)-linux-$(CrossCompileAbi)</TargetTriple>
Comment on lines +42 to +48
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@am11 care to review this part?

Copy link
Member

@am11 am11 Jan 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good and neat. Other related/interesting triplets are:

arm-linux-androideabi
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
arm-unknown-linux-musleabi
arm-unknown-linux-musleabihf
armv7-unknown-linux-gnueabi
armv7-unknown-linux-gnueabihf
armv7-unknown-linux-musleabi
armv7-unknown-linux-musleabihf

of which, maybe <CrossCompileAbi Condition="'$(CrossCompileRid)' == 'linux-musl-arm'">musleabihf</CrossCompileAbi> could be added? We have prereq docker image available for it as well.


Side note:
AFAIK, triplet is considered an archaic concept by some toolchain enthusiasts. They recommend using explicit -march, -mtune, -mabi, -mcpu and -mfpu options. At some point we can delve into that and bring nativeaot and mono targets to new age. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I'll have some follow up PRs later on, so I can add the "musl" then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, take your time. Basically the next line (49) can be deleted after that, since alpine is unnecessary and more restrictive than unknown or not setting the vendor at all; which we have for gnu and bionic.

<TargetTriple Condition="'$(CrossCompileArch)' != '' and ($(CrossCompileRid.StartsWith('linux-musl')) or $(CrossCompileRid.StartsWith('alpine')))">$(CrossCompileArch)-alpine-linux-musl</TargetTriple>
<TargetTriple Condition="'$(CrossCompileArch)' != '' and $(CrossCompileRid.StartsWith('linux-bionic'))">$(CrossCompileArch)-linux-android21</TargetTriple>
<TargetTriple Condition="'$(CrossCompileArch)' != '' and ($(CrossCompileRid.StartsWith('freebsd')))">$(CrossCompileArch)-unknown-freebsd12</TargetTriple>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<PTR_UInt8>(lsda);

Expand Down
140 changes: 77 additions & 63 deletions src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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<LocalAddressSpace, Registers_arm_rt> dwarfInst;
int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, unwind_info, *(Registers_arm_rt*)regs, isSignalFrame, /* stage2 */ false);
#else
DwarfInstructions<LocalAddressSpace, Registers_REGDISPLAY> dwarfInst;
int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, unwind_info, *(Registers_REGDISPLAY*)regs, isSignalFrame, /* stage2 */ false);
#endif

if (stepRet != UNW_STEP_SUCCESS)
{
Expand All @@ -819,7 +833,7 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio
#if defined(TARGET_AMD64)
libunwind::UnwindCursor<LocalAddressSpace, Registers_x86_64> uc(_addressSpace);
#elif defined(TARGET_ARM)
libunwind::UnwindCursor<LocalAddressSpace, Registers_arm_rt> uc(_addressSpace);
libunwind::UnwindCursor<LocalAddressSpace, Registers_arm> uc(_addressSpace);
#elif defined(TARGET_ARM64)
libunwind::UnwindCursor<LocalAddressSpace, Registers_arm64> uc(_addressSpace);
#elif defined(HOST_X86)
Expand Down
17 changes: 11 additions & 6 deletions src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,21 +154,21 @@ 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;
}

//*****************************************************************************
// 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));
Expand All @@ -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);
}

//*****************************************************************************
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
Comment on lines +152 to +159
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review note: This affects R2R. It generates the same pattern as the JIT but it's one extra instruction. If you feel like we need to special-case it, I'll try to do it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is fine. This pattern is better for private working set.

}

// b.w symbol
Expand Down
Loading