@@ -83,6 +83,24 @@ int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo) {
83
83
return SignExtend64<25 >(S << 14 | I1 | I2 | Imm10 << 12 | Imm11 << 1 );
84
84
}
85
85
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
+
86
104
// / Encode 16-bit immediate value for move instruction formats MOVT T1 and
87
105
// / MOVW T3.
88
106
// /
@@ -156,6 +174,23 @@ struct ThumbRelocation {
156
174
const support::ulittle16_t &Lo; // Second halfword
157
175
};
158
176
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
+
159
194
Error makeUnexpectedOpcodeError (const LinkGraph &G, const ThumbRelocation &R,
160
195
Edge::Kind Kind) {
161
196
return make_error<JITLinkError>(
@@ -164,19 +199,37 @@ Error makeUnexpectedOpcodeError(const LinkGraph &G, const ThumbRelocation &R,
164
199
G.getEdgeKindName (Kind)));
165
200
}
166
201
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
+
167
209
template <EdgeKind_aarch32 Kind> bool checkOpcode (const ThumbRelocation &R) {
168
210
uint16_t Hi = R.Hi & FixupInfo<Kind>::OpcodeMask.Hi ;
169
211
uint16_t Lo = R.Lo & FixupInfo<Kind>::OpcodeMask.Lo ;
170
212
return Hi == FixupInfo<Kind>::Opcode.Hi && Lo == FixupInfo<Kind>::Opcode.Lo ;
171
213
}
172
214
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
+
173
220
template <EdgeKind_aarch32 Kind>
174
221
bool checkRegister (const ThumbRelocation &R, HalfWords Reg) {
175
222
uint16_t Hi = R.Hi & FixupInfo<Kind>::RegMask.Hi ;
176
223
uint16_t Lo = R.Lo & FixupInfo<Kind>::RegMask.Lo ;
177
224
return Hi == Reg.Hi && Lo == Reg.Lo ;
178
225
}
179
226
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
+
180
233
template <EdgeKind_aarch32 Kind>
181
234
void writeRegister (WritableThumbRelocation &R, HalfWords Reg) {
182
235
static constexpr HalfWords Mask = FixupInfo<Kind>::RegMask;
@@ -186,6 +239,13 @@ void writeRegister(WritableThumbRelocation &R, HalfWords Reg) {
186
239
R.Lo = (R.Lo & ~Mask.Lo ) | Reg.Lo ;
187
240
}
188
241
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
+
189
249
template <EdgeKind_aarch32 Kind>
190
250
void writeImmediate (WritableThumbRelocation &R, HalfWords Imm) {
191
251
static constexpr HalfWords Mask = FixupInfo<Kind>::ImmMask;
@@ -195,6 +255,13 @@ void writeImmediate(WritableThumbRelocation &R, HalfWords Imm) {
195
255
R.Lo = (R.Lo & ~Mask.Lo ) | Imm.Lo ;
196
256
}
197
257
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
+
198
265
Expected<int64_t > readAddendData (LinkGraph &G, Block &B, const Edge &E) {
199
266
support::endianness Endian = G.getEndianness ();
200
267
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) {
216
283
}
217
284
218
285
Expected<int64_t > readAddendArm (LinkGraph &G, Block &B, const Edge &E) {
286
+ ArmRelocation R (B.getContent ().data () + E.getOffset ());
219
287
Edge::Kind Kind = E.getKind ();
220
288
221
289
switch (Kind) {
222
290
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
+
226
295
default :
227
296
return make_error<JITLinkError>(
228
297
" In graph " + G.getName () + " , section " + B.getSection ().getName () +
@@ -292,7 +361,6 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
292
361
int64_t Addend = E.getAddend ();
293
362
Symbol &TargetSymbol = E.getTarget ();
294
363
uint64_t TargetAddress = TargetSymbol.getAddress ().getValue ();
295
- assert (!hasTargetFlags (TargetSymbol, ThumbSymbol));
296
364
297
365
// Regular data relocations have size 4, alignment 1 and write the full 32-bit
298
366
// 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) {
321
389
}
322
390
323
391
Error applyFixupArm (LinkGraph &G, Block &B, const Edge &E) {
392
+ WritableArmRelocation R (B.getAlreadyMutableContent ().data () + E.getOffset ());
324
393
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 ;
325
400
326
401
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
+
331
438
default :
332
439
return make_error<JITLinkError>(
333
440
" In graph " + G.getName () + " , section " + B.getSection ().getName () +
0 commit comments