diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index c96d19c..9da4f9c 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -7,7 +7,6 @@
-
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 379af8f..36257a2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,14 +1,18 @@
cmake_minimum_required(VERSION 4.0)
-project(mach_detours C)
+project(mach_detours C CXX)
set(CMAKE_C_STANDARD 23)
+set(CMAKE_CXX_STANDARD 23)
add_library(mach_detours SHARED
include/mach_detours.h
- src/mach_detours.c
src/detours_internal.h
+ src/detours_disasm.h
+ src/mach_detours.c
+
src/arm64/detours_arm64.h
+ src/arm64/detours_arm64_disasm.cpp
)
target_include_directories(mach_detours
diff --git a/src/arm64/detours_arm64_disasm.cpp b/src/arm64/detours_arm64_disasm.cpp
new file mode 100644
index 0000000..b8d5ce2
--- /dev/null
+++ b/src/arm64/detours_arm64_disasm.cpp
@@ -0,0 +1,674 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#include "detours_disasm.h"
+
+#include
+
+#define c_LR 30 // The register number for the Link Register
+#define c_SP 31 // The register number for the Stack Pointer
+#define c_NOP 0xd503201f // A nop instruction
+#define c_BREAK (0xd4200000 | (0xf000 << 5)) // A break instruction
+
+//
+// Problematic instructions:
+//
+// ADR 0ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x10000000 (l = low, h = high, d = Rd)
+// ADRP 1ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x90000000 (l = low, h = high, d = Rd)
+//
+// B.cond 01010100 iiiiiiii iiiiiiii iii0cccc & 0xff000010 == 0x54000000 (i = delta = SignExtend(imm19:00, 64), c = cond)
+//
+// B 000101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x14000000 (i = delta = SignExtend(imm26:00, 64))
+// BL 100101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x94000000 (i = delta = SignExtend(imm26:00, 64))
+//
+// CBNZ z0110101 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x35000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt)
+// CBZ z0110100 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x34000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt)
+//
+// LDR Wt 00011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x18000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// LDR Xt 01011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x58000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// LDRSW 10011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x98000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// PRFM 11011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xd8000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// LDR St 00011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x1c000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// LDR Dt 01011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x5c000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// LDR Qt 10011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x9c000000 (i = SignExtend(imm19:00, 64), t = Rt)
+// LDR inv 11011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xdc000000 (i = SignExtend(imm19:00, 64), t = Rt)
+//
+// TBNZ z0110111 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x37000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt)
+// TBZ z0110110 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x36000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt)
+//
+
+class CDetourDis
+{
+ public:
+ explicit CDetourDis() = default;
+
+ uint8_t* CopyInstruction(uint8_t* pDst,
+ uint8_t* pSrc,
+ uint8_t* *ppTarget,
+ int32_t *plExtra);
+
+ public:
+ typedef uint8_t (CDetourDis::* COPYFUNC)(uint8_t* pbDst, uint8_t* pbSrc);
+
+ union AddImm12
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rd : 5; // Destination register
+ uint32_t Rn : 5; // Source register
+ uint32_t Imm12 : 12; // 12-bit immediate
+ uint32_t Shift : 2; // shift (must be 0 or 1)
+ uint32_t Opcode1 : 7; // Must be 0010001 == 0x11
+ uint32_t Size : 1; // 0 = 32-bit, 1 = 64-bit
+ } s;
+ static uint32_t Assemble(uint32_t size, uint32_t rd, uint32_t rn, uint32_t imm, uint32_t shift)
+ {
+ AddImm12 temp;
+ temp.s.Rd = rd;
+ temp.s.Rn = rn;
+ temp.s.Imm12 = imm & 0xfff;
+ temp.s.Shift = shift;
+ temp.s.Opcode1 = 0x11;
+ temp.s.Size = size;
+ return temp.Assembled;
+ }
+ static uint32_t AssembleAdd32(uint32_t rd, uint32_t rn, uint32_t imm, uint32_t shift) { return Assemble(0, rd, rn, imm, shift); }
+ static uint32_t AssembleAdd64(uint32_t rd, uint32_t rn, uint32_t imm, uint32_t shift) { return Assemble(1, rd, rn, imm, shift); }
+ };
+
+ union Adr19
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rd : 5; // Destination register
+ uint32_t Imm19 : 19; // 19-bit upper immediate
+ uint32_t Opcode1 : 5; // Must be 10000 == 0x10
+ uint32_t Imm2 : 2; // 2-bit lower immediate
+ uint32_t Type : 1; // 0 = ADR, 1 = ADRP
+ } s;
+ inline int32_t Imm() const { uint32_t Imm = (s.Imm19 << 2) | s.Imm2; return (int32_t)(Imm << 11) >> 11; }
+ static uint32_t Assemble(uint32_t type, uint32_t rd, int32_t delta)
+ {
+ Adr19 temp;
+ temp.s.Rd = rd;
+ temp.s.Imm19 = (delta >> 2) & 0x7ffff;
+ temp.s.Opcode1 = 0x10;
+ temp.s.Imm2 = delta & 3;
+ temp.s.Type = type;
+ return temp.Assembled;
+ }
+ static uint32_t AssembleAdr(uint32_t rd, int32_t delta) { return Assemble(0, rd, delta); }
+ static uint32_t AssembleAdrp(uint32_t rd, int32_t delta) { return Assemble(1, rd, delta); }
+ };
+
+ union Bcc19
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Condition : 4; // Condition
+ uint32_t Opcode1 : 1; // Must be 0
+ uint32_t Imm19 : 19; // 19-bit immediate
+ uint32_t Opcode2 : 8; // Must be 01010100 == 0x54
+ } s;
+ inline int32_t Imm() const { return (int32_t)(s.Imm19 << 13) >> 11; }
+ static uint32_t AssembleBcc(uint32_t condition, int32_t delta)
+ {
+ Bcc19 temp;
+ temp.s.Condition = condition;
+ temp.s.Opcode1 = 0;
+ temp.s.Imm19 = delta >> 2;
+ temp.s.Opcode2 = 0x54;
+ return temp.Assembled;
+ }
+ };
+
+ union Branch26
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Imm26 : 26; // 26-bit immediate
+ uint32_t Opcode1 : 5; // Must be 00101 == 0x5
+ uint32_t Link : 1; // 0 = B, 1 = BL
+ } s;
+ inline int32_t Imm() const { return (int32_t)(s.Imm26 << 6) >> 4; }
+ static uint32_t Assemble(uint32_t link, int32_t delta)
+ {
+ Branch26 temp;
+ temp.s.Imm26 = delta >> 2;
+ temp.s.Opcode1 = 0x5;
+ temp.s.Link = link;
+ return temp.Assembled;
+ }
+ static uint32_t AssembleB(int32_t delta) { return Assemble(0, delta); }
+ static uint32_t AssembleBl(int32_t delta) { return Assemble(1, delta); }
+ };
+
+ union Br
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Opcode1 : 5; // Must be 00000 == 0
+ uint32_t Rn : 5; // Register number
+ uint32_t Opcode2 : 22; // Must be 1101011000011111000000 == 0x3587c0 for Br
+ // 0x358fc0 for Brl
+ } s;
+ static uint32_t Assemble(uint32_t rn, bool link)
+ {
+ Br temp;
+ temp.s.Opcode1 = 0;
+ temp.s.Rn = rn;
+ temp.s.Opcode2 = 0x3587c0;
+ if (link)
+ temp.Assembled |= 0x00200000;
+ return temp.Assembled;
+ }
+ static uint32_t AssembleBr(uint32_t rn)
+ {
+ return Assemble(rn, false);
+ }
+ static uint32_t AssembleBrl(uint32_t rn)
+ {
+ return Assemble(rn, true);
+ }
+ };
+
+ union Cbz19
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rt : 5; // Register to test
+ uint32_t Imm19 : 19; // 19-bit immediate
+ uint32_t Nz : 1; // 0 = CBZ, 1 = CBNZ
+ uint32_t Opcode1 : 6; // Must be 011010 == 0x1a
+ uint32_t Size : 1; // 0 = 32-bit, 1 = 64-bit
+ } s;
+ inline int32_t Imm() const { return (int32_t)(s.Imm19 << 13) >> 11; }
+ static uint32_t Assemble(uint32_t size, uint32_t nz, uint32_t rt, int32_t delta)
+ {
+ Cbz19 temp;
+ temp.s.Rt = rt;
+ temp.s.Imm19 = delta >> 2;
+ temp.s.Nz = nz;
+ temp.s.Opcode1 = 0x1a;
+ temp.s.Size = size;
+ return temp.Assembled;
+ }
+ };
+
+ union LdrLit19
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rt : 5; // Destination register
+ uint32_t Imm19 : 19; // 19-bit immediate
+ uint32_t Opcode1 : 2; // Must be 0
+ uint32_t FpNeon : 1; // 0 = LDR Wt/LDR Xt/LDRSW/PRFM, 1 = LDR St/LDR Dt/LDR Qt
+ uint32_t Opcode2 : 3; // Must be 011 = 3
+ uint32_t Size : 2; // 00 = LDR Wt/LDR St, 01 = LDR Xt/LDR Dt, 10 = LDRSW/LDR Qt, 11 = PRFM/invalid
+ } s;
+ inline int32_t Imm() const { return (int32_t)(s.Imm19 << 13) >> 11; }
+ static uint32_t Assemble(uint32_t size, uint32_t fpneon, uint32_t rt, int32_t delta)
+ {
+ LdrLit19 temp;
+ temp.s.Rt = rt;
+ temp.s.Imm19 = delta >> 2;
+ temp.s.Opcode1 = 0;
+ temp.s.FpNeon = fpneon;
+ temp.s.Opcode2 = 3;
+ temp.s.Size = size;
+ return temp.Assembled;
+ }
+ };
+
+ union LdrFpNeonImm9
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rt : 5; // Destination register
+ uint32_t Rn : 5; // Base register
+ uint32_t Imm12 : 12; // 12-bit immediate
+ uint32_t Opcode1 : 1; // Must be 1 == 1
+ uint32_t Opc : 1; // Part of size
+ uint32_t Opcode2 : 6; // Must be 111101 == 0x3d
+ uint32_t Size : 2; // Size (0=8-bit, 1=16-bit, 2=32-bit, 3=64-bit, 4=128-bit)
+ } s;
+ static uint32_t Assemble(uint32_t size, uint32_t rt, uint32_t rn, uint32_t imm)
+ {
+ LdrFpNeonImm9 temp;
+ temp.s.Rt = rt;
+ temp.s.Rn = rn;
+ temp.s.Imm12 = imm;
+ temp.s.Opcode1 = 1;
+ temp.s.Opc = size >> 2;
+ temp.s.Opcode2 = 0x3d;
+ temp.s.Size = size & 3;
+ return temp.Assembled;
+ }
+ };
+
+ union Mov16
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rd : 5; // Destination register
+ uint32_t Imm16 : 16; // Immediate
+ uint32_t Shift : 2; // Shift amount (0=0, 1=16, 2=32, 3=48)
+ uint32_t Opcode : 6; // Must be 100101 == 0x25
+ uint32_t Type : 2; // 0 = MOVN, 1 = reserved, 2 = MOVZ, 3 = MOVK
+ uint32_t Size : 1; // 0 = 32-bit, 1 = 64-bit
+ } s;
+ static uint32_t Assemble(uint32_t size, uint32_t type, uint32_t rd, uint32_t imm, uint32_t shift)
+ {
+ Mov16 temp;
+ temp.s.Rd = rd;
+ temp.s.Imm16 = imm;
+ temp.s.Shift = shift;
+ temp.s.Opcode = 0x25;
+ temp.s.Type = type;
+ temp.s.Size = size;
+ return temp.Assembled;
+ }
+ static uint32_t AssembleMovn32(uint32_t rd, uint32_t imm, uint32_t shift) { return Assemble(0, 0, rd, imm, shift); }
+ static uint32_t AssembleMovn64(uint32_t rd, uint32_t imm, uint32_t shift) { return Assemble(1, 0, rd, imm, shift); }
+ static uint32_t AssembleMovz32(uint32_t rd, uint32_t imm, uint32_t shift) { return Assemble(0, 2, rd, imm, shift); }
+ static uint32_t AssembleMovz64(uint32_t rd, uint32_t imm, uint32_t shift) { return Assemble(1, 2, rd, imm, shift); }
+ static uint32_t AssembleMovk32(uint32_t rd, uint32_t imm, uint32_t shift) { return Assemble(0, 3, rd, imm, shift); }
+ static uint32_t AssembleMovk64(uint32_t rd, uint32_t imm, uint32_t shift) { return Assemble(1, 3, rd, imm, shift); }
+ };
+
+ union Tbz14
+ {
+ uint32_t Assembled;
+ struct
+ {
+ uint32_t Rt : 5; // Register to test
+ uint32_t Imm14 : 14; // 14-bit immediate
+ uint32_t Bit : 5; // 5-bit index
+ uint32_t Nz : 1; // 0 = TBZ, 1 = TBNZ
+ uint32_t Opcode1 : 6; // Must be 011011 == 0x1b
+ uint32_t Size : 1; // 0 = 32-bit, 1 = 64-bit
+ } s;
+ inline int32_t Imm() const { return (int32_t)(s.Imm14 << 18) >> 16; }
+ static uint32_t Assemble(uint32_t size, uint32_t nz, uint32_t rt, uint32_t bit, int32_t delta)
+ {
+ Tbz14 temp;
+ temp.s.Rt = rt;
+ temp.s.Imm14 = delta >> 2;
+ temp.s.Bit = bit;
+ temp.s.Nz = nz;
+ temp.s.Opcode1 = 0x1b;
+ temp.s.Size = size;
+ return temp.Assembled;
+ }
+ };
+
+
+ protected:
+ uint8_t PureCopy32(uint8_t* pSource, uint8_t* pDest);
+ uint8_t EmitMovImmediate(uint32_t*& pDstInst, uint8_t rd, uint64_t immediate);
+ uint8_t CopyAdr(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+ uint8_t CopyBcc(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+ uint8_t CopyB(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+ uint8_t CopyBl(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+ uint8_t CopyB_or_Bl(uint8_t* pSource, uint8_t* pDest, uint32_t instruction, bool link);
+ uint8_t CopyCbz(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+ uint8_t CopyTbz(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+ uint8_t CopyLdrLiteral(uint8_t* pSource, uint8_t* pDest, uint32_t instruction);
+
+ protected:
+ uint32_t GetInstruction(uint8_t* pSource)
+ {
+ return ((uint32_t*)pSource)[0];
+ }
+
+ uint8_t EmitInstruction(uint32_t*& pDstInst, uint32_t instruction)
+ {
+ *pDstInst++ = instruction;
+ return sizeof(uint32_t);
+ }
+
+ protected:
+ uint8_t* m_pbTarget = nullptr;
+ uint8_t m_rbScratchDst[128] {}; // matches or exceeds rbCode
+};
+
+uint8_t CDetourDis::PureCopy32(uint8_t* pSource, uint8_t* pDest)
+{
+ *(uint32_t *)pDest = *(uint32_t*)pSource;
+ return sizeof(uint32_t);
+}
+
+/////////////////////////////////////////////////////////// Disassembler Code.
+//
+
+uint8_t* CDetourDis::CopyInstruction(uint8_t* pDst,
+ uint8_t* pSrc,
+ uint8_t* *ppTarget,
+ int32_t *plExtra)
+{
+ if (pDst == nullptr) {
+ pDst = m_rbScratchDst;
+ }
+
+ uint32_t Instruction = GetInstruction(pSrc);
+
+ uint32_t CopiedSize;
+ if ((Instruction & 0x1f000000) == 0x10000000) {
+ CopiedSize = CopyAdr(pSrc, pDst, Instruction);
+ } else if ((Instruction & 0xff000010) == 0x54000000) {
+ CopiedSize = CopyBcc(pSrc, pDst, Instruction);
+ } else if ((Instruction & 0x7c000000) == 0x14000000) {
+ CopiedSize = CopyB_or_Bl(pSrc, pDst, Instruction, (Instruction & 0x80000000) != 0);
+ } else if ((Instruction & 0x7e000000) == 0x34000000) {
+ CopiedSize = CopyCbz(pSrc, pDst, Instruction);
+ } else if ((Instruction & 0x7e000000) == 0x36000000) {
+ CopiedSize = CopyTbz(pSrc, pDst, Instruction);
+ } else if ((Instruction & 0x3b000000) == 0x18000000) {
+ CopiedSize = CopyLdrLiteral(pSrc, pDst, Instruction);
+ } else {
+ CopiedSize = PureCopy32(pSrc, pDst);
+ }
+
+ // If the target is needed, store our target
+ if (ppTarget) {
+ *ppTarget = m_pbTarget;
+ }
+ if (plExtra) {
+ *plExtra = CopiedSize - sizeof(uint32_t);
+ }
+
+ return pSrc + 4;
+}
+
+uint8_t CDetourDis::EmitMovImmediate(uint32_t*& pDstInst, uint8_t rd, uint64_t immediate)
+{
+ uint32_t piece[4];
+ piece[3] = (uint32_t)((immediate >> 48) & 0xffff);
+ piece[2] = (uint32_t)((immediate >> 32) & 0xffff);
+ piece[1] = (uint32_t)((immediate >> 16) & 0xffff);
+ piece[0] = (uint32_t)((immediate >> 0) & 0xffff);
+ int count = 0;
+
+ // special case: MOVN with 32-bit dest
+ if (piece[3] == 0 && piece[2] == 0 && piece[1] == 0xffff)
+ {
+ EmitInstruction(pDstInst, Mov16::AssembleMovn32(rd, piece[0] ^ 0xffff, 0));
+ count++;
+ }
+
+ // MOVN/MOVZ with 64-bit dest
+ else
+ {
+ int zero_pieces = (piece[3] == 0x0000) + (piece[2] == 0x0000) + (piece[1] == 0x0000) + (piece[0] == 0x0000);
+ int ffff_pieces = (piece[3] == 0xffff) + (piece[2] == 0xffff) + (piece[1] == 0xffff) + (piece[0] == 0xffff);
+ uint32_t defaultPiece = (ffff_pieces > zero_pieces) ? 0xffff : 0x0000;
+ bool first = true;
+ for (int pieceNum = 3; pieceNum >= 0; pieceNum--)
+ {
+ uint32_t curPiece = piece[pieceNum];
+ if (curPiece != defaultPiece || (pieceNum == 0 && first))
+ {
+ count++;
+ if (first)
+ {
+ if (defaultPiece == 0xffff)
+ {
+ EmitInstruction(pDstInst, Mov16::AssembleMovn64(rd, curPiece ^ 0xffff, pieceNum));
+ }
+ else
+ {
+ EmitInstruction(pDstInst, Mov16::AssembleMovz64(rd, curPiece, pieceNum));
+ }
+ first = false;
+ }
+ else
+ {
+ EmitInstruction(pDstInst, Mov16::AssembleMovk64(rd, curPiece, pieceNum));
+ }
+ }
+ }
+ }
+ return (uint8_t)(count * sizeof(uint32_t));
+}
+
+uint8_t CDetourDis::CopyAdr(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ Adr19& decoded = (Adr19&)(instruction);
+ uint32_t* pDstInst = (uint32_t*)(pDest);
+
+ // ADR case
+ if (decoded.s.Type == 0)
+ {
+ uint8_t* pTarget = pSource + decoded.Imm();
+ int64_t delta = pTarget - pDest;
+ int64_t deltaPage = ((std::ptrdiff_t)pTarget >> 12) - ((std::ptrdiff_t)pDest >> 12);
+
+ // output as ADR
+ if (delta >= -(1 << 20) && delta < (1 << 20))
+ {
+ EmitInstruction(pDstInst, Adr19::AssembleAdr(decoded.s.Rd, (int32_t)delta));
+ }
+
+ // output as ADRP; ADD
+ else if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20)))
+ {
+ EmitInstruction(pDstInst, Adr19::AssembleAdrp(decoded.s.Rd, (int32_t)deltaPage));
+ EmitInstruction(pDstInst, AddImm12::AssembleAdd32(decoded.s.Rd, decoded.s.Rd, ((std::ptrdiff_t)pTarget) & 0xfff, 0));
+ }
+
+ // output as immediate move
+ else
+ {
+ EmitMovImmediate(pDstInst, decoded.s.Rd, (std::ptrdiff_t)pTarget);
+ }
+ }
+
+ // ADRP case
+ else
+ {
+ uint8_t* pTarget = (uint8_t*)((((std::ptrdiff_t)pSource >> 12) + decoded.Imm()) << 12);
+ int64_t deltaPage = ((std::ptrdiff_t)pTarget >> 12) - ((std::ptrdiff_t)pDest >> 12);
+
+ // output as ADRP
+ if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20)))
+ {
+ EmitInstruction(pDstInst, Adr19::AssembleAdrp(decoded.s.Rd, (int32_t)deltaPage));
+ }
+
+ // output as immediate move
+ else
+ {
+ EmitMovImmediate(pDstInst, decoded.s.Rd, (std::ptrdiff_t)pTarget);
+ }
+ }
+
+ return (uint8_t)((uint8_t*)pDstInst - pDest);
+}
+
+uint8_t CDetourDis::CopyBcc(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ Bcc19& decoded = (Bcc19&)(instruction);
+ uint32_t* pDstInst = (uint32_t*)(pDest);
+
+ uint8_t* pTarget = pSource + decoded.Imm();
+ m_pbTarget = pTarget;
+ int64_t delta = pTarget - pDest;
+ int64_t delta4 = pTarget - (pDest + 4);
+
+ // output as BCC
+ if (delta >= -(1 << 20) && delta < (1 << 20))
+ {
+ EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition, (int32_t)delta));
+ }
+
+ // output as BCC ; B
+ else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27)))
+ {
+ EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition ^ 1, 8));
+ EmitInstruction(pDstInst, Branch26::AssembleB((int32_t)delta4));
+ }
+
+ // output as MOV x17, Target; BCC ; BR x17 (BIG assumption that x17 isn't being used for anything!!)
+ else
+ {
+ EmitMovImmediate(pDstInst, 17, (std::ptrdiff_t)pTarget);
+ EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition ^ 1, 8));
+ EmitInstruction(pDstInst, Br::AssembleBr(17));
+ }
+
+ return (uint8_t)((uint8_t*)pDstInst - pDest);
+}
+
+uint8_t CDetourDis::CopyB_or_Bl(uint8_t* pSource, uint8_t* pDest, uint32_t instruction, bool link)
+{
+ Branch26& decoded = (Branch26&)(instruction);
+ uint32_t* pDstInst = (uint32_t*)(pDest);
+
+ uint8_t* pTarget = pSource + decoded.Imm();
+ m_pbTarget = pTarget;
+ int64_t delta = pTarget - pDest;
+
+ // output as B or BRL
+ if (delta >= -(1 << 27) && (delta < (1 << 27)))
+ {
+ EmitInstruction(pDstInst, Branch26::Assemble(link, (int32_t)delta));
+ }
+
+ // output as MOV x17, Target; BR or BRL x17 (BIG assumption that x17 isn't being used for anything!!)
+ else
+ {
+ EmitMovImmediate(pDstInst, 17, (std::ptrdiff_t)pTarget);
+ EmitInstruction(pDstInst, Br::Assemble(17, link));
+ }
+
+ return (uint8_t)((uint8_t*)pDstInst - pDest);
+}
+
+uint8_t CDetourDis::CopyB(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ return CopyB_or_Bl(pSource, pDest, instruction, false);
+}
+
+uint8_t CDetourDis::CopyBl(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ return CopyB_or_Bl(pSource, pDest, instruction, true);
+}
+
+uint8_t CDetourDis::CopyCbz(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ Cbz19& decoded = (Cbz19&)(instruction);
+ uint32_t* pDstInst = (uint32_t*)(pDest);
+
+ uint8_t* pTarget = pSource + decoded.Imm();
+ m_pbTarget = pTarget;
+ int64_t delta = pTarget - pDest;
+ int64_t delta4 = pTarget - (pDest + 4);
+
+ // output as CBZ/NZ
+ if (delta >= -(1 << 20) && delta < (1 << 20))
+ {
+ EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, (int32_t)delta));
+ }
+
+ // output as CBNZ/Z ; B
+ else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27)))
+ {
+ EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8));
+ EmitInstruction(pDstInst, Branch26::AssembleB((int32_t)delta4));
+ }
+
+ // output as MOV x17, Target; CBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!)
+ else
+ {
+ EmitMovImmediate(pDstInst, 17, (std::ptrdiff_t)pTarget);
+ EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8));
+ EmitInstruction(pDstInst, Br::AssembleBr(17));
+ }
+
+ return (uint8_t)((uint8_t*)pDstInst - pDest);
+}
+
+uint8_t CDetourDis::CopyTbz(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ Tbz14& decoded = (Tbz14&)(instruction);
+ uint32_t* pDstInst = (uint32_t*)(pDest);
+
+ uint8_t* pTarget = pSource + decoded.Imm();
+ m_pbTarget = pTarget;
+ int64_t delta = pTarget - pDest;
+ int64_t delta4 = pTarget - (pDest + 4);
+
+ // output as TBZ/NZ
+ if (delta >= -(1 << 13) && delta < (1 << 13))
+ {
+ EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, decoded.s.Bit, (int32_t)delta));
+ }
+
+ // output as TBNZ/Z ; B
+ else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27)))
+ {
+ EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8));
+ EmitInstruction(pDstInst, Branch26::AssembleB((int32_t)delta4));
+ }
+
+ // output as MOV x17, Target; TBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!)
+ else
+ {
+ EmitMovImmediate(pDstInst, 17, (std::ptrdiff_t)pTarget);
+ EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8));
+ EmitInstruction(pDstInst, Br::AssembleBr(17));
+ }
+
+ return (uint8_t)((uint8_t*)pDstInst - pDest);
+}
+
+uint8_t CDetourDis::CopyLdrLiteral(uint8_t* pSource, uint8_t* pDest, uint32_t instruction)
+{
+ LdrLit19& decoded = (LdrLit19&)(instruction);
+ uint32_t* pDstInst = (uint32_t*)(pDest);
+
+ uint8_t* pTarget = pSource + decoded.Imm();
+ int64_t delta = pTarget - pDest;
+
+ // output as LDR
+ if (delta >= -(1 << 21) && delta < (1 << 21))
+ {
+ EmitInstruction(pDstInst, LdrLit19::Assemble(decoded.s.Size, decoded.s.FpNeon, decoded.s.Rt, (int32_t)delta));
+ }
+
+ // output as move immediate
+ else if (decoded.s.FpNeon == 0)
+ {
+ uint64_t value = 0;
+ switch (decoded.s.Size)
+ {
+ case 0: value = *(uint32_t*)pTarget; break;
+ case 1: value = *(uint64_t*)pTarget; break;
+ case 2: value = *(int32_t*)pTarget; break;
+ }
+ EmitMovImmediate(pDstInst, decoded.s.Rt, value);
+ }
+
+ // FP/NEON register: compute address in x17 and load from there (BIG assumption that x17 isn't being used for anything!!)
+ else
+ {
+ EmitMovImmediate(pDstInst, 17, (std::ptrdiff_t)pTarget);
+ EmitInstruction(pDstInst, LdrFpNeonImm9::Assemble(2 + decoded.s.Size, decoded.s.Rt, 17, 0));
+ }
+
+ return (uint8_t)((uint8_t*)pDstInst - pDest);
+}
+
+void* internal_detour_copy_instruction(void* dst, void* src, void** out_target, int32_t* out_extra_len)
+{
+ CDetourDis state;
+ return (void*)state.CopyInstruction((uint8_t*)dst, (uint8_t*)src, (uint8_t**)out_target, out_extra_len);
+}
diff --git a/src/detours_disasm.h b/src/detours_disasm.h
new file mode 100644
index 0000000..e35b979
--- /dev/null
+++ b/src/detours_disasm.h
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Lysann Tranvouez. All rights reserved.
+
+#pragma once
+#ifndef MACH_DETOURS_DISASM_H
+#define MACH_DETOURS_DISASM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+void* internal_detour_copy_instruction(void* dst, void* src, void** out_target, int32_t* out_extra_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //MACH_DETOURS_DISASM_H
diff --git a/src/mach_detours.c b/src/mach_detours.c
index 0ca53aa..4b10c5e 100644
--- a/src/mach_detours.c
+++ b/src/mach_detours.c
@@ -13,6 +13,7 @@
#endif
#include "detours_internal.h"
+#include "detours_disasm.h"
#include "arm64/detours_arm64.h"
@@ -767,7 +768,7 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
int32_t extra_len = 0;
DETOUR_TRACE((" copy instruction src=%p, dest=%p\n", src, trampoline_code));
- src = (uint8_t*)internal_detour_copy_instruction(trampoline_code, (void**)&trampoline_code_limit, src, nullptr, &extra_len);
+ src = (uint8_t*)internal_detour_copy_instruction(trampoline_code, src, nullptr, &extra_len);
DETOUR_TRACE((" after: src=%p (copied %d bytes)\n", src, (int)(src - curr_op)));
trampoline_code += (src - curr_op) + extra_len;
offset_target = (int32_t)(src - target);