Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 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 <stddef.h> |
| 8 | #include <stdint.h> |
| 9 | |
| 10 | #include <algorithm> |
| 11 | #include <cctype> |
| 12 | #include <initializer_list> |
| 13 | #include <map> |
| 14 | #include <sstream> |
| 15 | #include <string> |
| 16 | #include <vector> |
| 17 | |
Hans Wennborg | 5a340be | 2020-04-28 11:06:24 +0000 | [diff] [blame] | 18 | #include "base/check_op.h" |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 19 | #include "components/zucchini/address_translator.h" |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 20 | #include "testing/gtest/include/gtest/gtest.h" |
| 21 | |
| 22 | namespace zucchini { |
| 23 | |
| 24 | namespace { |
| 25 | |
| 26 | // "Clean slate" |code|s for branch instruction encodings with |disp| = 0, and |
| 27 | // if applicable, |cond| = 0. |
| 28 | uint32_t kCleanSlateB_A1 = 0x0A000000; // A24. |
| 29 | uint32_t kCleanSlateBL_A1 = 0x0B000000; // A24. |
| 30 | uint32_t kCleanSlateBLX_A2 = 0xFA000000; // A24. |
| 31 | uint16_t kCleanSlateB_T1 = 0xD000; // T8. |
| 32 | uint16_t kCleanSlateB_T2 = 0xE000; // T11. |
Samuel Huang | 3321a9e | 2019-01-16 15:31:06 +0000 | [diff] [blame] | 33 | uint32_t kCleanSlateB_T3 = 0xF0008000; // T20. |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 34 | // For T24 encodings, |disp| = 0 means J1 = J2 = 1, so include 0x00002800. |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 35 | uint32_t kCleanSlateB_T4 = 0xF0009000 | 0x00002800; // T24. |
| 36 | uint32_t kCleanSlateBL_T1 = 0xF000D000 | 0x00002800; // T24. |
| 37 | uint32_t kCleanSlateBLX_T2 = 0xF000C000 | 0x00002800; // T24. |
| 38 | |
| 39 | // For AArch64. |
| 40 | uint32_t kCleanSlate64TBZw = 0x36000000; // Immd14. |
| 41 | uint32_t kCleanSlate64TBZz = 0xB6000000; // Immd14. |
| 42 | uint32_t kCleanSlate64TBNZw = 0x37000000; // Immd14. |
| 43 | uint32_t kCleanSlate64TBNZz = 0xB7000000; // Immd14. |
| 44 | uint32_t kCleanSlate64Bcond = 0x54000000; // Immd19. |
| 45 | uint32_t kCleanSlate64CBZw = 0x34000000; // Immd19. |
| 46 | uint32_t kCleanSlate64CBZz = 0xB4000000; // Immd19. |
| 47 | uint32_t kCleanSlate64CBNZw = 0x35000000; // Immd19. |
| 48 | uint32_t kCleanSlate64CBNZz = 0xB5000000; // Immd19. |
| 49 | uint32_t kCleanSlate64B = 0x14000000; // Immd26. |
| 50 | uint32_t kCleanSlate64BL = 0x94000000; // Immd26. |
| 51 | |
| 52 | // Special case: Cond = 0xE => AL. |
| 53 | uint32_t kCleanSlateBAL_A1 = kCleanSlateB_A1 | (0xE << 28); // |
| 54 | |
| 55 | // Test helper: Extracts |components| from |value| (may be |code| or |disp|) |
| 56 | // based on |pattern|. Also performs consistency checks. On success, writes to |
| 57 | // |*components| and returns true. Otherwise returns false. |
| 58 | // Example (all numbers are in binary): |
| 59 | // |pattern| = "11110Scc cciiiiii 10(J1)0(J2)jjj jjjj...." |
| 60 | // |value| = 11110111 00111000 10 1 0 0 111 11000101 |
| 61 | // Result: Noting that all 0's and 1's are consistent, returns true with: |
| 62 | // |*components| = {S: 1, c: 1100, i: 111000, J1: 1, J2: 0, j: 1111100} |
| 63 | // Rules for |pattern|: |
| 64 | // * Spaces are ignored. |
| 65 | // * '.' means "don't care". |
| 66 | // * '0' and '1' are expected literals; mismatch leads to failure. |
| 67 | // * A variable name is specified as: |
| 68 | // * A single letter. |
| 69 | // * "(var)", where "var" is a name that begins with a letter. |
| 70 | // * If a variable's first letter is uppercase, then it's a singleton bit. |
| 71 | // * If repeated, consistency check is applied (must be identical). |
| 72 | // * If a variable's first letter is lowercase, then it spans multiple bits. |
| 73 | // * These need not be contiguous, but order is preserved (big-endian). |
| 74 | static bool SplitBits(const std::string& pattern, |
| 75 | uint32_t value, |
| 76 | std::map<std::string, uint32_t>* components) { |
| 77 | CHECK(components); |
| 78 | |
| 79 | // Split |pattern| into |token_list|. |
| 80 | std::vector<std::string> token_list; |
| 81 | size_t bracket_start = std::string::npos; |
| 82 | for (size_t i = 0; i < pattern.size(); ++i) { |
| 83 | char ch = pattern[i]; |
| 84 | if (bracket_start == std::string::npos) { |
| 85 | if (ch == '(') |
| 86 | bracket_start = i + 1; |
| 87 | else if (ch != ' ') // Ignore space. |
| 88 | token_list.push_back(std::string(1, ch)); |
| 89 | } else if (ch == ')') { |
| 90 | token_list.push_back(pattern.substr(bracket_start, i - bracket_start)); |
| 91 | bracket_start = std::string::npos; |
| 92 | } |
| 93 | } |
| 94 | CHECK_EQ(std::string::npos, bracket_start); // No dangling "(". |
| 95 | |
| 96 | // Process each token. |
| 97 | size_t num_tokens = token_list.size(); |
| 98 | std::map<std::string, uint32_t> temp_components; |
| 99 | CHECK(num_tokens == 32 || (num_tokens == 16 && value <= 0xFFFF)); |
| 100 | for (size_t i = 0; i < num_tokens; ++i) { |
| 101 | const std::string& token = token_list[i]; |
| 102 | CHECK(!token.empty()); |
| 103 | uint32_t bit = (value >> (num_tokens - 1 - i)) & 1; |
| 104 | if (token == "0" || token == "1") { |
| 105 | if (token[0] != static_cast<char>('0' + bit)) |
| 106 | return false; // Fail: Mismatch. |
| 107 | } else if (isupper(token[0])) { |
| 108 | if (temp_components.count(token)) { |
| 109 | if (temp_components[token] != bit) |
| 110 | return false; // Fail: Singleton bit not uniform. |
| 111 | } else { |
| 112 | temp_components[token] = bit; |
| 113 | } |
| 114 | } else if (islower(token[0])) { |
| 115 | temp_components[token] = (temp_components[token] << 1) | bit; |
| 116 | } else if (token != ".") { |
| 117 | return false; // Fail: Unrecognized token. |
| 118 | } |
| 119 | } |
| 120 | components->swap(temp_components); |
| 121 | return true; |
| 122 | } |
| 123 | |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 124 | // AArch32 or AArch64 instruction specification for tests. May be 16-bit or |
| 125 | // 32-bit (determined by INT_T). |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 126 | template <typename INT_T> |
| 127 | struct ArmRelInstruction { |
| 128 | ArmRelInstruction(const std::string& code_pattern_in, INT_T code) |
| 129 | : code_pattern(code_pattern_in), clean_slate_code(code) {} |
| 130 | |
| 131 | // Code pattern for SplitBits(). |
| 132 | std::string code_pattern; |
| 133 | |
| 134 | // "Clean slate" |code| encodes |disp| = 0. |
| 135 | INT_T clean_slate_code; |
| 136 | }; |
| 137 | |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 138 | // Tester for ARM Encode / Decode functions for |disp| <-> |code|. |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 139 | template <typename TRAITS> |
| 140 | class ArmTranslatorEncodeDecodeTest { |
| 141 | public: |
| 142 | using CODE_T = typename TRAITS::code_t; |
| 143 | |
| 144 | ArmTranslatorEncodeDecodeTest() {} |
| 145 | |
| 146 | // For each instruction (with |clean_slate_code| in |instr_list|) and for each |
| 147 | // |disp| in |good_disp_list|, forms |code| with |encode_fun()| and checks for |
| 148 | // success. Extracts |disp_out| with |decode_fun()| and checks that it's the |
| 149 | // original |disp|. For each (|disp|, |code|) pair, extracts components using |
| 150 | // SplitBits(), and checks that components from |toks_list| are identical. For |
| 151 | // each |disp| in |bad_disp_list|, checks that |decode_fun_()| fails. |
| 152 | void Run(const std::string& disp_pattern, |
| 153 | const std::vector<std::string>& toks_list, |
| 154 | const std::vector<ArmRelInstruction<CODE_T>>& instr_list, |
| 155 | const std::vector<arm_disp_t>& good_disp_list, |
| 156 | const std::vector<arm_disp_t>& bad_disp_list) { |
| 157 | ArmAlign (*decode_fun)(CODE_T, arm_disp_t*) = TRAITS::Decode; |
| 158 | bool (*encode_fun)(arm_disp_t, CODE_T*) = TRAITS::Encode; |
| 159 | |
| 160 | for (const ArmRelInstruction<CODE_T> instr : instr_list) { |
| 161 | // Parse clean slate code bytes, and ensure it's well-formed. |
| 162 | std::map<std::string, uint32_t> clean_slate_code_components; |
| 163 | EXPECT_TRUE(SplitBits(instr.code_pattern, instr.clean_slate_code, |
| 164 | &clean_slate_code_components)); |
| 165 | |
| 166 | for (arm_disp_t disp : good_disp_list) { |
| 167 | CODE_T code = instr.clean_slate_code; |
| 168 | // Encode |disp| to |code|. |
| 169 | EXPECT_TRUE((*encode_fun)(disp, &code)) << disp; |
| 170 | arm_disp_t disp_out = 0; |
| 171 | |
| 172 | // Extract components (performs consistency checks) and compare. |
| 173 | std::map<std::string, uint32_t> disp_components; |
| 174 | EXPECT_TRUE(SplitBits(disp_pattern, static_cast<uint32_t>(disp), |
| 175 | &disp_components)); |
| 176 | std::map<std::string, uint32_t> code_components; |
| 177 | EXPECT_TRUE(SplitBits(instr.code_pattern, code, &code_components)); |
| 178 | for (const std::string& tok : toks_list) { |
| 179 | EXPECT_EQ(1U, disp_components.count(tok)) << tok; |
| 180 | EXPECT_EQ(1U, code_components.count(tok)) << tok; |
| 181 | EXPECT_EQ(disp_components[tok], code_components[tok]) << tok; |
| 182 | } |
| 183 | |
| 184 | // Decode |code| to |disp_out|, check fidelity. |
| 185 | EXPECT_NE(kArmAlignFail, (*decode_fun)(code, &disp_out)); |
| 186 | EXPECT_EQ(disp, disp_out); |
| 187 | |
| 188 | // Sanity check: Re-encode |disp| into |code|, ensure no change. |
| 189 | CODE_T code_copy = code; |
| 190 | EXPECT_TRUE((*encode_fun)(disp, &code)); |
| 191 | EXPECT_EQ(code_copy, code); |
| 192 | |
| 193 | // Encode 0, ensure we get clean slate |code| back. |
| 194 | EXPECT_TRUE((*encode_fun)(0, &code)); |
| 195 | EXPECT_EQ(instr.clean_slate_code, code); |
| 196 | } |
| 197 | |
| 198 | for (arm_disp_t disp : bad_disp_list) { |
| 199 | CODE_T code = instr.clean_slate_code; |
| 200 | EXPECT_FALSE((*encode_fun)(disp, &code)) << disp; |
| 201 | // Value does not get modified after failure. |
| 202 | EXPECT_EQ(instr.clean_slate_code, code); |
| 203 | } |
| 204 | } |
| 205 | } |
| 206 | }; |
| 207 | |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 208 | // Tester for ARM Write / Read functions for |target_rva| <-> |code|. |
| 209 | template <typename TRAITS> |
| 210 | class ArmTranslatorWriteReadTest { |
| 211 | public: |
| 212 | using CODE_T = typename TRAITS::code_t; |
| 213 | |
| 214 | ArmTranslatorWriteReadTest() {} |
| 215 | |
| 216 | // Expects successful Write() to |clean_slate_code| for each |target_rva_list| |
| 217 | // RVA, using each |instr_rva_list| RVA, and that the resulting |code| leads |
| 218 | // to successful Read(), which recovers |instr_rva|. |
| 219 | void Accept(CODE_T clean_slate_code, |
| 220 | const std::vector<rva_t>& instr_rva_list, |
| 221 | const std::vector<rva_t>& target_rva_list) { |
| 222 | bool (*read_fun)(rva_t, CODE_T, rva_t*) = TRAITS::Read; |
| 223 | bool (*write_fun)(rva_t, rva_t, CODE_T*) = TRAITS::Write; |
| 224 | |
| 225 | for (rva_t instr_rva : instr_rva_list) { |
| 226 | for (rva_t target_rva : target_rva_list) { |
| 227 | CODE_T code = clean_slate_code; |
| 228 | // Write |target_rva| to |code|. |
| 229 | EXPECT_TRUE((*write_fun)(instr_rva, target_rva, &code)) << target_rva; |
| 230 | rva_t target_rva_out = kInvalidRva; |
| 231 | |
| 232 | // Read |code| to |target_rva_out|, check fidelity. |
| 233 | EXPECT_TRUE((*read_fun)(instr_rva, code, &target_rva_out)); |
| 234 | EXPECT_EQ(target_rva, target_rva_out); |
| 235 | |
| 236 | // Sanity check: Rewrite |target_rva| into |code|, ensure no change. |
| 237 | CODE_T code_copy = code; |
| 238 | EXPECT_TRUE((*write_fun)(instr_rva, target_rva, &code)); |
| 239 | EXPECT_EQ(code_copy, code); |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | // Expects failed Write() to |clean_slate_code| for each |target_rva_list| |
| 245 | // RVA, using each |instr_rva_list| RVA. |
| 246 | void Reject(CODE_T clean_slate_code, |
| 247 | const std::vector<rva_t>& instr_rva_list, |
| 248 | const std::vector<rva_t>& target_rva_list) { |
| 249 | bool (*write_fun)(rva_t, rva_t, CODE_T*) = TRAITS::Write; |
| 250 | |
| 251 | for (rva_t instr_rva : instr_rva_list) { |
| 252 | for (rva_t target_rva : target_rva_list) { |
| 253 | CODE_T code = clean_slate_code; |
| 254 | EXPECT_FALSE((*write_fun)(instr_rva, target_rva, &code)) << target_rva; |
| 255 | // Output variable is unmodified after failure. |
| 256 | EXPECT_EQ(clean_slate_code, code); |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | }; |
| 261 | |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 262 | } // namespace |
| 263 | |
| 264 | // Test for test helper. |
| 265 | TEST(ArmUtilsTest, SplitBits) { |
| 266 | // If |expected| == "BAD" then we expect failure. |
| 267 | auto run_test = [](const std::string& expected, const std::string& pattern, |
| 268 | uint32_t value) { |
| 269 | std::map<std::string, uint32_t> components; |
| 270 | if (expected == "BAD") { |
| 271 | EXPECT_FALSE(SplitBits(pattern, value, &components)); |
| 272 | EXPECT_TRUE(components.empty()); |
| 273 | } else { |
| 274 | EXPECT_TRUE(SplitBits(pattern, value, &components)); |
| 275 | std::ostringstream oss; |
| 276 | // Not using AsHex<>, since number of digits is not fixed. |
| 277 | oss << std::uppercase << std::hex; |
| 278 | std::string sep = ""; |
| 279 | for (auto it : components) { |
| 280 | oss << sep << it.first << "=" << it.second; |
| 281 | sep = ","; |
| 282 | } |
| 283 | EXPECT_EQ(expected, oss.str()); |
| 284 | } |
| 285 | }; |
| 286 | |
| 287 | run_test("a=ABCD0123", "aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa", 0xABCD0123); |
| 288 | run_test("a=ABCD,b=123", "aaaaaaaa aaaaaaaa bbbbbbbb bbbbbbbb", 0xABCD0123); |
| 289 | run_test("a=23,b=1,c=CD,d=AB", "dddddddd cccccccc bbbbbbbb aaaaaaaa", |
| 290 | 0xABCD0123); |
| 291 | run_test("", "........ ........ ........ ........", 0xABCD0123); |
| 292 | run_test("t=AC02", " tttt.... tt tt.... tttt....tttt.... ", 0xABCD0123); |
| 293 | |
| 294 | run_test("a=8,b=C,c=E,d1=F", "aaaabbbb cccc(d1)(d1)(d1)(d1)", 0x8CEF); |
| 295 | run_test("a=F,b=7,c=3,d1=1", "abc(d1)abc(d1) abc(d1)abc(d1)", 0x8CEF); |
| 296 | |
| 297 | run_test("A1=0,X=1", "(A1)XX(A1) X(A1)(A1)(A1) (X)(A1)(X)X(X)(X)X(A1)", |
| 298 | 0x68BE); |
| 299 | run_test("BAD", "(A1)XX(A1) X(A1)(A1)(A1) (X)(A1)(X)X(X)(X)X(A1)", 0x68BF); |
| 300 | run_test("BAD", "(A1)XX(A1) X(A1)(A1)(A1) (X)(A1)(X)X(X)(X)X(A1)", 0x683E); |
| 301 | |
| 302 | run_test("A=1,B=0,a=C", "AAAAaaaa BBBB01..", 0xFC06); |
| 303 | run_test("A=1,B=0,a=4", "AAAAaaaa BBBB01..", 0xF406); |
| 304 | run_test("A=0,B=1,a=C", "AAAAaaaa BBBB01..", 0x0CF5); |
| 305 | run_test("BAD", "AAAAaaaa BBBB01..", 0xEC06); // Non-uniform A. |
| 306 | run_test("BAD", "AAAAaaaa BBBB01..", 0xFC16); // Non-uniform B. |
| 307 | run_test("BAD", "AAAAaaaa BBBB01..", 0xFC02); // Constant mismatch. |
| 308 | } |
| 309 | |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 310 | TEST(AArch32Rel32Translator, Fetch) { |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 311 | std::vector<uint8_t> bytes = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; |
| 312 | ConstBufferView region(&bytes[0], bytes.size()); |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 313 | AArch32Rel32Translator translator; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 314 | EXPECT_EQ(0x76543210U, translator.FetchArmCode32(region, 0U)); |
| 315 | EXPECT_EQ(0xFEDCBA98U, translator.FetchArmCode32(region, 4U)); |
| 316 | |
| 317 | EXPECT_EQ(0x3210U, translator.FetchThumb2Code16(region, 0U)); |
| 318 | EXPECT_EQ(0xFEDCU, translator.FetchThumb2Code16(region, 6U)); |
| 319 | |
| 320 | EXPECT_EQ(0x32107654U, translator.FetchThumb2Code32(region, 0U)); |
| 321 | EXPECT_EQ(0xBA98FEDCU, translator.FetchThumb2Code32(region, 4U)); |
| 322 | } |
| 323 | |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 324 | TEST(AArch32Rel32Translator, Store) { |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 325 | std::vector<uint8_t> expected = { |
| 326 | 0xFF, 0xFF, 0xFF, 0xFF, // Padding. |
| 327 | 0x10, 0x32, 0x54, 0x76, // ARM 32-bit. |
| 328 | 0xFF, 0xFF, // Padding. |
| 329 | 0x42, 0x86, // THUMB2 16-bit. |
| 330 | 0xFF, 0xFF, // Padding. |
| 331 | 0xDC, 0xFE, 0x98, 0xBA, // THUMB2 32-bit. |
| 332 | 0xFF, 0xFF, 0xFF, 0xFF // Padding. |
| 333 | }; |
| 334 | |
| 335 | std::vector<uint8_t> bytes(4 * 2 + 2 * 3 + 4 * 2, 0xFF); |
| 336 | MutableBufferView region(&bytes[0], bytes.size()); |
| 337 | CHECK_EQ(expected.size(), bytes.size()); |
| 338 | |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 339 | AArch32Rel32Translator translator; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 340 | translator.StoreArmCode32(region, 4U, 0x76543210U); |
| 341 | translator.StoreThumb2Code16(region, 10U, 0x8642U); |
| 342 | translator.StoreThumb2Code32(region, 14U, 0xFEDCBA98U); |
| 343 | |
| 344 | EXPECT_EQ(expected, bytes); |
| 345 | } |
| 346 | |
| 347 | // Detailed test of Encode/Decode: Check valid and invalid |disp| for various |
| 348 | // clean slate |code| cases. Also check |disp| and |code| binary components, |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 349 | // which in AArch32Rel32Translator comments. |
| 350 | TEST(AArch32Rel32Translator, EncodeDecode) { |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 351 | // A24 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 352 | ArmTranslatorEncodeDecodeTest<AArch32Rel32Translator::AddrTraits_A24> |
| 353 | test_A24; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 354 | for (int cond = 0; cond <= 0x0E; ++cond) { |
| 355 | ArmRelInstruction<uint32_t> B_A1_cond("cccc1010 Siiiiiii iiiiiiii iiiiiiii", |
| 356 | kCleanSlateB_A1 | (cond << 28)); |
| 357 | ArmRelInstruction<uint32_t> BL_A1_cond( |
| 358 | "cccc1011 Siiiiiii iiiiiiii iiiiiiii", kCleanSlateBL_A1 | (cond << 28)); |
| 359 | test_A24.Run("SSSSSSSi iiiiiiii iiiiiiii iiiiii00", {"S", "i"}, |
| 360 | {B_A1_cond, BL_A1_cond}, |
| 361 | {0x01FFFFFC, -0x02000000, 0, 4, -4, 0x40, 0x44}, |
| 362 | {2, -2, 0x41, 0x42, 0x43, 0x02000000, -0x02000004}); |
| 363 | } |
| 364 | // BLX encoding A2, which has 2-byte alignment. |
| 365 | ArmRelInstruction<uint32_t> BLX_A2("1111101H Siiiiiii iiiiiiii iiiiiiii", |
| 366 | kCleanSlateBLX_A2); |
| 367 | test_A24.Run("SSSSSSSi iiiiiiii iiiiiiii iiiiiiH0", {"S", "i", "H"}, {BLX_A2}, |
| 368 | {0x01FFFFFC, 0x01FFFFFE, -0x02000000, 0, 2, -2, 4, 0x40, 0x42}, |
| 369 | {1, -1, 0x41, 0x43, 0x02000000, -0x02000002}); |
| 370 | |
| 371 | // T8 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 372 | ArmTranslatorEncodeDecodeTest<AArch32Rel32Translator::AddrTraits_T8> test_T8; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 373 | for (int cond = 0; cond <= 0x0E; ++cond) { |
| 374 | ArmRelInstruction<uint16_t> B_T1_cond("1101cccc Siiiiiii", |
| 375 | kCleanSlateB_T1 | (cond << 8)); |
| 376 | test_T8.Run("SSSSSSSS SSSSSSSS SSSSSSSS iiiiiii0", {"S", "i"}, {B_T1_cond}, |
| 377 | {0x00FE, -0x0100, 0, 2, -2, 4, 0x40, 0x42}, |
| 378 | {1, -1, 0x41, 0x43, 0x0100, -0x0102}); |
| 379 | } |
| 380 | ArmRelInstruction<uint16_t> B_T1_invalid("11011111 ........", |
| 381 | kCleanSlateB_T1 | (0x0F << 8)); |
| 382 | test_T8.Run("........ ........ ........ ........", std::vector<std::string>(), |
| 383 | {B_T1_invalid}, std::vector<arm_disp_t>(), |
| 384 | {0x00FE, -0x0100, 0, 2, 4, 0x40, 0x41, 0x0100, -0x0102}); |
| 385 | |
| 386 | // T11 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 387 | ArmTranslatorEncodeDecodeTest<AArch32Rel32Translator::AddrTraits_T11> |
| 388 | test_T11; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 389 | ArmRelInstruction<uint16_t> B_T2("11100Sii iiiiiiii", kCleanSlateB_T2); |
| 390 | test_T11.Run("SSSSSSSS SSSSSSSS SSSSSiii iiiiiii0", {"S", "i"}, {B_T2}, |
| 391 | {0x07FE, -0x0800, 0, 2, -2, 4, 0x40, 0x42}, |
| 392 | {1, -1, 0x41, 0x43, 0x0800, -0x0802}); |
| 393 | |
Samuel Huang | 3321a9e | 2019-01-16 15:31:06 +0000 | [diff] [blame] | 394 | // T20 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 395 | ArmTranslatorEncodeDecodeTest<AArch32Rel32Translator::AddrTraits_T20> |
| 396 | test_T20; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 397 | for (int cond = 0; cond <= 0x0E; ++cond) { |
| 398 | ArmRelInstruction<uint32_t> B_T3_cond( |
| 399 | "11110Scc cciiiiii 10(J1)0(J2)jjj jjjjjjjj", |
| 400 | kCleanSlateB_T3 | (cond << 22)); |
Samuel Huang | 3321a9e | 2019-01-16 15:31:06 +0000 | [diff] [blame] | 401 | test_T20.Run("SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0", |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 402 | {"S", "J2", "J1", "i", "j"}, {B_T3_cond}, |
| 403 | {0x000FFFFE, -0x00100000, 0, 2, -2, 4, 0x40, 0x42}, |
| 404 | {1, -1, 0x41, 0x43, 0x00100000, -0x00100002}); |
| 405 | } |
| 406 | ArmRelInstruction<uint32_t> B_T3_invalid( |
| 407 | "11110.11 11...... 10.0.... ........", kCleanSlateB_T3 | (0x0F << 22)); |
Samuel Huang | 3321a9e | 2019-01-16 15:31:06 +0000 | [diff] [blame] | 408 | test_T20.Run("........ ........ ........ ........", |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 409 | std::vector<std::string>(), {B_T3_invalid}, |
| 410 | std::vector<arm_disp_t>(), |
| 411 | {0x000FFFFE, -0x00100000, 0, 2, 4, 0x40, 0x42, 1, 0x41, 0x43, |
| 412 | 0x00100000, -0x00100002}); |
| 413 | |
| 414 | // T24 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 415 | ArmTranslatorEncodeDecodeTest<AArch32Rel32Translator::AddrTraits_T24> |
| 416 | test_T24; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 417 | // "Clean slate" means J1 = J2 = 1, so we include 0x00002800. |
| 418 | ArmRelInstruction<uint32_t> B_T4("11110Sii iiiiiiii 10(J1)1(J2)jjj jjjjjjjj", |
| 419 | kCleanSlateB_T4); |
| 420 | ArmRelInstruction<uint32_t> BL_T1("11110Sii iiiiiiii 11(J1)1(J2)jjj jjjjjjjj", |
| 421 | kCleanSlateBL_T1); |
| 422 | test_T24.Run("SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0", |
| 423 | {"S", "i", "j"}, // Skip "J1", "J2", "I1", "I2" checks. |
| 424 | {B_T4, BL_T1}, |
| 425 | {0x00FFFFFE, -0x01000000, 0, 2, -2, 4, -4, 0x40, 0x42}, |
| 426 | {1, -1, 0x41, 0x43, 0x01000000, -0x01000002}); |
| 427 | |
| 428 | // For BLX encoding T2, |disp| must be multiple of 4. |
| 429 | ArmRelInstruction<uint32_t> BLX_T2( |
| 430 | "11110Sii iiiiiiii 11(J1)0(J2)jjj jjjjjjj0", kCleanSlateBLX_T2); |
| 431 | test_T24.Run( |
| 432 | "SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjj00", |
| 433 | {"S", "i", "j"}, // Skip "J1", "J2", "I1", "I2" checks. |
| 434 | {BLX_T2}, {0x00FFFFFC, -0x01000000, 0, 4, -4, 0x40}, |
| 435 | {1, -1, 2, -2, 0x41, 0x42, 0x43, 0x00FFFFFE, 0x01000000, -0x01000002}); |
| 436 | } |
| 437 | |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 438 | TEST(AArch32Rel32Translator, WriteRead) { |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 439 | std::vector<rva_t> aligned4; |
| 440 | std::vector<rva_t> misaligned4; |
| 441 | std::vector<rva_t> aligned2; |
| 442 | std::vector<rva_t> misaligned2; |
| 443 | for (rva_t rva = 0x1FFC; rva <= 0x2010; ++rva) { |
| 444 | ((rva % 4 == 0) ? aligned4 : misaligned4).push_back(rva); |
| 445 | ((rva % 2 == 0) ? aligned2 : misaligned2).push_back(rva); |
| 446 | } |
| 447 | CHECK_EQ(6U, aligned4.size()); |
| 448 | CHECK_EQ(15U, misaligned4.size()); |
| 449 | CHECK_EQ(11U, aligned2.size()); |
| 450 | CHECK_EQ(10U, misaligned2.size()); |
| 451 | |
| 452 | // Helpers to convert an instruction's RVA to PC. |
| 453 | auto pcArm = [](rva_t instr_rva) -> rva_t { return instr_rva + 8; }; |
| 454 | auto pcThumb2 = [](rva_t instr_rva) -> rva_t { return instr_rva + 4; }; |
| 455 | |
| 456 | // A24 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 457 | ArmTranslatorWriteReadTest<AArch32Rel32Translator::AddrTraits_A24> test_A24; |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 458 | for (uint32_t clean_slate_code : {kCleanSlateB_A1, kCleanSlateBL_A1}) { |
| 459 | test_A24.Accept(clean_slate_code, aligned4, aligned4); |
| 460 | test_A24.Reject(clean_slate_code, aligned4, misaligned4); |
| 461 | test_A24.Reject(clean_slate_code, misaligned4, aligned4); |
| 462 | test_A24.Reject(clean_slate_code, misaligned4, misaligned4); |
| 463 | // Signed (24 + 2)-bit range, 4-byte aligned: [-0x02000000, 0x01FFFFFC]. |
| 464 | test_A24.Accept(clean_slate_code, {0x15000000}, |
| 465 | {pcArm(0x13000000), pcArm(0x16FFFFFC)}); |
| 466 | test_A24.Reject(clean_slate_code, {0x15000000}, |
| 467 | {pcArm(0x13000000 - 4), pcArm(0x16FFFFFC + 4)}); |
| 468 | } |
| 469 | |
| 470 | // BLX complication: ARM -> THUMB2. |
| 471 | test_A24.Accept(kCleanSlateBLX_A2, aligned4, aligned2); |
| 472 | test_A24.Reject(kCleanSlateBLX_A2, aligned4, misaligned2); |
| 473 | test_A24.Reject(kCleanSlateBLX_A2, misaligned4, aligned2); |
| 474 | test_A24.Reject(kCleanSlateBLX_A2, misaligned4, misaligned2); |
| 475 | test_A24.Accept(kCleanSlateBLX_A2, {0x15000000}, |
| 476 | {pcArm(0x13000000), pcArm(0x16FFFFFE)}); |
| 477 | test_A24.Reject(kCleanSlateBLX_A2, {0x15000000}, |
| 478 | {pcArm(0x13000000 - 4), pcArm(0x13000000 - 2), |
| 479 | pcArm(0x16FFFFFE + 2), pcArm(0x16FFFFFE + 4)}); |
| 480 | |
| 481 | // T8 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 482 | ArmTranslatorWriteReadTest<AArch32Rel32Translator::AddrTraits_T8> test_T8; |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 483 | test_T8.Accept(kCleanSlateB_T1, aligned2, aligned2); |
| 484 | test_T8.Reject(kCleanSlateB_T1, aligned2, misaligned2); |
| 485 | test_T8.Reject(kCleanSlateB_T1, misaligned2, aligned2); |
| 486 | test_T8.Reject(kCleanSlateB_T1, misaligned2, misaligned2); |
| 487 | // Signed (8 + 1)-bit range, 2-byte aligned: [-0x0100, 0x00FE]. |
| 488 | test_T8.Accept(kCleanSlateB_T1, {0x10000500}, |
| 489 | {pcThumb2(0x10000400), pcThumb2(0x100005FE)}); |
| 490 | test_T8.Reject(kCleanSlateB_T1, {0x10000500}, |
| 491 | {pcThumb2(0x10000400 - 2), pcThumb2(0x100005FE + 2)}); |
| 492 | |
| 493 | // T11 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 494 | ArmTranslatorWriteReadTest<AArch32Rel32Translator::AddrTraits_T11> test_T11; |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 495 | test_T11.Accept(kCleanSlateB_T2, aligned2, aligned2); |
| 496 | test_T11.Reject(kCleanSlateB_T2, aligned2, misaligned2); |
| 497 | test_T11.Reject(kCleanSlateB_T2, misaligned2, aligned2); |
| 498 | test_T11.Reject(kCleanSlateB_T2, misaligned2, misaligned2); |
| 499 | // Signed (11 + 1)-bit range, 2-byte aligned: [-0x0800, 0x07FE]. |
| 500 | test_T11.Accept(kCleanSlateB_T2, {0x10003000}, |
| 501 | {pcThumb2(0x10002800), pcThumb2(0x100037FE)}); |
| 502 | test_T11.Reject(kCleanSlateB_T2, {0x10003000}, |
| 503 | {pcThumb2(0x10002800 - 2), pcThumb2(0x100037FE + 2)}); |
| 504 | |
| 505 | // T20 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 506 | ArmTranslatorWriteReadTest<AArch32Rel32Translator::AddrTraits_T20> test_T20; |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 507 | test_T20.Accept(kCleanSlateB_T3, aligned2, aligned2); |
| 508 | test_T20.Reject(kCleanSlateB_T3, aligned2, misaligned2); |
| 509 | test_T20.Reject(kCleanSlateB_T3, misaligned2, aligned2); |
| 510 | test_T20.Reject(kCleanSlateB_T3, misaligned2, misaligned2); |
| 511 | // Signed (20 + 1)-bit range, 2-byte aligned: [-0x00100000, 0x000FFFFE]. |
| 512 | test_T20.Accept(kCleanSlateB_T3, {0x10300000}, |
| 513 | {pcThumb2(0x10200000), pcThumb2(0x103FFFFE)}); |
| 514 | test_T20.Reject(kCleanSlateB_T3, {0x10300000}, |
| 515 | {pcThumb2(0x10200000 - 2), pcThumb2(0x103FFFFE + 2)}); |
| 516 | |
| 517 | // T24 tests. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 518 | ArmTranslatorWriteReadTest<AArch32Rel32Translator::AddrTraits_T24> test_T24; |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 519 | for (uint32_t clean_slate_code : {kCleanSlateB_T4, kCleanSlateBL_T1}) { |
| 520 | test_T24.Accept(clean_slate_code, aligned2, aligned2); |
| 521 | test_T24.Reject(clean_slate_code, aligned2, misaligned2); |
| 522 | test_T24.Reject(clean_slate_code, misaligned2, aligned2); |
| 523 | test_T24.Reject(clean_slate_code, misaligned2, misaligned2); |
| 524 | // Signed (24 + 1)-bit range, 2-byte aligned: [-0x01000000, 0x00FFFFFE]. |
| 525 | test_T24.Accept(clean_slate_code, {0x16000000}, |
| 526 | {pcThumb2(0x15000000), pcThumb2(0x16FFFFFE)}); |
| 527 | test_T24.Reject(clean_slate_code, {0x16000000}, |
| 528 | {pcThumb2(0x15000000 - 2), pcThumb2(0x16FFFFFE + 2)}); |
| 529 | } |
| 530 | |
| 531 | // BLX complication: THUMB2 -> ARM. |
| 532 | test_T24.Accept(kCleanSlateBLX_T2, aligned2, aligned4); |
| 533 | test_T24.Reject(kCleanSlateBLX_T2, aligned2, misaligned4); |
| 534 | test_T24.Reject(kCleanSlateBLX_T2, misaligned2, aligned4); |
| 535 | test_T24.Reject(kCleanSlateBLX_T2, misaligned2, misaligned4); |
| 536 | test_T24.Accept(kCleanSlateBLX_T2, {0x16000000}, |
| 537 | {pcThumb2(0x15000000), pcThumb2(0x16FFFFFC)}); |
| 538 | test_T24.Reject(kCleanSlateBLX_T2, {0x16000000}, |
| 539 | {pcThumb2(0x15000000 - 4), pcThumb2(0x15000000 - 2), |
| 540 | pcThumb2(0x16FFFFFC + 2), pcThumb2(0x16FFFFFC + 4)}); |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 541 | } |
| 542 | |
| 543 | // Typical usage in |target_rva| extraction. |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 544 | TEST(AArch32Rel32Translator, Main) { |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 545 | // ARM mode (32-bit). |
| 546 | // 00103050: 00 01 02 EA B 00183458 ; B encoding A1 (cond = AL). |
| 547 | { |
| 548 | rva_t instr_rva = 0x00103050U; |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 549 | AArch32Rel32Translator translator; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 550 | std::vector<uint8_t> bytes = {0x00, 0x01, 0x02, 0xEA}; |
| 551 | MutableBufferView region(&bytes[0], bytes.size()); |
| 552 | uint32_t code = translator.FetchArmCode32(region, 0U); |
| 553 | EXPECT_EQ(0xEA020100U, code); |
| 554 | |
| 555 | // |code| <-> |disp|. |
| 556 | arm_disp_t disp = 0; |
| 557 | EXPECT_EQ(kArmAlign4, translator.DecodeA24(code, &disp)); |
| 558 | EXPECT_EQ(+0x00080400, disp); |
| 559 | |
| 560 | uint32_t code_from_disp = kCleanSlateBAL_A1; |
| 561 | EXPECT_TRUE(translator.EncodeA24(disp, &code_from_disp)); |
| 562 | EXPECT_EQ(code, code_from_disp); |
| 563 | |
| 564 | // |code| <-> |target_rva|. |
| 565 | rva_t target_rva = kInvalidRva; |
| 566 | EXPECT_TRUE(translator.ReadA24(instr_rva, code, &target_rva)); |
| 567 | // 0x00103050 + 8 + 0x00080400. |
| 568 | EXPECT_EQ(0x00183458U, target_rva); |
| 569 | |
| 570 | uint32_t code_from_rva = kCleanSlateBAL_A1; |
| 571 | EXPECT_TRUE(translator.WriteA24(instr_rva, target_rva, &code_from_rva)); |
| 572 | EXPECT_EQ(code, code_from_rva); |
| 573 | } |
| 574 | |
| 575 | // THUMB2 mode (16-bit). |
| 576 | // 001030A2: F3 E7 B 0010308C ; B encoding T2. |
| 577 | { |
| 578 | rva_t instr_rva = 0x001030A2U; |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 579 | AArch32Rel32Translator translator; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 580 | std::vector<uint8_t> bytes = {0xF3, 0xE7}; |
| 581 | MutableBufferView region(&bytes[0], bytes.size()); |
| 582 | uint16_t code = translator.FetchThumb2Code16(region, 0U); |
| 583 | // Sii iiiiiiii = 111 11110011 = -1101 = -0x0D. |
| 584 | EXPECT_EQ(0xE7F3U, code); |
| 585 | |
| 586 | // |code| <-> |disp|. |
| 587 | arm_disp_t disp = 0; |
| 588 | EXPECT_EQ(kArmAlign2, translator.DecodeT11(code, &disp)); |
| 589 | EXPECT_EQ(-0x0000001A, disp); // -0x0D * 2 = -0x1A. |
| 590 | |
| 591 | uint16_t code_from_disp = kCleanSlateB_T2; |
| 592 | EXPECT_TRUE(translator.EncodeT11(disp, &code_from_disp)); |
| 593 | EXPECT_EQ(code, code_from_disp); |
| 594 | |
| 595 | // |code| <-> |target_rva|. |
| 596 | rva_t target_rva = kInvalidRva; |
| 597 | EXPECT_TRUE(translator.ReadT11(instr_rva, code, &target_rva)); |
| 598 | // 0x001030A2 + 4 - 0x0000001A. |
| 599 | EXPECT_EQ(0x0010308CU, target_rva); |
| 600 | |
| 601 | uint16_t code_from_rva = kCleanSlateB_T2; |
| 602 | EXPECT_TRUE(translator.WriteT11(instr_rva, target_rva, &code_from_rva)); |
| 603 | EXPECT_EQ(code, code_from_rva); |
| 604 | } |
| 605 | |
| 606 | // THUMB2 mode (32-bit). |
| 607 | // 001030A2: 00 F0 01 FA BL 001034A8 ; BL encoding T1. |
| 608 | { |
| 609 | rva_t instr_rva = 0x001030A2U; |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 610 | AArch32Rel32Translator translator; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 611 | std::vector<uint8_t> bytes = {0x00, 0xF0, 0x01, 0xFA}; |
| 612 | MutableBufferView region(&bytes[0], bytes.size()); |
| 613 | uint32_t code = translator.FetchThumb2Code32(region, 0U); |
| 614 | EXPECT_EQ(0xF000FA01U, code); |
| 615 | |
| 616 | // |code| <-> |disp|. |
| 617 | arm_disp_t disp = 0; |
| 618 | EXPECT_EQ(kArmAlign2, translator.DecodeT24(code, &disp)); |
| 619 | EXPECT_EQ(+0x00000402, disp); |
| 620 | |
| 621 | uint32_t code_from_disp = kCleanSlateBL_T1; |
| 622 | EXPECT_TRUE(translator.EncodeT24(disp, &code_from_disp)); |
| 623 | EXPECT_EQ(code, code_from_disp); |
| 624 | |
| 625 | // |code| <-> |target_rva|. |
| 626 | rva_t target_rva = kInvalidRva; |
| 627 | EXPECT_TRUE(translator.ReadT24(instr_rva, code, &target_rva)); |
| 628 | // 0x001030A2 + 4 + 0x00000002. |
| 629 | EXPECT_EQ(0x001034A8U, target_rva); |
| 630 | |
| 631 | uint32_t code_from_rva = kCleanSlateBL_T1; |
| 632 | EXPECT_TRUE(translator.WriteT24(instr_rva, target_rva, &code_from_rva)); |
| 633 | EXPECT_EQ(code, code_from_rva); |
| 634 | } |
| 635 | } |
| 636 | |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 637 | TEST(AArch32Rel32Translator, BLXComplication) { |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 638 | auto run_test = [](rva_t instr_rva, |
| 639 | std::vector<uint8_t> bytes, // Pass by value. |
| 640 | uint32_t expected_code, arm_disp_t expected_disp, |
| 641 | uint32_t clean_slate_code, rva_t expected_target_rva) { |
Samuel Huang | 769128e | 2021-07-22 16:26:47 +0000 | [diff] [blame] | 642 | AArch32Rel32Translator translator; |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 643 | MutableBufferView region(&bytes[0], bytes.size()); |
| 644 | uint32_t code = translator.FetchThumb2Code32(region, 0U); |
| 645 | EXPECT_EQ(expected_code, code); |
| 646 | |
| 647 | // |code| <-> |disp|. |
| 648 | arm_disp_t disp = 0; |
| 649 | EXPECT_TRUE(translator.DecodeT24(code, &disp)); |
| 650 | EXPECT_EQ(expected_disp, disp); |
| 651 | |
| 652 | uint32_t code_from_disp = clean_slate_code; |
| 653 | EXPECT_TRUE(translator.EncodeT24(disp, &code_from_disp)); |
| 654 | EXPECT_EQ(code, code_from_disp); |
| 655 | |
| 656 | // |code| <-> |target_rva|. |
| 657 | rva_t target_rva = kInvalidRva; |
| 658 | EXPECT_TRUE(translator.ReadT24(instr_rva, code, &target_rva)); |
| 659 | EXPECT_EQ(expected_target_rva, target_rva); |
| 660 | |
| 661 | uint32_t code_from_rva = clean_slate_code; |
| 662 | EXPECT_TRUE(translator.WriteT24(instr_rva, target_rva, &code_from_rva)); |
| 663 | EXPECT_EQ(code, code_from_rva); |
| 664 | }; |
| 665 | |
| 666 | // No complication, 4-byte aligned. |
| 667 | // 001030A0: 01 F0 06 B0 B 005040B0 ; B encoding T4. |
| 668 | run_test(0x001030A0U, // Multiple of 4. |
| 669 | {0x01, 0xF0, 0x06, 0xB0}, 0xF001B006U, 0x0040100C, kCleanSlateB_T4, |
| 670 | // "Canonical" |target_rva|: 0x001030A0 + 4 + 0x0040100C. |
| 671 | 0x005040B0U); |
| 672 | |
| 673 | // No complication, not 4-byte aligned. |
| 674 | // 001030A2: 01 F0 06 B0 B 005040B2 ; B encoding T4. |
| 675 | run_test(0x001030A2U, // Shift by 2: Not multiple of 4. |
| 676 | {0x01, 0xF0, 0x06, 0xB0}, 0xF001B006U, 0x0040100C, kCleanSlateB_T4, |
| 677 | // Shifted by 2: 0x001030A2 + 4 + 0x0040100C. |
| 678 | 0x005040B2U); |
| 679 | |
| 680 | // Repeat the above, but use BLX instead of B. |
| 681 | |
| 682 | // BLX complication, 4-byte aligned. |
| 683 | // 001030A0: 01 F0 06 E0 BLX 005040B0 ; BLX encoding T2. |
| 684 | run_test(0x001030A0U, // Multiple of 4. |
| 685 | {0x01, 0xF0, 0x06, 0xE0}, 0xF001E006U, 0x0040100C, kCleanSlateBLX_T2, |
| 686 | // Canonical again: align_down_4(0x001030A0 + 4 + 0x0040100C). |
| 687 | 0x005040B0U); |
| 688 | |
| 689 | // BLX complication, not 4-byte aligned. |
| 690 | // 001030A2: 01 F0 06 E0 BLX 005040B0 ; BLX encoding T2. |
| 691 | run_test(0x001030A2U, // Shift by 2: Not multiple of 4. |
| 692 | {0x01, 0xF0, 0x06, 0xE0}, 0xF001E006U, 0x0040100C, kCleanSlateBLX_T2, |
| 693 | // No shift: align_down_4(0x001030A2 + 4 + 0x0040100C). |
| 694 | 0x005040B0U); |
| 695 | } |
| 696 | |
| 697 | TEST(AArch64Rel32Translator, FetchStore) { |
| 698 | std::vector<uint8_t> bytes = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; |
| 699 | std::vector<uint8_t> expected = {0xAB, 0x33, 0x22, 0x11, |
| 700 | 0x69, 0x5A, 0xFF, 0x00}; |
| 701 | MutableBufferView region(&bytes[0], bytes.size()); |
| 702 | AArch64Rel32Translator translator; |
| 703 | EXPECT_EQ(0x76543210U, translator.FetchCode32(region, 0U)); |
| 704 | EXPECT_EQ(0xFEDCBA98U, translator.FetchCode32(region, 4U)); |
| 705 | |
| 706 | translator.StoreCode32(region, 0U, 0x112233ABU); |
| 707 | translator.StoreCode32(region, 4U, 0x00FF5A69); |
| 708 | EXPECT_EQ(expected, bytes); |
| 709 | } |
| 710 | |
| 711 | TEST(AArch64Rel32Translator, EncodeDecode) { |
| 712 | // Immd14 tests. |
| 713 | ArmTranslatorEncodeDecodeTest<AArch64Rel32Translator::AddrTraits_Immd14> |
| 714 | test_immd14; |
| 715 | for (int b40 : {0, 1, 7, 31}) { |
| 716 | uint32_t b40_mask = b40 << 19; |
| 717 | for (int Rt : {0, 1, 15, 30}) { |
| 718 | uint32_t mask = b40_mask | Rt; |
| 719 | ArmRelInstruction<uint32_t> TBZw_Rt("00110110 bbbbbSii iiiiiiii iiittttt", |
| 720 | kCleanSlate64TBZw | mask); |
| 721 | ArmRelInstruction<uint32_t> TBZz_Rt("10110110 bbbbbSii iiiiiiii iiittttt", |
| 722 | kCleanSlate64TBZz | mask); |
| 723 | ArmRelInstruction<uint32_t> TBNZw_Rt( |
| 724 | "00110111 bbbbbSii iiiiiiii iiittttt", kCleanSlate64TBNZw | mask); |
| 725 | ArmRelInstruction<uint32_t> TBNZz_Rt( |
| 726 | "10110111 bbbbbSii iiiiiiii iiittttt", kCleanSlate64TBNZz | mask); |
| 727 | test_immd14.Run("SSSSSSSS SSSSSSSS Siiiiiii iiiiii00", {"S", "i"}, |
| 728 | {TBZw_Rt, TBZz_Rt, TBNZw_Rt, TBNZz_Rt}, |
| 729 | {0x00007FFC, -0x00008000, 0, 4, -4, 0x40, 0x44}, |
| 730 | {2, -2, 0x41, 0x42, 0x43, 0x00008000, -0x00008004}); |
| 731 | } |
| 732 | } |
| 733 | |
| 734 | // Immd19 tests. |
| 735 | ArmTranslatorEncodeDecodeTest<AArch64Rel32Translator::AddrTraits_Immd19> |
| 736 | test_immd19; |
| 737 | for (int cond = 0; cond <= 0x0E; ++cond) { |
| 738 | ArmRelInstruction<uint32_t> B_cond("01010100 Siiiiiii iiiiiiii iii0cccc", |
| 739 | kCleanSlate64Bcond | cond); |
| 740 | test_immd19.Run("SSSSSSSS SSSSiiii iiiiiiii iiiiii00", {"S", "i"}, {B_cond}, |
| 741 | {0x000FFFFC, -0x00100000, 0, 4, -4, 0x40, 0x44}, |
| 742 | {2, -2, 0x41, 0x42, 0x43, 0x00100000, -0x00100004}); |
| 743 | } |
| 744 | for (int Rt : {0, 1, 15, 30}) { |
| 745 | ArmRelInstruction<uint32_t> CBZw_Rt("00110100 Siiiiiii iiiiiiii iiittttt", |
| 746 | kCleanSlate64CBZw | Rt); |
| 747 | ArmRelInstruction<uint32_t> CBZz_Rt("10110100 Siiiiiii iiiiiiii iiittttt", |
| 748 | kCleanSlate64CBZz | Rt); |
| 749 | ArmRelInstruction<uint32_t> CBNZw_Rt("00110101 Siiiiiii iiiiiiii iiittttt", |
| 750 | kCleanSlate64CBNZw | Rt); |
| 751 | ArmRelInstruction<uint32_t> CBNZz_Rt("10110101 Siiiiiii iiiiiiii iiittttt", |
| 752 | kCleanSlate64CBNZz | Rt); |
| 753 | test_immd19.Run("SSSSSSSS SSSSiiii iiiiiiii iiiiii00", {"S", "i"}, |
| 754 | {CBZw_Rt, CBZz_Rt, CBNZw_Rt, CBNZz_Rt}, |
| 755 | {0x000FFFFC, -0x00100000, 0, 4, -4, 0x40, 0x44}, |
| 756 | {2, -2, 0x41, 0x42, 0x43, 0x00100000, -0x00100004}); |
| 757 | } |
| 758 | |
| 759 | // Immd26 tests. |
| 760 | ArmTranslatorEncodeDecodeTest<AArch64Rel32Translator::AddrTraits_Immd26> |
| 761 | test_immd26; |
| 762 | ArmRelInstruction<uint32_t> B("000101Si iiiiiiii iiiiiiii iiiiiiii", |
| 763 | kCleanSlate64B); |
| 764 | ArmRelInstruction<uint32_t> BL("100101Si iiiiiiii iiiiiiii iiiiiiii", |
| 765 | kCleanSlate64BL); |
| 766 | test_immd26.Run("SSSSSiii iiiiiiii iiiiiiii iiiiii00", {"S", "i"}, {B, BL}, |
| 767 | {0x07FFFFFC, -0x08000000, 0, 4, -4, 0x40, 0x44}, |
| 768 | {2, -2, 0x41, 0x42, 0x43, 0x08000000, -0x08000004}); |
| 769 | } |
| 770 | |
| 771 | TEST(AArch64Rel32Translator, WriteRead) { |
Samuel Huang | 4d20d00 | 2019-01-18 19:36:50 +0000 | [diff] [blame] | 772 | std::vector<rva_t> aligned4; |
| 773 | std::vector<rva_t> misaligned4; |
| 774 | for (rva_t rva = 0x1FFC; rva <= 0x2010; ++rva) { |
| 775 | ((rva % 4 == 0) ? aligned4 : misaligned4).push_back(rva); |
| 776 | } |
| 777 | CHECK_EQ(6U, aligned4.size()); |
| 778 | CHECK_EQ(15U, misaligned4.size()); |
| 779 | |
| 780 | // Helper to convert an instruction's RVA to PC. |
| 781 | auto pcAArch64 = [](rva_t instr_rva) -> rva_t { return instr_rva; }; |
| 782 | |
| 783 | // Immd14 tests. |
| 784 | ArmTranslatorWriteReadTest<AArch64Rel32Translator::AddrTraits_Immd14> |
| 785 | test_immd14; |
| 786 | for (uint32_t clean_slate_code : {kCleanSlate64TBZw, kCleanSlate64TBZz, |
| 787 | kCleanSlate64TBNZw, kCleanSlate64TBNZz}) { |
| 788 | test_immd14.Accept(clean_slate_code, aligned4, aligned4); |
| 789 | test_immd14.Reject(clean_slate_code, aligned4, misaligned4); |
| 790 | test_immd14.Reject(clean_slate_code, misaligned4, aligned4); |
| 791 | test_immd14.Reject(clean_slate_code, misaligned4, misaligned4); |
| 792 | // Signed (14 + 2)-bit range, 4-byte aligned: [-0x00008000, 0x00007FFC]. |
| 793 | test_immd14.Accept(clean_slate_code, {0x10040000}, |
| 794 | {pcAArch64(0x10038000), pcAArch64(0x10047FFC)}); |
| 795 | test_immd14.Reject(clean_slate_code, {0x15000000}, |
| 796 | {pcAArch64(0x10038000 - 4), pcAArch64(0x10047FFC + 4)}); |
| 797 | } |
| 798 | |
| 799 | // Immd19 tests. |
| 800 | ArmTranslatorWriteReadTest<AArch64Rel32Translator::AddrTraits_Immd19> |
| 801 | test_immd19; |
| 802 | for (uint32_t clean_slate_code : |
| 803 | {kCleanSlate64Bcond, kCleanSlate64CBZw, kCleanSlate64CBZz, |
| 804 | kCleanSlate64CBNZw, kCleanSlate64CBNZz}) { |
| 805 | test_immd19.Accept(clean_slate_code, aligned4, aligned4); |
| 806 | test_immd19.Reject(clean_slate_code, aligned4, misaligned4); |
| 807 | test_immd19.Reject(clean_slate_code, misaligned4, aligned4); |
| 808 | test_immd19.Reject(clean_slate_code, misaligned4, misaligned4); |
| 809 | // Signed (19 + 2)-bit range, 4-byte aligned: [-0x00100000, 0x000FFFFC]. |
| 810 | test_immd19.Accept(clean_slate_code, {0x10300000}, |
| 811 | {pcAArch64(0x10200000), pcAArch64(0x103FFFFC)}); |
| 812 | test_immd19.Reject(clean_slate_code, {0x10300000}, |
| 813 | {pcAArch64(0x10200000 - 4), pcAArch64(0x103FFFFC + 4)}); |
| 814 | } |
| 815 | |
| 816 | // Immd26 tests. |
| 817 | ArmTranslatorWriteReadTest<AArch64Rel32Translator::AddrTraits_Immd26> |
| 818 | test_immd26; |
| 819 | for (uint32_t clean_slate_code : {kCleanSlate64B, kCleanSlate64BL}) { |
| 820 | test_immd26.Accept(clean_slate_code, aligned4, aligned4); |
| 821 | test_immd26.Reject(clean_slate_code, aligned4, misaligned4); |
| 822 | test_immd26.Reject(clean_slate_code, misaligned4, aligned4); |
| 823 | test_immd26.Reject(clean_slate_code, misaligned4, misaligned4); |
| 824 | // Signed (26 + 2)-bit range, 4-byte aligned: [-0x08000000, 0x07FFFFFC]. |
| 825 | test_immd26.Accept(clean_slate_code, {0x30000000}, |
| 826 | {pcAArch64(0x28000000), pcAArch64(0x37FFFFFC)}); |
| 827 | test_immd26.Reject(clean_slate_code, {0x30000000}, |
| 828 | {pcAArch64(0x28000000 - 4), pcAArch64(0x37FFFFFC + 4)}); |
| 829 | } |
Samuel Huang | 65b242d | 2019-01-11 20:00:13 +0000 | [diff] [blame] | 830 | } |
| 831 | |
| 832 | // Typical usage in |target_rva| extraction. |
| 833 | TEST(AArch64Rel32Translator, Main) { |
| 834 | // 00103050: 02 01 02 14 B 00183458 |
| 835 | rva_t instr_rva = 0x00103050U; |
| 836 | AArch64Rel32Translator translator; |
| 837 | std::vector<uint8_t> bytes = {0x02, 0x01, 0x02, 0x14}; |
| 838 | MutableBufferView region(&bytes[0], bytes.size()); |
| 839 | uint32_t code = translator.FetchCode32(region, 0U); |
| 840 | EXPECT_EQ(0x14020102U, code); |
| 841 | |
| 842 | // |code| <-> |disp|. |
| 843 | arm_disp_t disp = 0; |
| 844 | EXPECT_TRUE(translator.DecodeImmd26(code, &disp)); |
| 845 | EXPECT_EQ(+0x00080408, disp); |
| 846 | |
| 847 | uint32_t code_from_disp = kCleanSlate64B; |
| 848 | EXPECT_TRUE(translator.EncodeImmd26(disp, &code_from_disp)); |
| 849 | EXPECT_EQ(code, code_from_disp); |
| 850 | |
| 851 | // |code| <-> |target_rva|. |
| 852 | rva_t target_rva = kInvalidRva; |
| 853 | EXPECT_TRUE(translator.ReadImmd26(instr_rva, code, &target_rva)); |
| 854 | // 0x00103050 + 0 + 0x00080408. |
| 855 | EXPECT_EQ(0x00183458U, target_rva); |
| 856 | |
| 857 | uint32_t code_from_rva = kCleanSlate64B; |
| 858 | EXPECT_TRUE(translator.WriteImmd26(instr_rva, target_rva, &code_from_rva)); |
| 859 | EXPECT_EQ(code, code_from_rva); |
| 860 | } |
| 861 | |
| 862 | } // namespace zucchini |