Loading...
mach_o/Instructions.cpp dyld-1284.13 dyld-1340
--- dyld/dyld-1284.13/mach_o/Instructions.cpp
+++ dyld/dyld-1340/mach_o/Instructions.cpp
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <cassert>
 
 #include "Instructions.h"
 
@@ -216,16 +217,22 @@
 //
 
 
-bool Instructions::arm::isBranch24(uint32_t instruction, uint32_t instructionAddr, BranchKind& kind, uint32_t& targetAddr)
-{
-    // NOTE: b and bl can have 4-bit condition, but blx cannot.
-    // we do not support conditions because you cannot transform bl to blx
-    if ( (instruction & 0xFF000000) == 0xEB000000 )
-        kind = bl;
-    else if ( (instruction & 0xFE000000) == 0xFA000000 )
-        kind = blx;
-    else if ( (instruction & 0x0F000000) == 0x0A000000 )
-        kind = b;
+bool Instructions::arm::isBranch24(uint32_t instruction, uint32_t instructionAddr, BranchKind& kind, uint32_t& targetAddr, bool& isCond)
+{
+    // 4 top bits are used to encode the
+    // 0xE0000000 -> no condition
+    // 0xF0000000 -> blx/bx variants
+    // other values are used to encode a code for the conditional execution
+    if ( ((instruction & 0xF0000000) != 0xF0000000) && (instruction & 0x0F000000) == 0x0B000000 ) {
+        kind   = bl;
+        isCond = (instruction & 0xF0000000) != 0xE0000000;
+    } else if ( (instruction & 0xFE000000) == 0xFA000000 ) {
+        kind   = blx;
+        isCond = false;
+    } else if ( ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000 )) {
+        kind   = b;
+        isCond = (instruction & 0xF0000000) != 0xE0000000;
+    }
     else
         return false;
 
@@ -285,13 +292,20 @@
 
     if ( kind != b ) {
         if ( targetIsThumb ) {
+            uint32_t maskedCond = instruction & 0xF0000000;
+            assert(((maskedCond == 0xF0000000) || (maskedCond == 0xE0000000)) && "ARM can't branch to thumb with non-blx instruction");
             uint32_t opcode = 0xFA000000;  // blx
             uint32_t disp   = (uint32_t)(delta >> 2) & 0x00FFFFFF;
             uint32_t h_bit  = (uint32_t)(delta << 23) & 0x01000000;
             instruction     = opcode | h_bit | disp;
         }
         else {
-            uint32_t opcode = 0xEB000000;  //  bl
+            // preserve branch condition
+            uint32_t maskedCond = (instruction & 0xF0000000);
+            // except 0xf, that's an encoding for blx
+            if ( maskedCond == 0xF0000000 )
+                maskedCond = 0xE0000000; // unconditional
+            uint32_t opcode = 0x0B000000 | maskedCond;  //  bl
             uint32_t disp   = (uint32_t)(delta >> 2) & 0x00FFFFFF;
             instruction     = opcode | disp;
         }
@@ -302,7 +316,7 @@
     }
     else {
         // simple arm-to-arm branch
-        uint32_t opcode = 0xEA000000;  // b
+        uint32_t opcode = 0x0A000000 | (instruction & 0xF0000000);  // b
         instruction     = (opcode & 0xFF000000) | ((uint32_t)(delta >> 2) & 0x00FFFFFF);
         return true;
     }
@@ -311,6 +325,19 @@
 
 bool Instructions::arm::makeThumbBranch22(BranchKind kind, uint64_t instructionAddr, uint64_t targetAddr, bool targetIsThumb, uint32_t& instruction)
 {
+    if ( targetIsThumb ) {
+        // Thumb to thumb branch, we will be generating a bl instruction.
+        // Delta is always even, so mask out thumb bit in target.
+        targetAddr &= ~1ULL;
+    } else {
+        // Target is not thumb, we will be generating a blx instruction
+        // Since blx cannot have the low bit set, set bit[1] of the target to
+        // bit[1] of the base address, so that the difference is a multiple of
+        // 4 bytes.
+        targetAddr &= ~2ULL;
+        targetAddr |= (instructionAddr & 2ULL);
+    }
+
     int64_t delta = targetAddr - (instructionAddr + 4); // pcrel to start of BL instruction + 4
     const int64_t b22Limit  = 0x00FFFFFF;               // Note: thumb1 has only a +/-4MB range.  We only support thumb2 which has a +/-16MB branch range
     if ( (delta > b22Limit) || (delta < (-b22Limit)) )
@@ -593,7 +620,7 @@
 
 bool Instructions::riscv::setLUITarget(uint32_t& instruction, uint64_t targetAddr)
 {
-    const int64_t fourGBLimit  = 0x7FFFF000;               // Note: riscv32 will always be in range, riscv64 might not be
+    const int64_t fourGBLimit  = 0xFFFFF000;               // Note: riscv32 will always be in range, riscv64 might not be
     if ( targetAddr > fourGBLimit )
         return false;