[RuntimeDyld][ORC] Add support for Thumb mode to RuntimeDyldMachOARM.
authorLang Hames <lhames@gmail.com>
Wed, 9 Aug 2017 20:19:27 +0000 (20:19 +0000)
committerLang Hames <lhames@gmail.com>
Wed, 9 Aug 2017 20:19:27 +0000 (20:19 +0000)
This patch adds support for thumb relocations to RuntimeDyldMachOARM, and adds
a target-specific flags field to JITSymbolFlags (so that on ARM we can record
whether each symbol is Thumb-mode code).

RuntimeDyldImpl::emitSection is modified to ensure that stubs memory is
correctly aligned based on the size returned by getStubAlignment().

llvm-svn: 310517

llvm/include/llvm/ExecutionEngine/JITSymbol.h
llvm/lib/ExecutionEngine/RuntimeDyld/JITSymbol.cpp
llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.cpp
llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h
llvm/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldMachOARM.h
llvm/test/ExecutionEngine/RuntimeDyld/ARM/MachO_Thumb_Relocations.s [new file with mode: 0644]

index 4172f24..5e66140 100644 (file)
@@ -40,6 +40,7 @@ using JITTargetAddress = uint64_t;
 class JITSymbolFlags {
 public:
   using UnderlyingType = uint8_t;
+  using TargetFlagsType = uint64_t;
 
   enum FlagNames : UnderlyingType {
     None = 0,
@@ -56,6 +57,11 @@ public:
   /// @brief Construct a JITSymbolFlags instance from the given flags.
   JITSymbolFlags(FlagNames Flags) : Flags(Flags) {}
 
+  /// @brief Construct a JITSymbolFlags instance from the given flags and target
+  ///        flags.
+  JITSymbolFlags(FlagNames Flags, TargetFlagsType TargetFlags)
+    : Flags(Flags), TargetFlags(TargetFlags) {}
+
   /// @brief Return true if there was an error retrieving this symbol.
   bool hasError() const {
     return (Flags & HasError) == HasError;
@@ -80,8 +86,12 @@ public:
     return (Flags & Exported) == Exported;
   }
 
+  /// @brief Implicitly convert to the underlying flags type.
   operator UnderlyingType&() { return Flags; }
 
+  /// @brief Return a reference to the target-specific flags.
+  TargetFlagsType& getTargetFlags() { return TargetFlags; }
+
   /// Construct a JITSymbolFlags value based on the flags of the given global
   /// value.
   static JITSymbolFlags fromGlobalValue(const GlobalValue &GV);
@@ -92,6 +102,26 @@ public:
 
 private:
   UnderlyingType Flags = None;
+  TargetFlagsType TargetFlags = 0;
+};
+
+/// @brief ARM-specific JIT symbol flags.
+/// FIXME: This should be moved into a target-specific header.
+class ARMJITSymbolFlags {
+public:
+  ARMJITSymbolFlags() = default;
+
+  enum FlagNames {
+    None = 0,
+    Thumb = 1 << 0
+  };
+
+  operator JITSymbolFlags::TargetFlagsType&() { return Flags; }
+
+  static ARMJITSymbolFlags fromObjectSymbol(
+                                           const object::BasicSymbolRef &Symbol);
+private:
+  JITSymbolFlags::TargetFlagsType Flags = 0;
 };
 
 /// @brief Represents a symbol that has been evaluated to an address already.
index 8769dcf..87059ef 100644 (file)
@@ -39,3 +39,11 @@ llvm::JITSymbolFlags::fromObjectSymbol(const object::BasicSymbolRef &Symbol) {
     Flags |= JITSymbolFlags::Exported;
   return Flags;
 }
+
+ARMJITSymbolFlags llvm::ARMJITSymbolFlags::fromObjectSymbol(
+                                         const object::BasicSymbolRef &Symbol) {
+  ARMJITSymbolFlags Flags;
+  if (Symbol.getFlags() & object::BasicSymbolRef::SF_Thumb)
+    Flags |= ARMJITSymbolFlags::Thumb;
+  return Flags;
+}
index 8198836..4d1d74c 100644 (file)
@@ -233,7 +233,7 @@ RuntimeDyldImpl::loadObjectImpl(const object::ObjectFile &Obj) {
         return NameOrErr.takeError();
 
       // Compute JIT symbol flags.
-      JITSymbolFlags JITSymFlags = JITSymbolFlags::fromObjectSymbol(*I);
+      JITSymbolFlags JITSymFlags = getJITSymbolFlags(*I);
 
       // If this is a weak definition, check to see if there's a strong one.
       // If there is, skip this symbol (we won't be providing it: the strong
@@ -616,6 +616,10 @@ void RuntimeDyldImpl::writeBytesUnaligned(uint64_t Value, uint8_t *Dst,
   }
 }
 
+JITSymbolFlags RuntimeDyldImpl::getJITSymbolFlags(const BasicSymbolRef &SR) {
+  return JITSymbolFlags::fromObjectSymbol(SR);
+}
+
 Error RuntimeDyldImpl::emitCommonSymbols(const ObjectFile &Obj,
                                          CommonSymbolList &CommonSymbols) {
   if (CommonSymbols.empty())
@@ -685,7 +689,7 @@ Error RuntimeDyldImpl::emitCommonSymbols(const ObjectFile &Obj,
       Addr += AlignOffset;
       Offset += AlignOffset;
     }
-    JITSymbolFlags JITSymFlags = JITSymbolFlags::fromObjectSymbol(Sym);
+    JITSymbolFlags JITSymFlags = getJITSymbolFlags(Sym);
     DEBUG(dbgs() << "Allocating common symbol " << Name << " address "
                  << format("%p", Addr) << "\n");
     GlobalSymbolTable[Name] =
@@ -746,8 +750,11 @@ RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
   // Code section alignment needs to be at least as high as stub alignment or
   // padding calculations may by incorrect when the section is remapped to a
   // higher alignment.
-  if (IsCode)
+  if (IsCode) {
     Alignment = std::max(Alignment, getStubAlignment());
+    if (StubBufSize > 0)
+      PaddingSize += getStubAlignment() - 1;
+  }
 
   // Some sections, such as debug info, don't need to be loaded for execution.
   // Process those only if explicitly requested.
@@ -771,8 +778,13 @@ RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
     // Fill in any extra bytes we allocated for padding
     if (PaddingSize != 0) {
       memset(Addr + DataSize, 0, PaddingSize);
-      // Update the DataSize variable so that the stub offset is set correctly.
+      // Update the DataSize variable to include padding.
       DataSize += PaddingSize;
+
+      // Align DataSize to stub alignment if we have any stubs (PaddingSize will
+      // have been increased above to account for this).
+      if (StubBufSize > 0)
+        DataSize &= ~(getStubAlignment() - 1);
     }
 
     DEBUG(dbgs() << "emitSection SectionID: " << SectionID << " Name: " << Name
@@ -864,7 +876,7 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr,
   } else if (Arch == Triple::arm || Arch == Triple::armeb) {
     // TODO: There is only ARM far stub now. We should add the Thumb stub,
     // and stubs for branches Thumb - ARM and ARM - Thumb.
-    writeBytesUnaligned(0xe51ff004, Addr, 4); // ldr pc,<label>
+    writeBytesUnaligned(0xe51ff004, Addr, 4); // ldr pc, [pc, #-4]
     return Addr + 4;
   } else if (IsMipsO32ABI) {
     // 0:   3c190000        lui     t9,%hi(addr).
@@ -971,15 +983,17 @@ Error RuntimeDyldImpl::resolveExternalSymbols() {
       resolveRelocationList(Relocs, 0);
     } else {
       uint64_t Addr = 0;
+      JITSymbolFlags Flags;
       RTDyldSymbolTable::const_iterator Loc = GlobalSymbolTable.find(Name);
       if (Loc == GlobalSymbolTable.end()) {
         // This is an external symbol, try to get its address from the symbol
         // resolver.
         // First search for the symbol in this logical dylib.
         if (auto Sym = Resolver.findSymbolInLogicalDylib(Name.data())) {
-          if (auto AddrOrErr = Sym.getAddress())
+          if (auto AddrOrErr = Sym.getAddress()) {
             Addr = *AddrOrErr;
-          else
+            Flags = Sym.getFlags();
+          } else
             return AddrOrErr.takeError();
         } else if (auto Err = Sym.takeError())
           return Err;
@@ -987,9 +1001,10 @@ Error RuntimeDyldImpl::resolveExternalSymbols() {
         // If that fails, try searching for an external symbol.
         if (!Addr) {
           if (auto Sym = Resolver.findSymbol(Name.data())) {
-            if (auto AddrOrErr = Sym.getAddress())
+            if (auto AddrOrErr = Sym.getAddress()) {
               Addr = *AddrOrErr;
-            else
+              Flags = Sym.getFlags();
+            } else
               return AddrOrErr.takeError();
           } else if (auto Err = Sym.takeError())
             return Err;
@@ -1007,6 +1022,7 @@ Error RuntimeDyldImpl::resolveExternalSymbols() {
         const auto &SymInfo = Loc->second;
         Addr = getSectionLoadAddress(SymInfo.getSectionID()) +
                SymInfo.getOffset();
+        Flags = SymInfo.getFlags();
       }
 
       // FIXME: Implement error handling that doesn't kill the host program!
@@ -1017,6 +1033,12 @@ Error RuntimeDyldImpl::resolveExternalSymbols() {
       // If Resolver returned UINT64_MAX, the client wants to handle this symbol
       // manually and we shouldn't resolve its relocations.
       if (Addr != UINT64_MAX) {
+
+        // Tweak the address based on the symbol flags if necessary.
+        // For example, this is used by RuntimeDyldMachOARM to toggle the low bit
+        // if the target symbol is Thumb.
+        Addr = modifyAddressBasedOnFlags(Addr, Flags);
+
         DEBUG(dbgs() << "Resolving relocations Name: " << Name << "\t"
                      << format("0x%lx", Addr) << "\n");
         // This list may have been updated when we called getSymbolAddress, so
index 95b04fd..e046a85 100644 (file)
@@ -149,8 +149,8 @@ public:
   /// The size of this relocation (MachO specific).
   unsigned Size;
 
-  // COFF specific.
-  bool IsTargetThumbFunc;
+  // ARM (MachO and COFF) specific.
+  bool IsTargetThumbFunc = false;
 
   RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend)
       : SectionID(id), Offset(offset), RelType(type), Addend(addend),
@@ -195,12 +195,14 @@ public:
   uint64_t Offset;
   int64_t Addend;
   const char *SymbolName;
+  bool IsStubThumb = false;
   RelocationValueRef() : SectionID(0), Offset(0), Addend(0),
                          SymbolName(nullptr) {}
 
   inline bool operator==(const RelocationValueRef &Other) const {
     return SectionID == Other.SectionID && Offset == Other.Offset &&
-           Addend == Other.Addend && SymbolName == Other.SymbolName;
+           Addend == Other.Addend && SymbolName == Other.SymbolName &&
+           IsStubThumb == Other.IsStubThumb;
   }
   inline bool operator<(const RelocationValueRef &Other) const {
     if (SectionID != Other.SectionID)
@@ -209,6 +211,8 @@ public:
       return Offset < Other.Offset;
     if (Addend != Other.Addend)
       return Addend < Other.Addend;
+    if (IsStubThumb != Other.IsStubThumb)
+      return IsStubThumb < Other.IsStubThumb;
     return SymbolName < Other.SymbolName;
   }
 };
@@ -216,21 +220,21 @@ public:
 /// @brief Symbol info for RuntimeDyld.
 class SymbolTableEntry {
 public:
-  SymbolTableEntry()
-      : Offset(0), SectionID(0) {}
+  SymbolTableEntry() = default;
 
   SymbolTableEntry(unsigned SectionID, uint64_t Offset, JITSymbolFlags Flags)
       : Offset(Offset), SectionID(SectionID), Flags(Flags) {}
 
   unsigned getSectionID() const { return SectionID; }
   uint64_t getOffset() const { return Offset; }
+  void setOffset(uint64_t NewOffset) { Offset = NewOffset; }
 
   JITSymbolFlags getFlags() const { return Flags; }
 
 private:
-  uint64_t Offset;
-  unsigned SectionID;
-  JITSymbolFlags Flags;
+  uint64_t Offset = 0;
+  unsigned SectionID = 0;
+  JITSymbolFlags Flags = JITSymbolFlags::None;
 };
 
 typedef StringMap<SymbolTableEntry> RTDyldSymbolTable;
@@ -365,6 +369,18 @@ protected:
   /// Dst.
   void writeBytesUnaligned(uint64_t Value, uint8_t *Dst, unsigned Size) const;
 
+  /// Generate JITSymbolFlags from a libObject symbol.
+  virtual JITSymbolFlags getJITSymbolFlags(const BasicSymbolRef &Sym);
+
+  /// Modify the given target address based on the given symbol flags.
+  /// This can be used by subclasses to tweak addresses based on symbol flags,
+  /// For example: the MachO/ARM target uses it to set the low bit if the target
+  /// is a thumb symbol.
+  virtual uint64_t modifyAddressBasedOnFlags(uint64_t Addr,
+                                             JITSymbolFlags Flags) const {
+    return Addr;
+  }
+
   /// \brief Given the common symbols discovered in the object file, emit a
   /// new section for them and update the symbol mappings in the object and
   /// symbol table.
@@ -493,6 +509,12 @@ public:
     if (SymEntry.getSectionID() != AbsoluteSymbolSection)
       SectionAddr = getSectionLoadAddress(SymEntry.getSectionID());
     uint64_t TargetAddr = SectionAddr + SymEntry.getOffset();
+
+    // FIXME: Have getSymbol should return the actual address and the client
+    //        modify it based on the flags. This will require clients to be
+    //        aware of the target architecture, which we should build
+    //        infrastructure for.
+    TargetAddr = modifyAddressBasedOnFlags(TargetAddr, SymEntry.getFlags());
     return JITEvaluatedSymbol(TargetAddr, SymEntry.getFlags());
   }
 
index 80e9c7a..b0561f6 100644 (file)
@@ -55,7 +55,8 @@ Expected<relocation_iterator>
 RuntimeDyldMachO::processScatteredVANILLA(
                           unsigned SectionID, relocation_iterator RelI,
                           const ObjectFile &BaseObjT,
-                          RuntimeDyldMachO::ObjSectionToIDMap &ObjSectionToID) {
+                          RuntimeDyldMachO::ObjSectionToIDMap &ObjSectionToID,
+                          bool TargetIsLocalThumbFunc) {
   const MachOObjectFile &Obj =
     static_cast<const MachOObjectFile&>(BaseObjT);
   MachO::any_relocation_info RE =
@@ -85,6 +86,7 @@ RuntimeDyldMachO::processScatteredVANILLA(
 
   Addend -= SectionBaseAddr;
   RelocationEntry R(SectionID, Offset, RelocType, Addend, IsPCRel, Size);
+  R.IsTargetThumbFunc = TargetIsLocalThumbFunc;
 
   addRelocationForSection(R, TargetSectionID);
 
index 67a5020..d71ca4e 100644 (file)
@@ -83,7 +83,8 @@ protected:
   Expected<relocation_iterator>
   processScatteredVANILLA(unsigned SectionID, relocation_iterator RelI,
                           const ObjectFile &BaseObjT,
-                          RuntimeDyldMachO::ObjSectionToIDMap &ObjSectionToID);
+                          RuntimeDyldMachO::ObjSectionToIDMap &ObjSectionToID,
+                          bool TargetIsLocalThumbFunc = false);
 
   /// Construct a RelocationValueRef representing the relocation target.
   /// For Symbols in known sections, this will return a RelocationValueRef
index 43461de..990629d 100644 (file)
@@ -34,7 +34,20 @@ public:
 
   unsigned getStubAlignment() override { return 4; }
 
-  int64_t decodeAddend(const RelocationEntry &RE) const {
+  JITSymbolFlags getJITSymbolFlags(const BasicSymbolRef &SR) override {
+    auto Flags = RuntimeDyldImpl::getJITSymbolFlags(SR);
+    Flags.getTargetFlags() = ARMJITSymbolFlags::fromObjectSymbol(SR);
+    return Flags;
+  }
+
+  uint64_t modifyAddressBasedOnFlags(uint64_t Addr,
+                                     JITSymbolFlags Flags) const override {
+    if (Flags.getTargetFlags() & ARMJITSymbolFlags::Thumb)
+      Addr |= 0x1;
+    return Addr;
+  }
+
+  Expected<int64_t> decodeAddend(const RelocationEntry &RE) const {
     const SectionEntry &Section = Sections[RE.SectionID];
     uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
 
@@ -47,6 +60,27 @@ public:
         // Now we've got the shifted immediate, shift by 2, sign extend and ret.
         return SignExtend32<26>(Temp << 2);
       }
+
+      case MachO::ARM_THUMB_RELOC_BR22: {
+        // This is a pair of instructions whose operands combine to provide 22
+        // bits of displacement:
+        // Encoding for high bits 1111 0XXX XXXX XXXX
+        // Encoding for low bits  1111 1XXX XXXX XXXX
+        uint16_t HighInsn = readBytesUnaligned(LocalAddress, 2);
+        if ((HighInsn & 0xf800) != 0xf000)
+          return make_error<StringError>("Unrecognized thumb branch encoding "
+                                         "(BR22 high bits)",
+                                         inconvertibleErrorCode());
+
+        uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
+        if ((LowInsn & 0xf800) != 0xf800)
+          return make_error<StringError>("Unrecognized thumb branch encoding "
+                                         "(BR22 low bits)",
+                                         inconvertibleErrorCode());
+
+        return SignExtend64<23>(((HighInsn & 0x7ff) << 12) |
+                                ((LowInsn & 0x7ff) << 1));
+      }
     }
   }
 
@@ -61,12 +95,35 @@ public:
         Obj.getRelocation(RelI->getRawDataRefImpl());
     uint32_t RelType = Obj.getAnyRelocationType(RelInfo);
 
+    // Set to true for thumb functions in this (or previous) TUs.
+    // Will be used to set the TargetIsThumbFunc member on the relocation entry.
+    bool TargetIsLocalThumbFunc = false;
+    if (Obj.getPlainRelocationExternal(RelInfo)) {
+      auto Symbol = RelI->getSymbol();
+      StringRef TargetName;
+      if (auto TargetNameOrErr = Symbol->getName())
+        TargetName = *TargetNameOrErr;
+      else
+        return TargetNameOrErr.takeError();
+
+      // If the target is external but the value doesn't have a name then we've
+      // converted the value to a section/offset pair, but we still need to set
+      // the IsTargetThumbFunc bit, so look the value up in the globla symbol table.
+      auto EntryItr = GlobalSymbolTable.find(TargetName);
+      if (EntryItr != GlobalSymbolTable.end()) {
+        TargetIsLocalThumbFunc =
+          EntryItr->second.getFlags().getTargetFlags() &
+          ARMJITSymbolFlags::Thumb;
+      }
+    }
+
     if (Obj.isRelocationScattered(RelInfo)) {
       if (RelType == MachO::ARM_RELOC_HALF_SECTDIFF)
         return processHALFSECTDIFFRelocation(SectionID, RelI, Obj,
                                              ObjSectionToID);
       else if (RelType == MachO::GENERIC_RELOC_VANILLA)
-        return processScatteredVANILLA(SectionID, RelI, Obj, ObjSectionToID);
+        return processScatteredVANILLA(SectionID, RelI, Obj, ObjSectionToID,
+                                       TargetIsLocalThumbFunc);
       else
         return ++RelI;
     }
@@ -77,7 +134,6 @@ public:
     UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_SECTDIFF);
     UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_LOCAL_SECTDIFF);
     UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_PB_LA_PTR);
-    UNIMPLEMENTED_RELOC(MachO::ARM_THUMB_RELOC_BR22);
     UNIMPLEMENTED_RELOC(MachO::ARM_THUMB_32BIT_BRANCH);
     UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_HALF);
     default:
@@ -89,17 +145,30 @@ public:
     }
 
     RelocationEntry RE(getRelocationEntry(SectionID, Obj, RelI));
-    RE.Addend = decodeAddend(RE);
+    if (auto AddendOrErr = decodeAddend(RE))
+      RE.Addend = *AddendOrErr;
+    else
+      return AddendOrErr.takeError();
+    RE.IsTargetThumbFunc = TargetIsLocalThumbFunc;
+
     RelocationValueRef Value;
     if (auto ValueOrErr = getRelocationValueRef(Obj, RelI, RE, ObjSectionToID))
       Value = *ValueOrErr;
     else
       return ValueOrErr.takeError();
 
+    // If this is a branch from a thumb function (BR22) then make sure we mark
+    // the value as being a thumb stub: we don't want to mix it up with an ARM
+    // stub targeting the same function.
+    if (RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
+      Value.IsStubThumb = TargetIsLocalThumbFunc;
+
     if (RE.IsPCRel)
-      makeValueAddendPCRel(Value, RelI, 8);
+      makeValueAddendPCRel(Value, RelI,
+                           (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8);
 
-    if ((RE.RelType & 0xf) == MachO::ARM_RELOC_BR24)
+    if (RE.RelType == MachO::ARM_RELOC_BR24 ||
+        RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
       processBranchRelocation(RE, Value, Stubs);
     else {
       RE.Addend = Value.Offset;
@@ -124,12 +193,30 @@ public:
       Value -= FinalAddress;
       // ARM PCRel relocations have an effective-PC offset of two instructions
       // (four bytes in Thumb mode, 8 bytes in ARM mode).
-      // FIXME: For now, assume ARM mode.
-      Value -= 8;
+      Value -= (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8;
     }
 
     switch (RE.RelType) {
+    case MachO::ARM_THUMB_RELOC_BR22: {
+      Value += RE.Addend;
+      uint16_t HighInsn = readBytesUnaligned(LocalAddress, 2);
+      assert((HighInsn & 0xf800) == 0xf000 &&
+             "Unrecognized thumb branch encoding (BR22 high bits)");
+      HighInsn = (HighInsn & 0xf800) | ((Value >> 12) & 0x7ff);
+
+      uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
+      assert((LowInsn & 0xf800) != 0xf8000 &&
+             "Unrecognized thumb branch encoding (BR22 low bits)");
+      LowInsn = (LowInsn & 0xf800) | ((Value >> 1) & 0x7ff);
+
+      writeBytesUnaligned(HighInsn, LocalAddress, 2);
+      writeBytesUnaligned(LowInsn, LocalAddress + 2, 2);
+      break;
+    }
+
     case MachO::ARM_RELOC_VANILLA:
+      if (RE.IsTargetThumbFunc)
+        Value |= 0x01;
       writeBytesUnaligned(Value + RE.Addend, LocalAddress, 1 << RE.Size);
       break;
     case MachO::ARM_RELOC_BR24: {
@@ -158,10 +245,19 @@ public:
       Value = SectionABase - SectionBBase + RE.Addend;
       if (RE.Size & 0x1) // :upper16:
         Value = (Value >> 16);
+
+      bool IsThumb = RE.Size & 0x2;
+
       Value &= 0xffff;
 
       uint32_t Insn = readBytesUnaligned(LocalAddress, 4);
-      Insn = (Insn & 0xfff0f000) | ((Value & 0xf000) << 4) | (Value & 0x0fff);
+
+      if (IsThumb)
+        Insn = (Insn & 0x8f00fbf0) | ((Value & 0xf000) >> 12) |
+               ((Value & 0x0800) >> 1) | ((Value & 0x0700) << 20) |
+               ((Value & 0x00ff) << 16);
+      else
+        Insn = (Insn & 0xfff0f000) | ((Value & 0xf000) << 4) | (Value & 0x0fff);
       writeBytesUnaligned(Insn, LocalAddress, 4);
       break;
     }
@@ -196,17 +292,26 @@ private:
       Addr = Section.getAddressWithOffset(i->second);
     } else {
       // Create a new stub function.
+      assert(Section.getStubOffset() % 4 == 0 && "Misaligned stub");
       Stubs[Value] = Section.getStubOffset();
-      uint8_t *StubTargetAddr = createStubFunction(
-          Section.getAddressWithOffset(Section.getStubOffset()));
+      uint32_t StubOpcode = 0;
+      if (RE.RelType == MachO::ARM_RELOC_BR24)
+        StubOpcode = 0xe51ff004; // ldr pc, [pc, #-4]
+      else if (RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
+        StubOpcode = 0xf000f8df; // ldr pc, [pc]
+      else
+        llvm_unreachable("Unrecognized relocation");
+      Addr = Section.getAddressWithOffset(Section.getStubOffset());
+      writeBytesUnaligned(StubOpcode, Addr, 4);
+      uint8_t *StubTargetAddr = Addr + 4;
       RelocationEntry StubRE(
           RE.SectionID, StubTargetAddr - Section.getAddress(),
           MachO::GENERIC_RELOC_VANILLA, Value.Offset, false, 2);
+      StubRE.IsTargetThumbFunc = RE.IsTargetThumbFunc;
       if (Value.SymbolName)
         addRelocationForSymbol(StubRE, Value.SymbolName);
       else
         addRelocationForSection(StubRE, Value.SectionID);
-      Addr = Section.getAddressWithOffset(Section.getStubOffset());
       Section.advanceStubOffset(getMaxStubSize());
     }
     RelocationEntry TargetRE(RE.SectionID, RE.Offset, RE.RelType, 0,
@@ -223,14 +328,12 @@ private:
     MachO::any_relocation_info RE =
         MachO.getRelocation(RelI->getRawDataRefImpl());
 
-
     // For a half-diff relocation the length bits actually record whether this
     // is a movw/movt, and whether this is arm or thumb.
     // Bit 0 indicates movw (b0 == 0) or movt (b0 == 1).
     // Bit 1 indicates arm (b1 == 0) or thumb (b1 == 1).
     unsigned HalfDiffKindBits = MachO.getAnyRelocationLength(RE);
-    if (HalfDiffKindBits & 0x2)
-      llvm_unreachable("Thumb not yet supported.");
+    bool IsThumb = HalfDiffKindBits & 0x2;
 
     SectionEntry &Section = Sections[SectionID];
     uint32_t RelocType = MachO.getAnyRelocationType(RE);
@@ -238,7 +341,14 @@ private:
     uint64_t Offset = RelI->getOffset();
     uint8_t *LocalAddress = Section.getAddressWithOffset(Offset);
     int64_t Immediate = readBytesUnaligned(LocalAddress, 4); // Copy the whole instruction out.
-    Immediate = ((Immediate >> 4) & 0xf000) | (Immediate & 0xfff);
+
+    if (IsThumb)
+      Immediate = ((Immediate & 0x0000000f) << 12) |
+                  ((Immediate & 0x00000400) << 1) |
+                  ((Immediate & 0x70000000) >> 20) |
+                  ((Immediate & 0x00ff0000) >> 16);
+    else
+      Immediate = ((Immediate >> 4) & 0xf000) | (Immediate & 0xfff);
 
     ++RelI;
     MachO::any_relocation_info RE2 =
diff --git a/llvm/test/ExecutionEngine/RuntimeDyld/ARM/MachO_Thumb_Relocations.s b/llvm/test/ExecutionEngine/RuntimeDyld/ARM/MachO_Thumb_Relocations.s
new file mode 100644 (file)
index 0000000..8f0e873
--- /dev/null
@@ -0,0 +1,51 @@
+# RUN: llvm-mc -triple=thumbv7s-apple-ios7.0.0 -filetype=obj -o %T/MachO_Thumb.o %s
+# RUN: llvm-rtdyld -triple=thumbv7s-apple-ios7.0.0 -verify -check=%s %/T/MachO_Thumb.o
+
+        .section        __TEXT,__text,regular,pure_instructions
+        .syntax unified
+
+# Add 'aaa' to the common symbols to make sure 'baz' isn't at the start of the
+# section. This ensures that we test VANILLA relocation addends correctly.
+        .comm   aaa, 4, 2
+        .comm   baz, 4, 2
+
+
+        .globl  bar
+        .p2align        1
+        .code   16                      @ @bar
+        .thumb_func     bar
+
+bar:
+# Check lower 16-bits of section difference relocation
+# rtdyld-check: decode_operand(insn1, 1) = (foo-(nextPC+8))[15:0]
+insn1:
+        movw    r0, :lower16:(foo-(nextPC+8))
+# Check upper 16-bits of section difference relocation
+# rtdyld-check: decode_operand(insn2, 2) = (foo-(nextPC+8))[31:16]
+insn2:
+        movt    r0, :upper16:(foo-(nextPC+8))
+nextPC:
+        nop
+
+# Check stub generation for external symbols by referencing a common symbol, 'baz'.
+# Check both the content of the stub, and the reference to the stub.
+# Stub should contain '0xf000f8df' (ldr.w pc, [pc]), followed by the target.
+#
+# rtdyld-check: *{4}(stub_addr(MachO_Thumb.o, __text, baz)) = 0xf000f8df
+# rtdyld-check: *{4}(stub_addr(MachO_Thumb.o, __text, baz) + 4) = baz
+#
+# rtdyld-check: decode_operand(insn3, 0) = stub_addr(MachO_Thumb.o, __text, baz) - (insn3 + 4)
+insn3:
+        bl      baz
+
+# Check stub generation for internal symbols by referencing 'bar'.
+# rtdyld-check: *{4}(stub_addr(MachO_Thumb.o, __text, bar) + 4) = bar & 0xfffffffffffffffe
+insn4:
+        bl      bar
+
+        .section       __DATA,__data
+       .align  2
+foo:
+       .long   0
+
+.subsections_via_symbols