xref: /aosp_15_r20/external/zucchini/arm_utils.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/zucchini/arm_utils.h"
6 
7 #include "components/zucchini/algorithm.h"
8 
9 namespace zucchini {
10 
11 namespace {
12 
IsMisaligned(rva_t rva,ArmAlign align)13 inline bool IsMisaligned(rva_t rva, ArmAlign align) {
14   return (rva & (align - 1)) != 0;
15 }
16 
17 }  // namespace
18 
19 /******** AArch32Rel32Translator ********/
20 
21 AArch32Rel32Translator::AArch32Rel32Translator() = default;
22 
23 // The mapping between ARM instruction "Code" to "Displacement" involves complex
24 // bit manipulation. The comments below annotate bits mappings using a string.
25 // * Bits are listed from highest-order to lowerst-order (like in the manual).
26 // * '0' and '1' denote literals.
27 // * Uppercase letters denote a single bit in "Code". For example, 'S' denotes
28 //   a sign bit that gets extended in "Displacement". To follow naming in the
29 //   manual, these may enumerated, and written as "(I1)", "(I2)", etc.
30 // * Lowercase letters denote bit fields with orders preserved.
31 
32 // static
DecodeA24(uint32_t code32,arm_disp_t * disp)33 ArmAlign AArch32Rel32Translator::DecodeA24(uint32_t code32, arm_disp_t* disp) {
34   // Handle multiple instructions. Let cccc != 1111:
35   // B encoding A1:
36   //   Code:         cccc1010 Siiiiiii iiiiiiii iiiiiiii
37   //   Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiii00
38   // BL encoding A1:
39   //   Code:         cccc1011 Siiiiiii iiiiiiii iiiiiiii
40   //   Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiii00
41   // BLX encoding A2:
42   //   Code:         1111101H Siiiiiii iiiiiiii iiiiiiii
43   //   Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiiiH0
44   uint8_t bits = GetUnsignedBits<24, 27>(code32);
45   if (bits == 0xA || bits == 0xB) {  // B, BL, or BLX.
46     *disp = GetSignedBits<0, 23>(code32) << 2;
47     uint8_t cond = GetUnsignedBits<28, 31>(code32);
48     if (cond == 0xF) {  // BLX.
49       uint32_t H = GetBit<24>(code32);
50       *disp |= H << 1;
51       return kArmAlign2;
52     }
53     return kArmAlign4;
54   }
55   return kArmAlignFail;
56 }
57 
58 // static
EncodeA24(arm_disp_t disp,uint32_t * code32)59 bool AArch32Rel32Translator::EncodeA24(arm_disp_t disp, uint32_t* code32) {
60   uint32_t t = *code32;
61   uint8_t bits = GetUnsignedBits<24, 27>(t);
62   if (bits == 0xA || bits == 0xB) {
63     // B, BL, or BLX.
64     if (!SignedFit<26>(disp))  // Detect overflow.
65       return false;
66     uint8_t cond = GetUnsignedBits<28, 31>(t);
67     if (cond == 0xF) {
68       if (disp % 2)  // BLX (encoding A2) requires 2-byte alignment.
69         return false;
70       uint32_t H = GetBit<1>(disp);
71       t = (t & 0xFEFFFFFF) | (H << 24);
72     } else {
73       if (disp % 4)  // B and BL require 4-byte alignment.
74         return false;
75     }
76     t = (t & 0xFF000000) | ((disp >> 2) & 0x00FFFFFF);
77     *code32 = t;
78     return true;
79   }
80   return false;
81 }
82 
83 // static
ReadA24(rva_t instr_rva,uint32_t code32,rva_t * target_rva)84 bool AArch32Rel32Translator::ReadA24(rva_t instr_rva,
85                                      uint32_t code32,
86                                      rva_t* target_rva) {
87   constexpr ArmAlign kInstrAlign = kArmAlign4;
88   if (IsMisaligned(instr_rva, kInstrAlign))
89     return false;
90   arm_disp_t disp;
91   ArmAlign target_align = DecodeA24(code32, &disp);
92   if (target_align == kArmAlignFail)
93     return false;
94   *target_rva = GetArmTargetRvaFromDisp(instr_rva, disp, target_align);
95   return true;
96 }
97 
98 // static
WriteA24(rva_t instr_rva,rva_t target_rva,uint32_t * code32)99 bool AArch32Rel32Translator::WriteA24(rva_t instr_rva,
100                                       rva_t target_rva,
101                                       uint32_t* code32) {
102   constexpr ArmAlign kInstrAlign = kArmAlign4;
103   if (IsMisaligned(instr_rva, kInstrAlign))
104     return false;
105   // Dummy decode to get |target_align|.
106   arm_disp_t dummy_disp;
107   ArmAlign target_align = DecodeA24(*code32, &dummy_disp);
108   if (target_align == kArmAlignFail || IsMisaligned(target_rva, target_align))
109     return false;
110   arm_disp_t disp =
111       GetArmDispFromTargetRva(instr_rva, target_rva, target_align);
112   return EncodeA24(disp, code32);
113 }
114 
115 // static
DecodeT8(uint16_t code16,arm_disp_t * disp)116 ArmAlign AArch32Rel32Translator::DecodeT8(uint16_t code16, arm_disp_t* disp) {
117   if ((code16 & 0xF000) == 0xD000 && (code16 & 0x0F00) != 0x0F00) {
118     // B encoding T1:
119     //   Code:         1101cccc Siiiiiii
120     //   Displacement: SSSSSSSS SSSSSSSS SSSSSSSS iiiiiii0
121     *disp = GetSignedBits<0, 7>(code16) << 1;
122     return kArmAlign2;
123   }
124   return kArmAlignFail;
125 }
126 
127 // static
EncodeT8(arm_disp_t disp,uint16_t * code16)128 bool AArch32Rel32Translator::EncodeT8(arm_disp_t disp, uint16_t* code16) {
129   uint16_t t = *code16;
130   if ((t & 0xF000) == 0xD000 && (t & 0x0F00) != 0x0F00) {
131     if (disp % 2)  // Require 2-byte alignment.
132       return false;
133     if (!SignedFit<9>(disp))  // Detect overflow.
134       return false;
135     t = (t & 0xFF00) | ((disp >> 1) & 0x00FF);
136     *code16 = t;
137     return true;
138   }
139   return false;
140 }
141 
142 // static
ReadT8(rva_t instr_rva,uint16_t code16,rva_t * target_rva)143 bool AArch32Rel32Translator::ReadT8(rva_t instr_rva,
144                                     uint16_t code16,
145                                     rva_t* target_rva) {
146   constexpr ArmAlign kInstrAlign = kArmAlign2;
147   if (IsMisaligned(instr_rva, kInstrAlign))
148     return false;
149   arm_disp_t disp;
150   ArmAlign target_align = DecodeT8(code16, &disp);
151   if (target_align == kArmAlignFail)
152     return false;
153   *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align);
154   return true;
155 }
156 
157 // static
WriteT8(rva_t instr_rva,rva_t target_rva,uint16_t * code16)158 bool AArch32Rel32Translator::WriteT8(rva_t instr_rva,
159                                      rva_t target_rva,
160                                      uint16_t* code16) {
161   constexpr ArmAlign kInstrAlign = kArmAlign2;
162   constexpr ArmAlign kTargetAlign = kArmAlign2;
163   if (IsMisaligned(instr_rva, kInstrAlign) ||
164       IsMisaligned(target_rva, kTargetAlign)) {
165     return false;
166   }
167   arm_disp_t disp =
168       GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign);
169   return EncodeT8(disp, code16);
170 }
171 
172 // static
DecodeT11(uint16_t code16,arm_disp_t * disp)173 ArmAlign AArch32Rel32Translator::DecodeT11(uint16_t code16, arm_disp_t* disp) {
174   if ((code16 & 0xF800) == 0xE000) {
175     // B encoding T2:
176     //   Code:         11100Sii iiiiiiii
177     //   Displacement: SSSSSSSS SSSSSSSS SSSSSiii iiiiiii0
178     *disp = GetSignedBits<0, 10>(code16) << 1;
179     return kArmAlign2;
180   }
181   return kArmAlignFail;
182 }
183 
184 // static
EncodeT11(arm_disp_t disp,uint16_t * code16)185 bool AArch32Rel32Translator::EncodeT11(arm_disp_t disp, uint16_t* code16) {
186   uint16_t t = *code16;
187   if ((t & 0xF800) == 0xE000) {
188     if (disp % 2)  // Require 2-byte alignment.
189       return false;
190     if (!SignedFit<12>(disp))  // Detect overflow.
191       return false;
192     t = (t & 0xF800) | ((disp >> 1) & 0x07FF);
193     *code16 = t;
194     return true;
195   }
196   return false;
197 }
198 
199 // static
ReadT11(rva_t instr_rva,uint16_t code16,rva_t * target_rva)200 bool AArch32Rel32Translator::ReadT11(rva_t instr_rva,
201                                      uint16_t code16,
202                                      rva_t* target_rva) {
203   constexpr ArmAlign kInstrAlign = kArmAlign2;
204   if (IsMisaligned(instr_rva, kInstrAlign))
205     return false;
206   arm_disp_t disp;
207   ArmAlign target_align = DecodeT11(code16, &disp);
208   if (target_align == kArmAlignFail)
209     return false;
210   *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align);
211   return true;
212 }
213 
214 // static
WriteT11(rva_t instr_rva,rva_t target_rva,uint16_t * code16)215 bool AArch32Rel32Translator::WriteT11(rva_t instr_rva,
216                                       rva_t target_rva,
217                                       uint16_t* code16) {
218   constexpr ArmAlign kInstrAlign = kArmAlign2;
219   constexpr ArmAlign kTargetAlign = kArmAlign2;
220   if (IsMisaligned(instr_rva, kInstrAlign) ||
221       IsMisaligned(target_rva, kTargetAlign)) {
222     return false;
223   }
224   arm_disp_t disp =
225       GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign);
226   return EncodeT11(disp, code16);
227 }
228 
229 // static
DecodeT20(uint32_t code32,arm_disp_t * disp)230 ArmAlign AArch32Rel32Translator::DecodeT20(uint32_t code32, arm_disp_t* disp) {
231   if ((code32 & 0xF800D000) == 0xF0008000 &&
232       (code32 & 0x03C00000) != 0x03C00000) {
233     // B encoding T3. Note the reversal of "(J1)" and "(J2)".
234     //   Code:         11110Scc cciiiiii 10(J1)0(J2)jjj jjjjjjjj
235     //   Displacement: SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0
236     uint32_t imm11 = GetUnsignedBits<0, 10>(code32);  // jj...j.
237     uint32_t J2 = GetBit<11>(code32);
238     uint32_t J1 = GetBit<13>(code32);
239     uint32_t imm6 = GetUnsignedBits<16, 21>(code32);  // ii...i.
240     uint32_t S = GetBit<26>(code32);
241     uint32_t t = (imm6 << 12) | (imm11 << 1);
242     t |= (S << 20) | (J2 << 19) | (J1 << 18);
243     *disp = SignExtend<20, int32_t>(t);
244     return kArmAlign2;
245   }
246   return kArmAlignFail;
247 }
248 
249 // static
EncodeT20(arm_disp_t disp,uint32_t * code32)250 bool AArch32Rel32Translator::EncodeT20(arm_disp_t disp, uint32_t* code32) {
251   uint32_t t = *code32;
252   if ((t & 0xF800D000) == 0xF0008000 && (t & 0x03C00000) != 0x03C00000) {
253     if (disp % 2)  // Require 2-byte alignment.
254       return false;
255     if (!SignedFit<21>(disp))  // Detect overflow.
256       return false;
257     uint32_t S = GetBit<20>(disp);
258     uint32_t J2 = GetBit<19>(disp);
259     uint32_t J1 = GetBit<18>(disp);
260     uint32_t imm6 = GetUnsignedBits<12, 17>(disp);  // ii...i.
261     uint32_t imm11 = GetUnsignedBits<1, 11>(disp);  // jj...j.
262     t &= 0xFBC0D000;
263     t |= (S << 26) | (imm6 << 16) | (J1 << 13) | (J2 << 11) | imm11;
264     *code32 = t;
265     return true;
266   }
267   return false;
268 }
269 
270 // static
ReadT20(rva_t instr_rva,uint32_t code32,rva_t * target_rva)271 bool AArch32Rel32Translator::ReadT20(rva_t instr_rva,
272                                      uint32_t code32,
273                                      rva_t* target_rva) {
274   constexpr ArmAlign kInstrAlign = kArmAlign2;
275   if (IsMisaligned(instr_rva, kInstrAlign))
276     return false;
277   arm_disp_t disp;
278   ArmAlign target_align = DecodeT20(code32, &disp);
279   if (target_align == kArmAlignFail)
280     return false;
281   *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align);
282   return true;
283 }
284 
285 // static
WriteT20(rva_t instr_rva,rva_t target_rva,uint32_t * code32)286 bool AArch32Rel32Translator::WriteT20(rva_t instr_rva,
287                                       rva_t target_rva,
288                                       uint32_t* code32) {
289   constexpr ArmAlign kInstrAlign = kArmAlign2;
290   constexpr ArmAlign kTargetAlign = kArmAlign2;
291   if (IsMisaligned(instr_rva, kInstrAlign) ||
292       IsMisaligned(target_rva, kTargetAlign)) {
293     return false;
294   }
295   arm_disp_t disp =
296       GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign);
297   return EncodeT20(disp, code32);
298 }
299 
300 // static
DecodeT24(uint32_t code32,arm_disp_t * disp)301 ArmAlign AArch32Rel32Translator::DecodeT24(uint32_t code32, arm_disp_t* disp) {
302   uint32_t bits = code32 & 0xF800D000;
303   if (bits == 0xF0009000 || bits == 0xF000D000 || bits == 0xF000C000) {
304     // Let I1 = J1 ^ S ^ 1, I2 = J2 ^ S ^ 1.
305     // B encoding T4:
306     //   Code:         11110Sii iiiiiiii 10(J1)1(J2)jjj jjjjjjjj
307     //   Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0
308     // BL encoding T1:
309     //   Code:         11110Sii iiiiiiii 11(J1)1(J2)jjj jjjjjjjj
310     //   Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0
311     // BLX encoding T2: H should be 0:
312     //   Code:         11110Sii iiiiiiii 11(J1)0(J2)jjj jjjjjjjH
313     //   Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjH0
314     uint32_t imm11 = GetUnsignedBits<0, 10>(code32);  // jj...j.
315     uint32_t J2 = GetBit<11>(code32);
316     uint32_t J1 = GetBit<13>(code32);
317     uint32_t imm10 = GetUnsignedBits<16, 25>(code32);  // ii...i.
318     uint32_t S = GetBit<26>(code32);
319     uint32_t t = (imm10 << 12) | (imm11 << 1);
320     t |= (S << 24) | ((J1 ^ S ^ 1) << 23) | ((J2 ^ S ^ 1) << 22);
321     t = SignExtend<24, int32_t>(t);
322     // BLX encoding T2 requires final target to be 4-byte aligned by rounding
323     // downward. This is applied to |t| *after* clipping.
324     ArmAlign target_align = kArmAlign2;
325     if (bits == 0xF000C000) {
326       uint32_t H = GetBit<0>(code32);
327       if (H)
328         return kArmAlignFail;  // Illegal instruction: H must be 0.
329       target_align = kArmAlign4;
330     }
331     *disp = static_cast<int32_t>(t);
332     return target_align;
333   }
334   return kArmAlignFail;
335 }
336 
337 // static
EncodeT24(arm_disp_t disp,uint32_t * code32)338 bool AArch32Rel32Translator::EncodeT24(arm_disp_t disp, uint32_t* code32) {
339   uint32_t t = *code32;
340   uint32_t bits = t & 0xF800D000;
341   if (bits == 0xF0009000 || bits == 0xF000D000 || bits == 0xF000C000) {
342     if (disp % 2)  // Require 2-byte alignment.
343       return false;
344     // BLX encoding T2 requires H == 0, and that |disp| results in |target_rva|
345     // with a 4-byte aligned address.
346     if (bits == 0xF000C000) {
347       uint32_t H = GetBit<1>(disp);
348       if (H)
349         return false;  // Illegal |disp|: H must be 0.
350     }
351     if (!SignedFit<25>(disp))  // Detect overflow.
352       return false;
353     uint32_t imm11 = GetUnsignedBits<1, 11>(disp);   // jj...j.
354     uint32_t imm10 = GetUnsignedBits<12, 21>(disp);  // ii...i.
355     uint32_t I2 = GetBit<22>(disp);
356     uint32_t I1 = GetBit<23>(disp);
357     uint32_t S = GetBit<24>(disp);
358     t &= 0xF800D000;
359     t |= (S << 26) | (imm10 << 16) | ((I1 ^ S ^ 1) << 13) |
360          ((I2 ^ S ^ 1) << 11) | imm11;
361     *code32 = t;
362     return true;
363   }
364   return false;
365 }
366 
367 // static
ReadT24(rva_t instr_rva,uint32_t code32,rva_t * target_rva)368 bool AArch32Rel32Translator::ReadT24(rva_t instr_rva,
369                                      uint32_t code32,
370                                      rva_t* target_rva) {
371   constexpr ArmAlign kInstrAlign = kArmAlign2;
372   if (IsMisaligned(instr_rva, kInstrAlign))
373     return false;
374   arm_disp_t disp;
375   ArmAlign target_align = DecodeT24(code32, &disp);
376   if (target_align == kArmAlignFail)
377     return false;
378   *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align);
379   return true;
380 }
381 
382 // static
WriteT24(rva_t instr_rva,rva_t target_rva,uint32_t * code32)383 bool AArch32Rel32Translator::WriteT24(rva_t instr_rva,
384                                       rva_t target_rva,
385                                       uint32_t* code32) {
386   constexpr ArmAlign kInstrAlign = kArmAlign2;
387   if (IsMisaligned(instr_rva, kInstrAlign))
388     return false;
389   // Dummy decode to get |target_align|.
390   arm_disp_t dummy_disp;
391   ArmAlign target_align = DecodeT24(*code32, &dummy_disp);
392   if (target_align == kArmAlignFail || IsMisaligned(target_rva, target_align))
393     return false;
394   arm_disp_t disp =
395       GetThumb2DispFromTargetRva(instr_rva, target_rva, target_align);
396   return EncodeT24(disp, code32);
397 }
398 
399 /******** AArch64Rel32Translator ********/
400 
401 AArch64Rel32Translator::AArch64Rel32Translator() = default;
402 
403 // static
DecodeImmd14(uint32_t code32,arm_disp_t * disp)404 ArmAlign AArch64Rel32Translator::DecodeImmd14(uint32_t code32,
405                                               arm_disp_t* disp) {
406   // TBZ:
407   //   Code:         b0110110 bbbbbSii iiiiiiii iiittttt
408   //   Displacement: SSSSSSSS SSSSSSSS Siiiiiii iiiiii00
409   // TBNZ:
410   //   Code:         b0110111 bbbbbSii iiiiiiii iiittttt
411   //   Displacement: SSSSSSSS SSSSSSSS Siiiiiii iiiiii00
412   uint32_t bits = code32 & 0x7F000000;
413   if (bits == 0x36000000 || bits == 0x37000000) {
414     *disp = GetSignedBits<5, 18>(code32) << 2;
415     return kArmAlign4;
416   }
417   return kArmAlignFail;
418 }
419 
420 // static
EncodeImmd14(arm_disp_t disp,uint32_t * code32)421 bool AArch64Rel32Translator::EncodeImmd14(arm_disp_t disp, uint32_t* code32) {
422   uint32_t t = *code32;
423   uint32_t bits = t & 0x7F000000;
424   if (bits == 0x36000000 || bits == 0x37000000) {
425     if (disp % 4)  // Require 4-byte alignment.
426       return false;
427     if (!SignedFit<16>(disp))  // Detect overflow.
428       return false;
429     uint32_t imm14 = GetUnsignedBits<2, 15>(disp);  // ii...i.
430     t &= 0xFFF8001F;
431     t |= imm14 << 5;
432     *code32 = t;
433     return true;
434   }
435   return false;
436 }
437 
438 // static
ReadImmd14(rva_t instr_rva,uint32_t code32,rva_t * target_rva)439 bool AArch64Rel32Translator::ReadImmd14(rva_t instr_rva,
440                                         uint32_t code32,
441                                         rva_t* target_rva) {
442   constexpr ArmAlign kInstrAlign = kArmAlign4;
443   if (IsMisaligned(instr_rva, kInstrAlign))
444     return false;
445   arm_disp_t disp;
446   if (DecodeImmd14(code32, &disp) == kArmAlignFail)
447     return false;
448   *target_rva = GetTargetRvaFromDisp(instr_rva, disp);
449   return true;
450 }
451 
452 // static
WriteImmd14(rva_t instr_rva,rva_t target_rva,uint32_t * code32)453 bool AArch64Rel32Translator::WriteImmd14(rva_t instr_rva,
454                                          rva_t target_rva,
455                                          uint32_t* code32) {
456   constexpr ArmAlign kInstrAlign = kArmAlign4;
457   constexpr ArmAlign kTargetAlign = kArmAlign4;
458   if (IsMisaligned(instr_rva, kInstrAlign) ||
459       IsMisaligned(target_rva, kTargetAlign)) {
460     return false;
461   }
462   arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva);
463   return EncodeImmd14(disp, code32);
464 }
465 
466 // static
DecodeImmd19(uint32_t code32,arm_disp_t * disp)467 ArmAlign AArch64Rel32Translator::DecodeImmd19(uint32_t code32,
468                                               arm_disp_t* disp) {
469   // B.cond:
470   //   Code:         01010100 Siiiiiii iiiiiiii iii0cccc
471   //   Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00
472   // CBZ:
473   //   Code:         z0110100 Siiiiiii iiiiiiii iiittttt
474   //   Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00
475   // CBNZ:
476   //   Code:         z0110101 Siiiiiii iiiiiiii iiittttt
477   //   Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00
478   uint32_t bits1 = code32 & 0xFF000010;
479   uint32_t bits2 = code32 & 0x7F000000;
480   if (bits1 == 0x54000000 || bits2 == 0x34000000 || bits2 == 0x35000000) {
481     *disp = GetSignedBits<5, 23>(code32) << 2;
482     return kArmAlign4;
483   }
484   return kArmAlignFail;
485 }
486 
487 // static
EncodeImmd19(arm_disp_t disp,uint32_t * code32)488 bool AArch64Rel32Translator::EncodeImmd19(arm_disp_t disp, uint32_t* code32) {
489   uint32_t t = *code32;
490   uint32_t bits1 = t & 0xFF000010;
491   uint32_t bits2 = t & 0x7F000000;
492   if (bits1 == 0x54000000 || bits2 == 0x34000000 || bits2 == 0x35000000) {
493     if (disp % 4)  // Require 4-byte alignment.
494       return false;
495     if (!SignedFit<21>(disp))  // Detect overflow.
496       return false;
497     uint32_t imm19 = GetUnsignedBits<2, 20>(disp);  // ii...i.
498     t &= 0xFF00001F;
499     t |= imm19 << 5;
500     *code32 = t;
501     return true;
502   }
503   return false;
504 }
505 
506 // static
ReadImmd19(rva_t instr_rva,uint32_t code32,rva_t * target_rva)507 bool AArch64Rel32Translator::ReadImmd19(rva_t instr_rva,
508                                         uint32_t code32,
509                                         rva_t* target_rva) {
510   constexpr ArmAlign kInstrAlign = kArmAlign4;
511   if (IsMisaligned(instr_rva, kInstrAlign))
512     return false;
513   arm_disp_t disp;
514   if (DecodeImmd19(code32, &disp) == kArmAlignFail)
515     return false;
516   *target_rva = GetTargetRvaFromDisp(instr_rva, disp);
517   return true;
518 }
519 
520 // static
WriteImmd19(rva_t instr_rva,rva_t target_rva,uint32_t * code32)521 bool AArch64Rel32Translator::WriteImmd19(rva_t instr_rva,
522                                          rva_t target_rva,
523                                          uint32_t* code32) {
524   constexpr ArmAlign kInstrAlign = kArmAlign4;
525   constexpr ArmAlign kTargetAlign = kArmAlign4;
526   if (IsMisaligned(instr_rva, kInstrAlign) ||
527       IsMisaligned(target_rva, kTargetAlign)) {
528     return false;
529   }
530   arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva);
531   return EncodeImmd19(disp, code32);
532 }
533 
534 // static
DecodeImmd26(uint32_t code32,arm_disp_t * disp)535 ArmAlign AArch64Rel32Translator::DecodeImmd26(uint32_t code32,
536                                               arm_disp_t* disp) {
537   // B:
538   //   Code:         000101Si iiiiiiii iiiiiiii iiiiiiii
539   //   Displacement: SSSSSiii iiiiiiii iiiiiiii iiiiii00
540   // BL:
541   //   Code:         100101Si iiiiiiii iiiiiiii iiiiiiii
542   //   Displacement: SSSSSiii iiiiiiii iiiiiiii iiiiii00
543   uint32_t bits = code32 & 0xFC000000;
544   if (bits == 0x14000000 || bits == 0x94000000) {
545     *disp = GetSignedBits<0, 25>(code32) << 2;
546     return kArmAlign4;
547   }
548   return kArmAlignFail;
549 }
550 
551 // static
EncodeImmd26(arm_disp_t disp,uint32_t * code32)552 bool AArch64Rel32Translator::EncodeImmd26(arm_disp_t disp, uint32_t* code32) {
553   uint32_t t = *code32;
554   uint32_t bits = t & 0xFC000000;
555   if (bits == 0x14000000 || bits == 0x94000000) {
556     if (disp % 4)  // Require 4-byte alignment.
557       return false;
558     if (!SignedFit<28>(disp))  // Detect overflow.
559       return false;
560     uint32_t imm26 = GetUnsignedBits<2, 27>(disp);  // ii...i.
561     t &= 0xFC000000;
562     t |= imm26;
563     *code32 = t;
564     return true;
565   }
566   return false;
567 }
568 
569 // static
ReadImmd26(rva_t instr_rva,uint32_t code32,rva_t * target_rva)570 bool AArch64Rel32Translator::ReadImmd26(rva_t instr_rva,
571                                         uint32_t code32,
572                                         rva_t* target_rva) {
573   constexpr ArmAlign kInstrAlign = kArmAlign4;
574   if (IsMisaligned(instr_rva, kInstrAlign))
575     return false;
576   arm_disp_t disp;
577   if (DecodeImmd26(code32, &disp) == kArmAlignFail)
578     return false;
579   *target_rva = GetTargetRvaFromDisp(instr_rva, disp);
580   return true;
581 }
582 
583 // static
WriteImmd26(rva_t instr_rva,rva_t target_rva,uint32_t * code32)584 bool AArch64Rel32Translator::WriteImmd26(rva_t instr_rva,
585                                          rva_t target_rva,
586                                          uint32_t* code32) {
587   constexpr ArmAlign kInstrAlign = kArmAlign4;
588   constexpr ArmAlign kTargetAlign = kArmAlign4;
589   if (IsMisaligned(instr_rva, kInstrAlign) ||
590       IsMisaligned(target_rva, kTargetAlign)) {
591     return false;
592   }
593   arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva);
594   return EncodeImmd26(disp, code32);
595 }
596 
597 }  // namespace zucchini
598