blob: 1eb01c3730eb2304dbb98a16749d453d9837fc36 [file] [log] [blame]
// 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
/******** Arm32Rel32Translator ********/
Arm32Rel32Translator::Arm32Rel32Translator() = 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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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<int32_t>(t);
return target_align;
}
return kArmAlignFail;
}
// static
bool Arm32Rel32Translator::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 Arm32Rel32Translator::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 Arm32Rel32Translator::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