[llvm] 142c89c - [JITLink][AArch32] Implement ELF::R_ARM_CALL relocation
Stefan Gränitz via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 13 08:17:28 PDT 2023
Author: Eymen Ünay
Date: 2023-09-13T17:16:29+02:00
New Revision: 142c89c381bccba1c688a90d7e9df91eaf78d4e3
URL: https://github.com/llvm/llvm-project/commit/142c89c381bccba1c688a90d7e9df91eaf78d4e3
DIFF: https://github.com/llvm/llvm-project/commit/142c89c381bccba1c688a90d7e9df91eaf78d4e3.diff
LOG: [JITLink][AArch32] Implement ELF::R_ARM_CALL relocation
- Added WritableArmRelocation and ArmRelocation Structs
- Encode/Decode funcs for B/BL A1 and BLX A2 encodings
- Add ARM helper functions, consistent with the existing Thumb helper functions
- Add Test for ELF::R_ARM_CALL
Reviewed By: sgraenitz
Differential Revision: https://reviews.llvm.org/D157533
Added:
llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
Modified:
llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index d1d54c0ca633362..d33b422745c2d9d 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -48,7 +48,8 @@ enum EdgeKind_aarch32 : Edge::Kind {
///
FirstArmRelocation,
- /// TODO: Arm_Call is here only as a placeholder for now.
+ /// Write immediate value for PC-relative branch with link (can bridge between
+ /// Arm and Thumb).
Arm_Call = FirstArmRelocation,
LastArmRelocation = Arm_Call,
@@ -148,6 +149,16 @@ struct HalfWords {
///
template <EdgeKind_aarch32 Kind> struct FixupInfo {};
+template <> struct FixupInfo<Arm_Call> {
+ static constexpr uint32_t Opcode = 0x0a000000;
+ static constexpr uint32_t OpcodeMask = 0x0e000000;
+ static constexpr uint32_t ImmMask = 0x00ffffff;
+ static constexpr uint32_t Unconditional = 0xe0000000;
+ static constexpr uint32_t CondMask = 0xe0000000; // excluding BLX bit
+ static constexpr uint32_t BitH = 0x01000000;
+ static constexpr uint32_t BitBlx = 0x10000000;
+};
+
template <> struct FixupInfo<Thumb_Jump24> {
static constexpr HalfWords Opcode{0xf000, 0x8000};
static constexpr HalfWords OpcodeMask{0xf800, 0x8000};
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index 01c9e8b4fd9d18e..ac91d0bd6e6724f 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -83,6 +83,24 @@ int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo) {
return SignExtend64<25>(S << 14 | I1 | I2 | Imm10 << 12 | Imm11 << 1);
}
+/// Encode 26-bit immediate value for branch instructions
+/// (formats B A1, BL A1 and BLX A2).
+///
+/// Imm24:00 -> 00000000:Imm24
+///
+uint32_t encodeImmBA1BlA1BlxA2(int64_t Value) {
+ return (Value >> 2) & 0x00ffffff;
+}
+
+/// Decode 26-bit immediate value for branch instructions
+/// (formats B A1, BL A1 and BLX A2).
+///
+/// 00000000:Imm24 -> Imm24:00
+///
+int64_t decodeImmBA1BlA1BlxA2(int64_t Value) {
+ return SignExtend64<26>((Value & 0x00ffffff) << 2);
+}
+
/// Encode 16-bit immediate value for move instruction formats MOVT T1 and
/// MOVW T3.
///
@@ -156,6 +174,23 @@ struct ThumbRelocation {
const support::ulittle16_t &Lo; // Second halfword
};
+struct WritableArmRelocation {
+ WritableArmRelocation(char *FixupPtr)
+ : Wd{*reinterpret_cast<support::ulittle32_t *>(FixupPtr)} {}
+
+ support::ulittle32_t &Wd;
+};
+
+struct ArmRelocation {
+
+ ArmRelocation(const char *FixupPtr)
+ : Wd{*reinterpret_cast<const support::ulittle32_t *>(FixupPtr)} {}
+
+ ArmRelocation(WritableArmRelocation &Writable) : Wd{Writable.Wd} {}
+
+ const support::ulittle32_t &Wd;
+};
+
Error makeUnexpectedOpcodeError(const LinkGraph &G, const ThumbRelocation &R,
Edge::Kind Kind) {
return make_error<JITLinkError>(
@@ -164,12 +199,24 @@ Error makeUnexpectedOpcodeError(const LinkGraph &G, const ThumbRelocation &R,
G.getEdgeKindName(Kind)));
}
+Error makeUnexpectedOpcodeError(const LinkGraph &G, const ArmRelocation &R,
+ Edge::Kind Kind) {
+ return make_error<JITLinkError>(
+ formatv("Invalid opcode [ 0x{0:x8} ] for relocation: {1}",
+ static_cast<uint32_t>(R.Wd), G.getEdgeKindName(Kind)));
+}
+
template <EdgeKind_aarch32 Kind> bool checkOpcode(const ThumbRelocation &R) {
uint16_t Hi = R.Hi & FixupInfo<Kind>::OpcodeMask.Hi;
uint16_t Lo = R.Lo & FixupInfo<Kind>::OpcodeMask.Lo;
return Hi == FixupInfo<Kind>::Opcode.Hi && Lo == FixupInfo<Kind>::Opcode.Lo;
}
+template <EdgeKind_aarch32 Kind> bool checkOpcode(const ArmRelocation &R) {
+ uint32_t Wd = R.Wd & FixupInfo<Kind>::OpcodeMask;
+ return Wd == FixupInfo<Kind>::Opcode;
+}
+
template <EdgeKind_aarch32 Kind>
bool checkRegister(const ThumbRelocation &R, HalfWords Reg) {
uint16_t Hi = R.Hi & FixupInfo<Kind>::RegMask.Hi;
@@ -177,6 +224,12 @@ bool checkRegister(const ThumbRelocation &R, HalfWords Reg) {
return Hi == Reg.Hi && Lo == Reg.Lo;
}
+template <EdgeKind_aarch32 Kind>
+bool checkRegister(const ArmRelocation &R, uint32_t Reg) {
+ uint32_t Wd = R.Wd & FixupInfo<Kind>::RegMask;
+ return Wd == Reg;
+}
+
template <EdgeKind_aarch32 Kind>
void writeRegister(WritableThumbRelocation &R, HalfWords Reg) {
static constexpr HalfWords Mask = FixupInfo<Kind>::RegMask;
@@ -186,6 +239,13 @@ void writeRegister(WritableThumbRelocation &R, HalfWords Reg) {
R.Lo = (R.Lo & ~Mask.Lo) | Reg.Lo;
}
+template <EdgeKind_aarch32 Kind>
+void writeRegister(WritableArmRelocation &R, uint32_t Reg) {
+ static constexpr uint32_t Mask = FixupInfo<Kind>::RegMask;
+ assert((Mask & Reg) == Reg && "Value bits exceed bit range of given mask");
+ R.Wd = (R.Wd & ~Mask) | Reg;
+}
+
template <EdgeKind_aarch32 Kind>
void writeImmediate(WritableThumbRelocation &R, HalfWords Imm) {
static constexpr HalfWords Mask = FixupInfo<Kind>::ImmMask;
@@ -195,6 +255,13 @@ void writeImmediate(WritableThumbRelocation &R, HalfWords Imm) {
R.Lo = (R.Lo & ~Mask.Lo) | Imm.Lo;
}
+template <EdgeKind_aarch32 Kind>
+void writeImmediate(WritableArmRelocation &R, uint32_t Imm) {
+ static constexpr uint32_t Mask = FixupInfo<Kind>::ImmMask;
+ assert((Mask & Imm) == Imm && "Value bits exceed bit range of given mask");
+ R.Wd = (R.Wd & ~Mask) | Imm;
+}
+
Expected<int64_t> readAddendData(LinkGraph &G, Block &B, const Edge &E) {
support::endianness Endian = G.getEndianness();
assert(Endian != support::native && "Declare as little or big explicitly");
@@ -216,13 +283,15 @@ Expected<int64_t> readAddendData(LinkGraph &G, Block &B, const Edge &E) {
}
Expected<int64_t> readAddendArm(LinkGraph &G, Block &B, const Edge &E) {
+ ArmRelocation R(B.getContent().data() + E.getOffset());
Edge::Kind Kind = E.getKind();
switch (Kind) {
case Arm_Call:
- return make_error<JITLinkError>(
- "Addend extraction for relocation type not yet implemented: " +
- StringRef(G.getEdgeKindName(Kind)));
+ if (!checkOpcode<Arm_Call>(R))
+ return makeUnexpectedOpcodeError(G, R, Kind);
+ return decodeImmBA1BlA1BlxA2(R.Wd);
+
default:
return make_error<JITLinkError>(
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -292,7 +361,6 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
int64_t Addend = E.getAddend();
Symbol &TargetSymbol = E.getTarget();
uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
- assert(!hasTargetFlags(TargetSymbol, ThumbSymbol));
// Regular data relocations have size 4, alignment 1 and write the full 32-bit
// result to the place; no need for overflow checking. There are three
@@ -321,13 +389,52 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
}
Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E) {
+ WritableArmRelocation R(B.getAlreadyMutableContent().data() + E.getOffset());
Edge::Kind Kind = E.getKind();
+ uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
+ int64_t Addend = E.getAddend();
+ Symbol &TargetSymbol = E.getTarget();
+ uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
+ if (hasTargetFlags(TargetSymbol, ThumbSymbol))
+ TargetAddress |= 0x01;
switch (Kind) {
- case Arm_Call:
- return make_error<JITLinkError>(
- "Fix-up for relocation type not yet implemented: " +
- StringRef(G.getEdgeKindName(Kind)));
+ case Arm_Call: {
+ if (!checkOpcode<Arm_Call>(R))
+ return makeUnexpectedOpcodeError(G, R, Kind);
+
+ if ((R.Wd & FixupInfo<Arm_Call>::CondMask) !=
+ FixupInfo<Arm_Call>::Unconditional)
+ return make_error<JITLinkError>("Relocation expects an unconditional "
+ "BL/BLX branch instruction: " +
+ StringRef(G.getEdgeKindName(Kind)));
+
+ int64_t Value = TargetAddress - FixupAddress + Addend;
+
+ // The call instruction itself is Arm. The call destination can either be
+ // Thumb or Arm. We use BL to stay in Arm and BLX to change to Thumb.
+ bool TargetIsThumb = hasTargetFlags(TargetSymbol, ThumbSymbol);
+ bool InstrIsBlx = (~R.Wd & FixupInfo<Arm_Call>::BitBlx) == 0;
+ if (TargetIsThumb != InstrIsBlx) {
+ if (LLVM_LIKELY(TargetIsThumb)) {
+ // Change opcode BL -> BLX and fix range value
+ R.Wd = R.Wd | FixupInfo<Arm_Call>::BitBlx;
+ R.Wd = R.Wd & ~FixupInfo<Arm_Call>::BitH;
+ // Set Thumb bit
+ Value |= 0x01;
+ } else {
+ // Change opcode BLX -> BL
+ R.Wd = R.Wd & ~FixupInfo<Arm_Call>::BitBlx;
+ }
+ }
+
+ if (!isInt<26>(Value))
+ return makeTargetOutOfRangeError(G, B, E);
+ writeImmediate<Arm_Call>(R, encodeImmBA1BlA1BlxA2(Value));
+
+ return Error::success();
+ }
+
default:
return make_error<JITLinkError>(
"In graph " + G.getName() + ", section " + B.getSection().getName() +
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
new file mode 100644
index 000000000000000..30bba4c09e75046
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
@@ -0,0 +1,39 @@
+# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t.o %s
+# RUN: llvm-objdump -r %t.o | FileCheck --check-prefix=CHECK-TYPE %s
+# RUN: llvm-objdump --disassemble %t.o | FileCheck --check-prefix=CHECK-INSTR %s
+# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb \
+# RUN: -slab-page-size 4096 -show-entry-es -check %s %t.o
+
+
+ .text
+ .syntax unified
+
+# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_CALL call_target
+# CHECK-INSTR: 00000000 <call_site>:
+# CHECK-INSTR: 0: ebfffffe bl 0x0 <call_site>
+# CHECK-INSTR: 00000004 <call_target>:
+# CHECK-INSTR: 4: e12fff1e bx lr
+# ARM branch offset is 8, because it accounts for an additional prefetch
+# instruction that increments PC even though it is implicit
+# jitlink-check: decode_operand(call_site, 0) = call_target - (call_site + 8)
+ .globl call_site
+ .type call_site,%function
+ .p2align 2
+call_site:
+ bl call_target
+ .size call_site, .-call_site
+
+ .globl call_target
+ .type call_target,%function
+ .p2align 2
+call_target:
+ bx lr
+ .size call_target, .-call_target
+
+# Empty main function for jitlink to be happy
+ .globl main
+ .type main,%function
+ .p2align 2
+main:
+ bx lr
+ .size main, .-main
diff --git a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
index 0e41174040b684b..2f9e44daea7a1db 100644
--- a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
+++ b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
@@ -29,6 +29,13 @@ struct MutableHalfWords {
uint16_t Lo; // Second halfword
};
+struct MutableWord {
+ MutableWord(uint32_t Preset) : Wd(Preset) {}
+
+ void patch(uint32_t Value, uint32_t Mask) { Wd = (Wd & ~Mask) | Value; }
+
+ uint32_t Wd;
+};
namespace llvm {
namespace jitlink {
@@ -66,11 +73,13 @@ namespace aarch32 {
HalfWords encodeImmBT4BlT1BlxT2(int64_t Value);
HalfWords encodeImmBT4BlT1BlxT2_J1J2(int64_t Value);
+uint32_t encodeImmBA1BlA1BlxA2(int64_t Value);
HalfWords encodeImmMovtT1MovwT3(uint16_t Value);
HalfWords encodeRegMovtT1MovwT3(int64_t Value);
int64_t decodeImmBT4BlT1BlxT2(uint32_t Hi, uint32_t Lo);
int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo);
+int64_t decodeImmBA1BlA1BlxA2(int64_t Value);
uint16_t decodeImmMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
@@ -159,6 +168,41 @@ TEST(AArch32_Relocations, Thumb_Call_Bare) {
}
}
+/// 26-bit branch with link
+TEST(AArch32_Relocations, Arm_Call_Bare) {
+ static_assert(isInt<26>(33554430), "Max value");
+ static_assert(isInt<26>(-33554432), "Min value");
+ static_assert(!isInt<26>(33554432), "First overflow");
+ static_assert(!isInt<26>(-33554434), "First underflow");
+
+ constexpr uint32_t ImmMask = FixupInfo<Arm_Call>::ImmMask;
+
+ static std::array<uint32_t, 3> MemPresets{
+ 0xfeeffff7, // common
+ 0x00000000, // zeros
+ 0xffffffff, // ones
+ };
+
+ auto EncodeDecode = [](int64_t In, MutableWord &Mem) {
+ Mem.patch(encodeImmBA1BlA1BlxA2(In), ImmMask);
+ return decodeImmBA1BlA1BlxA2(Mem.Wd);
+ };
+
+ for (MutableWord Mem : MemPresets) {
+ MutableWord UnaffectedBits(Mem.Wd & ~ImmMask);
+
+ EXPECT_EQ(EncodeDecode(0, Mem), 0); // Zero value
+ EXPECT_EQ(EncodeDecode(0x40, Mem), 0x40); // Common value
+ EXPECT_EQ(EncodeDecode(33554428, Mem), 33554428); // Maximum value
+ EXPECT_EQ(EncodeDecode(-33554432, Mem), -33554432); // Minimum value
+ EXPECT_NE(EncodeDecode(33554434, Mem), 33554434); // First overflow
+ EXPECT_NE(EncodeDecode(-33554434, Mem), -33554434); // First underflow
+
+ EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~ImmMask))
+ << "Diff outside immediate field";
+ }
+}
+
/// Write immediate value to the top halfword of the destination register
TEST(AArch32_Relocations, Thumb_MovtAbs) {
static_assert(isUInt<16>(65535), "Max value");
More information about the llvm-commits
mailing list