// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/zucchini/arm_utils.h" #include "components/zucchini/algorithm.h" namespace zucchini { namespace { inline bool IsMisaligned(rva_t rva, ArmAlign align) { return (rva & (align - 1)) != 0; } } // namespace /******** AArch32Rel32Translator ********/ AArch32Rel32Translator::AArch32Rel32Translator() = default; // The mapping between ARM instruction "Code" to "Displacement" involves complex // bit manipulation. The comments below annotate bits mappings using a string. // * Bits are listed from highest-order to lowerst-order (like in the manual). // * '0' and '1' denote literals. // * Uppercase letters denote a single bit in "Code". For example, 'S' denotes // a sign bit that gets extended in "Displacement". To follow naming in the // manual, these may enumerated, and written as "(I1)", "(I2)", etc. // * Lowercase letters denote bit fields with orders preserved. // static ArmAlign AArch32Rel32Translator::DecodeA24(uint32_t code32, arm_disp_t* disp) { // Handle multiple instructions. Let cccc != 1111: // B encoding A1: // Code: cccc1010 Siiiiiii iiiiiiii iiiiiiii // Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiii00 // BL encoding A1: // Code: cccc1011 Siiiiiii iiiiiiii iiiiiiii // Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiii00 // BLX encoding A2: // Code: 1111101H Siiiiiii iiiiiiii iiiiiiii // Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiiiH0 uint8_t bits = GetUnsignedBits<24, 27>(code32); if (bits == 0xA || bits == 0xB) { // B, BL, or BLX. *disp = GetSignedBits<0, 23>(code32) << 2; uint8_t cond = GetUnsignedBits<28, 31>(code32); if (cond == 0xF) { // BLX. uint32_t H = GetBit<24>(code32); *disp |= H << 1; return kArmAlign2; } return kArmAlign4; } return kArmAlignFail; } // static bool AArch32Rel32Translator::EncodeA24(arm_disp_t disp, uint32_t* code32) { uint32_t t = *code32; uint8_t bits = GetUnsignedBits<24, 27>(t); if (bits == 0xA || bits == 0xB) { // B, BL, or BLX. if (!SignedFit<26>(disp)) // Detect overflow. return false; uint8_t cond = GetUnsignedBits<28, 31>(t); if (cond == 0xF) { if (disp % 2) // BLX (encoding A2) requires 2-byte alignment. return false; uint32_t H = GetBit<1>(disp); t = (t & 0xFEFFFFFF) | (H << 24); } else { if (disp % 4) // B and BL require 4-byte alignment. return false; } t = (t & 0xFF000000) | ((disp >> 2) & 0x00FFFFFF); *code32 = t; return true; } return false; } // static bool AArch32Rel32Translator::ReadA24(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; ArmAlign target_align = DecodeA24(code32, &disp); if (target_align == kArmAlignFail) return false; *target_rva = GetArmTargetRvaFromDisp(instr_rva, disp, target_align); return true; } // static bool AArch32Rel32Translator::WriteA24(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { constexpr ArmAlign kInstrAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign)) return false; // Dummy decode to get |target_align|. arm_disp_t dummy_disp; ArmAlign target_align = DecodeA24(*code32, &dummy_disp); if (target_align == kArmAlignFail || IsMisaligned(target_rva, target_align)) return false; arm_disp_t disp = GetArmDispFromTargetRva(instr_rva, target_rva, target_align); return EncodeA24(disp, code32); } // static ArmAlign AArch32Rel32Translator::DecodeT8(uint16_t code16, arm_disp_t* disp) { if ((code16 & 0xF000) == 0xD000 && (code16 & 0x0F00) != 0x0F00) { // B encoding T1: // Code: 1101cccc Siiiiiii // Displacement: SSSSSSSS SSSSSSSS SSSSSSSS iiiiiii0 *disp = GetSignedBits<0, 7>(code16) << 1; return kArmAlign2; } return kArmAlignFail; } // static bool AArch32Rel32Translator::EncodeT8(arm_disp_t disp, uint16_t* code16) { uint16_t t = *code16; if ((t & 0xF000) == 0xD000 && (t & 0x0F00) != 0x0F00) { if (disp % 2) // Require 2-byte alignment. return false; if (!SignedFit<9>(disp)) // Detect overflow. return false; t = (t & 0xFF00) | ((disp >> 1) & 0x00FF); *code16 = t; return true; } return false; } // static bool AArch32Rel32Translator::ReadT8(rva_t instr_rva, uint16_t code16, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; ArmAlign target_align = DecodeT8(code16, &disp); if (target_align == kArmAlignFail) return false; *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } // static bool AArch32Rel32Translator::WriteT8(rva_t instr_rva, rva_t target_rva, uint16_t* code16) { constexpr ArmAlign kInstrAlign = kArmAlign2; constexpr ArmAlign kTargetAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign) || IsMisaligned(target_rva, kTargetAlign)) { return false; } arm_disp_t disp = GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign); return EncodeT8(disp, code16); } // static ArmAlign AArch32Rel32Translator::DecodeT11(uint16_t code16, arm_disp_t* disp) { if ((code16 & 0xF800) == 0xE000) { // B encoding T2: // Code: 11100Sii iiiiiiii // Displacement: SSSSSSSS SSSSSSSS SSSSSiii iiiiiii0 *disp = GetSignedBits<0, 10>(code16) << 1; return kArmAlign2; } return kArmAlignFail; } // static bool AArch32Rel32Translator::EncodeT11(arm_disp_t disp, uint16_t* code16) { uint16_t t = *code16; if ((t & 0xF800) == 0xE000) { if (disp % 2) // Require 2-byte alignment. return false; if (!SignedFit<12>(disp)) // Detect overflow. return false; t = (t & 0xF800) | ((disp >> 1) & 0x07FF); *code16 = t; return true; } return false; } // static bool AArch32Rel32Translator::ReadT11(rva_t instr_rva, uint16_t code16, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; ArmAlign target_align = DecodeT11(code16, &disp); if (target_align == kArmAlignFail) return false; *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } // static bool AArch32Rel32Translator::WriteT11(rva_t instr_rva, rva_t target_rva, uint16_t* code16) { constexpr ArmAlign kInstrAlign = kArmAlign2; constexpr ArmAlign kTargetAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign) || IsMisaligned(target_rva, kTargetAlign)) { return false; } arm_disp_t disp = GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign); return EncodeT11(disp, code16); } // static ArmAlign AArch32Rel32Translator::DecodeT20(uint32_t code32, arm_disp_t* disp) { if ((code32 & 0xF800D000) == 0xF0008000 && (code32 & 0x03C00000) != 0x03C00000) { // B encoding T3. Note the reversal of "(J1)" and "(J2)". // Code: 11110Scc cciiiiii 10(J1)0(J2)jjj jjjjjjjj // Displacement: SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0 uint32_t imm11 = GetUnsignedBits<0, 10>(code32); // jj...j. uint32_t J2 = GetBit<11>(code32); uint32_t J1 = GetBit<13>(code32); uint32_t imm6 = GetUnsignedBits<16, 21>(code32); // ii...i. uint32_t S = GetBit<26>(code32); uint32_t t = (imm6 << 12) | (imm11 << 1); t |= (S << 20) | (J2 << 19) | (J1 << 18); *disp = SignExtend<20, int32_t>(t); return kArmAlign2; } return kArmAlignFail; } // static bool AArch32Rel32Translator::EncodeT20(arm_disp_t disp, uint32_t* code32) { uint32_t t = *code32; if ((t & 0xF800D000) == 0xF0008000 && (t & 0x03C00000) != 0x03C00000) { if (disp % 2) // Require 2-byte alignment. return false; if (!SignedFit<21>(disp)) // Detect overflow. return false; uint32_t S = GetBit<20>(disp); uint32_t J2 = GetBit<19>(disp); uint32_t J1 = GetBit<18>(disp); uint32_t imm6 = GetUnsignedBits<12, 17>(disp); // ii...i. uint32_t imm11 = GetUnsignedBits<1, 11>(disp); // jj...j. t &= 0xFBC0D000; t |= (S << 26) | (imm6 << 16) | (J1 << 13) | (J2 << 11) | imm11; *code32 = t; return true; } return false; } // static bool AArch32Rel32Translator::ReadT20(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; ArmAlign target_align = DecodeT20(code32, &disp); if (target_align == kArmAlignFail) return false; *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } // static bool AArch32Rel32Translator::WriteT20(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { constexpr ArmAlign kInstrAlign = kArmAlign2; constexpr ArmAlign kTargetAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign) || IsMisaligned(target_rva, kTargetAlign)) { return false; } arm_disp_t disp = GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign); return EncodeT20(disp, code32); } // static ArmAlign AArch32Rel32Translator::DecodeT24(uint32_t code32, arm_disp_t* disp) { uint32_t bits = code32 & 0xF800D000; if (bits == 0xF0009000 || bits == 0xF000D000 || bits == 0xF000C000) { // Let I1 = J1 ^ S ^ 1, I2 = J2 ^ S ^ 1. // B encoding T4: // Code: 11110Sii iiiiiiii 10(J1)1(J2)jjj jjjjjjjj // Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0 // BL encoding T1: // Code: 11110Sii iiiiiiii 11(J1)1(J2)jjj jjjjjjjj // Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0 // BLX encoding T2: H should be 0: // Code: 11110Sii iiiiiiii 11(J1)0(J2)jjj jjjjjjjH // Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjH0 uint32_t imm11 = GetUnsignedBits<0, 10>(code32); // jj...j. uint32_t J2 = GetBit<11>(code32); uint32_t J1 = GetBit<13>(code32); uint32_t imm10 = GetUnsignedBits<16, 25>(code32); // ii...i. uint32_t S = GetBit<26>(code32); uint32_t t = (imm10 << 12) | (imm11 << 1); t |= (S << 24) | ((J1 ^ S ^ 1) << 23) | ((J2 ^ S ^ 1) << 22); t = SignExtend<24, int32_t>(t); // BLX encoding T2 requires final target to be 4-byte aligned by rounding // downward. This is applied to |t| *after* clipping. ArmAlign target_align = kArmAlign2; if (bits == 0xF000C000) { uint32_t H = GetBit<0>(code32); if (H) return kArmAlignFail; // Illegal instruction: H must be 0. target_align = kArmAlign4; } *disp = static_cast(t); return target_align; } return kArmAlignFail; } // static bool AArch32Rel32Translator::EncodeT24(arm_disp_t disp, uint32_t* code32) { uint32_t t = *code32; uint32_t bits = t & 0xF800D000; if (bits == 0xF0009000 || bits == 0xF000D000 || bits == 0xF000C000) { if (disp % 2) // Require 2-byte alignment. return false; // BLX encoding T2 requires H == 0, and that |disp| results in |target_rva| // with a 4-byte aligned address. if (bits == 0xF000C000) { uint32_t H = GetBit<1>(disp); if (H) return false; // Illegal |disp|: H must be 0. } if (!SignedFit<25>(disp)) // Detect overflow. return false; uint32_t imm11 = GetUnsignedBits<1, 11>(disp); // jj...j. uint32_t imm10 = GetUnsignedBits<12, 21>(disp); // ii...i. uint32_t I2 = GetBit<22>(disp); uint32_t I1 = GetBit<23>(disp); uint32_t S = GetBit<24>(disp); t &= 0xF800D000; t |= (S << 26) | (imm10 << 16) | ((I1 ^ S ^ 1) << 13) | ((I2 ^ S ^ 1) << 11) | imm11; *code32 = t; return true; } return false; } // static bool AArch32Rel32Translator::ReadT24(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; ArmAlign target_align = DecodeT24(code32, &disp); if (target_align == kArmAlignFail) return false; *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } // static bool AArch32Rel32Translator::WriteT24(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { constexpr ArmAlign kInstrAlign = kArmAlign2; if (IsMisaligned(instr_rva, kInstrAlign)) return false; // Dummy decode to get |target_align|. arm_disp_t dummy_disp; ArmAlign target_align = DecodeT24(*code32, &dummy_disp); if (target_align == kArmAlignFail || IsMisaligned(target_rva, target_align)) return false; arm_disp_t disp = GetThumb2DispFromTargetRva(instr_rva, target_rva, target_align); return EncodeT24(disp, code32); } /******** AArch64Rel32Translator ********/ AArch64Rel32Translator::AArch64Rel32Translator() = default; // static ArmAlign AArch64Rel32Translator::DecodeImmd14(uint32_t code32, arm_disp_t* disp) { // TBZ: // Code: b0110110 bbbbbSii iiiiiiii iiittttt // Displacement: SSSSSSSS SSSSSSSS Siiiiiii iiiiii00 // TBNZ: // Code: b0110111 bbbbbSii iiiiiiii iiittttt // Displacement: SSSSSSSS SSSSSSSS Siiiiiii iiiiii00 uint32_t bits = code32 & 0x7F000000; if (bits == 0x36000000 || bits == 0x37000000) { *disp = GetSignedBits<5, 18>(code32) << 2; return kArmAlign4; } return kArmAlignFail; } // static bool AArch64Rel32Translator::EncodeImmd14(arm_disp_t disp, uint32_t* code32) { uint32_t t = *code32; uint32_t bits = t & 0x7F000000; if (bits == 0x36000000 || bits == 0x37000000) { if (disp % 4) // Require 4-byte alignment. return false; if (!SignedFit<16>(disp)) // Detect overflow. return false; uint32_t imm14 = GetUnsignedBits<2, 15>(disp); // ii...i. t &= 0xFFF8001F; t |= imm14 << 5; *code32 = t; return true; } return false; } // static bool AArch64Rel32Translator::ReadImmd14(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; if (DecodeImmd14(code32, &disp) == kArmAlignFail) return false; *target_rva = GetTargetRvaFromDisp(instr_rva, disp); return true; } // static bool AArch64Rel32Translator::WriteImmd14(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { constexpr ArmAlign kInstrAlign = kArmAlign4; constexpr ArmAlign kTargetAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign) || IsMisaligned(target_rva, kTargetAlign)) { return false; } arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva); return EncodeImmd14(disp, code32); } // static ArmAlign AArch64Rel32Translator::DecodeImmd19(uint32_t code32, arm_disp_t* disp) { // B.cond: // Code: 01010100 Siiiiiii iiiiiiii iii0cccc // Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00 // CBZ: // Code: z0110100 Siiiiiii iiiiiiii iiittttt // Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00 // CBNZ: // Code: z0110101 Siiiiiii iiiiiiii iiittttt // Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00 uint32_t bits1 = code32 & 0xFF000010; uint32_t bits2 = code32 & 0x7F000000; if (bits1 == 0x54000000 || bits2 == 0x34000000 || bits2 == 0x35000000) { *disp = GetSignedBits<5, 23>(code32) << 2; return kArmAlign4; } return kArmAlignFail; } // static bool AArch64Rel32Translator::EncodeImmd19(arm_disp_t disp, uint32_t* code32) { uint32_t t = *code32; uint32_t bits1 = t & 0xFF000010; uint32_t bits2 = t & 0x7F000000; if (bits1 == 0x54000000 || bits2 == 0x34000000 || bits2 == 0x35000000) { if (disp % 4) // Require 4-byte alignment. return false; if (!SignedFit<21>(disp)) // Detect overflow. return false; uint32_t imm19 = GetUnsignedBits<2, 20>(disp); // ii...i. t &= 0xFF00001F; t |= imm19 << 5; *code32 = t; return true; } return false; } // static bool AArch64Rel32Translator::ReadImmd19(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; if (DecodeImmd19(code32, &disp) == kArmAlignFail) return false; *target_rva = GetTargetRvaFromDisp(instr_rva, disp); return true; } // static bool AArch64Rel32Translator::WriteImmd19(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { constexpr ArmAlign kInstrAlign = kArmAlign4; constexpr ArmAlign kTargetAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign) || IsMisaligned(target_rva, kTargetAlign)) { return false; } arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva); return EncodeImmd19(disp, code32); } // static ArmAlign AArch64Rel32Translator::DecodeImmd26(uint32_t code32, arm_disp_t* disp) { // B: // Code: 000101Si iiiiiiii iiiiiiii iiiiiiii // Displacement: SSSSSiii iiiiiiii iiiiiiii iiiiii00 // BL: // Code: 100101Si iiiiiiii iiiiiiii iiiiiiii // Displacement: SSSSSiii iiiiiiii iiiiiiii iiiiii00 uint32_t bits = code32 & 0xFC000000; if (bits == 0x14000000 || bits == 0x94000000) { *disp = GetSignedBits<0, 25>(code32) << 2; return kArmAlign4; } return kArmAlignFail; } // static bool AArch64Rel32Translator::EncodeImmd26(arm_disp_t disp, uint32_t* code32) { uint32_t t = *code32; uint32_t bits = t & 0xFC000000; if (bits == 0x14000000 || bits == 0x94000000) { if (disp % 4) // Require 4-byte alignment. return false; if (!SignedFit<28>(disp)) // Detect overflow. return false; uint32_t imm26 = GetUnsignedBits<2, 27>(disp); // ii...i. t &= 0xFC000000; t |= imm26; *code32 = t; return true; } return false; } // static bool AArch64Rel32Translator::ReadImmd26(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { constexpr ArmAlign kInstrAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign)) return false; arm_disp_t disp; if (DecodeImmd26(code32, &disp) == kArmAlignFail) return false; *target_rva = GetTargetRvaFromDisp(instr_rva, disp); return true; } // static bool AArch64Rel32Translator::WriteImmd26(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { constexpr ArmAlign kInstrAlign = kArmAlign4; constexpr ArmAlign kTargetAlign = kArmAlign4; if (IsMisaligned(instr_rva, kInstrAlign) || IsMisaligned(target_rva, kTargetAlign)) { return false; } arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva); return EncodeImmd26(disp, code32); } } // namespace zucchini