[RuntimeDyld][MachO][AArch64] Add a helper function for encoding addends in instructions.
[oota-llvm.git] / lib / ExecutionEngine / RuntimeDyld / Targets / RuntimeDyldMachOAArch64.h
index 486cb56dc11c315044a284655f16e20f5cf1903f..7043ba36f4280db535ff2fac874a689a1ffdb515 100644 (file)
@@ -118,6 +118,109 @@ public:
     return Addend;
   }
 
+  /// Extract the addend encoded in the instruction.
+  void encodeAddend(uint8_t *LocalAddress, uint32_t RelType,
+                    int64_t Addend) const {
+    // Verify that the relocation has the correct alignment.
+    switch (RelType) {
+    default:
+      llvm_unreachable("Unsupported relocation type!");
+    case MachO::ARM64_RELOC_UNSIGNED:
+      llvm_unreachable("Invalid relocation type for instruction.");
+    case MachO::ARM64_RELOC_BRANCH26:
+    case MachO::ARM64_RELOC_PAGE21:
+    case MachO::ARM64_RELOC_PAGEOFF12:
+    case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
+    case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:
+      assert((((uintptr_t)LocalAddress & 0x3) == 0) &&
+             "Instruction address is not aligned to 4 bytes.");
+      break;
+    }
+
+    switch (RelType) {
+    default:
+      llvm_unreachable("Unsupported relocation type!");
+    case MachO::ARM64_RELOC_BRANCH26: {
+      // Verify that the relocation points to the expected branch instruction.
+      uint32_t *p = (uint32_t *)LocalAddress;
+      assert((*p & 0xFC000000) == 0x14000000 && "Expected branch instruction.");
+
+      // Verify addend value.
+      assert((Addend & 0x3) == 0 && "Branch target is not aligned");
+      assert(isInt<28>(Addend) && "Branch target is out of range.");
+
+      // Encode the addend as 26 bit immediate in the branch instruction.
+      *p = (*p & 0xFC000000) | ((uint32_t)(Addend >> 2) & 0x03FFFFFF);
+      break;
+    }
+    case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
+    case MachO::ARM64_RELOC_PAGE21: {
+      // Verify that the relocation points to the expected adrp instruction.
+      uint32_t *p = (uint32_t *)LocalAddress;
+      assert((*p & 0x9F000000) == 0x90000000 && "Expected adrp instruction.");
+
+      // Check that the addend fits into 21 bits (+ 12 lower bits).
+      assert((Addend & 0xFFF) == 0 && "ADRP target is not page aligned.");
+      assert(isInt<33>(Addend) && "Invalid page reloc value.");
+
+      // Encode the addend into the instruction.
+      uint32_t ImmLoValue = (uint32_t)(Addend << 17) & 0x60000000;
+      uint32_t ImmHiValue = (uint32_t)(Addend >> 9) & 0x00FFFFE0;
+      *p = (*p & 0x9F00001F) | ImmHiValue | ImmLoValue;
+      break;
+    }
+    case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: {
+      // Verify that the relocation points to one of the expected load / store
+      // instructions.
+      uint32_t *p = (uint32_t *)LocalAddress;
+      assert((*p & 0x3B000000) == 0x39000000 &&
+             "Only expected load / store instructions.");
+    } // fall-through
+    case MachO::ARM64_RELOC_PAGEOFF12: {
+      // Verify that the relocation points to one of the expected load / store
+      // or add / sub instructions.
+      uint32_t *p = (uint32_t *)LocalAddress;
+      assert((((*p & 0x3B000000) == 0x39000000) ||
+              ((*p & 0x11C00000) == 0x11000000)   ) &&
+             "Expected load / store  or add/sub instruction.");
+
+      // Check which instruction we are decoding to obtain the implicit shift
+      // factor of the instruction and verify alignment.
+      int ImplicitShift = 0;
+      if ((*p & 0x3B000000) == 0x39000000) { // << load / store
+        // For load / store instructions the size is encoded in bits 31:30.
+        ImplicitShift = ((*p >> 30) & 0x3);
+        switch (ImplicitShift) {
+        case 0:
+          // Check if this a vector op to get the correct shift value.
+          if ((*p & 0x04800000) == 0x04800000) {
+            ImplicitShift = 4;
+            assert(((Addend & 0xF) == 0) &&
+                   "128-bit LDR/STR not 16-byte aligned.");
+          }
+          break;
+        case 1:
+          assert(((Addend & 0x1) == 0) && "16-bit LDR/STR not 2-byte aligned.");
+          break;
+        case 2:
+          assert(((Addend & 0x3) == 0) && "32-bit LDR/STR not 4-byte aligned.");
+          break;
+        case 3:
+          assert(((Addend & 0x7) == 0) && "64-bit LDR/STR not 8-byte aligned.");
+          break;
+        }
+      }
+      // Compensate for implicit shift.
+      Addend >>= ImplicitShift;
+      assert(isUInt<12>(Addend) && "Addend cannot be encoded.");
+
+      // Encode the addend into the instruction.
+      *p = (*p & 0xFFC003FF) | ((uint32_t)(Addend << 10) & 0x003FFC00);
+      break;
+    }
+    }
+  }
+
   relocation_iterator
   processRelocationRef(unsigned SectionID, relocation_iterator RelI,
                        ObjectImage &ObjImg, ObjSectionToIDMap &ObjSectionToID,
@@ -196,105 +299,37 @@ public:
     }
     case MachO::ARM64_RELOC_BRANCH26: {
       assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_BRANCH26 not supported");
-      // Mask the value into the target address. We know instructions are
-      // 32-bit aligned, so we can do it all at once.
-      uint32_t *p = (uint32_t *)LocalAddress;
-      // Check if the addend is encoded in the instruction.
-      uint32_t EncodedAddend = *p & 0x03FFFFFF;
-      if (EncodedAddend != 0) {
-        if (RE.Addend == 0)
-          llvm_unreachable("branch26 instruction has embedded addend.");
-        else
-          llvm_unreachable("branch26 instruction has embedded addend and"
-                           "ARM64_RELOC_ADDEND.");
-      }
       // Check if branch is in range.
       uint64_t FinalAddress = Section.LoadAddress + RE.Offset;
-      uint64_t PCRelVal = Value - FinalAddress + RE.Addend;
-      assert(isInt<26>(PCRelVal) && "Branch target out of range!");
-      // Insert the value into the instruction.
-      *p = (*p & 0xFC000000) | ((uint32_t)(PCRelVal >> 2) & 0x03FFFFFF);
+      int64_t PCRelVal = Value - FinalAddress + RE.Addend;
+      encodeAddend(LocalAddress, RE.RelType, PCRelVal);
       break;
     }
     case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
     case MachO::ARM64_RELOC_PAGE21: {
       assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_PAGE21 not supported");
-      // Mask the value into the target address. We know instructions are
-      // 32-bit aligned, so we can do it all at once.
-      uint32_t *p = (uint32_t *)LocalAddress;
-      // Check if the addend is encoded in the instruction.
-      uint32_t EncodedAddend =
-          ((*p & 0x60000000) >> 29) | ((*p & 0x01FFFFE0) >> 3);
-      if (EncodedAddend != 0) {
-        if (RE.Addend == 0)
-          llvm_unreachable("adrp instruction has embedded addend.");
-        else
-          llvm_unreachable("adrp instruction has embedded addend and"
-                           "ARM64_RELOC_ADDEND.");
-      }
       // Adjust for PC-relative relocation and offset.
       uint64_t FinalAddress = Section.LoadAddress + RE.Offset;
-      uint64_t PCRelVal =
-          ((Value + RE.Addend) & (-4096)) - (FinalAddress & (-4096));
-      // Check that the value fits into 21 bits (+ 12 lower bits).
-      assert(isInt<33>(PCRelVal) && "Invalid page reloc value!");
-      // Insert the value into the instruction.
-      uint32_t ImmLoValue = (uint32_t)(PCRelVal << 17) & 0x60000000;
-      uint32_t ImmHiValue = (uint32_t)(PCRelVal >> 9) & 0x00FFFFE0;
-      *p = (*p & 0x9F00001F) | ImmHiValue | ImmLoValue;
+      int64_t PCRelVal =
+        ((Value + RE.Addend) & (-4096)) - (FinalAddress & (-4096));
+      encodeAddend(LocalAddress, RE.RelType, PCRelVal);
       break;
     }
     case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:
     case MachO::ARM64_RELOC_PAGEOFF12: {
       assert(!RE.IsPCRel && "PCRel and ARM64_RELOC_PAGEOFF21 not supported");
-      // Mask the value into the target address. We know instructions are
-      // 32-bit aligned, so we can do it all at once.
-      uint32_t *p = (uint32_t *)LocalAddress;
-      // Check if the addend is encoded in the instruction.
-      uint32_t EncodedAddend = *p & 0x003FFC00;
-      if (EncodedAddend != 0) {
-        if (RE.Addend == 0)
-          llvm_unreachable("adrp instruction has embedded addend.");
-        else
-          llvm_unreachable("adrp instruction has embedded addend and"
-                           "ARM64_RELOC_ADDEND.");
-      }
       // Add the offset from the symbol.
       Value += RE.Addend;
       // Mask out the page address and only use the lower 12 bits.
       Value &= 0xFFF;
-      // Check which instruction we are updating to obtain the implicit shift
-      // factor from LDR/STR instructions.
-      if (*p & 0x08000000) {
-        uint32_t ImplicitShift = ((*p >> 30) & 0x3);
-        switch (ImplicitShift) {
-        case 0:
-          // Check if this a vector op.
-          if ((*p & 0x04800000) == 0x04800000) {
-            ImplicitShift = 4;
-            assert(((Value & 0xF) == 0) &&
-                   "128-bit LDR/STR not 16-byte aligned.");
-          }
-          break;
-        case 1:
-          assert(((Value & 0x1) == 0) && "16-bit LDR/STR not 2-byte aligned.");
-        case 2:
-          assert(((Value & 0x3) == 0) && "32-bit LDR/STR not 4-byte aligned.");
-        case 3:
-          assert(((Value & 0x7) == 0) && "64-bit LDR/STR not 8-byte aligned.");
-        }
-        // Compensate for implicit shift.
-        Value >>= ImplicitShift;
-      }
-      // Insert the value into the instruction.
-      *p = (*p & 0xFFC003FF) | ((uint32_t)(Value << 10) & 0x003FFC00);
+      encodeAddend(LocalAddress, RE.RelType, Value);
       break;
     }
     case MachO::ARM64_RELOC_SUBTRACTOR:
     case MachO::ARM64_RELOC_POINTER_TO_GOT:
     case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21:
     case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
-      llvm_unreachable("Relocation type not implemented yet!");
+      llvm_unreachable("Relocation type not yet implemented!");
     case MachO::ARM64_RELOC_ADDEND:
       llvm_unreachable("ARM64_RELOC_ADDEND should have been handeled by "
                        "processRelocationRef!");