Skip to content

Commit 142c89c

Browse files
eymayweliveindetail
authored andcommitted
[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
1 parent a7f256f commit 142c89c

File tree

4 files changed

+210
-9
lines changed

4 files changed

+210
-9
lines changed

llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ enum EdgeKind_aarch32 : Edge::Kind {
4848
///
4949
FirstArmRelocation,
5050

51-
/// TODO: Arm_Call is here only as a placeholder for now.
51+
/// Write immediate value for PC-relative branch with link (can bridge between
52+
/// Arm and Thumb).
5253
Arm_Call = FirstArmRelocation,
5354

5455
LastArmRelocation = Arm_Call,
@@ -148,6 +149,16 @@ struct HalfWords {
148149
///
149150
template <EdgeKind_aarch32 Kind> struct FixupInfo {};
150151

152+
template <> struct FixupInfo<Arm_Call> {
153+
static constexpr uint32_t Opcode = 0x0a000000;
154+
static constexpr uint32_t OpcodeMask = 0x0e000000;
155+
static constexpr uint32_t ImmMask = 0x00ffffff;
156+
static constexpr uint32_t Unconditional = 0xe0000000;
157+
static constexpr uint32_t CondMask = 0xe0000000; // excluding BLX bit
158+
static constexpr uint32_t BitH = 0x01000000;
159+
static constexpr uint32_t BitBlx = 0x10000000;
160+
};
161+
151162
template <> struct FixupInfo<Thumb_Jump24> {
152163
static constexpr HalfWords Opcode{0xf000, 0x8000};
153164
static constexpr HalfWords OpcodeMask{0xf800, 0x8000};

llvm/lib/ExecutionEngine/JITLink/aarch32.cpp

Lines changed: 115 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,24 @@ int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo) {
8383
return SignExtend64<25>(S << 14 | I1 | I2 | Imm10 << 12 | Imm11 << 1);
8484
}
8585

86+
/// Encode 26-bit immediate value for branch instructions
87+
/// (formats B A1, BL A1 and BLX A2).
88+
///
89+
/// Imm24:00 -> 00000000:Imm24
90+
///
91+
uint32_t encodeImmBA1BlA1BlxA2(int64_t Value) {
92+
return (Value >> 2) & 0x00ffffff;
93+
}
94+
95+
/// Decode 26-bit immediate value for branch instructions
96+
/// (formats B A1, BL A1 and BLX A2).
97+
///
98+
/// 00000000:Imm24 -> Imm24:00
99+
///
100+
int64_t decodeImmBA1BlA1BlxA2(int64_t Value) {
101+
return SignExtend64<26>((Value & 0x00ffffff) << 2);
102+
}
103+
86104
/// Encode 16-bit immediate value for move instruction formats MOVT T1 and
87105
/// MOVW T3.
88106
///
@@ -156,6 +174,23 @@ struct ThumbRelocation {
156174
const support::ulittle16_t &Lo; // Second halfword
157175
};
158176

177+
struct WritableArmRelocation {
178+
WritableArmRelocation(char *FixupPtr)
179+
: Wd{*reinterpret_cast<support::ulittle32_t *>(FixupPtr)} {}
180+
181+
support::ulittle32_t &Wd;
182+
};
183+
184+
struct ArmRelocation {
185+
186+
ArmRelocation(const char *FixupPtr)
187+
: Wd{*reinterpret_cast<const support::ulittle32_t *>(FixupPtr)} {}
188+
189+
ArmRelocation(WritableArmRelocation &Writable) : Wd{Writable.Wd} {}
190+
191+
const support::ulittle32_t &Wd;
192+
};
193+
159194
Error makeUnexpectedOpcodeError(const LinkGraph &G, const ThumbRelocation &R,
160195
Edge::Kind Kind) {
161196
return make_error<JITLinkError>(
@@ -164,19 +199,37 @@ Error makeUnexpectedOpcodeError(const LinkGraph &G, const ThumbRelocation &R,
164199
G.getEdgeKindName(Kind)));
165200
}
166201

202+
Error makeUnexpectedOpcodeError(const LinkGraph &G, const ArmRelocation &R,
203+
Edge::Kind Kind) {
204+
return make_error<JITLinkError>(
205+
formatv("Invalid opcode [ 0x{0:x8} ] for relocation: {1}",
206+
static_cast<uint32_t>(R.Wd), G.getEdgeKindName(Kind)));
207+
}
208+
167209
template <EdgeKind_aarch32 Kind> bool checkOpcode(const ThumbRelocation &R) {
168210
uint16_t Hi = R.Hi & FixupInfo<Kind>::OpcodeMask.Hi;
169211
uint16_t Lo = R.Lo & FixupInfo<Kind>::OpcodeMask.Lo;
170212
return Hi == FixupInfo<Kind>::Opcode.Hi && Lo == FixupInfo<Kind>::Opcode.Lo;
171213
}
172214

215+
template <EdgeKind_aarch32 Kind> bool checkOpcode(const ArmRelocation &R) {
216+
uint32_t Wd = R.Wd & FixupInfo<Kind>::OpcodeMask;
217+
return Wd == FixupInfo<Kind>::Opcode;
218+
}
219+
173220
template <EdgeKind_aarch32 Kind>
174221
bool checkRegister(const ThumbRelocation &R, HalfWords Reg) {
175222
uint16_t Hi = R.Hi & FixupInfo<Kind>::RegMask.Hi;
176223
uint16_t Lo = R.Lo & FixupInfo<Kind>::RegMask.Lo;
177224
return Hi == Reg.Hi && Lo == Reg.Lo;
178225
}
179226

227+
template <EdgeKind_aarch32 Kind>
228+
bool checkRegister(const ArmRelocation &R, uint32_t Reg) {
229+
uint32_t Wd = R.Wd & FixupInfo<Kind>::RegMask;
230+
return Wd == Reg;
231+
}
232+
180233
template <EdgeKind_aarch32 Kind>
181234
void writeRegister(WritableThumbRelocation &R, HalfWords Reg) {
182235
static constexpr HalfWords Mask = FixupInfo<Kind>::RegMask;
@@ -186,6 +239,13 @@ void writeRegister(WritableThumbRelocation &R, HalfWords Reg) {
186239
R.Lo = (R.Lo & ~Mask.Lo) | Reg.Lo;
187240
}
188241

242+
template <EdgeKind_aarch32 Kind>
243+
void writeRegister(WritableArmRelocation &R, uint32_t Reg) {
244+
static constexpr uint32_t Mask = FixupInfo<Kind>::RegMask;
245+
assert((Mask & Reg) == Reg && "Value bits exceed bit range of given mask");
246+
R.Wd = (R.Wd & ~Mask) | Reg;
247+
}
248+
189249
template <EdgeKind_aarch32 Kind>
190250
void writeImmediate(WritableThumbRelocation &R, HalfWords Imm) {
191251
static constexpr HalfWords Mask = FixupInfo<Kind>::ImmMask;
@@ -195,6 +255,13 @@ void writeImmediate(WritableThumbRelocation &R, HalfWords Imm) {
195255
R.Lo = (R.Lo & ~Mask.Lo) | Imm.Lo;
196256
}
197257

258+
template <EdgeKind_aarch32 Kind>
259+
void writeImmediate(WritableArmRelocation &R, uint32_t Imm) {
260+
static constexpr uint32_t Mask = FixupInfo<Kind>::ImmMask;
261+
assert((Mask & Imm) == Imm && "Value bits exceed bit range of given mask");
262+
R.Wd = (R.Wd & ~Mask) | Imm;
263+
}
264+
198265
Expected<int64_t> readAddendData(LinkGraph &G, Block &B, const Edge &E) {
199266
support::endianness Endian = G.getEndianness();
200267
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) {
216283
}
217284

218285
Expected<int64_t> readAddendArm(LinkGraph &G, Block &B, const Edge &E) {
286+
ArmRelocation R(B.getContent().data() + E.getOffset());
219287
Edge::Kind Kind = E.getKind();
220288

221289
switch (Kind) {
222290
case Arm_Call:
223-
return make_error<JITLinkError>(
224-
"Addend extraction for relocation type not yet implemented: " +
225-
StringRef(G.getEdgeKindName(Kind)));
291+
if (!checkOpcode<Arm_Call>(R))
292+
return makeUnexpectedOpcodeError(G, R, Kind);
293+
return decodeImmBA1BlA1BlxA2(R.Wd);
294+
226295
default:
227296
return make_error<JITLinkError>(
228297
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -292,7 +361,6 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
292361
int64_t Addend = E.getAddend();
293362
Symbol &TargetSymbol = E.getTarget();
294363
uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
295-
assert(!hasTargetFlags(TargetSymbol, ThumbSymbol));
296364

297365
// Regular data relocations have size 4, alignment 1 and write the full 32-bit
298366
// 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) {
321389
}
322390

323391
Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E) {
392+
WritableArmRelocation R(B.getAlreadyMutableContent().data() + E.getOffset());
324393
Edge::Kind Kind = E.getKind();
394+
uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
395+
int64_t Addend = E.getAddend();
396+
Symbol &TargetSymbol = E.getTarget();
397+
uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
398+
if (hasTargetFlags(TargetSymbol, ThumbSymbol))
399+
TargetAddress |= 0x01;
325400

326401
switch (Kind) {
327-
case Arm_Call:
328-
return make_error<JITLinkError>(
329-
"Fix-up for relocation type not yet implemented: " +
330-
StringRef(G.getEdgeKindName(Kind)));
402+
case Arm_Call: {
403+
if (!checkOpcode<Arm_Call>(R))
404+
return makeUnexpectedOpcodeError(G, R, Kind);
405+
406+
if ((R.Wd & FixupInfo<Arm_Call>::CondMask) !=
407+
FixupInfo<Arm_Call>::Unconditional)
408+
return make_error<JITLinkError>("Relocation expects an unconditional "
409+
"BL/BLX branch instruction: " +
410+
StringRef(G.getEdgeKindName(Kind)));
411+
412+
int64_t Value = TargetAddress - FixupAddress + Addend;
413+
414+
// The call instruction itself is Arm. The call destination can either be
415+
// Thumb or Arm. We use BL to stay in Arm and BLX to change to Thumb.
416+
bool TargetIsThumb = hasTargetFlags(TargetSymbol, ThumbSymbol);
417+
bool InstrIsBlx = (~R.Wd & FixupInfo<Arm_Call>::BitBlx) == 0;
418+
if (TargetIsThumb != InstrIsBlx) {
419+
if (LLVM_LIKELY(TargetIsThumb)) {
420+
// Change opcode BL -> BLX and fix range value
421+
R.Wd = R.Wd | FixupInfo<Arm_Call>::BitBlx;
422+
R.Wd = R.Wd & ~FixupInfo<Arm_Call>::BitH;
423+
// Set Thumb bit
424+
Value |= 0x01;
425+
} else {
426+
// Change opcode BLX -> BL
427+
R.Wd = R.Wd & ~FixupInfo<Arm_Call>::BitBlx;
428+
}
429+
}
430+
431+
if (!isInt<26>(Value))
432+
return makeTargetOutOfRangeError(G, B, E);
433+
writeImmediate<Arm_Call>(R, encodeImmBA1BlA1BlxA2(Value));
434+
435+
return Error::success();
436+
}
437+
331438
default:
332439
return make_error<JITLinkError>(
333440
"In graph " + G.getName() + ", section " + B.getSection().getName() +
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t.o %s
2+
# RUN: llvm-objdump -r %t.o | FileCheck --check-prefix=CHECK-TYPE %s
3+
# RUN: llvm-objdump --disassemble %t.o | FileCheck --check-prefix=CHECK-INSTR %s
4+
# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb \
5+
# RUN: -slab-page-size 4096 -show-entry-es -check %s %t.o
6+
7+
8+
.text
9+
.syntax unified
10+
11+
# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_CALL call_target
12+
# CHECK-INSTR: 00000000 <call_site>:
13+
# CHECK-INSTR: 0: ebfffffe bl 0x0 <call_site>
14+
# CHECK-INSTR: 00000004 <call_target>:
15+
# CHECK-INSTR: 4: e12fff1e bx lr
16+
# ARM branch offset is 8, because it accounts for an additional prefetch
17+
# instruction that increments PC even though it is implicit
18+
# jitlink-check: decode_operand(call_site, 0) = call_target - (call_site + 8)
19+
.globl call_site
20+
.type call_site,%function
21+
.p2align 2
22+
call_site:
23+
bl call_target
24+
.size call_site, .-call_site
25+
26+
.globl call_target
27+
.type call_target,%function
28+
.p2align 2
29+
call_target:
30+
bx lr
31+
.size call_target, .-call_target
32+
33+
# Empty main function for jitlink to be happy
34+
.globl main
35+
.type main,%function
36+
.p2align 2
37+
main:
38+
bx lr
39+
.size main, .-main

llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ struct MutableHalfWords {
2929
uint16_t Lo; // Second halfword
3030
};
3131

32+
struct MutableWord {
33+
MutableWord(uint32_t Preset) : Wd(Preset) {}
34+
35+
void patch(uint32_t Value, uint32_t Mask) { Wd = (Wd & ~Mask) | Value; }
36+
37+
uint32_t Wd;
38+
};
3239
namespace llvm {
3340
namespace jitlink {
3441

@@ -66,11 +73,13 @@ namespace aarch32 {
6673

6774
HalfWords encodeImmBT4BlT1BlxT2(int64_t Value);
6875
HalfWords encodeImmBT4BlT1BlxT2_J1J2(int64_t Value);
76+
uint32_t encodeImmBA1BlA1BlxA2(int64_t Value);
6977
HalfWords encodeImmMovtT1MovwT3(uint16_t Value);
7078
HalfWords encodeRegMovtT1MovwT3(int64_t Value);
7179

7280
int64_t decodeImmBT4BlT1BlxT2(uint32_t Hi, uint32_t Lo);
7381
int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo);
82+
int64_t decodeImmBA1BlA1BlxA2(int64_t Value);
7483
uint16_t decodeImmMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
7584
int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
7685

@@ -159,6 +168,41 @@ TEST(AArch32_Relocations, Thumb_Call_Bare) {
159168
}
160169
}
161170

171+
/// 26-bit branch with link
172+
TEST(AArch32_Relocations, Arm_Call_Bare) {
173+
static_assert(isInt<26>(33554430), "Max value");
174+
static_assert(isInt<26>(-33554432), "Min value");
175+
static_assert(!isInt<26>(33554432), "First overflow");
176+
static_assert(!isInt<26>(-33554434), "First underflow");
177+
178+
constexpr uint32_t ImmMask = FixupInfo<Arm_Call>::ImmMask;
179+
180+
static std::array<uint32_t, 3> MemPresets{
181+
0xfeeffff7, // common
182+
0x00000000, // zeros
183+
0xffffffff, // ones
184+
};
185+
186+
auto EncodeDecode = [](int64_t In, MutableWord &Mem) {
187+
Mem.patch(encodeImmBA1BlA1BlxA2(In), ImmMask);
188+
return decodeImmBA1BlA1BlxA2(Mem.Wd);
189+
};
190+
191+
for (MutableWord Mem : MemPresets) {
192+
MutableWord UnaffectedBits(Mem.Wd & ~ImmMask);
193+
194+
EXPECT_EQ(EncodeDecode(0, Mem), 0); // Zero value
195+
EXPECT_EQ(EncodeDecode(0x40, Mem), 0x40); // Common value
196+
EXPECT_EQ(EncodeDecode(33554428, Mem), 33554428); // Maximum value
197+
EXPECT_EQ(EncodeDecode(-33554432, Mem), -33554432); // Minimum value
198+
EXPECT_NE(EncodeDecode(33554434, Mem), 33554434); // First overflow
199+
EXPECT_NE(EncodeDecode(-33554434, Mem), -33554434); // First underflow
200+
201+
EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~ImmMask))
202+
<< "Diff outside immediate field";
203+
}
204+
}
205+
162206
/// Write immediate value to the top halfword of the destination register
163207
TEST(AArch32_Relocations, Thumb_MovtAbs) {
164208
static_assert(isUInt<16>(65535), "Max value");

0 commit comments

Comments
 (0)