diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0ec829ab..b93ec885 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,7 +122,7 @@ if (DYNARMIC_USE_LLVM)
     find_package(LLVM REQUIRED CONFIG)
     include_directories(${LLVM_INCLUDE_DIRS})
     add_definitions(-DDYNARMIC_USE_LLVM ${LLVM_DEFINITIONS})
-    llvm_map_components_to_libnames(llvm_libs x86desc x86disassembler)
+    llvm_map_components_to_libnames(llvm_libs aarch64desc aarch64disassembler x86desc x86disassembler)
 endif()
 
 if (DYNARMIC_TESTS_USE_UNICORN)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9d421368..c195f972 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -15,6 +15,8 @@ add_library(dynarmic
     common/crc32.h
     common/intrusive_list.h
     common/iterator_util.h
+    common/llvm_disassemble.cpp
+    common/llvm_disassemble.h
     common/memory_pool.cpp
     common/memory_pool.h
     common/mp.h
@@ -158,8 +160,6 @@ if (ARCHITECTURE_x86_64)
          backend_x64/constant_pool.cpp
          backend_x64/constant_pool.h
          backend_x64/devirtualize.h
-         backend_x64/disassemble_x64.cpp
-         backend_x64/disassemble_x64.h
          backend_x64/emit_x64.cpp
          backend_x64/emit_x64.h
          backend_x64/emit_x64_aes.cpp
diff --git a/src/backend_x64/a32_interface.cpp b/src/backend_x64/a32_interface.cpp
index 5458489c..cb03f8c7 100644
--- a/src/backend_x64/a32_interface.cpp
+++ b/src/backend_x64/a32_interface.cpp
@@ -9,20 +9,15 @@
 #include <boost/icl/interval_set.hpp>
 #include <fmt/format.h>
 
-#ifdef DYNARMIC_USE_LLVM
-#include <llvm-c/Disassembler.h>
-#include <llvm-c/Target.h>
-#endif
-
 #include "backend_x64/a32_emit_x64.h"
 #include "backend_x64/a32_jitstate.h"
 #include "backend_x64/block_of_code.h"
 #include "backend_x64/callback.h"
 #include "backend_x64/devirtualize.h"
-#include "backend_x64/disassemble_x64.h"
 #include "backend_x64/jitstate_info.h"
 #include "common/assert.h"
 #include "common/common_types.h"
+#include "common/llvm_disassemble.h"
 #include "common/scope_exit.h"
 #include "dynarmic/A32/a32.h"
 #include "dynarmic/A32/context.h"
@@ -74,7 +69,7 @@ struct Jit::Impl {
     std::string Disassemble(const IR::LocationDescriptor& descriptor) {
         auto block = GetBasicBlock(descriptor);
         std::string result = fmt::format("address: {}\nsize: {} bytes\n", block.entrypoint, block.size);
-        result += DisassembleX64(block.entrypoint, reinterpret_cast<const char*>(block.entrypoint) + block.size);
+        result += Common::DisassembleX64(block.entrypoint, reinterpret_cast<const char*>(block.entrypoint) + block.size);
         return result;
     }
 
diff --git a/src/backend_x64/a64_interface.cpp b/src/backend_x64/a64_interface.cpp
index 14e99983..46e5e470 100644
--- a/src/backend_x64/a64_interface.cpp
+++ b/src/backend_x64/a64_interface.cpp
@@ -13,10 +13,10 @@
 #include "backend_x64/a64_jitstate.h"
 #include "backend_x64/block_of_code.h"
 #include "backend_x64/devirtualize.h"
-#include "backend_x64/disassemble_x64.h"
 #include "backend_x64/jitstate_info.h"
 #include "common/assert.h"
 #include "common/scope_exit.h"
+#include "common/llvm_disassemble.h"
 #include "dynarmic/A64/a64.h"
 #include "frontend/A64/translate/translate.h"
 #include "frontend/ir/basic_block.h"
@@ -164,7 +164,7 @@ public:
     }
 
     std::string Disassemble() const {
-        return DisassembleX64(block_of_code.GetCodeBegin(), block_of_code.getCurr());
+        return Common::DisassembleX64(block_of_code.GetCodeBegin(), block_of_code.getCurr());
     }
 
 private:
diff --git a/src/backend_x64/disassemble_x64.cpp b/src/common/llvm_disassemble.cpp
similarity index 66%
rename from src/backend_x64/disassemble_x64.cpp
rename to src/common/llvm_disassemble.cpp
index a93e41fb..b88e36b2 100644
--- a/src/backend_x64/disassemble_x64.cpp
+++ b/src/common/llvm_disassemble.cpp
@@ -13,11 +13,11 @@
 #include <llvm-c/Target.h>
 #endif
 
-#include "backend_x64/disassemble_x64.h"
 #include "common/assert.h"
 #include "common/common_types.h"
+#include "common/llvm_disassemble.h"
 
-namespace Dynarmic::BackendX64 {
+namespace Dynarmic::Common {
 
 std::string DisassembleX64(const void* begin, const void* end) {
     std::string result;
@@ -55,4 +55,28 @@ std::string DisassembleX64(const void* begin, const void* end) {
     return result;
 }
 
-} // namespace Dynarmic::BackendX64
+std::string DisassembleAArch64([[maybe_unused]] u32 instruction, [[maybe_unused]] u64 pc) {
+    std::string result;
+
+#ifdef DYNARMIC_USE_LLVM
+    LLVMInitializeAArch64TargetInfo();
+    LLVMInitializeAArch64TargetMC();
+    LLVMInitializeAArch64Disassembler();
+    LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("aarch64", nullptr, 0, nullptr, nullptr);
+    LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
+
+    char buffer[80];
+    size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
+    ASSERT(inst_size);
+    result = buffer;
+    result += '\n';
+
+    LLVMDisasmDispose(llvm_ctx);
+#else
+    result += fmt::format("(disassembly disabled)\n");
+#endif
+
+    return result;
+}
+
+} // namespace Dynarmic::Common
diff --git a/src/backend_x64/disassemble_x64.h b/src/common/llvm_disassemble.h
similarity index 66%
rename from src/backend_x64/disassemble_x64.h
rename to src/common/llvm_disassemble.h
index f1daf7ee..b7257bbc 100644
--- a/src/backend_x64/disassemble_x64.h
+++ b/src/common/llvm_disassemble.h
@@ -6,8 +6,11 @@
 
 #include <string>
 
-namespace Dynarmic::BackendX64 {
+#include "common/common_types.h"
+
+namespace Dynarmic::Common {
 
 std::string DisassembleX64(const void* pos, const void* end);
+std::string DisassembleAArch64(u32 instruction, u64 pc = 0);
 
-} // namespace Dynarmic::BackendX64
+} // namespace Dynarmic::Common
diff --git a/tests/A64/fuzz_with_unicorn.cpp b/tests/A64/fuzz_with_unicorn.cpp
index eedf1b71..22857c3a 100644
--- a/tests/A64/fuzz_with_unicorn.cpp
+++ b/tests/A64/fuzz_with_unicorn.cpp
@@ -11,6 +11,7 @@
 
 #include <catch.hpp>
 
+#include "common/llvm_disassemble.h"
 #include "common/scope_exit.h"
 #include "frontend/A64/location_descriptor.h"
 #include "frontend/A64/translate/translate.h"
@@ -127,7 +128,7 @@ static void RunTestInstance(const std::array<u64, 31>& regs, const std::array<Ve
     SCOPE_FAIL {
         fmt::print("Instruction Listing:\n");
         for (u32 instruction : instructions)
-            fmt::print("{:08x}\n", instruction);
+            fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch64(instruction));
         fmt::print("\n");
 
         fmt::print("Initial register listing:\n");