/ tests / A64 / fuzz_with_unicorn.cpp
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  }