#define _CRT_SECURE_NO_WARNINGS
#define NOMINMAX

#include <cstdint>
#include <inttypes.h>
#include <map>
#include <stdio.h>
#include <string.h>

#include "arm64dis.h"
#include "binaryninjaapi.h"
#include "il.h"
#include "lowlevelilinstruction.h"
#include "neon_intrinsics.h"

using namespace BinaryNinja;
using namespace std;

#if defined(_MSC_VER)
	#define snprintf _snprintf
#endif

#define EMPTY(S) (S[0] == '\0')

enum MachoArm64RelocationType : uint32_t
{
	ARM64_RELOC_UNSIGNED = 0,
	ARM64_RELOC_SUBTRACTOR = 1,
	ARM64_RELOC_BRANCH26 = 2,
	ARM64_RELOC_PAGE21 = 3,
	ARM64_RELOC_PAGEOFF12 = 4,
	ARM64_RELOC_GOT_LOAD_PAGE21 = 5,
	ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6,
	ARM64_RELOC_POINTER_TO_GOT = 7,
	ARM64_RELOC_TLVP_LOAD_PAGE21 = 8,
	ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9,
	ARM64_RELOC_ADDEND = 10,
	MACHO_MAX_ARM64_RELOCATION = 11
};

enum ElfArm64RelocationType : uint32_t
{
	R_ARM_NONE                    = 0,
	R_AARCH64_P32_COPY            = 180,
	R_AARCH64_P32_GLOB_DAT        = 181,
	R_AARCH64_P32_JUMP_SLOT       = 182,
	R_AARCH64_P32_RELATIVE        = 183,
	R_AARCH64_NONE                = 256,
	// Data
	R_AARCH64_ABS64 = 257,
	R_AARCH64_ABS32 = 258,
	R_AARCH64_ABS16 = 259,
	R_AARCH64_PREL64 = 260,
	R_AARCH64_PREL32 = 261,
	R_AARCH64_PREL16 = 262,
	// Instructions
	R_AARCH64_MOVW_UABS_G0 = 263,
	R_AARCH64_MOVW_UABS_G0_NC = 264,
	R_AARCH64_MOVW_UABS_G1 = 265,
	R_AARCH64_MOVW_UABS_G1_NC = 266,
	R_AARCH64_MOVW_UABS_G2 = 267,
	R_AARCH64_MOVW_UABS_G2_NC = 268,
	R_AARCH64_MOVW_UABS_G3 = 269,
	R_AARCH64_MOVW_SABS_G0 = 270,
	R_AARCH64_MOVW_SABS_G1 = 271,
	R_AARCH64_MOVW_SABS_G2 = 272,
	R_AARCH64_LD_PREL_LO19 = 273,
	R_AARCH64_ADR_PREL_LO21 = 274,
	R_AARCH64_ADR_PREL_PG_HI21 = 275,
	R_AARCH64_ADR_PREL_PG_HI21_NC = 276,
	R_AARCH64_ADD_ABS_LO12_NC = 277,
	R_AARCH64_LDST8_ABS_LO12_NC = 278,
	R_AARCH64_TSTBR14 = 279,
	R_AARCH64_CONDBR19 = 280,
	R_AARCH64_JUMP26 = 282,
	R_AARCH64_CALL26 = 283,
	R_AARCH64_LDST16_ABS_LO12_NC = 284,
	R_AARCH64_LDST32_ABS_LO12_NC = 285,
	R_AARCH64_LDST64_ABS_LO12_NC = 286,
	R_AARCH64_LDST128_ABS_LO12_NC = 299,
	R_AARCH64_MOVW_PREL_G0 = 287,
	R_AARCH64_MOVW_PREL_G0_NC = 288,
	R_AARCH64_MOVW_PREL_G1 = 289,
	R_AARCH64_MOVW_PREL_G1_NC = 290,
	R_AARCH64_MOVW_PREL_G2 = 291,
	R_AARCH64_MOVW_PREL_G2_NC = 292,
	R_AARCH64_MOVW_PREL_G3 = 293,
	R_AARCH64_MOVW_GOTOFF_G0 = 300,
	R_AARCH64_MOVW_GOTOFF_G0_NC = 301,
	R_AARCH64_MOVW_GOTOFF_G1 = 302,
	R_AARCH64_MOVW_GOTOFF_G1_NC = 303,
	R_AARCH64_MOVW_GOTOFF_G2 = 304,
	R_AARCH64_MOVW_GOTOFF_G2_NC = 305,
	R_AARCH64_MOVW_GOTOFF_G3 = 306,
	R_AARCH64_GOTREL64 = 307,
	R_AARCH64_GOTREL32 = 308,
	R_AARCH64_GOT_LD_PREL19 = 309,
	R_AARCH64_LD64_GOTOFF_LO15 = 310,
	R_AARCH64_ADR_GOT_PAGE = 311,
	R_AARCH64_LD64_GOT_LO12_NC = 312,
	R_AARCH64_LD64_GOTPAGE_LO15 = 313,

	R_AARCH64_COPY = 1024,
	R_AARCH64_GLOB_DAT = 1025,   // Create GOT entry.
	R_AARCH64_JUMP_SLOT = 1026,  // Create PLT entry.
	R_AARCH64_RELATIVE = 1027,   // Adjust by program base.
	R_AARCH64_TLS_DTPREL64 = 1028,
	R_AARCH64_TLS_DTPMOD64 = 1029,
	R_AARCH64_TLS_TPREL64 = 1030,
	R_AARCH64_TLS_DTPREL32 = 1031,
	R_AARCH64_TLSDESC = 1031,
	R_AARCH64_IRELATIVE = 1032,
};

enum PeArm64RelocationType : uint32_t
{
	PE_IMAGE_REL_ARM64_ABSOLUTE       = 0x0000, //	The relocation is ignored.
	PE_IMAGE_REL_ARM64_ADDR32         = 0x0001, //	The 32-bit VA of the target.
	PE_IMAGE_REL_ARM64_ADDR32NB       = 0x0002, //	The 32-bit RVA of the target.
	PE_IMAGE_REL_ARM64_BRANCH26       = 0x0003, //	The 26-bit relative displacement to the target, for B and BL instructions. 
	PE_IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004, //	The page base of the target, for ADRP instruction.
	PE_IMAGE_REL_ARM64_REL21          = 0x0005, //	The 12-bit relative displacement to the target, for instruction ADR
	PE_IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006, //	The 12-bit page offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
	PE_IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007, //	The 12-bit page offset of the target, for instruction LDR (indexed, unsigned immediate).
	PE_IMAGE_REL_ARM64_SECREL         = 0x0008, //	The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage.
	PE_IMAGE_REL_ARM64_SECREL_LOW12A  = 0x0009, //	Bit 0:11 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
	PE_IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A, //	Bit 12:23 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
	PE_IMAGE_REL_ARM64_SECREL_LOW12L  = 0x000B, //	Bit 0:11 of section offset of the target, for instruction LDR (indexed, unsigned immediate).
	PE_IMAGE_REL_ARM64_TOKEN          = 0x000C, //	CLR token.
	PE_IMAGE_REL_ARM64_SECTION        = 0x000D, //	The 16-bit section index of the section that contains the target. This is used to support debugging information.
	PE_IMAGE_REL_ARM64_ADDR64         = 0x000E, //	The 64-bit VA of the relocation target.
	PE_IMAGE_REL_ARM64_BRANCH19       = 0x000F, //	The 19-bit offset to the relocation target, for conditional B instruction.
	PE_IMAGE_REL_ARM64_BRANCH14       = 0x0010, //	The 14-bit offset to the relocation target, for instructions TBZ and TBNZ.
	IMAGE_REL_ARM64_REL32             = 0x0011, //	The 32-bit relative address from the byte following the relocation.
	MAX_PE_ARM64_RELOCATION           = 0x0012
};

static const char* GetRelocationString(MachoArm64RelocationType rel)
{
	static const char* relocTable[] = {"ARM64_RELOC_UNSIGNED", "ARM64_RELOC_SUBTRACTOR",
	    "ARM64_RELOC_BRANCH26", "ARM64_RELOC_PAGE21", "ARM64_RELOC_PAGEOFF12",
	    "ARM64_RELOC_GOT_LOAD_PAGE21", "ARM64_RELOC_GOT_LOAD_PAGEOFF12", "ARM64_RELOC_POINTER_TO_GOT",
	    "ARM64_RELOC_TLVP_LOAD_PAGE21", "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", "ARM64_RELOC_ADDEND"};
	if (rel < MACHO_MAX_ARM64_RELOCATION)
	{
		return relocTable[rel];
	}
	return "Unknown Aarch64 relocation";
}


static const char* GetRelocationString(PeArm64RelocationType rel)
{
	static const char* relocTable[] = {
		"IMAGE_REL_ARM64_ABSOLUTE",
		"IMAGE_REL_ARM64_ADDR32",
		"IMAGE_REL_ARM64_ADDR32NB",
		"IMAGE_REL_ARM64_BRANCH26",
		"IMAGE_REL_ARM64_PAGEBASE_REL21",
		"IMAGE_REL_ARM64_REL21",
		"IMAGE_REL_ARM64_PAGEOFFSET_12A",
		"IMAGE_REL_ARM64_PAGEOFFSET_12L",
		"IMAGE_REL_ARM64_SECREL",
		"IMAGE_REL_ARM64_SECREL_LOW12A",
		"IMAGE_REL_ARM64_SECREL_HIGH12A",
		"IMAGE_REL_ARM64_SECREL_LOW12L",
		"IMAGE_REL_ARM64_TOKEN",
		"IMAGE_REL_ARM64_SECTION",
		"IMAGE_REL_ARM64_ADDR64",
		"IMAGE_REL_ARM64_BRANCH19",
		"IMAGE_REL_ARM64_BRANCH14",
		"IMAGE_REL_ARM64_REL32"
	};

	if (rel < MAX_PE_ARM64_RELOCATION)
	{
		return relocTable[rel];
	}
	return "Unknown Aarch64 relocation";
}


static const char* GetRelocationString(ElfArm64RelocationType rel)
{
	static map<ElfArm64RelocationType, const char*> relocMap = {
		{R_ARM_NONE,                    "R_ARM_NONE"},
		{R_AARCH64_P32_COPY,            "R_AARCH64_P32_COPY"},
		{R_AARCH64_P32_GLOB_DAT,        "R_AARCH64_P32_GLOB_DAT"},
		{R_AARCH64_P32_JUMP_SLOT,       "R_AARCH64_P32_JUMP_SLOT"},
		{R_AARCH64_P32_RELATIVE,        "R_AARCH64_P32_RELATIVE"},
		{R_AARCH64_NONE,                "R_AARCH64_NONE"},
		{R_AARCH64_ABS64,               "R_AARCH64_ABS64"},
		{R_AARCH64_ABS32,               "R_AARCH64_ABS32"},
		{R_AARCH64_ABS16,               "R_AARCH64_ABS16"},
		{R_AARCH64_PREL64,              "R_AARCH64_PREL64"},
		{R_AARCH64_PREL32,              "R_AARCH64_PREL32"},
		{R_AARCH64_PREL16,              "R_AARCH64_PREL16"},
		{R_AARCH64_MOVW_UABS_G0,        "R_AARCH64_MOVW_UABS_G0"},
		{R_AARCH64_MOVW_UABS_G0_NC,     "R_AARCH64_MOVW_UABS_G0_NC"},
		{R_AARCH64_MOVW_UABS_G1,        "R_AARCH64_MOVW_UABS_G1"},
		{R_AARCH64_MOVW_UABS_G1_NC,     "R_AARCH64_MOVW_UABS_G1_NC"},
		{R_AARCH64_MOVW_UABS_G2,        "R_AARCH64_MOVW_UABS_G2"},
		{R_AARCH64_MOVW_UABS_G2_NC,     "R_AARCH64_MOVW_UABS_G2_NC"},
		{R_AARCH64_MOVW_UABS_G3,        "R_AARCH64_MOVW_UABS_G3"},
		{R_AARCH64_MOVW_SABS_G0,        "R_AARCH64_MOVW_SABS_G0"},
		{R_AARCH64_MOVW_SABS_G1,        "R_AARCH64_MOVW_SABS_G1"},
		{R_AARCH64_MOVW_SABS_G2,        "R_AARCH64_MOVW_SABS_G2"},
		{R_AARCH64_LD_PREL_LO19,        "R_AARCH64_LD_PREL_LO19"},
		{R_AARCH64_ADR_PREL_LO21,       "R_AARCH64_ADR_PREL_LO21"},
		{R_AARCH64_ADR_PREL_PG_HI21,    "R_AARCH64_ADR_PREL_PG_HI21"},
		{R_AARCH64_ADR_PREL_PG_HI21_NC, "R_AARCH64_ADR_PREL_PG_HI21_NC"},
		{R_AARCH64_ADD_ABS_LO12_NC,     "R_AARCH64_ADD_ABS_LO12_NC"},
		{R_AARCH64_LDST8_ABS_LO12_NC,   "R_AARCH64_LDST8_ABS_LO12_NC"},
		{R_AARCH64_TSTBR14,             "R_AARCH64_TSTBR14"},
		{R_AARCH64_CONDBR19,            "R_AARCH64_CONDBR19"},
		{R_AARCH64_JUMP26,              "R_AARCH64_JUMP26"},
		{R_AARCH64_CALL26,              "R_AARCH64_CALL26"},
		{R_AARCH64_LDST16_ABS_LO12_NC,  "R_AARCH64_LDST16_ABS_LO12_NC"},
		{R_AARCH64_LDST32_ABS_LO12_NC,  "R_AARCH64_LDST32_ABS_LO12_NC"},
		{R_AARCH64_LDST64_ABS_LO12_NC,  "R_AARCH64_LDST64_ABS_LO12_NC"},
		{R_AARCH64_LDST128_ABS_LO12_NC, "R_AARCH64_LDST128_ABS_LO12_NC"},
		{R_AARCH64_MOVW_PREL_G0,        "R_AARCH64_MOVW_PREL_G0"},
		{R_AARCH64_MOVW_PREL_G0_NC,     "R_AARCH64_MOVW_PREL_G0_NC"},
		{R_AARCH64_MOVW_PREL_G1,        "R_AARCH64_MOVW_PREL_G1"},
		{R_AARCH64_MOVW_PREL_G1_NC,     "R_AARCH64_MOVW_PREL_G1_NC"},
		{R_AARCH64_MOVW_PREL_G2,        "R_AARCH64_MOVW_PREL_G2"},
		{R_AARCH64_MOVW_PREL_G2_NC,     "R_AARCH64_MOVW_PREL_G2_NC"},
		{R_AARCH64_MOVW_PREL_G3,        "R_AARCH64_MOVW_PREL_G3"},
		{R_AARCH64_MOVW_GOTOFF_G0,      "R_AARCH64_MOVW_GOTOFF_G0"},
		{R_AARCH64_MOVW_GOTOFF_G0_NC,   "R_AARCH64_MOVW_GOTOFF_G0_NC"},
		{R_AARCH64_MOVW_GOTOFF_G1,      "R_AARCH64_MOVW_GOTOFF_G1"},
		{R_AARCH64_MOVW_GOTOFF_G1_NC,   "R_AARCH64_MOVW_GOTOFF_G1_NC"},
		{R_AARCH64_MOVW_GOTOFF_G2,      "R_AARCH64_MOVW_GOTOFF_G2"},
		{R_AARCH64_MOVW_GOTOFF_G2_NC,   "R_AARCH64_MOVW_GOTOFF_G2_NC"},
		{R_AARCH64_MOVW_GOTOFF_G3,      "R_AARCH64_MOVW_GOTOFF_G3"},
		{R_AARCH64_GOTREL64,            "R_AARCH64_GOTREL64"},
		{R_AARCH64_GOTREL32,            "R_AARCH64_GOTREL32"},
		{R_AARCH64_GOT_LD_PREL19,       "R_AARCH64_GOT_LD_PREL19"},
		{R_AARCH64_LD64_GOTOFF_LO15,    "R_AARCH64_LD64_GOTOFF_LO15"},
		{R_AARCH64_ADR_GOT_PAGE,        "R_AARCH64_ADR_GOT_PAGE"},
		{R_AARCH64_LD64_GOT_LO12_NC,    "R_AARCH64_LD64_GOT_LO12_NC"},
		{R_AARCH64_LD64_GOTPAGE_LO15,   "R_AARCH64_LD64_GOTPAGE_LO15"},
		{R_AARCH64_COPY,                "R_AARCH64_COPY"},
		{R_AARCH64_GLOB_DAT,            "R_AARCH64_GLOB_DAT"},
		{R_AARCH64_JUMP_SLOT,           "R_AARCH64_JUMP_SLOT"},
		{R_AARCH64_RELATIVE,            "R_AARCH64_RELATIVE"},
		{R_AARCH64_TLS_DTPREL64,        "R_AARCH64_TLS_DTPREL64"},
		{R_AARCH64_TLS_DTPMOD64,        "R_AARCH64_TLS_DTPMOD64"},
		{R_AARCH64_TLS_TPREL64,         "R_AARCH64_TLS_TPREL64"},
		{R_AARCH64_TLS_DTPREL32,        "R_AARCH64_TLS_DTPREL32"},
		{R_AARCH64_IRELATIVE,           "R_AARCH64_IRELATIVE"}
	};

	if (relocMap.count(rel))
		return relocMap.at(rel);

	return "Unknown Aarch64 relocation";
}


class Arm64Architecture : public Architecture
{
 protected:
	size_t m_bits;

	virtual bool Disassemble(const uint8_t* data, uint64_t addr, size_t maxLen, Instruction& result)
	{
		(void)addr;
		(void)maxLen;
		memset(&result, 0, sizeof(result));
		if (aarch64_decompose(*(uint32_t*)data, &result, addr) != 0)
			return false;
		return true;
	}


	virtual size_t GetAddressSize() const override { return 8; }


	virtual size_t GetInstructionAlignment() const override { return 4; }


	virtual size_t GetMaxInstructionLength() const override { return 4; }


	bool IsTestAndBranch(const Instruction& instr)
	{
		return instr.operation == ARM64_TBZ || instr.operation == ARM64_TBNZ;
	}


	bool IsCompareAndBranch(const Instruction& instr)
	{
		return instr.operation == ARM64_CBZ || instr.operation == ARM64_CBNZ;
	}


	bool IsConditionalBranch(const Instruction& instr)
	{
		switch (instr.operation)
		{
		case ARM64_B_EQ:
		case ARM64_B_NE:
		case ARM64_B_CS:
		case ARM64_B_CC:
		case ARM64_B_MI:
		case ARM64_B_PL:
		case ARM64_B_VS:
		case ARM64_B_VC:
		case ARM64_B_HI:
		case ARM64_B_LS:
		case ARM64_B_GE:
		case ARM64_B_LT:
		case ARM64_B_GT:
		case ARM64_B_LE:
		case ARM64_B_AL:
		case ARM64_B_NV:
			return true;
		default:
			return false;
		}
	}


	bool IsConditionalJump(const Instruction& instr)
	{
		return IsConditionalBranch(instr) || IsTestAndBranch(instr) || IsCompareAndBranch(instr);
	}


	void SetInstructionInfoForInstruction(
	    uint64_t addr, const Instruction& instr, InstructionInfo& result)
	{
		result.length = 4;
		switch (instr.operation)
		{
		case ARM64_BL:
			if (instr.operands[0].operandClass == LABEL)
				result.AddBranch(CallDestination, instr.operands[0].immediate);
			break;

		case ARM64_B:
			if (instr.operands[0].operandClass == LABEL)
				result.AddBranch(UnconditionalBranch, instr.operands[0].immediate);
			else
				result.AddBranch(UnresolvedBranch);
			break;

		case ARM64_B_EQ:
		case ARM64_B_NE:
		case ARM64_B_CS:
		case ARM64_B_CC:
		case ARM64_B_MI:
		case ARM64_B_PL:
		case ARM64_B_VS:
		case ARM64_B_VC:
		case ARM64_B_HI:
		case ARM64_B_LS:
		case ARM64_B_GE:
		case ARM64_B_LT:
		case ARM64_B_GT:
		case ARM64_B_LE:
		case ARM64_B_AL:
		case ARM64_B_NV:
			result.AddBranch(TrueBranch, instr.operands[0].immediate);
			result.AddBranch(FalseBranch, addr + 4);
			break;
		case ARM64_TBZ:
		case ARM64_TBNZ:
			result.AddBranch(TrueBranch, instr.operands[2].immediate);
			result.AddBranch(FalseBranch, addr + 4);
			break;
		case ARM64_CBZ:
		case ARM64_CBNZ:
			result.AddBranch(TrueBranch, instr.operands[1].immediate);
			result.AddBranch(FalseBranch, addr + 4);
			break;
		case ARM64_BR:
		case ARM64_BRAA:
		case ARM64_BRAAZ:
		case ARM64_BRAB:
		case ARM64_BRABZ:
		case ARM64_DRPS:
			result.AddBranch(UnresolvedBranch);
			break;
		case ARM64_ERET:
		case ARM64_ERETAA:
		case ARM64_ERETAB:
		case ARM64_RET:
		case ARM64_RETAA:
		case ARM64_RETAB:
			result.AddBranch(FunctionReturn);
			break;
		case ARM64_SVC:
		case ARM64_HVC:
		case ARM64_SMC:
			result.AddBranch(SystemCall);
			break;
		case ARM64_UDF:
			result.AddBranch(ExceptionBranch);
			break;

		default:
			break;
		}
	}


	uint32_t tokenize_shift(
	    const InstructionOperand* __restrict operand, vector<InstructionTextToken>& result)
	{
		if (operand->shiftType != ShiftType_NONE)
		{
			const char* shiftStr = get_shift(operand->shiftType);
			if (shiftStr == NULL)
				return FAILED_TO_DISASSEMBLE_OPERAND;

			result.emplace_back(TextToken, ", ");
			result.emplace_back(TextToken, shiftStr);
			if (operand->shiftValueUsed != 0)
			{
				char buf[64] = {0};
				snprintf(buf, sizeof(buf), "%#x", (uint32_t)operand->shiftValue);
				result.emplace_back(TextToken, " #");
				result.emplace_back(IntegerToken, buf, operand->shiftValue);
			}
		}
		return DISASM_SUCCESS;
	}


	uint32_t tokenize_shifted_immediate(
	    const InstructionOperand* __restrict operand, vector<InstructionTextToken>& result)
	{
		char buf[64] = {0};
		const char* sign = "";
		if (operand == NULL)
			return FAILED_TO_DISASSEMBLE_OPERAND;

		uint64_t imm = operand->immediate;
		if (operand->signedImm == 1 && ((int64_t)imm) < 0)
		{
			sign = "-";
			imm = -(int64_t)imm;
		}

		switch (operand->operandClass)
		{
		case FIMM32:
		{
			union
			{
				uint32_t intValue;
				float floatValue;
			} f;
			f.intValue = (uint32_t)operand->immediate;
			snprintf(buf, sizeof(buf), "%.08f", f.floatValue);
			result.emplace_back(TextToken, "#");
			result.emplace_back(FloatingPointToken, buf);
			break;
		}
		case IMM32:
			snprintf(buf, sizeof(buf), "%s%#x", sign, (uint32_t)imm);
			result.emplace_back(TextToken, "#");
			result.emplace_back(IntegerToken, buf, operand->immediate);
			break;
		case IMM64:
			snprintf(buf, sizeof(buf), "%s%#" PRIx64, sign, imm);
			result.emplace_back(TextToken, "#");
			result.emplace_back(IntegerToken, buf, operand->immediate);
			break;
		case LABEL:
			snprintf(buf, sizeof(buf), "%#" PRIx64, operand->immediate);
			result.emplace_back(PossibleAddressToken, buf, operand->immediate);
			break;
		default:
			return FAILED_TO_DISASSEMBLE_OPERAND;
		}

		tokenize_shift(operand, result);
		return DISASM_SUCCESS;
	}


	uint32_t tokenize_shifted_register(const InstructionOperand* restrict operand,
	    uint32_t registerNumber, vector<InstructionTextToken>& result)
	{
		const char* reg = get_register_name(operand->reg[registerNumber]);
		if (EMPTY(reg))
			return FAILED_TO_DISASSEMBLE_REGISTER;

		result.emplace_back(RegisterToken, reg);
		tokenize_shift(operand, result);
		return DISASM_SUCCESS;
	}

	uint32_t tokenize_register(const InstructionOperand* restrict operand, uint32_t registerNumber,
	    vector<InstructionTextToken>& result)
	{
		char buf[64] = {0};

		/* case: system registers */
		if (operand->operandClass == SYS_REG)
		{
			snprintf(buf, sizeof(buf), "%s", get_system_register_name((SystemReg)operand->sysreg));
			result.emplace_back(RegisterToken, buf);
			return DISASM_SUCCESS;
		}

		if (operand->operandClass != REG && operand->operandClass != MULTI_REG)
			return OPERAND_IS_NOT_REGISTER;

		/* case: shifted registers */
		if (operand->shiftType != ShiftType_NONE)
		{
			return tokenize_shifted_register(operand, registerNumber, result);
		}

		const char* reg = get_register_name(operand->reg[registerNumber]);
		if (EMPTY(reg))
			return FAILED_TO_DISASSEMBLE_REGISTER;

		/* case: predicate registers */
		if (operand->pred_qual && operand->reg[registerNumber] >= REG_P0 &&
		    operand->reg[registerNumber] <= REG_P31)
		{
			result.emplace_back(RegisterToken, reg);
			result.emplace_back(TextToken, "/");
			result.emplace_back(TextToken, string(1, operand->pred_qual));
			return DISASM_SUCCESS;
		}

		/* case other regs */
		result.emplace_back(RegisterToken, reg);
		const char* arrspec = get_register_arrspec(operand->reg[registerNumber], operand);
		if (arrspec)
			result.emplace_back(TextToken, arrspec);

		/* only use index if this is isolated REG (not, for example, MULTIREG */
		if (operand->operandClass == REG && operand->laneUsed)
		{
			sprintf(buf, "%u", operand->lane);
			result.emplace_back(TextToken, "[");
			result.emplace_back(IntegerToken, buf);
			result.emplace_back(TextToken, "]");
		}

		return DISASM_SUCCESS;
	}


	uint32_t tokenize_memory_operand(
	    const InstructionOperand* restrict operand, vector<InstructionTextToken>& result)
	{
		char immBuff[32] = {0};
		char paramBuff[32] = {0};
		const char *reg0, *reg1;

		reg0 = get_register_name(operand->reg[0]);
		if (EMPTY(reg0))
			return FAILED_TO_DISASSEMBLE_REGISTER;

		const char* sign = "";
		int64_t imm = operand->immediate;
		if (operand->signedImm && (int64_t)imm < 0)
		{
			sign = "-";
			imm = -imm;
		}
		const char* startToken = "[";
		const char* endToken = "]";
		result.emplace_back(BeginMemoryOperandToken, startToken);
		result.emplace_back(RegisterToken, reg0);
		result.emplace_back(TextToken, get_register_arrspec(operand->reg[0], operand));

		switch (operand->operandClass)
		{
		case MEM_REG:
			break;
		case MEM_PRE_IDX:
			endToken = "]!";
			snprintf(immBuff, sizeof(immBuff), "%s%#" PRIx64, sign, (uint64_t)imm);
			result.emplace_back(TextToken, ", #");
			result.emplace_back(IntegerToken, immBuff, operand->immediate);
			break;
		case MEM_POST_IDX:  // [<reg>], <reg|imm>
			endToken = NULL;
			if (operand->reg[1] == REG_NONE)
			{
				snprintf(paramBuff, sizeof(paramBuff), "%s%#" PRIx64, sign, (uint64_t)imm);
				result.emplace_back(EndMemoryOperandToken, "], #");
				result.emplace_back(IntegerToken, paramBuff, operand->immediate);
			}
			else
			{
				reg1 = get_register_name(operand->reg[1]);
				if (EMPTY(reg1))
					return FAILED_TO_DISASSEMBLE_REGISTER;
				result.emplace_back(EndMemoryOperandToken, "], ");
				result.emplace_back(RegisterToken, reg1);
				result.emplace_back(TextToken, get_register_arrspec(operand->reg[1], operand));
			}
			break;
		case MEM_OFFSET:  // [<reg> optional(imm)]
			if (operand->immediate != 0)
			{
				snprintf(immBuff, sizeof(immBuff), "%s%#" PRIx64, sign, (uint64_t)imm);
				result.emplace_back(TextToken, ", #");
				result.emplace_back(IntegerToken, immBuff, operand->immediate);

				if (operand->mul_vl)
					result.emplace_back(TextToken, ", mul vl");
			}
			break;
		case MEM_EXTENDED:  // [<reg>, <reg> optional(shift optional(imm))]
			result.emplace_back(TextToken, ", ");
			reg1 = get_register_name(operand->reg[1]);
			if (EMPTY(reg1))
				return FAILED_TO_DISASSEMBLE_REGISTER;
			result.emplace_back(RegisterToken, reg1);
			result.emplace_back(TextToken, get_register_arrspec(operand->reg[1], operand));
			tokenize_shift(operand, result);
			break;
		default:
			return NOT_MEMORY_OPERAND;
		}
		if (endToken != NULL)
			result.emplace_back(EndMemoryOperandToken, endToken);
		return DISASM_SUCCESS;
	}


	uint32_t tokenize_multireg_operand(
	    const InstructionOperand* restrict operand, vector<InstructionTextToken>& result)
	{
		char index[32] = {0};
		uint32_t elementCount = 0;

		result.emplace_back(TextToken, "{");
		for (; elementCount < 4 && operand->reg[elementCount] != REG_NONE; elementCount++)
		{
			if (elementCount != 0)
				result.emplace_back(TextToken, ", ");

			if (tokenize_register(operand, elementCount, result) != 0)
				return FAILED_TO_DISASSEMBLE_OPERAND;
		}
		result.emplace_back(TextToken, "}");

		if (operand->laneUsed)
		{
			result.emplace_back(TextToken, "[");
			snprintf(index, sizeof(index), "%d", operand->lane);
			result.emplace_back(IntegerToken, index, operand->lane);
			result.emplace_back(TextToken, "]");
		}
		return DISASM_SUCCESS;
	}


	uint32_t tokenize_condition(
	    const InstructionOperand* restrict operand, vector<InstructionTextToken>& result)
	{
		const char* condStr = get_condition((Condition)operand->cond);
		if (condStr == NULL)
			return FAILED_TO_DISASSEMBLE_OPERAND;

		result.emplace_back(TextToken, condStr);
		return DISASM_SUCCESS;
	}


	uint32_t tokenize_implementation_specific(
	    const InstructionOperand* restrict operand, vector<InstructionTextToken>& result)
	{
		char buf[32] = {0};
		get_implementation_specific(operand, buf, sizeof(buf));
		result.emplace_back(RegisterToken, buf);
		return DISASM_SUCCESS;
	}


	BNRegisterInfo RegisterInfo(
	    uint32_t fullWidthReg, size_t offset, size_t size, bool zeroExtend = false)
	{
		BNRegisterInfo result;
		result.fullWidthRegister = fullWidthReg;
		result.offset = offset;
		result.size = size;
		result.extend = zeroExtend ? ZeroExtendToFullWidth : NoExtend;
		return result;
	}


 public:
	Arm64Architecture() : Architecture("aarch64"), m_bits(64) {}

	bool CanAssemble() override { return true; }

	bool Assemble(const string& code, uint64_t addr, DataBuffer& result, string& errors) override
	{
		(void)addr;

		int assembleResult;
		char *instrBytes = NULL, *err = NULL;
		int instrBytesLen = 0, errLen = 0;

		string prepend = ".arch_extension crc\n" ".arch_extension sm4\n"
			".arch_extension sha3\n" ".arch_extension sha2\n" ".arch_extension aes\n"
			".arch_extension crypto\n" ".arch_extension fp\n" ".arch_extension simd\n"
			".arch_extension ras\n" ".arch_extension lse\n" ".arch_extension predres\n"
			".arch_extension ccdp\n" ".arch_extension mte\n" ".arch_extension memtag\n"
			".arch_extension tlb-rmi\n" ".arch_extension pan\n" ".arch_extension pan-rwv\n"
			".arch_extension ccpp\n" ".arch_extension rcpc\n" ".arch_extension rng\n"
			".arch_extension sve\n" ".arch_extension sve2\n" ".arch_extension sve2-aes\n"
			".arch_extension sve2-sm4\n" ".arch_extension sve2-sha3\n" ".arch_extension sve2-bitperm\n"
			".arch_extension ls64\n" ".arch_extension xs\n" ".arch_extension pauth\n"
			".arch_extension flagm\n" ".arch_extension rme\n" ".arch_extension sme\n"
			".arch_extension sme-f64\n" ".arch_extension sme-i64\n" ".arch_extension hbc\n"
			".arch_extension mops\n";

		BNLlvmServicesInit();

		errors.clear();
		assembleResult =
		    BNLlvmServicesAssemble((prepend + code).c_str(), LLVM_SVCS_DIALECT_UNSPEC, "aarch64-none-none",
		        LLVM_SVCS_CM_DEFAULT, LLVM_SVCS_RM_STATIC, &instrBytes, &instrBytesLen, &err, &errLen);

		if (assembleResult || errLen)
		{
			errors = err;
			BNLlvmServicesAssembleFree(instrBytes, err);
			return false;
		}

		result.Clear();
		result.Append(instrBytes, instrBytesLen);
		BNLlvmServicesAssembleFree(instrBytes, err);
		return true;
	}

	virtual BNEndianness GetEndianness() const override { return LittleEndian; }


	virtual bool GetInstructionInfo(
	    const uint8_t* data, uint64_t addr, size_t maxLen, InstructionInfo& result) override
	{
		if (maxLen < 4)
			return false;

		Instruction instr;
		if (!Disassemble(data, addr, maxLen, instr))
			return false;

		SetInstructionInfoForInstruction(addr, instr, result);
		return true;
	}


	virtual bool GetInstructionText(const uint8_t* data, uint64_t addr, size_t& len,
	    vector<InstructionTextToken>& result) override
	{
		len = 4;
		Instruction instr;
		bool tokenizeSuccess = false;
		char buf[9];
		if (!Disassemble(data, addr, len, instr))
			return false;

		memset(buf, 0x20, sizeof(buf));
		const char* operation = get_operation(&instr);
		if (operation == nullptr)
			return false;

		size_t operationLen = strlen(operation);
		if (operationLen < 8)
		{
			buf[8 - operationLen] = '\0';
		}
		else
			buf[1] = '\0';

		result.emplace_back(InstructionToken, operation);
		result.emplace_back(TextToken, buf);
		size_t i;
		for (i = 0; i < MAX_OPERANDS; i++)
		{
			if (instr.operands[i].operandClass == NONE)
				return true;

			struct InstructionOperand *operand = &(instr.operands[i]);

			if (i != 0)
				result.emplace_back(OperandSeparatorToken, ", ");

			switch (instr.operands[i].operandClass)
			{
			case FIMM32:
			case IMM32:
			case IMM64:
			case LABEL:
				tokenizeSuccess = tokenize_shifted_immediate(&instr.operands[i], result) == 0;
				break;
			case MEM_REG:
			case MEM_PRE_IDX:
			case MEM_POST_IDX:
			case MEM_OFFSET:
			case MEM_EXTENDED:
				tokenizeSuccess = tokenize_memory_operand(&instr.operands[i], result) == 0;
				break;
			case REG:
			case SYS_REG:
				tokenizeSuccess = tokenize_register(&instr.operands[i], 0, result) == 0;
				break;
			case MULTI_REG:
				tokenizeSuccess = tokenize_multireg_operand(&instr.operands[i], result) == 0;
				break;
			case CONDITION:
				tokenizeSuccess = tokenize_condition(&instr.operands[i], result) == 0;
				break;
			case IMPLEMENTATION_SPECIFIC:
				tokenizeSuccess = tokenize_implementation_specific(&instr.operands[i], result) == 0;
				break;
			case NAME:
				result.emplace_back(TextToken, instr.operands[i].name);
				tokenizeSuccess = true;
				break;
			case STR_IMM: /* eg: "mul #0xe" */
				result.emplace_back(TextToken, instr.operands[i].name);
				result.emplace_back(TextToken, " #");
				snprintf(buf, sizeof(buf), "0x%" PRIx64, instr.operands[i].immediate);
				result.emplace_back(IntegerToken, buf);
				tokenizeSuccess = true;
				break;
			case ACCUM_ARRAY: /* eg: "za[w12, #0x6]" */
				result.emplace_back(TextToken, "ZA");
				result.emplace_back(TextToken, "[");
				snprintf(buf, sizeof(buf), "%s", get_register_name(operand->reg[0]));
				result.emplace_back(RegisterToken, buf);
				result.emplace_back(OperandSeparatorToken, ", ");
				result.emplace_back(TextToken, " #");
				snprintf(buf, sizeof(buf), "0x%" PRIx64, operand->immediate);
				result.emplace_back(IntegerToken, buf);
				result.emplace_back(TextToken, "]");
				tokenizeSuccess = true;
				break;
			case SME_TILE: /* eg: "z0v.b[w12, #0xb]" */
				snprintf(buf, sizeof(buf), "Z%d", operand->tile);
				result.emplace_back(TextToken, buf);
				if (operand->slice == SLICE_HORIZONTAL)
					result.emplace_back(TextToken, "h");
				else if (operand->slice == SLICE_VERTICAL)
					result.emplace_back(TextToken, "v");
				result.emplace_back(TextToken, get_arrspec_str_truncated(operand->arrSpec));
				if (operand->reg[0] != REG_NONE)
				{
					result.emplace_back(TextToken, "[");
					snprintf(buf, sizeof(buf), "%s", get_register_name(operand->reg[0]));
					result.emplace_back(RegisterToken, buf);
					if (operand->arrSpec != ARRSPEC_FULL)
					{
						result.emplace_back(OperandSeparatorToken, ", ");
						result.emplace_back(TextToken, " #");
						snprintf(buf, sizeof(buf), "0x%" PRIx64, instr.operands[i].immediate);
						result.emplace_back(IntegerToken, buf);
					}
					result.emplace_back(TextToken, "]");
				}
				tokenizeSuccess = true;
				break;
			case INDEXED_ELEMENT: /* eg: "p12.d[w15, #0xf]" */
				result.emplace_back(RegisterToken, get_register_name(operand->reg[0]));
				result.emplace_back(TextToken, get_arrspec_str_truncated(operand->arrSpec));
				result.emplace_back(TextToken, "[");
				result.emplace_back(RegisterToken, get_register_name(operand->reg[1]));
				if (operand->immediate)
				{
					result.emplace_back(OperandSeparatorToken, ", ");
					result.emplace_back(TextToken, "#");
					snprintf(buf, sizeof(buf), "0x%" PRIx64, operand->immediate);
					result.emplace_back(IntegerToken, buf);
				}
				result.emplace_back(TextToken, "]");
				tokenizeSuccess = true;
				break;
			default:
				LogError("operandClass %x\n", instr.operands[i].operandClass);
				return false;
			}
			if (!tokenizeSuccess)
			{
				LogError("tokenize failed operandClass %x\n", instr.operands[i].operandClass);
				return false;
			}
		}
		return true;
	}


	virtual string GetIntrinsicName(uint32_t intrinsic) override
	{
		switch (intrinsic)
		{
		case ARM64_INTRIN_AUTDA:
			return "__autda";
		case ARM64_INTRIN_AUTDB:
			return "__autdb";
		case ARM64_INTRIN_AUTIA:
			return "__autia";
		case ARM64_INTRIN_AUTIB:
			return "__autib";
		case ARM64_INTRIN_ISB:
			return "__isb";
		case ARM64_INTRIN_WFE:
			return "__wfe";
		case ARM64_INTRIN_WFI:
			return "__wfi";
		case ARM64_INTRIN_MSR:
			return "_WriteStatusReg";
		case ARM64_INTRIN_MRS:
			return "_ReadStatusReg";
		case ARM64_INTRIN_HINT_DGH:
			return "SystemHintOp_DGH";
		case ARM64_INTRIN_ESB:
			return "SystemHintOp_ESB";
		case ARM64_INTRIN_PACDA:
			return "__pacda";
		case ARM64_INTRIN_PACDB:
			return "__pacdb";
		case ARM64_INTRIN_PACGA:
			return "__pacga";
		case ARM64_INTRIN_PACIA:
			return "__pacia";
		case ARM64_INTRIN_PACIB:
			return "__pacib";
		case ARM64_INTRIN_PSBCSYNC:
			return "SystemHintOp_PSB";
		case ARM64_INTRIN_HINT_TSB:
			return "SystemHintOp_TSB";
		case ARM64_INTRIN_HINT_CSDB:
			return "SystemHintOp_CSDB";
		case ARM64_INTRIN_HINT_BTI:
			return "SystemHintOp_BTI";
		case ARM64_INTRIN_SEV:
			return "__sev";
		case ARM64_INTRIN_SEVL:
			return "__sevl";
		case ARM64_INTRIN_DC:
			return "__dc";
		case ARM64_INTRIN_DMB:
			return "__dmb";
		case ARM64_INTRIN_DSB:
			return "__dsb";
		case ARM64_INTRIN_YIELD:
			return "__yield";
		case ARM64_INTRIN_PRFM:
			return "__prefetch";
		case ARM64_INTRIN_XPACD:
			return "__xpacd";
		case ARM64_INTRIN_XPACI:
			return "__xpaci";
		case ARM64_INTRIN_ERET:
			return "_eret";
		case ARM64_INTRIN_CLZ:
			return "_CountLeadingZeros";
		case ARM64_INTRIN_CLREX:
			return "__clrex";
		case ARM64_INTRIN_REV:
			return "_byteswap";
		case ARM64_INTRIN_RBIT:
			return "__rbit";
		case ARM64_INTRIN_AESD:
			return "__aesd";
		case ARM64_INTRIN_AESE:
			return "__aese";
		default:
			break;
		}

		return NeonGetIntrinsicName(intrinsic);
	}


	virtual vector<uint32_t> GetAllIntrinsics() override
	{
		vector<uint32_t> result = NeonGetAllIntrinsics();

		vector<uint32_t> tmp = {
			ARM64_INTRIN_AUTDA, ARM64_INTRIN_AUTDB, ARM64_INTRIN_AUTIA, ARM64_INTRIN_AUTIB,
		    ARM64_INTRIN_DC, ARM64_INTRIN_DMB, ARM64_INTRIN_DSB, ARM64_INTRIN_ESB,
		    ARM64_INTRIN_HINT_BTI, ARM64_INTRIN_HINT_CSDB, ARM64_INTRIN_HINT_DGH, ARM64_INTRIN_HINT_TSB,
		    ARM64_INTRIN_ISB, ARM64_INTRIN_MRS, ARM64_INTRIN_MSR, ARM64_INTRIN_PACDA,
		    ARM64_INTRIN_PACDB, ARM64_INTRIN_PACGA, ARM64_INTRIN_PACIA, ARM64_INTRIN_PACIB,
		    ARM64_INTRIN_PRFM, ARM64_INTRIN_PSBCSYNC,
		    ARM64_INTRIN_SEV, ARM64_INTRIN_SEVL, ARM64_INTRIN_WFE, ARM64_INTRIN_WFI, ARM64_INTRIN_YIELD,
		    ARM64_INTRIN_XPACD, ARM64_INTRIN_XPACI, ARM64_INTRIN_ERET,
		    ARM64_INTRIN_CLZ, ARM64_INTRIN_CLREX, ARM64_INTRIN_REV, ARM64_INTRIN_RBIT,
		    ARM64_INTRIN_AESD, ARM64_INTRIN_AESE};

		result.insert(result.end(), tmp.begin(), tmp.end());
		return result;
	}


	virtual vector<NameAndType> GetIntrinsicInputs(uint32_t intrinsic) override
	{
		switch (intrinsic)
		{
		case ARM64_INTRIN_CLZ:        // reads <Xn>
		case ARM64_INTRIN_DC:         // reads <Xt>
		case ARM64_INTRIN_MSR:
		case ARM64_INTRIN_MRS:
		case ARM64_INTRIN_PRFM:
		case ARM64_INTRIN_REV:   // reads <Xn>
		case ARM64_INTRIN_RBIT:  // reads <Xn>
			return {NameAndType(Type::IntegerType(8, false))};
		case ARM64_INTRIN_AUTDA:      // reads <Xd>, <Xn|SP>
		case ARM64_INTRIN_AUTDB:      // reads <Xd>, <Xn|SP>
		case ARM64_INTRIN_AUTIA:      // reads <Xd>, <Xn|SP>
		case ARM64_INTRIN_AUTIB:      // reads <Xd>, <Xn|SP>
		case ARM64_INTRIN_PACGA:      // reads <Xn>, <Xm|SP>
		case ARM64_INTRIN_PACDA:      // reads <Xd>, <Xn>
		case ARM64_INTRIN_PACDB:      // reads <Xd>, <Xn>
		case ARM64_INTRIN_PACIA:      // reads <Xd>, <Xn>
		case ARM64_INTRIN_PACIB:      // reads <Xd>, <Xn>
			return {NameAndType(Type::IntegerType(8, false)), NameAndType(Type::IntegerType(8, false))};
		case ARM64_INTRIN_AESD:
		case ARM64_INTRIN_AESE:
			return {NameAndType(Type::IntegerType(16, false)), NameAndType(Type::IntegerType(16, false))};
		default:
			break;
		}

		return NeonGetIntrinsicInputs(intrinsic);
	}


	virtual vector<Confidence<Ref<Type>>> GetIntrinsicOutputs(uint32_t intrinsic) override
	{
		switch (intrinsic)
		{
		case ARM64_INTRIN_MSR:
		case ARM64_INTRIN_AUTDA:      // writes <Xd>
		case ARM64_INTRIN_AUTDB:      // writes <Xd>
		case ARM64_INTRIN_AUTIA:      // writes <Xd>
		case ARM64_INTRIN_AUTIB:      // writes <Xd>
		case ARM64_INTRIN_MRS:
		case ARM64_INTRIN_PACDA:      // writes <Xd>
		case ARM64_INTRIN_PACDB:      // writes <Xd>
		case ARM64_INTRIN_PACIA:      // writes <Xd>
		case ARM64_INTRIN_PACGA:      // writes <Xd>
		case ARM64_INTRIN_PACIB:      // writes <Xd>
		case ARM64_INTRIN_XPACD:      // writes <Xd>
		case ARM64_INTRIN_XPACI:      // writes <Xd>
		case ARM64_INTRIN_CLZ:        // writes <Xd>
		case ARM64_INTRIN_REV:        // writes <Xd>
		case ARM64_INTRIN_RBIT:       // writes <Xd>
			return {Type::IntegerType(8, false)};
		case ARM64_INTRIN_AESD:
		case ARM64_INTRIN_AESE:
			return {Type::IntegerType(16, false)};
		default:
			break;
		}

		return NeonGetIntrinsicOutputs(intrinsic);
	}


	virtual bool IsNeverBranchPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;
		return IsConditionalBranch(instr);
	}


	virtual bool IsAlwaysBranchPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;
		return IsConditionalBranch(instr);
	}


	virtual bool IsInvertBranchPatchAvailable(const uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;
		return IsConditionalJump(instr);
	}


	virtual bool IsSkipAndReturnZeroPatchAvailable(
	    const uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;
		return instr.operation == ARM64_BL || instr.operation == ARM64_BR ||
		       instr.operation == ARM64_BLR;
	}


	virtual bool IsSkipAndReturnValuePatchAvailable(
	    const uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;
		return instr.operation == ARM64_BL || instr.operation == ARM64_BR ||
		       instr.operation == ARM64_BLR;
	}


	virtual bool ConvertToNop(uint8_t* data, uint64_t, size_t len) override
	{
		uint32_t arm64_nop = 0xd503201f;
		if (len < sizeof(arm64_nop))
			return false;
		size_t i;
		for (i = 0; i < len / sizeof(arm64_nop); i++)
			((uint32_t*)data)[i] = arm64_nop;
		return true;
	}


	virtual bool AlwaysBranch(uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;

		uint32_t* value = (uint32_t*)data;
		// Combine the immediate in the first operand with the unconditional branch opcode to form
		// an unconditional branch instruction
		*value = (5 << 26) | (uint32_t)((instr.operands[0].immediate - addr) >> 2);
		return true;
	}


	virtual bool InvertBranch(uint8_t* data, uint64_t addr, size_t len) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
			return false;

		uint32_t* value = (uint32_t*)data;
		if (IsConditionalBranch(instr))
		{
			// The inverted branch is the inversion of the low order nibble
			*value ^= 1;
		}
		else if (IsTestAndBranch(instr) || IsCompareAndBranch(instr))
		{
			// invert bit 24
			*value ^= (1 << 24);
		}
		return true;
	}


	virtual bool SkipAndReturnValue(uint8_t* data, uint64_t addr, size_t len, uint64_t value) override
	{
		(void)addr;
		// Return value is put in X0. The largest value that we can put into a single integer is 16 bits
		if (value > 0xffff || len > 4)
			return false;

		uint32_t movValueR0 = 0xd2800000;
		uint32_t* inst = (uint32_t*)data;
		*inst = movValueR0 | ((uint32_t)value << 5);
		return true;
	}


	virtual bool GetInstructionLowLevelIL(
	    const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il) override
	{
		Instruction instr;
		if (!Disassemble(data, addr, len, instr))
		{
			il.AddInstruction(il.Undefined());
			return false;
		}

		len = 4;
		return GetLowLevelILForInstruction(this, addr, il, instr, GetAddressSize());
	}


	/* flags and their names */

	virtual vector<uint32_t> GetAllFlags() override
	{
		return vector<uint32_t> {IL_FLAG_N, IL_FLAG_Z, IL_FLAG_C, IL_FLAG_V};
	}


	virtual string GetFlagName(uint32_t flag) override
	{
		char result[32];
		switch (flag)
		{
		case IL_FLAG_N:
			return "n";
		case IL_FLAG_Z:
			return "z";
		case IL_FLAG_C:
			return "c";
		case IL_FLAG_V:
			return "v";
		default:
			sprintf(result, "flag%" PRIu32, flag);
			return result;
		}
	}

	virtual vector<uint32_t> GetAllSemanticFlagClasses() override
	{
		return vector<uint32_t> {IL_FLAG_CLASS_INT, IL_FLAG_CLASS_FLOAT};
	}

	virtual string GetSemanticFlagClassName(uint32_t semClass) override
	{
		switch (semClass) {
			case IL_FLAG_CLASS_INT:
				return "int";
			case IL_FLAG_CLASS_FLOAT:
				return "float";
			default:
				return "";
		}
	}

	virtual uint32_t GetSemanticClassForFlagWriteType(uint32_t writeType) override
	{
		switch (writeType) {
		case IL_FLAG_WRITE_ALL_FLOAT:
			return IL_FLAG_CLASS_FLOAT;
		case IL_FLAG_WRITE_ALL:
		default:
			return IL_FLAG_CLASS_INT;
		}
	}

	virtual vector<uint32_t> GetAllSemanticFlagGroups() override
	{

		return vector<uint32_t> {
			IL_FLAG_GROUP_EQ, IL_FLAG_GROUP_NE, IL_FLAG_GROUP_CS, IL_FLAG_GROUP_CC,
			IL_FLAG_GROUP_MI, IL_FLAG_GROUP_PL, IL_FLAG_GROUP_VS, IL_FLAG_GROUP_VC,
			IL_FLAG_GROUP_HI, IL_FLAG_GROUP_LS, IL_FLAG_GROUP_GE, IL_FLAG_GROUP_LT,
			IL_FLAG_GROUP_GT, IL_FLAG_GROUP_LE};
	}

	virtual string GetSemanticFlagGroupName(uint32_t semGroup) override
	{
		switch (semGroup)
		{
		case IL_FLAG_GROUP_EQ:
			return "eq";
		case IL_FLAG_GROUP_NE:
			return "ne";
		case IL_FLAG_GROUP_CS:
			return "cs";
		case IL_FLAG_GROUP_CC:
			return "cc";
		case IL_FLAG_GROUP_MI:
			return "mi";
		case IL_FLAG_GROUP_PL:
			return "pl";
		case IL_FLAG_GROUP_VS:
			return "vs";
		case IL_FLAG_GROUP_VC:
			return "vc";
		case IL_FLAG_GROUP_HI:
			return "hi";
		case IL_FLAG_GROUP_LS:
			return "ls";
		case IL_FLAG_GROUP_GE:
			return "ge";
		case IL_FLAG_GROUP_LT:
			return "lt";
		case IL_FLAG_GROUP_GT:
			return "gt";
		case IL_FLAG_GROUP_LE:
			return "le";
		default:
			return "";
		}
	}

	virtual vector<uint32_t> GetFlagsRequiredForSemanticFlagGroup(uint32_t semGroup) override
	{
		switch (semGroup)
		{
		case IL_FLAG_GROUP_EQ:
		case IL_FLAG_GROUP_NE:
			return vector<uint32_t> {IL_FLAG_Z};
		case IL_FLAG_GROUP_CS:
		case IL_FLAG_GROUP_CC:
			return vector<uint32_t> {IL_FLAG_C};
		case IL_FLAG_GROUP_MI:
		case IL_FLAG_GROUP_PL:
			return vector<uint32_t> {IL_FLAG_N};
		case IL_FLAG_GROUP_VS:
		case IL_FLAG_GROUP_VC:
			return vector<uint32_t> {IL_FLAG_V};
		case IL_FLAG_GROUP_HI:
		case IL_FLAG_GROUP_LS:
			return vector<uint32_t> {IL_FLAG_C, IL_FLAG_Z};
		case IL_FLAG_GROUP_GE:
		case IL_FLAG_GROUP_LT:
			return vector<uint32_t> {IL_FLAG_N, IL_FLAG_V};
		case IL_FLAG_GROUP_GT:
		case IL_FLAG_GROUP_LE:
			return vector<uint32_t> {IL_FLAG_Z, IL_FLAG_N, IL_FLAG_V};
		default:
			return vector<uint32_t>();
		}
	}

	virtual map<uint32_t, BNLowLevelILFlagCondition> GetFlagConditionsForSemanticFlagGroup(uint32_t semGroup) override
	{
		switch (semGroup) {
		case IL_FLAG_GROUP_EQ:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_E},
				{IL_FLAG_CLASS_FLOAT, LLFC_FE},
			};
		case IL_FLAG_GROUP_NE:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_NE},
				{IL_FLAG_CLASS_FLOAT, LLFC_FNE},
			};
		case IL_FLAG_GROUP_CS:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_UGE},
				{IL_FLAG_CLASS_FLOAT, LLFC_FGE},
			};
		case IL_FLAG_GROUP_CC:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_ULT},
				{IL_FLAG_CLASS_FLOAT, LLFC_FLT},
			};
		case IL_FLAG_GROUP_MI:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_NEG},
				{IL_FLAG_CLASS_FLOAT, LLFC_FLT},
			};
		case IL_FLAG_GROUP_PL:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_POS},
				{IL_FLAG_CLASS_FLOAT, LLFC_FGE},
			};
		case IL_FLAG_GROUP_VS:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_O},
				{IL_FLAG_CLASS_FLOAT, LLFC_FO},
			};
		case IL_FLAG_GROUP_VC:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_NO},
				{IL_FLAG_CLASS_FLOAT, LLFC_FUO},
			};
		case IL_FLAG_GROUP_HI:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_UGT},
				{IL_FLAG_CLASS_FLOAT, LLFC_FGT},
			};
		case IL_FLAG_GROUP_LS:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_ULE},
				{IL_FLAG_CLASS_FLOAT, LLFC_FLE},
			};
		case IL_FLAG_GROUP_GE:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_SGE},
				{IL_FLAG_CLASS_FLOAT, LLFC_FGE},
			};
		case IL_FLAG_GROUP_LT:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_SLT},
				{IL_FLAG_CLASS_FLOAT, LLFC_FLT},
			};
		case IL_FLAG_GROUP_GT:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_SGT},
				{IL_FLAG_CLASS_FLOAT, LLFC_FGT},
			};
		case IL_FLAG_GROUP_LE:
			return map<uint32_t, BNLowLevelILFlagCondition> {
				{IL_FLAG_CLASS_INT, LLFC_SLE},
				{IL_FLAG_CLASS_FLOAT, LLFC_FLE},
			};
		default:
			return map<uint32_t, BNLowLevelILFlagCondition>();
		}
	}

	virtual size_t GetSemanticFlagGroupLowLevelIL(uint32_t semGroup, LowLevelILFunction& il) override
	{
		switch (semGroup)
		{
		case IL_FLAG_GROUP_EQ:
			return GetFlagConditionLowLevelIL(LLFC_E, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_NE:
			return GetFlagConditionLowLevelIL(LLFC_NE, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_CS:
			return GetFlagConditionLowLevelIL(LLFC_UGE, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_CC:
			return GetFlagConditionLowLevelIL(LLFC_ULT, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_MI:
			return GetFlagConditionLowLevelIL(LLFC_NEG, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_PL:
			return GetFlagConditionLowLevelIL(LLFC_POS, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_VS:
			return GetFlagConditionLowLevelIL(LLFC_O, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_VC:
			return GetFlagConditionLowLevelIL(LLFC_NO, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_HI:
			return GetFlagConditionLowLevelIL(LLFC_UGT, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_LS:
			return GetFlagConditionLowLevelIL(LLFC_ULE, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_GE:
			return GetFlagConditionLowLevelIL(LLFC_SGE, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_LT:
			return GetFlagConditionLowLevelIL(LLFC_SLT, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_GT:
			return GetFlagConditionLowLevelIL(LLFC_SGT, IL_FLAG_CLASS_INT, il);
		case IL_FLAG_GROUP_LE:
			return GetFlagConditionLowLevelIL(LLFC_SLE, IL_FLAG_CLASS_INT, il);
		default:
			return il.Unimplemented();
		}
	}


	/* flag roles */

	virtual BNFlagRole GetFlagRole(uint32_t flag, uint32_t) override
	{
		switch (flag)
		{
		case IL_FLAG_N:
			return NegativeSignFlagRole;
		case IL_FLAG_Z:
			return ZeroFlagRole;
		case IL_FLAG_C:
			return CarryFlagRole;
		case IL_FLAG_V:
			return OverflowFlagRole;
		default:
			return SpecialFlagRole;
		}
	}


	/* flag write types */

	virtual vector<uint32_t> GetAllFlagWriteTypes() override
	{
		return vector<uint32_t> {IL_FLAG_WRITE_ALL, IL_FLAG_WRITE_ALL_FLOAT};
	}


	virtual string GetFlagWriteTypeName(uint32_t flags) override
	{
		switch (flags)
		{
		case IL_FLAG_WRITE_ALL:
			return "*";
		case IL_FLAG_WRITE_ALL_FLOAT:
			return "f*";
		default:
			return "";
		}
	}


	virtual vector<uint32_t> GetFlagsWrittenByFlagWriteType(uint32_t flags) override
	{
		switch (flags)
		{
		case IL_FLAG_WRITE_ALL:
		case IL_FLAG_WRITE_ALL_FLOAT:
			return vector<uint32_t> {IL_FLAG_N, IL_FLAG_Z, IL_FLAG_C, IL_FLAG_V};
		default:
			return vector<uint32_t> {};
		}
	}


	/* override default flag setting expressions */

	virtual size_t GetFlagWriteLowLevelIL(BNLowLevelILOperation op, size_t size,
	    uint32_t flagWriteType, uint32_t flag, BNRegisterOrConstant* operands, size_t operandCount,
	    LowLevelILFunction& il) override
	{
		switch (op)
		{
		case LLIL_SBB:
			switch (flag)
			{
			case IL_FLAG_C:
				// r u< a || (r == a && flag_c)
				return il.Or(0,
				    il.CompareUnsignedLessThan(size,
				        il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount),
				        il.GetExprForRegisterOrConstant(operands[0], size)),
				    il.And(0,
				        il.CompareEqual(size,
				            il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount),
				            il.GetExprForRegisterOrConstant(operands[0], size)),
				        il.Flag(IL_FLAG_C)));
			}
		default:
			break;
		}

		BNFlagRole role = GetFlagRole(flag, GetSemanticClassForFlagWriteType(flagWriteType));
		return GetDefaultFlagWriteLowLevelIL(op, size, role, operands, operandCount, il);
	}


	virtual string GetRegisterName(uint32_t reg_) override
	{
		if (reg_ > REG_NONE && reg_ < REG_END)
			return get_register_name((enum Register)reg_);

		if (reg_ > SYSREG_NONE && reg_ < SYSREG_END)
			return get_system_register_name((enum SystemReg)reg_);

		if (reg_ == FAKEREG_SYSREG_UNKNOWN)
			return "sysreg_unknown";

		if (reg_ == FAKEREG_SYSCALL_INFO)
			return "syscall_info";

		return "";
	}


	virtual vector<uint32_t> GetFullWidthRegisters() override
	{
		return vector<uint32_t>{
			REG_X0,   REG_X1,  REG_X2,  REG_X3,   REG_X4,  REG_X5,  REG_X6,  REG_X7,
			REG_X8,   REG_X9,  REG_X10, REG_X11,  REG_X12, REG_X13, REG_X14, REG_X15,
			REG_X16,  REG_X17, REG_X18, REG_X19,  REG_X20, REG_X21, REG_X22, REG_X23,
			REG_X24,  REG_X25, REG_X26, REG_X27,  REG_X28, REG_X29, REG_X30, REG_SP,
			// Vector
			REG_V0,   REG_V1,  REG_V2,  REG_V3,   REG_V4,  REG_V5,  REG_V6,  REG_V7,
			REG_V8,   REG_V9,  REG_V10, REG_V11,  REG_V12, REG_V13, REG_V14, REG_V15,
			REG_V16,  REG_V17, REG_V18, REG_V19,  REG_V20, REG_V21, REG_V22, REG_V23,
			REG_V24,  REG_V25, REG_V26, REG_V27,  REG_V28, REG_V29, REG_V30, REG_V31,
			// SVE
			REG_P0,   REG_P1,  REG_P2,  REG_P3,   REG_P4,  REG_P5,  REG_P6,  REG_P7,
			REG_P8,   REG_P9,  REG_P10,  REG_P11,   REG_P12,  REG_P13,  REG_P14,  REG_P15,
			REG_P16,   REG_P17,  REG_P18,  REG_P19,   REG_P20,  REG_P21,  REG_P22,  REG_P23,
			REG_P24,   REG_P25,  REG_P26,  REG_P27,   REG_P29,  REG_P29,  REG_P30,  REG_P31,
		};
	}


	virtual vector<uint32_t> GetAllRegisters() override
	{
		vector<uint32_t> r = {
			/* regular registers */
			REG_W0,  REG_W1,  REG_W2,  REG_W3,  REG_W4,  REG_W5,  REG_W6,  REG_W7,
			REG_W8,  REG_W9,  REG_W10, REG_W11, REG_W12, REG_W13, REG_W14, REG_W15,
			REG_W16, REG_W17, REG_W18, REG_W19, REG_W20, REG_W21, REG_W22, REG_W23,
			REG_W24, REG_W25, REG_W26, REG_W27, REG_W28, REG_W29, REG_W30, REG_WSP,
			REG_X0,  REG_X1,  REG_X2,  REG_X3,  REG_X4,  REG_X5,  REG_X6,  REG_X7,
			REG_X8,  REG_X9,  REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15,
			REG_X16, REG_X17, REG_X18, REG_X19, REG_X20, REG_X21, REG_X22, REG_X23,
			REG_X24, REG_X25, REG_X26, REG_X27, REG_X28, REG_X29, REG_X30, REG_SP,
			REG_V0,  REG_V1,  REG_V2,  REG_V3,  REG_V4,  REG_V5,  REG_V6,  REG_V7,
			REG_V8,  REG_V9,  REG_V10, REG_V11, REG_V12, REG_V13, REG_V14, REG_V15,
			REG_V16, REG_V17, REG_V18, REG_V19, REG_V20, REG_V21, REG_V22, REG_V23,
			REG_V24, REG_V25, REG_V26, REG_V27, REG_V28, REG_V29, REG_V30, REG_V31,
			REG_B0,  REG_B1,  REG_B2,  REG_B3,  REG_B4,  REG_B5,  REG_B6,  REG_B7,
			REG_B8,  REG_B9,  REG_B10, REG_B11, REG_B12, REG_B13, REG_B14, REG_B15,
			REG_B16, REG_B17, REG_B18, REG_B19, REG_B20, REG_B21, REG_B22, REG_B23,
			REG_B24, REG_B25, REG_B26, REG_B27, REG_B28, REG_B29, REG_B30, REG_B31,
			REG_H0,  REG_H1,  REG_H2,  REG_H3,  REG_H4,  REG_H5,  REG_H6,  REG_H7,
			REG_H8,  REG_H9,  REG_H10, REG_H11, REG_H12, REG_H13, REG_H14, REG_H15,
			REG_H16, REG_H17, REG_H18, REG_H19, REG_H20, REG_H21, REG_H22, REG_H23,
			REG_H24, REG_H25, REG_H26, REG_H27, REG_H28, REG_H29, REG_H30, REG_H31,
			REG_S0,  REG_S1,  REG_S2,  REG_S3,  REG_S4,  REG_S5,  REG_S6,  REG_S7,
			REG_S8,  REG_S9,  REG_S10, REG_S11, REG_S12, REG_S13, REG_S14, REG_S15,
			REG_S16, REG_S17, REG_S18, REG_S19, REG_S20, REG_S21, REG_S22, REG_S23,
			REG_S24, REG_S25, REG_S26, REG_S27, REG_S28, REG_S29, REG_S30, REG_S31,
			REG_D0,  REG_D1,  REG_D2,  REG_D3,  REG_D4,  REG_D5,  REG_D6,  REG_D7,
			REG_D8,  REG_D9,  REG_D10, REG_D11, REG_D12, REG_D13, REG_D14, REG_D15,
			REG_D16, REG_D17, REG_D18, REG_D19, REG_D20, REG_D21, REG_D22, REG_D23,
			REG_D24, REG_D25, REG_D26, REG_D27, REG_D28, REG_D29, REG_D30, REG_D31,
			REG_Q0,  REG_Q1,  REG_Q2,  REG_Q3,  REG_Q4,  REG_Q5,  REG_Q6,  REG_Q7,
			REG_Q8,  REG_Q9,  REG_Q10, REG_Q11, REG_Q12, REG_Q13, REG_Q14, REG_Q15,
			REG_Q16, REG_Q17, REG_Q18, REG_Q19, REG_Q20, REG_Q21, REG_Q22, REG_Q23,
			REG_Q24, REG_Q25, REG_Q26, REG_Q27, REG_Q28, REG_Q29, REG_Q30, REG_Q31,
			// B vectors
			REG_V0_B0, REG_V0_B1, REG_V0_B2, REG_V0_B3, REG_V0_B4, REG_V0_B5, REG_V0_B6, REG_V0_B7,
			REG_V0_B8, REG_V0_B9, REG_V0_B10, REG_V0_B11, REG_V0_B12, REG_V0_B13, REG_V0_B14, REG_V0_B15,
			REG_V1_B0, REG_V1_B1, REG_V1_B2, REG_V1_B3, REG_V1_B4, REG_V1_B5, REG_V1_B6, REG_V1_B7,
			REG_V1_B8, REG_V1_B9, REG_V1_B10, REG_V1_B11, REG_V1_B12, REG_V1_B13, REG_V1_B14, REG_V1_B15,
			REG_V2_B0, REG_V2_B1, REG_V2_B2, REG_V2_B3, REG_V2_B4, REG_V2_B5, REG_V2_B6, REG_V2_B7,
			REG_V2_B8, REG_V2_B9, REG_V2_B10, REG_V2_B11, REG_V2_B12, REG_V2_B13, REG_V2_B14, REG_V2_B15,
			REG_V3_B0, REG_V3_B1, REG_V3_B2, REG_V3_B3, REG_V3_B4, REG_V3_B5, REG_V3_B6, REG_V3_B7,
			REG_V3_B8, REG_V3_B9, REG_V3_B10, REG_V3_B11, REG_V3_B12, REG_V3_B13, REG_V3_B14, REG_V3_B15,
			REG_V4_B0, REG_V4_B1, REG_V4_B2, REG_V4_B3, REG_V4_B4, REG_V4_B5, REG_V4_B6, REG_V4_B7,
			REG_V4_B8, REG_V4_B9, REG_V4_B10, REG_V4_B11, REG_V4_B12, REG_V4_B13, REG_V4_B14, REG_V4_B15,
			REG_V5_B0, REG_V5_B1, REG_V5_B2, REG_V5_B3, REG_V5_B4, REG_V5_B5, REG_V5_B6, REG_V5_B7,
			REG_V5_B8, REG_V5_B9, REG_V5_B10, REG_V5_B11, REG_V5_B12, REG_V5_B13, REG_V5_B14, REG_V5_B15,
			REG_V6_B0, REG_V6_B1, REG_V6_B2, REG_V6_B3, REG_V6_B4, REG_V6_B5, REG_V6_B6, REG_V6_B7,
			REG_V6_B8, REG_V6_B9, REG_V6_B10, REG_V6_B11, REG_V6_B12, REG_V6_B13, REG_V6_B14, REG_V6_B15,
			REG_V7_B0, REG_V7_B1, REG_V7_B2, REG_V7_B3, REG_V7_B4, REG_V7_B5, REG_V7_B6, REG_V7_B7,
			REG_V7_B8, REG_V7_B9, REG_V7_B10, REG_V7_B11, REG_V7_B12, REG_V7_B13, REG_V7_B14, REG_V7_B15,
			REG_V8_B0, REG_V8_B1, REG_V8_B2, REG_V8_B3, REG_V8_B4, REG_V8_B5, REG_V8_B6, REG_V8_B7,
			REG_V8_B8, REG_V8_B9, REG_V8_B10, REG_V8_B11, REG_V8_B12, REG_V8_B13, REG_V8_B14, REG_V8_B15,
			REG_V9_B0, REG_V9_B1, REG_V9_B2, REG_V9_B3, REG_V9_B4, REG_V9_B5, REG_V9_B6, REG_V9_B7,
			REG_V9_B8, REG_V9_B9, REG_V9_B10, REG_V9_B11, REG_V9_B12, REG_V9_B13, REG_V9_B14, REG_V9_B15,
			REG_V10_B0, REG_V10_B1, REG_V10_B2, REG_V10_B3, REG_V10_B4, REG_V10_B5, REG_V10_B6, REG_V10_B7,
			REG_V10_B8, REG_V10_B9, REG_V10_B10, REG_V10_B11, REG_V10_B12, REG_V10_B13, REG_V10_B14, REG_V10_B15,
			REG_V11_B0, REG_V11_B1, REG_V11_B2, REG_V11_B3, REG_V11_B4, REG_V11_B5, REG_V11_B6, REG_V11_B7,
			REG_V11_B8, REG_V11_B9, REG_V11_B10, REG_V11_B11, REG_V11_B12, REG_V11_B13, REG_V11_B14, REG_V11_B15,
			REG_V12_B0, REG_V12_B1, REG_V12_B2, REG_V12_B3, REG_V12_B4, REG_V12_B5, REG_V12_B6, REG_V12_B7,
			REG_V12_B8, REG_V12_B9, REG_V12_B10, REG_V12_B11, REG_V12_B12, REG_V12_B13, REG_V12_B14, REG_V12_B15,
			REG_V13_B0, REG_V13_B1, REG_V13_B2, REG_V13_B3, REG_V13_B4, REG_V13_B5, REG_V13_B6, REG_V13_B7,
			REG_V13_B8, REG_V13_B9, REG_V13_B10, REG_V13_B11, REG_V13_B12, REG_V13_B13, REG_V13_B14, REG_V13_B15,
			REG_V14_B0, REG_V14_B1, REG_V14_B2, REG_V14_B3, REG_V14_B4, REG_V14_B5, REG_V14_B6, REG_V14_B7,
			REG_V14_B8, REG_V14_B9, REG_V14_B10, REG_V14_B11, REG_V14_B12, REG_V14_B13, REG_V14_B14, REG_V14_B15,
			REG_V15_B0, REG_V15_B1, REG_V15_B2, REG_V15_B3, REG_V15_B4, REG_V15_B5, REG_V15_B6, REG_V15_B7,
			REG_V15_B8, REG_V15_B9, REG_V15_B10, REG_V15_B11, REG_V15_B12, REG_V15_B13, REG_V15_B14, REG_V15_B15,
			REG_V16_B0, REG_V16_B1, REG_V16_B2, REG_V16_B3, REG_V16_B4, REG_V16_B5, REG_V16_B6, REG_V16_B7,
			REG_V16_B8, REG_V16_B9, REG_V16_B10, REG_V16_B11, REG_V16_B12, REG_V16_B13, REG_V16_B14, REG_V16_B15,
			REG_V17_B0, REG_V17_B1, REG_V17_B2, REG_V17_B3, REG_V17_B4, REG_V17_B5, REG_V17_B6, REG_V17_B7,
			REG_V17_B8, REG_V17_B9, REG_V17_B10, REG_V17_B11, REG_V17_B12, REG_V17_B13, REG_V17_B14, REG_V17_B15,
			REG_V18_B0, REG_V18_B1, REG_V18_B2, REG_V18_B3, REG_V18_B4, REG_V18_B5, REG_V18_B6, REG_V18_B7,
			REG_V18_B8, REG_V18_B9, REG_V18_B10, REG_V18_B11, REG_V18_B12, REG_V18_B13, REG_V18_B14, REG_V18_B15,
			REG_V19_B0, REG_V19_B1, REG_V19_B2, REG_V19_B3, REG_V19_B4, REG_V19_B5, REG_V19_B6, REG_V19_B7,
			REG_V19_B8, REG_V19_B9, REG_V19_B10, REG_V19_B11, REG_V19_B12, REG_V19_B13, REG_V19_B14, REG_V19_B15,
			REG_V20_B0, REG_V20_B1, REG_V20_B2, REG_V20_B3, REG_V20_B4, REG_V20_B5, REG_V20_B6, REG_V20_B7,
			REG_V20_B8, REG_V20_B9, REG_V20_B10, REG_V20_B11, REG_V20_B12, REG_V20_B13, REG_V20_B14, REG_V20_B15,
			REG_V21_B0, REG_V21_B1, REG_V21_B2, REG_V21_B3, REG_V21_B4, REG_V21_B5, REG_V21_B6, REG_V21_B7,
			REG_V21_B8, REG_V21_B9, REG_V21_B10, REG_V21_B11, REG_V21_B12, REG_V21_B13, REG_V21_B14, REG_V21_B15,
			REG_V22_B0, REG_V22_B1, REG_V22_B2, REG_V22_B3, REG_V22_B4, REG_V22_B5, REG_V22_B6, REG_V22_B7,
			REG_V22_B8, REG_V22_B9, REG_V22_B10, REG_V22_B11, REG_V22_B12, REG_V22_B13, REG_V22_B14, REG_V22_B15,
			REG_V23_B0, REG_V23_B1, REG_V23_B2, REG_V23_B3, REG_V23_B4, REG_V23_B5, REG_V23_B6, REG_V23_B7,
			REG_V23_B8, REG_V23_B9, REG_V23_B10, REG_V23_B11, REG_V23_B12, REG_V23_B13, REG_V23_B14, REG_V23_B15,
			REG_V24_B0, REG_V24_B1, REG_V24_B2, REG_V24_B3, REG_V24_B4, REG_V24_B5, REG_V24_B6, REG_V24_B7,
			REG_V24_B8, REG_V24_B9, REG_V24_B10, REG_V24_B11, REG_V24_B12, REG_V24_B13, REG_V24_B14, REG_V24_B15,
			REG_V25_B0, REG_V25_B1, REG_V25_B2, REG_V25_B3, REG_V25_B4, REG_V25_B5, REG_V25_B6, REG_V25_B7,
			REG_V25_B8, REG_V25_B9, REG_V25_B10, REG_V25_B11, REG_V25_B12, REG_V25_B13, REG_V25_B14, REG_V25_B15,
			REG_V26_B0, REG_V26_B1, REG_V26_B2, REG_V26_B3, REG_V26_B4, REG_V26_B5, REG_V26_B6, REG_V26_B7,
			REG_V26_B8, REG_V26_B9, REG_V26_B10, REG_V26_B11, REG_V26_B12, REG_V26_B13, REG_V26_B14, REG_V26_B15,
			REG_V27_B0, REG_V27_B1, REG_V27_B2, REG_V27_B3, REG_V27_B4, REG_V27_B5, REG_V27_B6, REG_V27_B7,
			REG_V27_B8, REG_V27_B9, REG_V27_B10, REG_V27_B11, REG_V27_B12, REG_V27_B13, REG_V27_B14, REG_V27_B15,
			REG_V28_B0, REG_V28_B1, REG_V28_B2, REG_V28_B3, REG_V28_B4, REG_V28_B5, REG_V28_B6, REG_V28_B7,
			REG_V28_B8, REG_V28_B9, REG_V28_B10, REG_V28_B11, REG_V28_B12, REG_V28_B13, REG_V28_B14, REG_V28_B15,
			REG_V29_B0, REG_V29_B1, REG_V29_B2, REG_V29_B3, REG_V29_B4, REG_V29_B5, REG_V29_B6, REG_V29_B7,
			REG_V29_B8, REG_V29_B9, REG_V29_B10, REG_V29_B11, REG_V29_B12, REG_V29_B13, REG_V29_B14, REG_V29_B15,
			REG_V30_B0, REG_V30_B1, REG_V30_B2, REG_V30_B3, REG_V30_B4, REG_V30_B5, REG_V30_B6, REG_V30_B7,
			REG_V30_B8, REG_V30_B9, REG_V30_B10, REG_V30_B11, REG_V30_B12, REG_V30_B13, REG_V30_B14, REG_V30_B15,
			REG_V31_B0, REG_V31_B1, REG_V31_B2, REG_V31_B3, REG_V31_B4, REG_V31_B5, REG_V31_B6, REG_V31_B7,
			REG_V31_B8, REG_V31_B9, REG_V31_B10, REG_V31_B11, REG_V31_B12, REG_V31_B13, REG_V31_B14, REG_V31_B15,
			// H vectors
			REG_V0_H0, REG_V0_H1, REG_V0_H2, REG_V0_H3, REG_V0_H4, REG_V0_H5, REG_V0_H6, REG_V0_H7,
			REG_V1_H0, REG_V1_H1, REG_V1_H2, REG_V1_H3, REG_V1_H4, REG_V1_H5, REG_V1_H6, REG_V1_H7,
			REG_V2_H0, REG_V2_H1, REG_V2_H2, REG_V2_H3, REG_V2_H4, REG_V2_H5, REG_V2_H6, REG_V2_H7,
			REG_V3_H0, REG_V3_H1, REG_V3_H2, REG_V3_H3, REG_V3_H4, REG_V3_H5, REG_V3_H6, REG_V3_H7,
			REG_V4_H0, REG_V4_H1, REG_V4_H2, REG_V4_H3, REG_V4_H4, REG_V4_H5, REG_V4_H6, REG_V4_H7,
			REG_V5_H0, REG_V5_H1, REG_V5_H2, REG_V5_H3, REG_V5_H4, REG_V5_H5, REG_V5_H6, REG_V5_H7,
			REG_V6_H0, REG_V6_H1, REG_V6_H2, REG_V6_H3, REG_V6_H4, REG_V6_H5, REG_V6_H6, REG_V6_H7,
			REG_V7_H0, REG_V7_H1, REG_V7_H2, REG_V7_H3, REG_V7_H4, REG_V7_H5, REG_V7_H6, REG_V7_H7,
			REG_V8_H0, REG_V8_H1, REG_V8_H2, REG_V8_H3, REG_V8_H4, REG_V8_H5, REG_V8_H6, REG_V8_H7,
			REG_V9_H0, REG_V9_H1, REG_V9_H2, REG_V9_H3, REG_V9_H4, REG_V9_H5, REG_V9_H6, REG_V9_H7,
			REG_V10_H0, REG_V10_H1, REG_V10_H2, REG_V10_H3, REG_V10_H4, REG_V10_H5, REG_V10_H6, REG_V10_H7,
			REG_V11_H0, REG_V11_H1, REG_V11_H2, REG_V11_H3, REG_V11_H4, REG_V11_H5, REG_V11_H6, REG_V11_H7,
			REG_V12_H0, REG_V12_H1, REG_V12_H2, REG_V12_H3, REG_V12_H4, REG_V12_H5, REG_V12_H6, REG_V12_H7,
			REG_V13_H0, REG_V13_H1, REG_V13_H2, REG_V13_H3, REG_V13_H4, REG_V13_H5, REG_V13_H6, REG_V13_H7,
			REG_V14_H0, REG_V14_H1, REG_V14_H2, REG_V14_H3, REG_V14_H4, REG_V14_H5, REG_V14_H6, REG_V14_H7,
			REG_V15_H0, REG_V15_H1, REG_V15_H2, REG_V15_H3, REG_V15_H4, REG_V15_H5, REG_V15_H6, REG_V15_H7,
			REG_V16_H0, REG_V16_H1, REG_V16_H2, REG_V16_H3, REG_V16_H4, REG_V16_H5, REG_V16_H6, REG_V16_H7,
			REG_V17_H0, REG_V17_H1, REG_V17_H2, REG_V17_H3, REG_V17_H4, REG_V17_H5, REG_V17_H6, REG_V17_H7,
			REG_V18_H0, REG_V18_H1, REG_V18_H2, REG_V18_H3, REG_V18_H4, REG_V18_H5, REG_V18_H6, REG_V18_H7,
			REG_V19_H0, REG_V19_H1, REG_V19_H2, REG_V19_H3, REG_V19_H4, REG_V19_H5, REG_V19_H6, REG_V19_H7,
			REG_V20_H0, REG_V20_H1, REG_V20_H2, REG_V20_H3, REG_V20_H4, REG_V20_H5, REG_V20_H6, REG_V20_H7,
			REG_V21_H0, REG_V21_H1, REG_V21_H2, REG_V21_H3, REG_V21_H4, REG_V21_H5, REG_V21_H6, REG_V21_H7,
			REG_V22_H0, REG_V22_H1, REG_V22_H2, REG_V22_H3, REG_V22_H4, REG_V22_H5, REG_V22_H6, REG_V22_H7,
			REG_V23_H0, REG_V23_H1, REG_V23_H2, REG_V23_H3, REG_V23_H4, REG_V23_H5, REG_V23_H6, REG_V23_H7,
			REG_V24_H0, REG_V24_H1, REG_V24_H2, REG_V24_H3, REG_V24_H4, REG_V24_H5, REG_V24_H6, REG_V24_H7,
			REG_V25_H0, REG_V25_H1, REG_V25_H2, REG_V25_H3, REG_V25_H4, REG_V25_H5, REG_V25_H6, REG_V25_H7,
			REG_V26_H0, REG_V26_H1, REG_V26_H2, REG_V26_H3, REG_V26_H4, REG_V26_H5, REG_V26_H6, REG_V26_H7,
			REG_V27_H0, REG_V27_H1, REG_V27_H2, REG_V27_H3, REG_V27_H4, REG_V27_H5, REG_V27_H6, REG_V27_H7,
			REG_V28_H0, REG_V28_H1, REG_V28_H2, REG_V28_H3, REG_V28_H4, REG_V28_H5, REG_V28_H6, REG_V28_H7,
			REG_V29_H0, REG_V29_H1, REG_V29_H2, REG_V29_H3, REG_V29_H4, REG_V29_H5, REG_V29_H6, REG_V29_H7,
			REG_V30_H0, REG_V30_H1, REG_V30_H2, REG_V30_H3, REG_V30_H4, REG_V30_H5, REG_V30_H6, REG_V30_H7,
			REG_V31_H0, REG_V31_H1, REG_V31_H2, REG_V31_H3, REG_V31_H4, REG_V31_H5, REG_V31_H6, REG_V31_H7,
			// S vectors
			REG_V0_S0, REG_V0_S1, REG_V0_S2, REG_V0_S3, REG_V1_S0, REG_V1_S1, REG_V1_S2, REG_V1_S3,
			REG_V2_S0, REG_V2_S1, REG_V2_S2, REG_V2_S3, REG_V3_S0, REG_V3_S1, REG_V3_S2, REG_V3_S3,
			REG_V4_S0, REG_V4_S1, REG_V4_S2, REG_V4_S3, REG_V5_S0, REG_V5_S1, REG_V5_S2, REG_V5_S3,
			REG_V6_S0, REG_V6_S1, REG_V6_S2, REG_V6_S3, REG_V7_S0, REG_V7_S1, REG_V7_S2, REG_V7_S3,
			REG_V8_S0, REG_V8_S1, REG_V8_S2, REG_V8_S3, REG_V9_S0, REG_V9_S1, REG_V9_S2, REG_V9_S3,
			REG_V10_S0, REG_V10_S1, REG_V10_S2, REG_V10_S3, REG_V11_S0, REG_V11_S1, REG_V11_S2, REG_V11_S3,
			REG_V12_S0, REG_V12_S1, REG_V12_S2, REG_V12_S3, REG_V13_S0, REG_V13_S1, REG_V13_S2, REG_V13_S3,
			REG_V14_S0, REG_V14_S1, REG_V14_S2, REG_V14_S3, REG_V15_S0, REG_V15_S1, REG_V15_S2, REG_V15_S3,
			REG_V16_S0, REG_V16_S1, REG_V16_S2, REG_V16_S3, REG_V17_S0, REG_V17_S1, REG_V17_S2, REG_V17_S3,
			REG_V18_S0, REG_V18_S1, REG_V18_S2, REG_V18_S3, REG_V19_S0, REG_V19_S1, REG_V19_S2, REG_V19_S3,
			REG_V20_S0, REG_V20_S1, REG_V20_S2, REG_V20_S3, REG_V21_S0, REG_V21_S1, REG_V21_S2, REG_V21_S3,
			REG_V22_S0, REG_V22_S1, REG_V22_S2, REG_V22_S3, REG_V23_S0, REG_V23_S1, REG_V23_S2, REG_V23_S3,
			REG_V24_S0, REG_V24_S1, REG_V24_S2, REG_V24_S3, REG_V25_S0, REG_V25_S1, REG_V25_S2, REG_V25_S3,
			REG_V26_S0, REG_V26_S1, REG_V26_S2, REG_V26_S3, REG_V27_S0, REG_V27_S1, REG_V27_S2, REG_V27_S3,
			REG_V28_S0, REG_V28_S1, REG_V28_S2, REG_V28_S3, REG_V29_S0, REG_V29_S1, REG_V29_S2, REG_V29_S3,
			REG_V30_S0, REG_V30_S1, REG_V30_S2, REG_V30_S3, REG_V31_S0, REG_V31_S1, REG_V31_S2, REG_V31_S3,
			// D vectors
			REG_V0_D0, REG_V0_D1, REG_V1_D0, REG_V1_D1, REG_V2_D0, REG_V2_D1, REG_V3_D0, REG_V3_D1,
			REG_V4_D0, REG_V4_D1, REG_V5_D0, REG_V5_D1, REG_V6_D0, REG_V6_D1, REG_V7_D0, REG_V7_D1,
			REG_V8_D0, REG_V8_D1, REG_V9_D0, REG_V9_D1, REG_V10_D0, REG_V10_D1, REG_V11_D0, REG_V11_D1,
			REG_V12_D0, REG_V12_D1, REG_V13_D0, REG_V13_D1, REG_V14_D0, REG_V14_D1, REG_V15_D0, REG_V15_D1,
			REG_V16_D0, REG_V16_D1, REG_V17_D0, REG_V17_D1, REG_V18_D0, REG_V18_D1, REG_V19_D0, REG_V19_D1,
			REG_V20_D0, REG_V20_D1, REG_V21_D0, REG_V21_D1, REG_V22_D0, REG_V22_D1, REG_V23_D0, REG_V23_D1,
			REG_V24_D0, REG_V24_D1, REG_V25_D0, REG_V25_D1, REG_V26_D0, REG_V26_D1, REG_V27_D0, REG_V27_D1,
			REG_V28_D0, REG_V28_D1, REG_V29_D0, REG_V29_D1, REG_V30_D0, REG_V30_D1, REG_V31_D0, REG_V31_D1,
			// SVE
			REG_Z0,  REG_Z1,  REG_Z2,  REG_Z3,  REG_Z4,  REG_Z5,  REG_Z6,  REG_Z7,
			REG_Z8,  REG_Z9,  REG_Z10, REG_Z11, REG_Z12, REG_Z13, REG_Z14, REG_Z15,
			REG_Z16, REG_Z17, REG_Z18, REG_Z19, REG_Z20, REG_Z21, REG_Z22, REG_Z23,
			REG_Z24, REG_Z25, REG_Z26, REG_Z27, REG_Z28, REG_Z29, REG_Z30, REG_Z31,
			REG_P0,  REG_P1,  REG_P2,  REG_P3,  REG_P4,  REG_P5,  REG_P6,  REG_P7,
			REG_P8,  REG_P9,  REG_P10, REG_P11, REG_P12, REG_P13, REG_P14, REG_P15,
			REG_P16, REG_P17, REG_P18, REG_P19, REG_P20, REG_P21, REG_P22, REG_P23,
			REG_P24, REG_P25, REG_P26, REG_P27, REG_P28, REG_P29, REG_P30, REG_P31,
			/* system registers */
			REG_OSDTRRX_EL1, REG_DBGBVR0_EL1, REG_DBGBCR0_EL1, REG_DBGWVR0_EL1,
			REG_DBGWCR0_EL1, REG_DBGBVR1_EL1, REG_DBGBCR1_EL1, REG_DBGWVR1_EL1,
			REG_DBGWCR1_EL1, REG_MDCCINT_EL1, REG_MDSCR_EL1, REG_DBGBVR2_EL1,
			REG_DBGBCR2_EL1, REG_DBGWVR2_EL1, REG_DBGWCR2_EL1, REG_OSDTRTX_EL1,
			REG_DBGBVR3_EL1, REG_DBGBCR3_EL1, REG_DBGWVR3_EL1, REG_DBGWCR3_EL1,
			REG_DBGBVR4_EL1, REG_DBGBCR4_EL1, REG_DBGWVR4_EL1, REG_DBGWCR4_EL1,
			REG_DBGBVR5_EL1, REG_DBGBCR5_EL1, REG_DBGWVR5_EL1, REG_DBGWCR5_EL1,
			REG_OSECCR_EL1, REG_DBGBVR6_EL1, REG_DBGBCR6_EL1, REG_DBGWVR6_EL1,
			REG_DBGWCR6_EL1, REG_DBGBVR7_EL1, REG_DBGBCR7_EL1, REG_DBGWVR7_EL1,
			REG_DBGWCR7_EL1, REG_DBGBVR8_EL1, REG_DBGBCR8_EL1, REG_DBGWVR8_EL1,
			REG_DBGWCR8_EL1, REG_DBGBVR9_EL1, REG_DBGBCR9_EL1, REG_DBGWVR9_EL1,
			REG_DBGWCR9_EL1, REG_DBGBVR10_EL1, REG_DBGBCR10_EL1, REG_DBGWVR10_EL1,
			REG_DBGWCR10_EL1, REG_DBGBVR11_EL1, REG_DBGBCR11_EL1, REG_DBGWVR11_EL1,
			REG_DBGWCR11_EL1, REG_DBGBVR12_EL1, REG_DBGBCR12_EL1, REG_DBGWVR12_EL1,
			REG_DBGWCR12_EL1, REG_DBGBVR13_EL1, REG_DBGBCR13_EL1, REG_DBGWVR13_EL1,
			REG_DBGWCR13_EL1, REG_DBGBVR14_EL1, REG_DBGBCR14_EL1, REG_DBGWVR14_EL1,
			REG_DBGWCR14_EL1, REG_DBGBVR15_EL1, REG_DBGBCR15_EL1, REG_DBGWVR15_EL1,
			REG_DBGWCR15_EL1, REG_OSLAR_EL1, REG_OSDLR_EL1, REG_DBGPRCR_EL1,
			REG_DBGCLAIMSET_EL1, REG_DBGCLAIMCLR_EL1, REG_TRCTRACEIDR, REG_TRCVICTLR,
			REG_TRCSEQEVR0, REG_TRCCNTRLDVR0, REG_TRCIMSPEC0, REG_TRCPRGCTLR, REG_TRCQCTLR,
			REG_TRCVIIECTLR, REG_TRCSEQEVR1, REG_TRCCNTRLDVR1, REG_TRCIMSPEC1,
			REG_TRCPROCSELR, REG_TRCVISSCTLR, REG_TRCSEQEVR2, REG_TRCCNTRLDVR2,
			REG_TRCIMSPEC2, REG_TRCVIPCSSCTLR, REG_TRCCNTRLDVR3, REG_TRCIMSPEC3,
			REG_TRCCONFIGR, REG_TRCCNTCTLR0, REG_TRCIMSPEC4, REG_TRCCNTCTLR1,
			REG_TRCIMSPEC5, REG_TRCAUXCTLR, REG_TRCSEQRSTEVR, REG_TRCCNTCTLR2,
			REG_TRCIMSPEC6, REG_TRCSEQSTR, REG_TRCCNTCTLR3, REG_TRCIMSPEC7,
			REG_TRCEVENTCTL0R, REG_TRCVDCTLR, REG_TRCEXTINSELR, REG_TRCCNTVR0,
			REG_TRCEVENTCTL1R, REG_TRCVDSACCTLR, REG_TRCEXTINSELR1, REG_TRCCNTVR1,
			REG_TRCRSR, REG_TRCVDARCCTLR, REG_TRCEXTINSELR2, REG_TRCCNTVR2,
			REG_TRCSTALLCTLR, REG_TRCEXTINSELR3, REG_TRCCNTVR3, REG_TRCTSCTLR,
			REG_TRCSYNCPR, REG_TRCCCCTLR, REG_TRCBBCTLR, REG_TRCRSCTLR16, REG_TRCSSCCR0,
			REG_TRCSSPCICR0, REG_TRCOSLAR, REG_TRCRSCTLR17, REG_TRCSSCCR1, REG_TRCSSPCICR1,
			REG_TRCRSCTLR2, REG_TRCRSCTLR18, REG_TRCSSCCR2, REG_TRCSSPCICR2,
			REG_TRCRSCTLR3, REG_TRCRSCTLR19, REG_TRCSSCCR3, REG_TRCSSPCICR3,
			REG_TRCRSCTLR4, REG_TRCRSCTLR20, REG_TRCSSCCR4, REG_TRCSSPCICR4, REG_TRCPDCR,
			REG_TRCRSCTLR5, REG_TRCRSCTLR21, REG_TRCSSCCR5, REG_TRCSSPCICR5,
			REG_TRCRSCTLR6, REG_TRCRSCTLR22, REG_TRCSSCCR6, REG_TRCSSPCICR6,
			REG_TRCRSCTLR7, REG_TRCRSCTLR23, REG_TRCSSCCR7, REG_TRCSSPCICR7,
			REG_TRCRSCTLR8, REG_TRCRSCTLR24, REG_TRCSSCSR0, REG_TRCRSCTLR9,
			REG_TRCRSCTLR25, REG_TRCSSCSR1, REG_TRCRSCTLR10, REG_TRCRSCTLR26,
			REG_TRCSSCSR2, REG_TRCRSCTLR11, REG_TRCRSCTLR27, REG_TRCSSCSR3,
			REG_TRCRSCTLR12, REG_TRCRSCTLR28, REG_TRCSSCSR4, REG_TRCRSCTLR13,
			REG_TRCRSCTLR29, REG_TRCSSCSR5, REG_TRCRSCTLR14, REG_TRCRSCTLR30,
			REG_TRCSSCSR6, REG_TRCRSCTLR15, REG_TRCRSCTLR31, REG_TRCSSCSR7, REG_TRCACVR0,
			REG_TRCACVR8, REG_TRCACATR0, REG_TRCACATR8, REG_TRCDVCVR0, REG_TRCDVCVR4,
			REG_TRCDVCMR0, REG_TRCDVCMR4, REG_TRCACVR1, REG_TRCACVR9, REG_TRCACATR1,
			REG_TRCACATR9, REG_TRCACVR2, REG_TRCACVR10, REG_TRCACATR2, REG_TRCACATR10,
			REG_TRCDVCVR1, REG_TRCDVCVR5, REG_TRCDVCMR1, REG_TRCDVCMR5, REG_TRCACVR3,
			REG_TRCACVR11, REG_TRCACATR3, REG_TRCACATR11, REG_TRCACVR4, REG_TRCACVR12,
			REG_TRCACATR4, REG_TRCACATR12, REG_TRCDVCVR2, REG_TRCDVCVR6, REG_TRCDVCMR2,
			REG_TRCDVCMR6, REG_TRCACVR5, REG_TRCACVR13, REG_TRCACATR5, REG_TRCACATR13,
			REG_TRCACVR6, REG_TRCACVR14, REG_TRCACATR6, REG_TRCACATR14, REG_TRCDVCVR3,
			REG_TRCDVCVR7, REG_TRCDVCMR3, REG_TRCDVCMR7, REG_TRCACVR7, REG_TRCACVR15,
			REG_TRCACATR7, REG_TRCACATR15, REG_TRCCIDCVR0, REG_TRCVMIDCVR0,
			REG_TRCCIDCCTLR0, REG_TRCCIDCCTLR1, REG_TRCCIDCVR1, REG_TRCVMIDCVR1,
			REG_TRCVMIDCCTLR0, REG_TRCVMIDCCTLR1, REG_TRCCIDCVR2, REG_TRCVMIDCVR2,
			REG_TRCCIDCVR3, REG_TRCVMIDCVR3, REG_TRCCIDCVR4, REG_TRCVMIDCVR4,
			REG_TRCCIDCVR5, REG_TRCVMIDCVR5, REG_TRCCIDCVR6, REG_TRCVMIDCVR6,
			REG_TRCCIDCVR7, REG_TRCVMIDCVR7, REG_TRCITCTRL, REG_TRCCLAIMSET,
			REG_TRCCLAIMCLR, REG_TRCLAR, REG_TEECR32_EL1, REG_TEEHBR32_EL1, REG_DBGDTR_EL0,
			REG_DBGDTRTX_EL0, REG_DBGVCR32_EL2, REG_SCTLR_EL1, REG_ACTLR_EL1,
			REG_CPACR_EL1, REG_RGSR_EL1, REG_GCR_EL1, REG_TRFCR_EL1, REG_TTBR0_EL1,
			REG_TTBR1_EL1, REG_TCR_EL1, REG_APIAKEYLO_EL1, REG_APIAKEYHI_EL1,
			REG_APIBKEYLO_EL1, REG_APIBKEYHI_EL1, REG_APDAKEYLO_EL1, REG_APDAKEYHI_EL1,
			REG_APDBKEYLO_EL1, REG_APDBKEYHI_EL1, REG_APGAKEYLO_EL1, REG_APGAKEYHI_EL1,
			REG_SPSR_EL1, REG_ELR_EL1, REG_SP_EL0, REG_SPSEL, REG_CURRENTEL, REG_PAN,
			REG_UAO, REG_ICC_PMR_EL1, REG_AFSR0_EL1, REG_AFSR1_EL1, REG_ESR_EL1,
			REG_ERRSELR_EL1, REG_ERXCTLR_EL1, REG_ERXSTATUS_EL1, REG_ERXADDR_EL1,
			REG_ERXPFGCTL_EL1, REG_ERXPFGCDN_EL1, REG_ERXMISC0_EL1, REG_ERXMISC1_EL1,
			REG_ERXMISC2_EL1, REG_ERXMISC3_EL1, REG_ERXTS_EL1, REG_TFSR_EL1,
			REG_TFSRE0_EL1, REG_FAR_EL1, REG_PAR_EL1, REG_PMSCR_EL1, REG_PMSICR_EL1,
			REG_PMSIRR_EL1, REG_PMSFCR_EL1, REG_PMSEVFR_EL1, REG_PMSLATFR_EL1,
			REG_PMSIDR_EL1, REG_PMBLIMITR_EL1, REG_PMBPTR_EL1, REG_PMBSR_EL1,
			REG_PMBIDR_EL1, REG_TRBLIMITR_EL1, REG_TRBPTR_EL1, REG_TRBBASER_EL1,
			REG_TRBSR_EL1, REG_TRBMAR_EL1, REG_TRBTRG_EL1, REG_PMINTENSET_EL1,
			REG_PMINTENCLR_EL1, REG_PMMIR_EL1, REG_MAIR_EL1, REG_AMAIR_EL1, REG_LORSA_EL1,
			REG_LOREA_EL1, REG_LORN_EL1, REG_LORC_EL1, REG_MPAM1_EL1, REG_MPAM0_EL1,
			REG_VBAR_EL1, REG_RMR_EL1, REG_DISR_EL1, REG_ICC_EOIR0_EL1, REG_ICC_BPR0_EL1,
			REG_ICC_AP0R0_EL1, REG_ICC_AP0R1_EL1, REG_ICC_AP0R2_EL1, REG_ICC_AP0R3_EL1,
			REG_ICC_AP1R0_EL1, REG_ICC_AP1R1_EL1, REG_ICC_AP1R2_EL1, REG_ICC_AP1R3_EL1,
			REG_ICC_DIR_EL1, REG_ICC_SGI1R_EL1, REG_ICC_ASGI1R_EL1, REG_ICC_SGI0R_EL1,
			REG_ICC_EOIR1_EL1, REG_ICC_BPR1_EL1, REG_ICC_CTLR_EL1, REG_ICC_SRE_EL1,
			REG_ICC_IGRPEN0_EL1, REG_ICC_IGRPEN1_EL1, REG_ICC_SEIEN_EL1,
			REG_CONTEXTIDR_EL1, REG_TPIDR_EL1, REG_SCXTNUM_EL1, REG_CNTKCTL_EL1,
			REG_CSSELR_EL1, REG_NZCV, REG_DAIFSET, REG_DIT, REG_SSBS, REG_TCO, REG_FPCR,
			REG_FPSR, REG_DSPSR_EL0, REG_DLR_EL0, REG_PMCR_EL0, REG_PMCNTENSET_EL0,
			REG_PMCNTENCLR_EL0, REG_PMOVSCLR_EL0, REG_PMSWINC_EL0, REG_PMSELR_EL0,
			REG_PMCCNTR_EL0, REG_PMXEVTYPER_EL0, REG_PMXEVCNTR_EL0, REG_DAIFCLR, REG_PMUSERENR_EL0,
			REG_PMOVSSET_EL0, REG_TPIDR_EL0, REG_TPIDRRO_EL0, REG_SCXTNUM_EL0,
			REG_AMCR_EL0, REG_AMUSERENR_EL0, REG_AMCNTENCLR0_EL0, REG_AMCNTENSET0_EL0,
			REG_AMCNTENCLR1_EL0, REG_AMCNTENSET1_EL0, REG_AMEVCNTR00_EL0,
			REG_AMEVCNTR01_EL0, REG_AMEVCNTR02_EL0, REG_AMEVCNTR03_EL0, REG_AMEVCNTR10_EL0,
			REG_AMEVCNTR11_EL0, REG_AMEVCNTR12_EL0, REG_AMEVCNTR13_EL0, REG_AMEVCNTR14_EL0,
			REG_AMEVCNTR15_EL0, REG_AMEVCNTR16_EL0, REG_AMEVCNTR17_EL0, REG_AMEVCNTR18_EL0,
			REG_AMEVCNTR19_EL0, REG_AMEVCNTR110_EL0, REG_AMEVCNTR111_EL0,
			REG_AMEVCNTR112_EL0, REG_AMEVCNTR113_EL0, REG_AMEVCNTR114_EL0,
			REG_AMEVCNTR115_EL0, REG_AMEVTYPER10_EL0, REG_AMEVTYPER11_EL0,
			REG_AMEVTYPER12_EL0, REG_AMEVTYPER13_EL0, REG_AMEVTYPER14_EL0,
			REG_AMEVTYPER15_EL0, REG_AMEVTYPER16_EL0, REG_AMEVTYPER17_EL0,
			REG_AMEVTYPER18_EL0, REG_AMEVTYPER19_EL0, REG_AMEVTYPER110_EL0,
			REG_AMEVTYPER111_EL0, REG_AMEVTYPER112_EL0, REG_AMEVTYPER113_EL0,
			REG_AMEVTYPER114_EL0, REG_AMEVTYPER115_EL0, REG_CNTFRQ_EL0, REG_CNTP_TVAL_EL0,
			REG_CNTP_CTL_EL0, REG_CNTP_CVAL_EL0, REG_CNTV_TVAL_EL0, REG_CNTV_CTL_EL0,
			REG_CNTV_CVAL_EL0, REG_PMEVCNTR0_EL0, REG_PMEVCNTR1_EL0, REG_PMEVCNTR2_EL0,
			REG_PMEVCNTR3_EL0, REG_PMEVCNTR4_EL0, REG_PMEVCNTR5_EL0, REG_PMEVCNTR6_EL0,
			REG_PMEVCNTR7_EL0, REG_PMEVCNTR8_EL0, REG_PMEVCNTR9_EL0, REG_PMEVCNTR10_EL0,
			REG_PMEVCNTR11_EL0, REG_PMEVCNTR12_EL0, REG_PMEVCNTR13_EL0, REG_PMEVCNTR14_EL0,
			REG_PMEVCNTR15_EL0, REG_PMEVCNTR16_EL0, REG_PMEVCNTR17_EL0, REG_PMEVCNTR18_EL0,
			REG_PMEVCNTR19_EL0, REG_PMEVCNTR20_EL0, REG_PMEVCNTR21_EL0, REG_PMEVCNTR22_EL0,
			REG_PMEVCNTR23_EL0, REG_PMEVCNTR24_EL0, REG_PMEVCNTR25_EL0, REG_PMEVCNTR26_EL0,
			REG_PMEVCNTR27_EL0, REG_PMEVCNTR28_EL0, REG_PMEVCNTR29_EL0, REG_PMEVCNTR30_EL0,
			REG_PMEVTYPER0_EL0, REG_PMEVTYPER1_EL0, REG_PMEVTYPER2_EL0, REG_PMEVTYPER3_EL0,
			REG_PMEVTYPER4_EL0, REG_PMEVTYPER5_EL0, REG_PMEVTYPER6_EL0, REG_PMEVTYPER7_EL0,
			REG_PMEVTYPER8_EL0, REG_PMEVTYPER9_EL0, REG_PMEVTYPER10_EL0,
			REG_PMEVTYPER11_EL0, REG_PMEVTYPER12_EL0, REG_PMEVTYPER13_EL0,
			REG_PMEVTYPER14_EL0, REG_PMEVTYPER15_EL0, REG_PMEVTYPER16_EL0,
			REG_PMEVTYPER17_EL0, REG_PMEVTYPER18_EL0, REG_PMEVTYPER19_EL0,
			REG_PMEVTYPER20_EL0, REG_PMEVTYPER21_EL0, REG_PMEVTYPER22_EL0,
			REG_PMEVTYPER23_EL0, REG_PMEVTYPER24_EL0, REG_PMEVTYPER25_EL0,
			REG_PMEVTYPER26_EL0, REG_PMEVTYPER27_EL0, REG_PMEVTYPER28_EL0,
			REG_PMEVTYPER29_EL0, REG_PMEVTYPER30_EL0, REG_PMCCFILTR_EL0, REG_VPIDR_EL2,
			REG_VMPIDR_EL2, REG_SCTLR_EL2, REG_ACTLR_EL2, REG_HCR_EL2, REG_MDCR_EL2,
			REG_CPTR_EL2, REG_HSTR_EL2, REG_HACR_EL2, REG_TRFCR_EL2, REG_SDER32_EL2,
			REG_TTBR0_EL2, REG_TTBR1_EL2, REG_TCR_EL2, REG_VTTBR_EL2, REG_VTCR_EL2,
			REG_VNCR_EL2, REG_VSTTBR_EL2, REG_VSTCR_EL2, REG_DACR32_EL2, REG_SPSR_EL2,
			REG_ELR_EL2, REG_SP_EL1, REG_SPSR_IRQ, REG_SPSR_ABT, REG_SPSR_UND,
			REG_SPSR_FIQ, REG_IFSR32_EL2, REG_AFSR0_EL2, REG_AFSR1_EL2, REG_ESR_EL2,
			REG_VSESR_EL2, REG_FPEXC32_EL2, REG_TFSR_EL2, REG_FAR_EL2, REG_HPFAR_EL2,
			REG_PMSCR_EL2, REG_MAIR_EL2, REG_AMAIR_EL2, REG_MPAMHCR_EL2, REG_MPAMVPMV_EL2,
			REG_MPAM2_EL2, REG_MPAMVPM0_EL2, REG_MPAMVPM1_EL2, REG_MPAMVPM2_EL2,
			REG_MPAMVPM3_EL2, REG_MPAMVPM4_EL2, REG_MPAMVPM5_EL2, REG_MPAMVPM6_EL2,
			REG_MPAMVPM7_EL2, REG_VBAR_EL2, REG_RMR_EL2, REG_VDISR_EL2, REG_ICH_AP0R0_EL2,
			REG_ICH_AP0R1_EL2, REG_ICH_AP0R2_EL2, REG_ICH_AP0R3_EL2, REG_ICH_AP1R0_EL2,
			REG_ICH_AP1R1_EL2, REG_ICH_AP1R2_EL2, REG_ICH_AP1R3_EL2, REG_ICH_VSEIR_EL2,
			REG_ICC_SRE_EL2, REG_ICH_HCR_EL2, REG_ICH_MISR_EL2, REG_ICH_VMCR_EL2,
			REG_ICH_LR0_EL2, REG_ICH_LR1_EL2, REG_ICH_LR2_EL2, REG_ICH_LR3_EL2,
			REG_ICH_LR4_EL2, REG_ICH_LR5_EL2, REG_ICH_LR6_EL2, REG_ICH_LR7_EL2,
			REG_ICH_LR8_EL2, REG_ICH_LR9_EL2, REG_ICH_LR10_EL2, REG_ICH_LR11_EL2,
			REG_ICH_LR12_EL2, REG_ICH_LR13_EL2, REG_ICH_LR14_EL2, REG_ICH_LR15_EL2,
			REG_CONTEXTIDR_EL2, REG_TPIDR_EL2, REG_SCXTNUM_EL2, REG_CNTVOFF_EL2,
			REG_CNTHCTL_EL2, REG_CNTHP_TVAL_EL2, REG_CNTHP_CTL_EL2, REG_CNTHP_CVAL_EL2,
			REG_CNTHV_TVAL_EL2, REG_CNTHV_CTL_EL2, REG_CNTHV_CVAL_EL2, REG_CNTHVS_TVAL_EL2,
			REG_CNTHVS_CTL_EL2, REG_CNTHVS_CVAL_EL2, REG_CNTHPS_TVAL_EL2,
			REG_CNTHPS_CTL_EL2, REG_CNTHPS_CVAL_EL2, REG_SCTLR_EL12, REG_CPACR_EL12,
			REG_TRFCR_EL12, REG_TTBR0_EL12, REG_TTBR1_EL12, REG_TCR_EL12, REG_SPSR_EL12,
			REG_ELR_EL12, REG_AFSR0_EL12, REG_AFSR1_EL12, REG_ESR_EL12, REG_TFSR_EL12,
			REG_FAR_EL12, REG_PMSCR_EL12, REG_MAIR_EL12, REG_AMAIR_EL12, REG_MPAM1_EL12,
			REG_VBAR_EL12, REG_CONTEXTIDR_EL12, REG_SCXTNUM_EL12, REG_CNTKCTL_EL12,
			REG_CNTP_TVAL_EL02, REG_CNTP_CTL_EL02, REG_CNTP_CVAL_EL02, REG_CNTV_TVAL_EL02,
			REG_CNTV_CTL_EL02, REG_CNTV_CVAL_EL02, REG_SCTLR_EL3, REG_ACTLR_EL3,
			REG_SCR_EL3, REG_SDER32_EL3, REG_CPTR_EL3, REG_MDCR_EL3, REG_TTBR0_EL3,
			REG_TCR_EL3, REG_SPSR_EL3, REG_ELR_EL3, REG_SP_EL2, REG_AFSR0_EL3,
			REG_AFSR1_EL3, REG_ESR_EL3, REG_TFSR_EL3, REG_FAR_EL3, REG_MAIR_EL3,
			REG_AMAIR_EL3, REG_MPAM3_EL3, REG_VBAR_EL3, REG_RMR_EL3, REG_ICC_CTLR_EL3,
			REG_ICC_SRE_EL3, REG_ICC_IGRPEN1_EL3, REG_TPIDR_EL3, REG_SCXTNUM_EL3,
			REG_CNTPS_TVAL_EL1, REG_CNTPS_CTL_EL1, REG_CNTPS_CVAL_EL1, REG_PSTATE_SPSEL,
			/* fake registers */
			FAKEREG_SYSREG_UNKNOWN, /* acts as an input/output to ARM64_INTRIN_MSR,
										ARM64_INTRIN_MRS intrinsics when the sysreg
										has no name (is implementation specific) */
			FAKEREG_SYSCALL_INFO
		};

		return r;
	}


	virtual BNRegisterInfo GetRegisterInfo(uint32_t reg) override
	{
		switch (reg)
		{
			case REG_W0:
			case REG_W1:
			case REG_W2:
			case REG_W3:
			case REG_W4:
			case REG_W5:
			case REG_W6:
			case REG_W7:
			case REG_W8:
			case REG_W9:
			case REG_W10:
			case REG_W11:
			case REG_W12:
			case REG_W13:
			case REG_W14:
			case REG_W15:
			case REG_W16:
			case REG_W17:
			case REG_W18:
			case REG_W19:
			case REG_W20:
			case REG_W21:
			case REG_W22:
			case REG_W23:
			case REG_W24:
			case REG_W25:
			case REG_W26:
			case REG_W27:
			case REG_W28:
			case REG_W29:
			case REG_W30:
			case REG_WSP:
					return RegisterInfo(REG_X0 + (reg-REG_W0), 0, 4, true);
			case REG_X0:
			case REG_X1:
			case REG_X2:
			case REG_X3:
			case REG_X4:
			case REG_X5:
			case REG_X6:
			case REG_X7:
			case REG_X8:
			case REG_X9:
			case REG_X10:
			case REG_X11:
			case REG_X12:
			case REG_X13:
			case REG_X14:
			case REG_X15:
			case REG_X16:
			case REG_X17:
			case REG_X18:
			case REG_X19:
			case REG_X20:
			case REG_X21:
			case REG_X22:
			case REG_X23:
			case REG_X24:
			case REG_X25:
			case REG_X26:
			case REG_X27:
			case REG_X28:
			case REG_X29:
			case REG_X30:
			case REG_SP:
				return RegisterInfo(reg, 0, 8);
			case REG_V0:
			case REG_V1:
			case REG_V2:
			case REG_V3:
			case REG_V4:
			case REG_V5:
			case REG_V6:
			case REG_V7:
			case REG_V8:
			case REG_V9:
			case REG_V10:
			case REG_V11:
			case REG_V12:
			case REG_V13:
			case REG_V14:
			case REG_V15:
			case REG_V16:
			case REG_V17:
			case REG_V18:
			case REG_V19:
			case REG_V20:
			case REG_V21:
			case REG_V22:
			case REG_V23:
			case REG_V24:
			case REG_V25:
			case REG_V26:
			case REG_V27:
			case REG_V28:
			case REG_V29:
			case REG_V30:
			case REG_V31:
				return RegisterInfo(reg, 0, 16);
			case REG_B0:
			case REG_B1:
			case REG_B2:
			case REG_B3:
			case REG_B4:
			case REG_B5:
			case REG_B6:
			case REG_B7:
			case REG_B8:
			case REG_B9:
			case REG_B10:
			case REG_B11:
			case REG_B12:
			case REG_B13:
			case REG_B14:
			case REG_B15:
			case REG_B16:
			case REG_B17:
			case REG_B18:
			case REG_B19:
			case REG_B20:
			case REG_B21:
			case REG_B22:
			case REG_B23:
			case REG_B24:
			case REG_B25:
			case REG_B26:
			case REG_B27:
			case REG_B28:
			case REG_B29:
			case REG_B30:
			case REG_B31:
				return RegisterInfo(REG_V0+(reg-REG_B0), 0, 1);
			case REG_H0:
			case REG_H1:
			case REG_H2:
			case REG_H3:
			case REG_H4:
			case REG_H5:
			case REG_H6:
			case REG_H7:
			case REG_H8:
			case REG_H9:
			case REG_H10:
			case REG_H11:
			case REG_H12:
			case REG_H13:
			case REG_H14:
			case REG_H15:
			case REG_H16:
			case REG_H17:
			case REG_H18:
			case REG_H19:
			case REG_H20:
			case REG_H21:
			case REG_H22:
			case REG_H23:
			case REG_H24:
			case REG_H25:
			case REG_H26:
			case REG_H27:
			case REG_H28:
			case REG_H29:
			case REG_H30:
			case REG_H31:
				return RegisterInfo(REG_V0+(reg-REG_H0), 0, 2);
			case REG_S0:
			case REG_S1:
			case REG_S2:
			case REG_S3:
			case REG_S4:
			case REG_S5:
			case REG_S6:
			case REG_S7:
			case REG_S8:
			case REG_S9:
			case REG_S10:
			case REG_S11:
			case REG_S12:
			case REG_S13:
			case REG_S14:
			case REG_S15:
			case REG_S16:
			case REG_S17:
			case REG_S18:
			case REG_S19:
			case REG_S20:
			case REG_S21:
			case REG_S22:
			case REG_S23:
			case REG_S24:
			case REG_S25:
			case REG_S26:
			case REG_S27:
			case REG_S28:
			case REG_S29:
			case REG_S30:
			case REG_S31:
				return RegisterInfo(REG_V0+(reg-REG_S0), 0, 4);
			case REG_D0:
			case REG_D1:
			case REG_D2:
			case REG_D3:
			case REG_D4:
			case REG_D5:
			case REG_D6:
			case REG_D7:
			case REG_D8:
			case REG_D9:
			case REG_D10:
			case REG_D11:
			case REG_D12:
			case REG_D13:
			case REG_D14:
			case REG_D15:
			case REG_D16:
			case REG_D17:
			case REG_D18:
			case REG_D19:
			case REG_D20:
			case REG_D21:
			case REG_D22:
			case REG_D23:
			case REG_D24:
			case REG_D25:
			case REG_D26:
			case REG_D27:
			case REG_D28:
			case REG_D29:
			case REG_D30:
			case REG_D31:
				return RegisterInfo(REG_V0+(reg-REG_D0), 0, 8);
			case REG_Q0:
			case REG_Q1:
			case REG_Q2:
			case REG_Q3:
			case REG_Q4:
			case REG_Q5:
			case REG_Q6:
			case REG_Q7:
			case REG_Q8:
			case REG_Q9:
			case REG_Q10:
			case REG_Q11:
			case REG_Q12:
			case REG_Q13:
			case REG_Q14:
			case REG_Q15:
			case REG_Q16:
			case REG_Q17:
			case REG_Q18:
			case REG_Q19:
			case REG_Q20:
			case REG_Q21:
			case REG_Q22:
			case REG_Q23:
			case REG_Q24:
			case REG_Q25:
			case REG_Q26:
			case REG_Q27:
			case REG_Q28:
			case REG_Q29:
			case REG_Q30:
			case REG_Q31:
				return RegisterInfo(REG_V0+(reg-REG_Q0), 0, 16);
			case REG_Z0:
			case REG_Z1:
			case REG_Z2:
			case REG_Z3:
			case REG_Z4:
			case REG_Z5:
			case REG_Z6:
			case REG_Z7:
			case REG_Z8:
			case REG_Z9:
			case REG_Z10:
			case REG_Z11:
			case REG_Z12:
			case REG_Z13:
			case REG_Z14:
			case REG_Z15:
			case REG_Z16:
			case REG_Z17:
			case REG_Z18:
			case REG_Z19:
			case REG_Z20:
			case REG_Z21:
			case REG_Z22:
			case REG_Z23:
			case REG_Z24:
			case REG_Z25:
			case REG_Z26:
			case REG_Z27:
			case REG_Z28:
			case REG_Z29:
			case REG_Z30:
			case REG_Z31:
				return RegisterInfo(REG_V0+(reg-REG_Z0), 0, 16);
			case REG_P0:
			case REG_P1:
			case REG_P2:
			case REG_P3:
			case REG_P4:
			case REG_P5:
			case REG_P6:
			case REG_P7:
			case REG_P8:
			case REG_P9:
			case REG_P10:
			case REG_P11:
			case REG_P12:
			case REG_P13:
			case REG_P14:
			case REG_P15:
			case REG_P16:
			case REG_P17:
			case REG_P18:
			case REG_P19:
			case REG_P20:
			case REG_P21:
			case REG_P22:
			case REG_P23:
			case REG_P24:
			case REG_P25:
			case REG_P26:
			case REG_P27:
			case REG_P28:
			case REG_P29:
			case REG_P30:
			case REG_P31:
				return RegisterInfo(reg, 0, 32);
		}

		if (reg >= REG_V0_B0 && reg <= REG_V31_B15) {
			uint32_t r = reg - REG_V0_B0;
			uint32_t v = r / 16;
			uint32_t idx = r % 16;
			return RegisterInfo(REG_V0 + v, idx, 1);
		}

		if (reg >= REG_V0_H0 && reg <= REG_V31_H7)
		{
			uint32_t r = reg - REG_V0_H0;
			uint32_t v = r / 8;
			uint32_t idx = r % 8;
			return RegisterInfo(REG_V0 + v, idx * 2, 2);
		}

		if (reg >= REG_V0_S0 && reg <= REG_V31_S3)
		{
			uint32_t r = reg - REG_V0_S0;
			uint32_t v = r / 4;
			uint32_t idx = r % 4;
			return RegisterInfo(REG_V0 + v, idx * 4, 4);
		}

		if (reg >= REG_V0_D0 && reg <= REG_V31_D1)
		{
			uint32_t r = reg - REG_V0_D0;
			uint32_t v = r / 2;
			uint32_t idx = r % 2;
			return RegisterInfo(REG_V0 + v, idx * 8, 8);
		}

		if (reg == FAKEREG_SYSREG_UNKNOWN)
			return RegisterInfo(reg, 0, 8);

		if (reg == FAKEREG_SYSCALL_INFO)
			return RegisterInfo(reg, 0, 4);

		if (reg > SYSREG_NONE && reg < SYSREG_END)
			return RegisterInfo(reg, 0, 8);

		return RegisterInfo(0, 0, 0);
	}

	virtual uint32_t GetStackPointerRegister() override { return REG_SP; }

	virtual uint32_t GetLinkRegister() override { return REG_X30; }

	virtual vector<uint32_t> GetSystemRegisters() override
	{
		vector<uint32_t> system_regs = {};

		uint32_t ii;
		for (ii = SYSREG_NONE + 1; ii < SYSREG_END; ++ii)
		{
			system_regs.push_back(ii);
		}

		system_regs.push_back(FAKEREG_SYSREG_UNKNOWN);

		return system_regs;
	}
};


class Arm64ImportedFunctionRecognizer : public FunctionRecognizer
{
 private:
	bool RecognizeELFPLTEntries(BinaryView* data, Function* func, LowLevelILFunction* il)
	{
		// Look for the following code pattern:
		// x16.q = plt
		// x17.q = [add.q(x16.q, pltoffset)].q || x17.q = [x16.q].q
		// x16.q = add.q(x16.q, pltoffset) || x16.q = x16.q
		// jump(x17.q)

		if (il->GetInstructionCount() < 4)
			return false;

		LowLevelILInstruction adrp = il->GetInstruction(0);
		if (adrp.operation != LLIL_SET_REG)
			return false;
		LowLevelILInstruction adrpOperand = adrp.GetSourceExpr<LLIL_SET_REG>();
		if (!LowLevelILFunction::IsConstantType(adrpOperand.operation))
			return false;
		if (adrpOperand.size != func->GetArchitecture()->GetAddressSize())
			return false;
		uint64_t pltPage = adrpOperand.GetConstant();
		uint32_t pltReg = adrp.GetDestRegister<LLIL_SET_REG>();

		LowLevelILInstruction ld = il->GetInstruction(1);
		if (ld.operation != LLIL_SET_REG)
			return false;
		LowLevelILInstruction ldOperand = ld.GetSourceExpr<LLIL_SET_REG>();
		if (ldOperand.operation != LLIL_LOAD)
			return false;
		if (ldOperand.size != func->GetArchitecture()->GetAddressSize())
			return false;
		LowLevelILInstruction ldAddrOperand = ldOperand.GetSourceExpr<LLIL_LOAD>();
		uint64_t entry = pltPage;
		uint64_t targetReg;
		int64_t ldAddrRightOperandValue = 0;
		if (ldAddrOperand.operation == LLIL_ADD)
		{
			LowLevelILInstruction ldAddrLeftOperand = ldAddrOperand.GetLeftExpr<LLIL_ADD>();
			LowLevelILInstruction ldAddrRightOperand = ldAddrOperand.GetRightExpr<LLIL_ADD>();
			if (ldAddrLeftOperand.operation != LLIL_REG)
				return false;
			if (ldAddrLeftOperand.GetSourceRegister<LLIL_REG>() != pltReg)
				return false;

			if (!LowLevelILFunction::IsConstantType(ldAddrRightOperand.operation))
				return false;
			ldAddrRightOperandValue = ldAddrRightOperand.GetConstant();
			entry = pltPage + ldAddrRightOperandValue;
		}
		else if (ldAddrOperand.operation != LLIL_REG)  // If theres no constant
			return false;

		targetReg = ld.GetDestRegister<LLIL_SET_REG>();
		Ref<Symbol> sym = data->GetSymbolByAddress(entry);
		if (!sym)
			return false;
		if (sym->GetType() != ImportAddressSymbol)
			return false;

		LowLevelILInstruction add = il->GetInstruction(2);
		if (add.operation != LLIL_SET_REG)
			return false;
		if (add.GetDestRegister<LLIL_SET_REG>() != pltReg)
			return false;
		LowLevelILInstruction addOperand = add.GetSourceExpr<LLIL_SET_REG>();

		if (addOperand.operation == LLIL_ADD)
		{
			LowLevelILInstruction addLeftOperand = addOperand.GetLeftExpr<LLIL_ADD>();
			LowLevelILInstruction addRightOperand = addOperand.GetRightExpr<LLIL_ADD>();
			if (addLeftOperand.operation != LLIL_REG)
				return false;
			if (addLeftOperand.GetSourceRegister<LLIL_REG>() != pltReg)
				return false;
			if (!LowLevelILFunction::IsConstantType(addRightOperand.operation))
				return false;
			if (addRightOperand.GetConstant() != ldAddrRightOperandValue)
				return false;
		}
		else if ((addOperand.operation != LLIL_REG) ||
		         (addOperand.GetSourceRegister<LLIL_REG>() != pltReg))  // Simple assignment
			return false;

		LowLevelILInstruction jump = il->GetInstruction(3);
		if ((jump.operation != LLIL_JUMP) && (jump.operation != LLIL_TAILCALL))
			return false;
		LowLevelILInstruction jumpOperand = (jump.operation == LLIL_JUMP) ?
                                            jump.GetDestExpr<LLIL_JUMP>() :
                                            jump.GetDestExpr<LLIL_TAILCALL>();
		if (jumpOperand.operation != LLIL_REG)
			return false;
		if (jumpOperand.GetSourceRegister<LLIL_REG>() != targetReg)
			return false;

		Ref<Symbol> funcSym = Symbol::ImportedFunctionFromImportAddressSymbol(sym, func->GetStart());
		data->DefineAutoSymbol(funcSym);
		func->ApplyImportedTypes(funcSym);
		return true;
	}


	bool RecognizeMachoPLTEntries(BinaryView* data, Function* func, LowLevelILFunction* il)
	{
		if ((il->GetInstructionCount() == 2) || (il->GetInstructionCount() == 3))
		{
			// 0: nop OR x16 = symbol@PLT
			// 1: x16 = [symbol@PLT]
			// 2: jump(x16)
			size_t instrIndex = 0;
			if (il->GetInstructionCount() == 3)
			{
				// check that the first instruction is a nop
				LowLevelILInstruction insn = il->GetInstruction(instrIndex++);
				if ((insn.operation != LLIL_NOP) && (insn.operation != LLIL_SET_REG))
					return false;
			}

			// check that the second operation is a set register
			LowLevelILInstruction load = il->GetInstruction(instrIndex++);
			if (load.operation != LLIL_SET_REG)
				return false;

			// check that the rhs is a load operand
			LowLevelILInstruction loadOperand = load.GetSourceExpr<LLIL_SET_REG>();
			if (loadOperand.operation != LLIL_LOAD)
				return false;

			// ensure that the operand is the same size as the address
			if (loadOperand.size != func->GetArchitecture()->GetAddressSize())
				return false;

			// ensure that what we are loading is a const
			RegisterValue loadAddrConstant = loadOperand.GetValue();
			if (loadAddrConstant.state != ImportedAddressValue)
				return false;

			// check if the type of symbol is a PLT symbol
			Ref<Symbol> sym = data->GetSymbolByAddress(loadAddrConstant.value);
			if (!sym ||
			    ((sym->GetType() != ImportAddressSymbol) && (sym->GetType() != ImportedDataSymbol)))
				return false;

			// we have what looks like a PLT entry, record the targetReg
			uint32_t targetReg = load.GetDestRegister<LLIL_SET_REG>();

			// ensure we have a jump instruction
			LowLevelILInstruction jump = il->GetInstruction(instrIndex++);
			if ((jump.operation != LLIL_JUMP) && (jump.operation != LLIL_TAILCALL))
				return false;

			// ensure we are jumping to a register
			LowLevelILInstruction jumpOperand = (jump.operation == LLIL_JUMP) ?
                                              jump.GetDestExpr<LLIL_JUMP>() :
                                              jump.GetDestExpr<LLIL_TAILCALL>();
			if (jumpOperand.operation != LLIL_REG)
				return false;

			// is the jump target our target register?
			if (jumpOperand.GetSourceRegister<LLIL_REG>() != targetReg)
				return false;

			data->DefineImportedFunction(sym, func);
			return true;
		}
		else if (il->GetInstructionCount() == 4)
		{
			// 0: x17 = symbol@PLT (hi)
			// 1: x17 = x17 + symbol@PLT (lo)
			// 2: x16 = [symbol@PLT]
			// 3: tailcall(x16)
			size_t instrIndex = 0;

			// check that the first operation is a set register
			LowLevelILInstruction setTemp = il->GetInstruction(instrIndex++);
			if (setTemp.operation != LLIL_SET_REG)
				return false;

			uint32_t tempReg = setTemp.GetDestRegister<LLIL_SET_REG>();

			// check that the rhs is a constant
			LowLevelILInstruction temp = setTemp.GetSourceExpr<LLIL_SET_REG>();
			if (!LowLevelILFunction::IsConstantType(temp.operation))
				return false;

			LowLevelILInstruction finalAddress = il->GetInstruction(instrIndex++);
			if (finalAddress.operation != LLIL_SET_REG)
				return false;

			if (tempReg != finalAddress.GetDestRegister<LLIL_SET_REG>())
				return false;

			// check that the second operation is a set register
			LowLevelILInstruction load = il->GetInstruction(instrIndex++);
			if (load.operation != LLIL_SET_REG)
				return false;

			// check that the rhs is a load operand
			LowLevelILInstruction loadOperand = load.GetSourceExpr<LLIL_SET_REG>();
			if (loadOperand.operation != LLIL_LOAD)
				return false;

			// ensure that the operand is the same size as the address
			if (loadOperand.size != func->GetArchitecture()->GetAddressSize())
				return false;

			// ensure that what we are loading is a const
			LowLevelILInstruction loadAddrOperand = loadOperand.GetSourceExpr<LLIL_LOAD>();
			if (loadAddrOperand.operation != LLIL_REG ||
			    loadAddrOperand.GetSourceRegister<LLIL_REG>() != tempReg)
				return false;

			RegisterValue loadAddrConstant = loadOperand.GetValue();
			if (loadAddrConstant.state != ImportedAddressValue)
				return false;

			// check if the type of symbol is a PLT/GOT symbol
			Ref<Symbol> sym = data->GetSymbolByAddress(loadAddrConstant.value);
			if (!sym ||
			    ((sym->GetType() != ImportAddressSymbol) && (sym->GetType() != ImportedDataSymbol)))
				return false;

			// we have what looks like a PLT entry, record the targetReg
			uint32_t targetReg = load.GetDestRegister<LLIL_SET_REG>();

			// ensure we have a jump instruction
			LowLevelILInstruction jump = il->GetInstruction(instrIndex++);
			if ((jump.operation != LLIL_JUMP) && (jump.operation != LLIL_TAILCALL))
				return false;

			// ensure we are jumping to a register
			LowLevelILInstruction jumpOperand = (jump.operation == LLIL_JUMP) ?
                                              jump.GetDestExpr<LLIL_JUMP>() :
                                              jump.GetDestExpr<LLIL_TAILCALL>();
			if (jumpOperand.operation != LLIL_REG)
				return false;

			// is the jump target our target register?
			if (jumpOperand.GetSourceRegister<LLIL_REG>() != targetReg)
				return false;

			data->DefineImportedFunction(sym, func);
			return true;
		}

		return false;
	}


 public:
	virtual bool RecognizeLowLevelIL(
	    BinaryView* data, Function* func, LowLevelILFunction* il) override
	{
		if (RecognizeELFPLTEntries(data, func, il))
			return true;
		else if (RecognizeMachoPLTEntries(data, func, il))
			return true;
		return false;
	}
};


class Arm64CallingConvention : public CallingConvention
{
 public:
	Arm64CallingConvention(Architecture* arch) : CallingConvention(arch, "cdecl") {}


	Arm64CallingConvention(Architecture* arch, const string& name): CallingConvention(arch, name)
	{
	}


	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
	{
		return vector<uint32_t> {REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7};
	}


	virtual vector<uint32_t> GetFloatArgumentRegisters() override
	{
		return vector<uint32_t> {REG_V0, REG_V1, REG_V2, REG_V3, REG_V4, REG_V5, REG_V6, REG_V7};
	}


	virtual vector<uint32_t> GetCallerSavedRegisters() override
	{
		return vector<uint32_t> {REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7, REG_X8,
		    REG_X9, REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15, REG_X16, REG_X17, REG_X18,
		    REG_X30, REG_V0, REG_V1, REG_V2, REG_V3, REG_V4, REG_V5, REG_V6, REG_V7, REG_V16, REG_V17,
		    REG_V18, REG_V19, REG_V20, REG_V21, REG_V22, REG_V23, REG_V24, REG_V25, REG_V26, REG_V27,
		    REG_V28, REG_V29, REG_V30, REG_V31};
	}


	virtual vector<uint32_t> GetCalleeSavedRegisters() override
	{
		return vector<uint32_t> {REG_X19, REG_X20, REG_X21, REG_X22, REG_X23, REG_X24, REG_X25, REG_X26,
		    REG_X27, REG_X28, REG_X29};
	}


	virtual uint32_t GetIntegerReturnValueRegister() override { return REG_X0; }


	virtual uint32_t GetFloatReturnValueRegister() override { return REG_V0; }
};


class AppleArm64CallingConvention: public Arm64CallingConvention
{
public:
	AppleArm64CallingConvention(Architecture* arch): Arm64CallingConvention(arch, "apple-arm64")
	{
	}


	virtual bool AreArgumentRegistersUsedForVarArgs() override
	{
		return false;
	}
};


class LinuxArm64SystemCallConvention: public CallingConvention
{
 public:
	LinuxArm64SystemCallConvention(Architecture* arch) : CallingConvention(arch, "linux-syscall") {}


	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
	{
		return vector<uint32_t> {REG_X8, REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5};
	}


	virtual vector<uint32_t> GetCallerSavedRegisters() override { return vector<uint32_t> {REG_X0}; }


	virtual vector<uint32_t> GetCalleeSavedRegisters() override
	{
		return vector<uint32_t> {REG_X19, REG_X20, REG_X21, REG_X22, REG_X23, REG_X24, REG_X25, REG_X26,
		    REG_X27, REG_X28, REG_X29};
	}


	virtual uint32_t GetIntegerReturnValueRegister() override { return REG_X0; }


	virtual bool IsEligibleForHeuristics() override { return false; }
};

class WindowsArm64SystemCallConvention : public CallingConvention
{
 public:
	WindowsArm64SystemCallConvention(Architecture* arch) : CallingConvention(arch, "windows-syscall")
	{}


	virtual vector<uint32_t> GetIntegerArgumentRegisters() override { return {FAKEREG_SYSCALL_INFO}; }


	virtual vector<uint32_t> GetCallerSavedRegisters() override { return vector<uint32_t> {REG_X0}; }


	virtual vector<uint32_t> GetCalleeSavedRegisters() override { return {}; }


	virtual uint32_t GetIntegerReturnValueRegister() override { return REG_X0; }


	virtual bool IsEligibleForHeuristics() override { return false; }
};

class MacosArm64SystemCallConvention : public CallingConvention
{
 public:
	MacosArm64SystemCallConvention(Architecture* arch) : CallingConvention(arch, "macos-syscall") {}


	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
	{
		return vector<uint32_t> {REG_X16, REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5};
	}


	virtual vector<uint32_t> GetCallerSavedRegisters() override { return vector<uint32_t> {REG_X0}; }


	virtual vector<uint32_t> GetCalleeSavedRegisters() override
	{
		return vector<uint32_t> {REG_X19, REG_X20, REG_X21, REG_X22, REG_X23, REG_X24, REG_X25, REG_X26,
		    REG_X27, REG_X28, REG_X29};
	}


	virtual uint32_t GetIntegerReturnValueRegister() override { return REG_X0; }


	virtual bool IsEligibleForHeuristics() override { return false; }
};

#define PAGE(x)        (uint32_t)((x) >> 12)
#define PAGE_OFF(x)    (uint32_t)((x)&0xfff)
#define PAGE_NO_OFF(x) (uint32_t)((x)&0xFFFFF000)

class Arm64MachoRelocationHandler : public RelocationHandler
{
 public:
	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc,
	    uint8_t* dest, size_t len) override
	{
		(void)view;
		(void)arch;
		(void)len;

		uint32_t insword = *(uint32_t*)dest;
		auto info = reloc->GetInfo();

		// printf("insword: 0x%X\n", insword);
		// printf("reloc->GetTarget(): 0x%llX\n", reloc->GetTarget());
		// printf("reloc->GetAddress(): 0x%llX\n", reloc->GetAddress());

		if (info.nativeType == (uint64_t)-2)
		{  // Magic number defined in MachOView.cpp for tagged pointers
			*(uint64_t*)dest = info.target;
		}
		else if (info.nativeType == ARM64_RELOC_PAGE21)
		{
			// 21 bits across IMMHI:IMMLO
			// OP=1|IMMLO=XX|10000|IMMHI=XXXXXXXXXXXXXXXXXXX|RD=XXXXX
			int64_t page_delta = PAGE(reloc->GetTarget()) - PAGE(reloc->GetAddress());
			insword = insword & 0b10011111000000000000000000011111;
			insword = insword | ((page_delta & 3) << 29);  // IMMLO
			insword = insword | ((page_delta >> 2) << 5);  // IMMHI
			*(uint32_t*)dest = insword;
		}
		else if (info.nativeType == ARM64_RELOC_PAGEOFF12)
		{
			/* verify relocation point to qualifying instructions */
			if ((insword & 0x3B000000) != 0x39000000 && (insword & 0x11C00000) != 0x11000000)
				return false;

			/* verify it's a positive/forward jump (the imm12 is unsigned) */
			int64_t delta = reloc->GetTarget() - PAGE_NO_OFF(reloc->GetAddress());
			if (delta < 0)
				return false;

			/* disassemble instruction, is last operand an immediate? is there a shift? */
			Instruction instr;
			if (aarch64_decompose(*(uint32_t*)dest, &instr, reloc->GetAddress()) != 0)
				return false;

			int n_operands = 0;
			while (instr.operands[n_operands].operandClass != NONE)
				n_operands++;

			if (instr.operands[n_operands - 1].operandClass != IMM32 &&
			    instr.operands[n_operands - 1].operandClass != IMM64)
				return false;

			int left_shift = (instr.operands[n_operands - 1].shiftValueUsed) ?
                           instr.operands[n_operands - 1].shiftValue :
                           0;

			/* re-encode */
			/* left shift is upon DECODING, we right shift to bias this */
			delta = delta >> left_shift;
			// SF=X|OP=0|S=0|100010|SH=X|IMM12=XXXXXXXXXXXX|RN=XXXXX|RD=XXXXX
			uint16_t imm12 = (insword & 0x3FFC00) >> 10;
			imm12 = PAGE_OFF(imm12 + delta);
			insword = (insword & 0xFFC003FF) | (imm12 << 10);
			*(uint32_t*)dest = insword;
		}

		return true;
	}

	virtual bool GetRelocationInfo(
	    Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
	{
		(void)view;
		(void)arch;

		set<MachoArm64RelocationType> unsupportedRelocations;
		size_t i;
		for (i = 0; i < result.size(); i++)
		{
			result[i].type = StandardRelocationType;
			switch (result[i].nativeType)
			{
			case ARM64_RELOC_UNSIGNED:
				result[i].pcRelative = false;
				result[i].baseRelative = false;
				result[i].size = 8;
				result[i].truncateSize = 8;
				result[i].hasSign = false;
				break;
			case ARM64_RELOC_SUBTRACTOR:
				if (i >= result.size() - 1 || result[i + 1].nativeType != ARM64_RELOC_UNSIGNED)
					return false;
				result[i].pcRelative = false;
				result[i].baseRelative = false;
				result[i].size = 8;
				result[i].truncateSize = 8;
				result[i].hasSign = true;
				break;
			case ARM64_RELOC_POINTER_TO_GOT:
				result[i].pcRelative = false;
				result[i].baseRelative = false;
				result[i].size = 8;
				result[i].truncateSize = 8;
				result[i].hasSign = false;
				break;
			case ARM64_RELOC_PAGE21:
				// eg: the number of pages to get to <addr> in "adrp x1, <addr>"
				// printf("GetRelocationInfo(): ARM64_RELOC_PAGE21 .address=0x%llX\n", result[i].address);
				break;
			case ARM64_RELOC_PAGEOFF12:
				// eg: the 12-bit <immediate> in "add x8, x8, #<immediate>"
				// printf("GetRelocationInfo(): ARM64_RELOC_PAGEOFF12 .address=0x%llX\n",
				// result[i].address);
				break;
			case ARM64_RELOC_BRANCH26:
			case ARM64_RELOC_GOT_LOAD_PAGE21:
			case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
			case ARM64_RELOC_TLVP_LOAD_PAGE21:
			case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
			case ARM64_RELOC_ADDEND:
			default:
				result[i].type = UnhandledRelocation;
				unsupportedRelocations.insert((MachoArm64RelocationType)result[i].nativeType);
			}
		}

		for (auto& relocType : unsupportedRelocations)
			LogWarn("Unsupported relocation: %s (%x)", GetRelocationString(relocType), relocType);
		return true;
	}
};

/* structs used in relocation handling */
struct PC_REL_ADDRESSING
{
	uint32_t Rd : 5;
	int32_t immhi : 19;
	uint32_t group1 : 5;
	uint32_t immlo : 2;
	uint32_t op : 1;
};

struct ADD_SUB_IMM
{
	uint32_t Rd : 5;
	uint32_t Rn : 5;
	uint32_t imm : 12;
	uint32_t shift : 2;
	uint32_t group1 : 5;
	uint32_t S : 1;
	uint32_t op : 1;
	uint32_t sf : 1;
};

struct UNCONDITIONAL_BRANCH
{
	int32_t imm : 26;
	uint32_t opcode : 5;
	uint32_t op : 1;
};

struct CONDITIONAL_BRANCH{
	uint32_t cond:4;
	uint32_t o0:1;
	int32_t imm:19;
	uint32_t o1:1;
	uint32_t opcode:7;
};
struct COMPARE_AND_BRANCH{
	uint32_t Rt:5;
	int32_t imm:19;
	uint32_t op:1;
	uint32_t opcode:6;
	uint32_t sf:1;
};

struct TEST_AND_BRANCH{
	uint32_t Rt:5;
	int32_t imm:14;
	uint32_t b40:5;
	uint32_t op:1;
	uint32_t opcode:6;
	uint32_t b5:1;
};

struct LDST_REG_UNSIGNED_IMM{
	uint32_t Rt:5;
	uint32_t Rn:5;
	uint32_t imm:12;
	uint32_t opc:2;
	uint32_t group1:2;
	uint32_t V:1;
	uint32_t group2:3;
	uint32_t size:2;
};

struct MOV_WIDE_IMM{
    uint32_t Rd:5;
    uint32_t imm:16;
    uint32_t shift:2;
    uint32_t opcode:6;
    uint32_t variant:3;
};

class Arm64ElfRelocationHandler : public RelocationHandler
{
 public:
	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc,
	    uint8_t* dest, size_t len) override
	{
		(void)view;
		(void)arch;
		auto info = reloc->GetInfo();
		if (len < info.size)
			return false;
		uint64_t* dest64 = (uint64_t*)dest;
		uint32_t* dest32 = (uint32_t*)dest;
		uint16_t* dest16 = (uint16_t*)dest;
		// auto swap = [&arch](uint32_t x) { return (arch->GetEndianness() == LittleEndian)? x :
		// bswap32(x); };
		uint64_t target = reloc->GetTarget();
		Instruction inst;
		switch (info.nativeType)
		{
		case R_ARM_NONE:
		case R_AARCH64_NONE:
			return true;
		case R_AARCH64_P32_COPY:
		case R_AARCH64_P32_GLOB_DAT:
		case R_AARCH64_P32_JUMP_SLOT:
			dest32[0] = target;
			break;
		case R_AARCH64_COPY:
		case R_AARCH64_GLOB_DAT:
		case R_AARCH64_JUMP_SLOT:
			dest64[0] = target;
			break;
		case R_AARCH64_ADR_PREL_LO21:
			break;
		case R_AARCH64_ADR_PREL_PG_HI21:
		{
			PC_REL_ADDRESSING* decode = (PC_REL_ADDRESSING*)dest;
			uint32_t imm = PAGE(info.addend + target) - PAGE(reloc->GetAddress());
			decode->immhi = imm >> 2;
			decode->immlo = imm & 3;
			break;
		}
		case R_AARCH64_ADR_PREL_PG_HI21_NC:
			break;
		case R_AARCH64_ADD_ABS_LO12_NC:
		{
			ADD_SUB_IMM* decode = (ADD_SUB_IMM*)dest;
			aarch64_decompose(dest32[0], &inst, reloc->GetAddress());
			decode->imm = inst.operands[2].immediate + target;
			break;
		}
		case R_AARCH64_CALL26:
		case R_AARCH64_JUMP26:
		{
			UNCONDITIONAL_BRANCH* decode = (UNCONDITIONAL_BRANCH*)dest;
			aarch64_decompose(dest32[0], &inst, 0);
			decode->imm = (inst.operands[0].immediate + target - reloc->GetAddress()) >> 2;
			break;
		}
		case R_AARCH64_ABS16:
			dest16[0] = (uint16_t)(target + info.addend);
			break;
		case R_AARCH64_ABS32:
			dest32[0] = (uint32_t)(target + info.addend);
			break;
		case R_AARCH64_ABS64:
			dest64[0] = target + info.addend;
			break;
		case R_AARCH64_PREL16:
			dest16[0] = (uint16_t)(info.addend + target - reloc->GetAddress());
			break;
		case R_AARCH64_PREL32:
			dest32[0] = (uint32_t)(info.addend + target - reloc->GetAddress());
			break;
		case R_AARCH64_PREL64:
			dest64[0] = info.addend + target - reloc->GetAddress();
			break;
		case R_AARCH64_P32_RELATIVE:
			dest32[0] = target + info.addend;
			break;
		case R_AARCH64_RELATIVE:
			dest64[0] = target + info.addend;
			break;
		case R_AARCH64_LDST8_ABS_LO12_NC:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & 0xfff);
			break;
		}
		case R_AARCH64_LDST16_ABS_LO12_NC:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & 0xffe) >> 1;
			break;
		}
		case R_AARCH64_LDST32_ABS_LO12_NC:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & 0xffc) >> 2;
			break;
		}
		case R_AARCH64_LDST64_ABS_LO12_NC:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & 0xff8) >> 3;
			break;
		}
		case R_AARCH64_LDST128_ABS_LO12_NC:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & 0xff0) >> 4;
			break;
		}
		case R_AARCH64_ADR_GOT_PAGE:
		{
			PC_REL_ADDRESSING* decode = (PC_REL_ADDRESSING*)dest;
			uint32_t imm = PAGE(info.addend + target) - PAGE(reloc->GetAddress());
			decode->immhi = imm >> 2;
			decode->immlo = imm & 3;
			break;
		}
		case R_AARCH64_LD64_GOT_LO12_NC:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & 0xff8) >> 3;
			break;
		}
		case R_AARCH64_MOVW_UABS_G0:
		case R_AARCH64_MOVW_UABS_G0_NC:
		{
			MOV_WIDE_IMM* decode = (MOV_WIDE_IMM*)dest;
			decode->imm = (target + info.addend);
			break;
		}
		case R_AARCH64_MOVW_UABS_G1:
		case R_AARCH64_MOVW_UABS_G1_NC:
		{
			MOV_WIDE_IMM* decode = (MOV_WIDE_IMM*)dest;
			decode->imm = (target + info.addend)>>16;
			break;
		}
		case R_AARCH64_MOVW_UABS_G2:
		case R_AARCH64_MOVW_UABS_G2_NC:
		{
			MOV_WIDE_IMM* decode = (MOV_WIDE_IMM*)dest;
			decode->imm = (target + info.addend)>>32;
			break;
		}
		case R_AARCH64_MOVW_UABS_G3:
		{
			MOV_WIDE_IMM* decode = (MOV_WIDE_IMM*)dest;
			decode->imm = (target + info.addend)>>48;
			break;
		}
		case R_AARCH64_MOVW_SABS_G0:
		case R_AARCH64_MOVW_SABS_G1:
		case R_AARCH64_MOVW_SABS_G2:
		case R_AARCH64_LD_PREL_LO19:
		case R_AARCH64_TSTBR14:
		case R_AARCH64_CONDBR19:
		case R_AARCH64_MOVW_PREL_G0:
		case R_AARCH64_MOVW_PREL_G0_NC:
		case R_AARCH64_MOVW_PREL_G1:
		case R_AARCH64_MOVW_PREL_G1_NC:
		case R_AARCH64_MOVW_PREL_G2:
		case R_AARCH64_MOVW_PREL_G2_NC:
		case R_AARCH64_MOVW_PREL_G3:
		case R_AARCH64_TLS_TPREL64:
		case R_AARCH64_TLS_DTPREL32:
		case R_AARCH64_IRELATIVE:
			return false;
		}
		return true;
	}

	virtual bool GetRelocationInfo(
	    Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
	{
		(void)view;
		(void)arch;
		(void)result;
		set<uint64_t> relocTypes;
		for (auto& reloc : result)
		{
			reloc.type = StandardRelocationType;
			reloc.size = 4;
			switch (reloc.nativeType)
			{
			case R_AARCH64_P32_COPY:
				reloc.type = ELFCopyRelocationType;
				reloc.size = 4;
				break;
			case R_AARCH64_P32_GLOB_DAT:
				reloc.type = ELFGlobalRelocationType;
				reloc.size = 4;
				break;
			case R_AARCH64_P32_JUMP_SLOT:
				reloc.type = ELFJumpSlotRelocationType;
				reloc.size = 4;
				break;
			case R_AARCH64_COPY:
				reloc.type = ELFCopyRelocationType;
				reloc.size = 8;
				break;
			case R_AARCH64_GLOB_DAT:
				reloc.type = ELFGlobalRelocationType;
				reloc.size = 8;
				break;
			case R_AARCH64_JUMP_SLOT:
				reloc.type = ELFJumpSlotRelocationType;
				reloc.size = 8;
				break;
			case R_AARCH64_ABS16:
				reloc.pcRelative = false;
				reloc.size = 2;
				break;
			case R_AARCH64_PREL16:
				reloc.pcRelative = true;
				reloc.size = 2;
				break;
			case R_AARCH64_PREL32:
			case R_AARCH64_ADR_PREL_PG_HI21:
			case R_AARCH64_CALL26:
			case R_AARCH64_JUMP26:
				reloc.pcRelative = true;
				reloc.size = 4;
				break;
			case R_AARCH64_MOVW_UABS_G0:
			case R_AARCH64_MOVW_UABS_G0_NC:
			case R_AARCH64_MOVW_UABS_G1:
			case R_AARCH64_MOVW_UABS_G1_NC:
			case R_AARCH64_MOVW_UABS_G2:
			case R_AARCH64_MOVW_UABS_G2_NC:
			case R_AARCH64_MOVW_UABS_G3:
				reloc.size = 4;
				break;
			case R_AARCH64_ABS32:
			case R_AARCH64_ADD_ABS_LO12_NC:
			case R_AARCH64_LDST8_ABS_LO12_NC:
			case R_AARCH64_LDST16_ABS_LO12_NC:
			case R_AARCH64_LDST32_ABS_LO12_NC:
			case R_AARCH64_LDST64_ABS_LO12_NC:
			case R_AARCH64_LDST128_ABS_LO12_NC:
			case R_AARCH64_ADR_GOT_PAGE:
			case R_AARCH64_LD64_GOT_LO12_NC:
				reloc.pcRelative = false;
				reloc.size = 4;
				break;
			case R_AARCH64_ABS64:
				reloc.pcRelative = false;
				reloc.size = 8;
				break;
			case R_AARCH64_P32_RELATIVE:
				reloc.pcRelative = true;
				reloc.size = 4;
				break;
			case R_AARCH64_RELATIVE:
				reloc.pcRelative = true;
				reloc.size = 8;
				break;
			default:
				reloc.type = UnhandledRelocation;
				relocTypes.insert(reloc.nativeType);
				break;
			}
		}
		for (auto& reloc : relocTypes)
			LogWarn("Unsupported ELF relocation type: %s",
			    GetRelocationString((ElfArm64RelocationType)reloc));
		return true;
	}

	virtual size_t GetOperandForExternalRelocation(const uint8_t* data, uint64_t addr, size_t length,
	    Ref<LowLevelILFunction> il, Ref<Relocation> relocation) override
	{
		(void)data;
		(void)addr;
		(void)length;
		(void)il;
		auto info = relocation->GetInfo();
		switch (info.nativeType)
		{
		case R_AARCH64_ADR_PREL_PG_HI21:
			return BN_NOCOERCE_EXTERN_PTR;
		default:
			return BN_AUTOCOERCE_EXTERN_PTR;
		}
	}
};


class Arm64PeRelocationHandler : public RelocationHandler
{
 public:
	virtual bool GetRelocationInfo(
	    Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
	{
		(void)view;
		(void)arch;
		set<uint64_t> relocTypes;
		for (auto& reloc : result)
		{
			reloc.type = UnhandledRelocation;
			relocTypes.insert(reloc.nativeType);
		}
		for (auto& reloc : relocTypes)
			LogWarn(
			    "Unsupported PE relocation type: %s", GetRelocationString((PeArm64RelocationType)reloc));
		return false;
	}
};


class Arm64COFFRelocationHandler: public RelocationHandler
{
public:
	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override
	{
		// Note: info.base contains preferred base address and the base where the image is actually loaded
		(void)view;
		(void)arch;
		(void)len;
		BNRelocationInfo info = reloc->GetInfo();
		uint64_t target = reloc->GetTarget();
		uint64_t pc = info.pcRelative ? reloc->GetAddress() : 0;
		uint64_t base = (info.baseRelative && !target) ? view->GetStart() : 0;
		uint64_t address = info.address;
		uint64_t* dest64 = (uint64_t*)dest;
		uint32_t* dest32 = (uint32_t*)dest;
		uint16_t* dest16 = (uint16_t*)dest;
		(void)pc;
		(void)base;
		(void)dest16;
		(void)dest64;
		Instruction inst;

		Ref<Architecture> associatedArch = arch->GetAssociatedArchitectureByAddress(address);

		if (len < info.size)
		{
			return false;
		}

		switch (info.nativeType)
		{
		case PE_IMAGE_REL_ARM64_REL21:
		{
			PC_REL_ADDRESSING* decode = (PC_REL_ADDRESSING*)dest;
			uint32_t imm = info.addend + target - reloc->GetAddress();
			decode->immhi = imm >> 2;
			decode->immlo = imm & 3;
			break;
		}
		case PE_IMAGE_REL_ARM64_PAGEBASE_REL21:
		{
			PC_REL_ADDRESSING* decode = (PC_REL_ADDRESSING*)dest;
			uint32_t imm = PAGE(info.addend + target) - PAGE(reloc->GetAddress());
			decode->immhi = imm >> 2;
			decode->immlo = imm & 3;
			break;
		}
		case PE_IMAGE_REL_ARM64_PAGEOFFSET_12A:
		{
			ADD_SUB_IMM* decode = (ADD_SUB_IMM*)dest;
			aarch64_decompose(dest32[0], &inst, reloc->GetAddress());
			decode->imm = inst.operands[2].immediate + target;
			break;
		}
		case PE_IMAGE_REL_ARM64_PAGEOFFSET_12L:
		{
			LDST_REG_UNSIGNED_IMM* decode = (LDST_REG_UNSIGNED_IMM*)dest;
			decode->imm = ((target + info.addend) & (0xfff & ~decode->size)) >> decode->size;
			break;
		}
		case PE_IMAGE_REL_ARM64_BRANCH26:
		{
			UNCONDITIONAL_BRANCH* decode = (UNCONDITIONAL_BRANCH*)dest;
			aarch64_decompose(dest32[0], &inst, 0);
			decode->imm = (inst.operands[0].immediate + target - reloc->GetAddress()) >> 2;
			break;
		}
		case PE_IMAGE_REL_ARM64_BRANCH19:
		{
			// B.cond (CONDITIONAL_BRANCH) & CBZ / CBNZ (COMPARE_AND_BRANCH)
			CONDITIONAL_BRANCH* decode = (CONDITIONAL_BRANCH*)dest;
			aarch64_decompose(dest32[0], &inst, 0);
			decode->imm = (inst.operands[0].immediate + target - reloc->GetAddress()) >> 2;
			break;
		}
		case PE_IMAGE_REL_ARM64_BRANCH14:
		{
			// TBZ / TBNZ
			TEST_AND_BRANCH* decode = (TEST_AND_BRANCH*)dest;
			aarch64_decompose(dest32[0], &inst, 0);
			decode->imm = (inst.operands[0].immediate + target - reloc->GetAddress()) >> 2;
			break;
		}
		case PE_IMAGE_REL_ARM64_SECTION:
			// TODO: test this implementation, but for now, just don't warn about it
			dest16[0] = info.sectionIndex + 1;
			break;
		case PE_IMAGE_REL_ARM64_SECREL:
		{
			// TODO: test this implementation, but for now, just don't warn about it
			auto sections = view->GetSectionsAt(info.target);
			if (sections.size() > 0)
			{
				dest32[0] = info.target - sections[0]->GetStart();
			}
			break;
		}
		case PE_IMAGE_REL_ARM64_ADDR32NB:
		case PE_IMAGE_REL_ARM64_ADDR64:
		case IMAGE_REL_ARM64_REL32:
		default:
			return RelocationHandler::ApplyRelocation(view, arch, reloc, dest, len);
		}
		return true;
	}

	virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
	{
		(void)view;
		(void)arch;
		set<uint64_t> relocTypes;
		for (auto& reloc : result)
		{
			// LogDebug("%s COFF relocation %s at 0x%" PRIx64, __func__, GetRelocationString((PeArm64RelocationType)reloc.nativeType), reloc.address);
			switch (reloc.nativeType)
			{
			case PE_IMAGE_REL_ARM64_ABSOLUTE:
				reloc.type = IgnoredRelocation;
				break;
			case PE_IMAGE_REL_ARM64_ADDR32NB:
				reloc.pcRelative = false;
				reloc.baseRelative = false;
				break;
			case PE_IMAGE_REL_ARM64_BRANCH26:
			case PE_IMAGE_REL_ARM64_PAGEBASE_REL21:
			case PE_IMAGE_REL_ARM64_REL21:
			case PE_IMAGE_REL_ARM64_PAGEOFFSET_12A:
			case PE_IMAGE_REL_ARM64_BRANCH19:
			case PE_IMAGE_REL_ARM64_BRANCH14:
				reloc.pcRelative = true;
				reloc.baseRelative = false;
				reloc.size = 4;
				break;
			case PE_IMAGE_REL_ARM64_PAGEOFFSET_12L:
				if (! reloc.external)
					reloc.addend = 6;
				break;
			case PE_IMAGE_REL_ARM64_ADDR64:
				reloc.pcRelative = false;
				reloc.baseRelative = true;
				reloc.size = 8;
				break;
			case PE_IMAGE_REL_ARM64_ADDR32:
				reloc.pcRelative = false;
				reloc.baseRelative = true;
				reloc.size = 4;
				break;
			case IMAGE_REL_ARM64_REL32:
				reloc.pcRelative = true;
				reloc.baseRelative = false;
				reloc.size = 4;
				break;
			case PE_IMAGE_REL_ARM64_SECTION:
				// The 16-bit section index of the section that contains the target. This is used to support debugging information.
				reloc.baseRelative = false;
				reloc.size = 2;
				reloc.addend = 0;
				break;
			case PE_IMAGE_REL_ARM64_SECREL:
				// The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage.				reloc.baseRelative = false;
				reloc.baseRelative = false;
				reloc.size = 4;
				reloc.addend = 0;
				break;
			case PE_IMAGE_REL_ARM64_SECREL_LOW12A:
				// TODO
			case PE_IMAGE_REL_ARM64_SECREL_HIGH12A:
				// TODO
			case PE_IMAGE_REL_ARM64_SECREL_LOW12L:
				// TODO
			case PE_IMAGE_REL_ARM64_TOKEN:
			default:
				reloc.type = UnhandledRelocation;
				relocTypes.insert(reloc.nativeType);
				break;
			}
		}
		for (auto& reloc : relocTypes)
			LogWarn("Unsupported PE relocation type: %s", GetRelocationString((PeArm64RelocationType)reloc));
		return false;
	}
};


extern "C"
{
	BN_DECLARE_CORE_ABI_VERSION

#ifdef DEMO_VERSION
	bool Arm64PluginInit()
#else
	BINARYNINJAPLUGIN bool CorePluginInit()
#endif
	{
		Architecture* arm64 = new Arm64Architecture();

		Architecture::Register(arm64);

		// Register calling convention
		Ref<CallingConvention> conv;
		conv = new Arm64CallingConvention(arm64);
		arm64->RegisterCallingConvention(conv);
		arm64->SetDefaultCallingConvention(conv);
		arm64->SetCdeclCallingConvention(conv);
		arm64->SetFastcallCallingConvention(conv);
		arm64->SetStdcallCallingConvention(conv);

		conv = new LinuxArm64SystemCallConvention(arm64);
		arm64->RegisterCallingConvention(conv);

		conv = new WindowsArm64SystemCallConvention(arm64);
		arm64->RegisterCallingConvention(conv);

		conv = new AppleArm64CallingConvention(arm64);
		arm64->RegisterCallingConvention(conv);

		// Register ARM64 specific PLT trampoline recognizer
		arm64->RegisterFunctionRecognizer(new Arm64ImportedFunctionRecognizer());

		// Register ARM64 Relocation handlers
		arm64->RegisterRelocationHandler("Mach-O", new Arm64MachoRelocationHandler());
		arm64->RegisterRelocationHandler("ELF", new Arm64ElfRelocationHandler());
		arm64->RegisterRelocationHandler("PE", new Arm64PeRelocationHandler());
		arm64->RegisterRelocationHandler("COFF", new Arm64COFFRelocationHandler());

		// Register the architectures with the binary format parsers so that they know when to use
		// these architectures for disassembling an executable file
		BinaryViewType::RegisterArchitecture("Mach-O", 0x0100000c, LittleEndian, arm64);
		BinaryViewType::RegisterArchitecture("Mach-O", 0x0200000c, LittleEndian, arm64);
		BinaryViewType::RegisterArchitecture("ELF", 0xb7, LittleEndian, arm64);
		BinaryViewType::RegisterArchitecture("ELF", 0xb7, BigEndian, arm64);
		BinaryViewType::RegisterArchitecture("COFF", 0xaa64, LittleEndian, arm64);
		BinaryViewType::RegisterArchitecture("PE", 0xaa64, LittleEndian, arm64);
		BinaryViewType::RegisterArchitecture("PE", 0xaa64, BigEndian, arm64);
		arm64->SetBinaryViewTypeConstant("ELF", "R_COPY", 0x400);
		arm64->SetBinaryViewTypeConstant("ELF", "R_JUMP_SLOT", 0x402);

		return true;
	}
}
