fuzz_with_unicorn.cpp
1 /* This file is part of the dynarmic project. 2 * Copyright (c) 2018 MerryMage 3 * SPDX-License-Identifier: 0BSD 4 */ 5 6 #include <algorithm> 7 #include <cstring> 8 #include <string> 9 #include <vector> 10 11 #include <catch2/catch_test_macros.hpp> 12 #include <mcl/scope_exit.hpp> 13 #include <mcl/stdint.hpp> 14 15 #include "../fuzz_util.h" 16 #include "../rand_int.h" 17 #include "../unicorn_emu/a64_unicorn.h" 18 #include "./testenv.h" 19 #include "dynarmic/common/fp/fpcr.h" 20 #include "dynarmic/common/fp/fpsr.h" 21 #include "dynarmic/common/llvm_disassemble.h" 22 #include "dynarmic/frontend/A64/a64_location_descriptor.h" 23 #include "dynarmic/frontend/A64/a64_types.h" 24 #include "dynarmic/frontend/A64/decoder/a64.h" 25 #include "dynarmic/frontend/A64/translate/a64_translate.h" 26 #include "dynarmic/ir/basic_block.h" 27 #include "dynarmic/ir/opcodes.h" 28 #include "dynarmic/ir/opt/passes.h" 29 30 // Must be declared last for all necessary operator<< to be declared prior to this. 31 #include <fmt/format.h> 32 #include <fmt/ostream.h> 33 34 using namespace Dynarmic; 35 36 static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) { 37 const A64::LocationDescriptor location{pc, {}}; 38 IR::Block block{location}; 39 bool should_continue = A64::TranslateSingleInstruction(block, location, instruction); 40 if (!should_continue && !is_last_inst) 41 return false; 42 if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal)) 43 return false; 44 for (const auto& ir_inst : block) { 45 switch (ir_inst.GetOpcode()) { 46 case IR::Opcode::A64ExceptionRaised: 47 case IR::Opcode::A64CallSupervisor: 48 case IR::Opcode::A64DataCacheOperationRaised: 49 case IR::Opcode::A64GetCNTPCT: 50 return false; 51 default: 52 continue; 53 } 54 } 55 return true; 56 } 57 58 static u32 GenRandomInst(u64 pc, bool is_last_inst) { 59 static const struct InstructionGeneratorInfo { 60 std::vector<InstructionGenerator> generators; 61 std::vector<InstructionGenerator> invalid; 62 } instructions = [] { 63 const std::vector<std::tuple<std::string, const char*>> list{ 64 #define INST(fn, name, bitstring) {#fn, bitstring}, 65 #include "dynarmic/frontend/A64/decoder/a64.inc" 66 #undef INST 67 }; 68 69 std::vector<InstructionGenerator> generators; 70 std::vector<InstructionGenerator> invalid; 71 72 // List of instructions not to test 73 const std::vector<std::string> do_not_test{ 74 // Unimplemented in QEMU 75 "STLLR", 76 // Unimplemented in QEMU 77 "LDLAR", 78 // Dynarmic and QEMU currently differ on how the exclusive monitor's address range works. 79 "STXR", 80 "STLXR", 81 "STXP", 82 "STLXP", 83 "LDXR", 84 "LDAXR", 85 "LDXP", 86 "LDAXP", 87 // Behaviour differs from QEMU 88 "MSR_reg", 89 "MSR_imm", 90 "MRS", 91 }; 92 93 for (const auto& [fn, bitstring] : list) { 94 if (fn == "UnallocatedEncoding") { 95 continue; 96 } 97 if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { 98 invalid.emplace_back(InstructionGenerator{bitstring}); 99 continue; 100 } 101 generators.emplace_back(InstructionGenerator{bitstring}); 102 } 103 return InstructionGeneratorInfo{generators, invalid}; 104 }(); 105 106 while (true) { 107 const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1); 108 const u32 inst = instructions.generators[index].Generate(); 109 110 if (std::any_of(instructions.invalid.begin(), instructions.invalid.end(), [inst](const auto& invalid) { return invalid.Match(inst); })) { 111 continue; 112 } 113 if (ShouldTestInst(inst, pc, is_last_inst)) { 114 return inst; 115 } 116 } 117 } 118 119 static u32 GenFloatInst(u64 pc, bool is_last_inst) { 120 static const std::vector<InstructionGenerator> instruction_generators = [] { 121 const std::vector<std::tuple<std::string, std::string, const char*>> list{ 122 #define INST(fn, name, bitstring) {#fn, #name, bitstring}, 123 #include "dynarmic/frontend/A64/decoder/a64.inc" 124 #undef INST 125 }; 126 127 // List of instructions not to test 128 const std::vector<std::string> do_not_test{}; 129 130 std::vector<InstructionGenerator> result; 131 132 for (const auto& [fn, name, bitstring] : list) { 133 (void)name; 134 135 if (fn[0] != 'F') { 136 continue; 137 } else if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { 138 continue; 139 } 140 result.emplace_back(InstructionGenerator{bitstring}); 141 } 142 143 return result; 144 }(); 145 146 while (true) { 147 const size_t index = RandInt<size_t>(0, instruction_generators.size() - 1); 148 const u32 instruction = instruction_generators[index].Generate(); 149 150 if (ShouldTestInst(instruction, pc, is_last_inst)) { 151 return instruction; 152 } 153 } 154 } 155 156 static Dynarmic::A64::UserConfig GetUserConfig(A64TestEnv& jit_env) { 157 Dynarmic::A64::UserConfig jit_user_config{&jit_env}; 158 jit_user_config.optimizations &= ~OptimizationFlag::FastDispatch; 159 // The below corresponds to the settings for qemu's aarch64_max_initfn 160 jit_user_config.dczid_el0 = 7; 161 jit_user_config.ctr_el0 = 0x80038003; 162 return jit_user_config; 163 } 164 165 static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env, const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) { 166 jit_env.code_mem = instructions; 167 uni_env.code_mem = instructions; 168 jit_env.code_mem.emplace_back(0x14000000); // B . 169 uni_env.code_mem.emplace_back(0x14000000); // B . 170 jit_env.code_mem_start_address = instructions_start; 171 uni_env.code_mem_start_address = instructions_start; 172 jit_env.modified_memory.clear(); 173 uni_env.modified_memory.clear(); 174 jit_env.interrupts.clear(); 175 uni_env.interrupts.clear(); 176 177 const u64 initial_sp = RandInt<u64>(0x30'0000'0000, 0x40'0000'0000) * 4; 178 179 jit.SetRegisters(regs); 180 jit.SetVectors(vecs); 181 jit.SetPC(instructions_start); 182 jit.SetSP(initial_sp); 183 jit.SetFpcr(fpcr); 184 jit.SetFpsr(0); 185 jit.SetPstate(pstate); 186 jit.ClearCache(); 187 uni.SetRegisters(regs); 188 uni.SetVectors(vecs); 189 uni.SetPC(instructions_start); 190 uni.SetSP(initial_sp); 191 uni.SetFpcr(fpcr); 192 uni.SetFpsr(0); 193 uni.SetPstate(pstate); 194 uni.ClearPageCache(); 195 196 jit_env.ticks_left = instructions.size(); 197 jit.Run(); 198 199 uni_env.ticks_left = instructions.size(); 200 uni.Run(); 201 202 SCOPE_FAIL { 203 fmt::print("Instruction Listing:\n"); 204 for (u32 instruction : instructions) { 205 fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch64(instruction)); 206 } 207 fmt::print("\n"); 208 209 fmt::print("Initial register listing:\n"); 210 for (size_t i = 0; i < regs.size(); ++i) { 211 fmt::print("{:3s}: {:016x}\n", A64::RegToString(static_cast<A64::Reg>(i)), regs[i]); 212 } 213 for (size_t i = 0; i < vecs.size(); ++i) { 214 fmt::print("{:3s}: {:016x}{:016x}\n", A64::VecToString(static_cast<A64::Vec>(i)), vecs[i][1], vecs[i][0]); 215 } 216 fmt::print("sp : {:016x}\n", initial_sp); 217 fmt::print("pc : {:016x}\n", instructions_start); 218 fmt::print("p : {:08x}\n", pstate); 219 fmt::print("fpcr {:08x}\n", fpcr); 220 fmt::print("fpcr.AHP {}\n", FP::FPCR{fpcr}.AHP()); 221 fmt::print("fpcr.DN {}\n", FP::FPCR{fpcr}.DN()); 222 fmt::print("fpcr.FZ {}\n", FP::FPCR{fpcr}.FZ()); 223 fmt::print("fpcr.RMode {}\n", static_cast<size_t>(FP::FPCR{fpcr}.RMode())); 224 fmt::print("fpcr.FZ16 {}\n", FP::FPCR{fpcr}.FZ16()); 225 fmt::print("\n"); 226 227 fmt::print("Final register listing:\n"); 228 fmt::print(" unicorn dynarmic\n"); 229 const auto uni_regs = uni.GetRegisters(); 230 for (size_t i = 0; i < regs.size(); ++i) { 231 fmt::print("{:3s}: {:016x} {:016x} {}\n", A64::RegToString(static_cast<A64::Reg>(i)), uni_regs[i], jit.GetRegisters()[i], uni_regs[i] != jit.GetRegisters()[i] ? "*" : ""); 232 } 233 const auto uni_vecs = uni.GetVectors(); 234 for (size_t i = 0; i < vecs.size(); ++i) { 235 fmt::print("{:3s}: {:016x}{:016x} {:016x}{:016x} {}\n", A64::VecToString(static_cast<A64::Vec>(i)), 236 uni_vecs[i][1], uni_vecs[i][0], 237 jit.GetVectors()[i][1], jit.GetVectors()[i][0], 238 uni_vecs[i] != jit.GetVectors()[i] ? "*" : ""); 239 } 240 fmt::print("sp : {:016x} {:016x} {}\n", uni.GetSP(), jit.GetSP(), uni.GetSP() != jit.GetSP() ? "*" : ""); 241 fmt::print("pc : {:016x} {:016x} {}\n", uni.GetPC(), jit.GetPC(), uni.GetPC() != jit.GetPC() ? "*" : ""); 242 fmt::print("p : {:08x} {:08x} {}\n", uni.GetPstate(), jit.GetPstate(), (uni.GetPstate() & 0xF0000000) != (jit.GetPstate() & 0xF0000000) ? "*" : ""); 243 fmt::print("qc : {:08x} {:08x} {}\n", uni.GetFpsr(), jit.GetFpsr(), FP::FPSR{uni.GetFpsr()}.QC() != FP::FPSR{jit.GetFpsr()}.QC() ? "*" : ""); 244 fmt::print("\n"); 245 246 fmt::print("Modified memory:\n"); 247 fmt::print(" uni dyn\n"); 248 auto uni_iter = uni_env.modified_memory.begin(); 249 auto jit_iter = jit_env.modified_memory.begin(); 250 while (uni_iter != uni_env.modified_memory.end() || jit_iter != jit_env.modified_memory.end()) { 251 if (uni_iter == uni_env.modified_memory.end() || (jit_iter != jit_env.modified_memory.end() && uni_iter->first > jit_iter->first)) { 252 fmt::print("{:016x}: {:02x} *\n", jit_iter->first, jit_iter->second); 253 jit_iter++; 254 } else if (jit_iter == jit_env.modified_memory.end() || jit_iter->first > uni_iter->first) { 255 fmt::print("{:016x}: {:02x} *\n", uni_iter->first, uni_iter->second); 256 uni_iter++; 257 } else if (uni_iter->first == jit_iter->first) { 258 fmt::print("{:016x}: {:02x} {:02x} {}\n", uni_iter->first, uni_iter->second, jit_iter->second, uni_iter->second != jit_iter->second ? "*" : ""); 259 uni_iter++; 260 jit_iter++; 261 } 262 } 263 fmt::print("\n"); 264 265 const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); }; 266 IR::Block ir_block = A64::Translate({instructions_start, FP::FPCR{fpcr}}, get_code, {}); 267 Optimization::A64CallbackConfigPass(ir_block, GetUserConfig(jit_env)); 268 Optimization::NamingPass(ir_block); 269 270 fmt::print("IR:\n"); 271 fmt::print("{}\n", IR::DumpBlock(ir_block)); 272 273 Optimization::A64GetSetElimination(ir_block); 274 Optimization::DeadCodeElimination(ir_block); 275 Optimization::ConstantPropagation(ir_block); 276 Optimization::DeadCodeElimination(ir_block); 277 278 fmt::print("Optimized IR:\n"); 279 fmt::print("{}\n", IR::DumpBlock(ir_block)); 280 281 fmt::print("x86_64:\n"); 282 jit.DumpDisassembly(); 283 284 fmt::print("Interrupts:\n"); 285 for (auto& i : uni_env.interrupts) { 286 puts(i.c_str()); 287 } 288 }; 289 290 REQUIRE(uni_env.code_mem_modified_by_guest == jit_env.code_mem_modified_by_guest); 291 if (uni_env.code_mem_modified_by_guest) { 292 return; 293 } 294 295 REQUIRE(uni.GetPC() == jit.GetPC()); 296 REQUIRE(uni.GetRegisters() == jit.GetRegisters()); 297 REQUIRE(uni.GetVectors() == jit.GetVectors()); 298 REQUIRE(uni.GetSP() == jit.GetSP()); 299 REQUIRE((uni.GetPstate() & 0xF0000000) == (jit.GetPstate() & 0xF0000000)); 300 REQUIRE(uni_env.modified_memory == jit_env.modified_memory); 301 REQUIRE(uni_env.interrupts.empty()); 302 REQUIRE(FP::FPSR{uni.GetFpsr()}.QC() == FP::FPSR{jit.GetFpsr()}.QC()); 303 } 304 305 TEST_CASE("A64: Single random instruction", "[a64]") { 306 A64TestEnv jit_env{}; 307 A64TestEnv uni_env{}; 308 309 Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; 310 A64Unicorn uni{uni_env}; 311 312 A64Unicorn::RegisterArray regs; 313 A64Unicorn::VectorArray vecs; 314 std::vector<u32> instructions(1); 315 316 for (size_t iteration = 0; iteration < 100000; ++iteration) { 317 std::generate(regs.begin(), regs.end(), [] { return RandInt<u64>(0, ~u64(0)); }); 318 std::generate(vecs.begin(), vecs.end(), RandomVector); 319 320 instructions[0] = GenRandomInst(0, true); 321 322 const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4; 323 const u32 pstate = RandInt<u32>(0, 0xF) << 28; 324 const u32 fpcr = RandomFpcr(); 325 326 INFO("Instruction: 0x" << std::hex << instructions[0]); 327 328 RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); 329 } 330 } 331 332 TEST_CASE("A64: Floating point instructions", "[a64]") { 333 A64TestEnv jit_env{}; 334 A64TestEnv uni_env{}; 335 336 Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; 337 A64Unicorn uni{uni_env}; 338 339 static constexpr std::array<u64, 80> float_numbers{ 340 0x00000000, // positive zero 341 0x00000001, // smallest positive denormal 342 0x00000076, // 343 0x00002b94, // 344 0x00636d24, // 345 0x007fffff, // largest positive denormal 346 0x00800000, // smallest positive normalised real 347 0x00800002, // 348 0x01398437, // 349 0x0ba98d27, // 350 0x0ba98d7a, // 351 0x751f853a, // 352 0x7f7ffff0, // 353 0x7f7fffff, // largest positive normalised real 354 0x7f800000, // positive infinity 355 0x7f800001, // first positive SNaN 356 0x7f984a37, // 357 0x7fbfffff, // last positive SNaN 358 0x7fc00000, // first positive QNaN 359 0x7fd9ba98, // 360 0x7fffffff, // last positive QNaN 361 0x80000000, // negative zero 362 0x80000001, // smallest negative denormal 363 0x80000076, // 364 0x80002b94, // 365 0x80636d24, // 366 0x807fffff, // largest negative denormal 367 0x80800000, // smallest negative normalised real 368 0x80800002, // 369 0x81398437, // 370 0x8ba98d27, // 371 0x8ba98d7a, // 372 0xf51f853a, // 373 0xff7ffff0, // 374 0xff7fffff, // largest negative normalised real 375 0xff800000, // negative infinity 376 0xff800001, // first negative SNaN 377 0xff984a37, // 378 0xffbfffff, // last negative SNaN 379 0xffc00000, // first negative QNaN 380 0xffd9ba98, // 381 0xffffffff, // last negative QNaN 382 // some random numbers follow 383 0x4f3495cb, 384 0xe73a5134, 385 0x7c994e9e, 386 0x6164bd6c, 387 0x09503366, 388 0xbf5a97c9, 389 0xe6ff1a14, 390 0x77f31e2f, 391 0xaab4d7d8, 392 0x0966320b, 393 0xb26bddee, 394 0xb5c8e5d3, 395 0x317285d3, 396 0x3c9623b1, 397 0x51fd2c7c, 398 0x7b906a6c, 399 0x3f800000, 400 0x3dcccccd, 401 0x3f000000, 402 0x42280000, 403 0x3eaaaaab, 404 0xc1200000, 405 0xbf800000, 406 0xbf8147ae, 407 0x3f8147ae, 408 0x415df525, 409 0xc79b271e, 410 0x460e8c84, 411 // some 64-bit-float upper-halves 412 0x7ff00000, // +SNaN / +Inf 413 0x7ff0abcd, // +SNaN 414 0x7ff80000, // +QNaN 415 0x7ff81234, // +QNaN 416 0xfff00000, // -SNaN / -Inf 417 0xfff05678, // -SNaN 418 0xfff80000, // -QNaN 419 0xfff809ef, // -QNaN 420 0x3ff00000, // Number near +1.0 421 0xbff00000, // Number near -1.0 422 }; 423 424 const auto gen_float = [&] { 425 if (RandInt<size_t>(0, 1) == 0) { 426 return RandInt<u64>(0, 0xffffffff); 427 } 428 return float_numbers[RandInt<size_t>(0, float_numbers.size() - 1)]; 429 }; 430 431 const auto gen_vector = [&] { 432 u64 upper = (gen_float() << 32) | gen_float(); 433 u64 lower = (gen_float() << 32) | gen_float(); 434 return Vector{lower, upper}; 435 }; 436 437 A64Unicorn::RegisterArray regs; 438 A64Unicorn::VectorArray vecs; 439 std::vector<u32> instructions(1); 440 441 for (size_t iteration = 0; iteration < 100000; ++iteration) { 442 std::generate(regs.begin(), regs.end(), gen_float); 443 std::generate(vecs.begin(), vecs.end(), gen_vector); 444 445 instructions[0] = GenFloatInst(0, true); 446 447 const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4; 448 const u32 pstate = RandInt<u32>(0, 0xF) << 28; 449 const u32 fpcr = RandomFpcr(); 450 451 INFO("Instruction: 0x" << std::hex << instructions[0]); 452 453 RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); 454 } 455 } 456 457 TEST_CASE("A64: Small random block", "[a64]") { 458 A64TestEnv jit_env{}; 459 A64TestEnv uni_env{}; 460 461 Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; 462 A64Unicorn uni{uni_env}; 463 464 A64Unicorn::RegisterArray regs; 465 A64Unicorn::VectorArray vecs; 466 std::vector<u32> instructions(5); 467 468 for (size_t iteration = 0; iteration < 100000; ++iteration) { 469 std::generate(regs.begin(), regs.end(), [] { return RandInt<u64>(0, ~u64(0)); }); 470 std::generate(vecs.begin(), vecs.end(), RandomVector); 471 472 instructions[0] = GenRandomInst(0, false); 473 instructions[1] = GenRandomInst(4, false); 474 instructions[2] = GenRandomInst(8, false); 475 instructions[3] = GenRandomInst(12, false); 476 instructions[4] = GenRandomInst(16, true); 477 478 const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4; 479 const u32 pstate = RandInt<u32>(0, 0xF) << 28; 480 const u32 fpcr = RandomFpcr(); 481 482 INFO("Instruction 1: 0x" << std::hex << instructions[0]); 483 INFO("Instruction 2: 0x" << std::hex << instructions[1]); 484 INFO("Instruction 3: 0x" << std::hex << instructions[2]); 485 INFO("Instruction 4: 0x" << std::hex << instructions[3]); 486 INFO("Instruction 5: 0x" << std::hex << instructions[4]); 487 488 RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); 489 } 490 } 491 492 TEST_CASE("A64: Large random block", "[a64]") { 493 A64TestEnv jit_env{}; 494 A64TestEnv uni_env{}; 495 496 Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; 497 A64Unicorn uni{uni_env}; 498 499 A64Unicorn::RegisterArray regs; 500 A64Unicorn::VectorArray vecs; 501 502 constexpr size_t instruction_count = 100; 503 std::vector<u32> instructions(instruction_count); 504 505 for (size_t iteration = 0; iteration < 500; ++iteration) { 506 std::generate(regs.begin(), regs.end(), [] { return RandInt<u64>(0, ~u64(0)); }); 507 std::generate(vecs.begin(), vecs.end(), RandomVector); 508 509 for (size_t j = 0; j < instruction_count; ++j) { 510 instructions[j] = GenRandomInst(j * 4, j == instruction_count - 1); 511 } 512 513 const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4; 514 const u32 pstate = RandInt<u32>(0, 0xF) << 28; 515 const u32 fpcr = RandomFpcr(); 516 517 RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); 518 } 519 }