1//===------ MachOPlatform.cpp - Utilities for executing MachO in Orc ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
10
11#include "llvm/BinaryFormat/MachO.h"
12#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
13#include "llvm/Support/BinaryByteStream.h"
14#include "llvm/Support/Debug.h"
15
16#define DEBUG_TYPE "orc"
17
18namespace {
19
20struct objc_class;
21struct objc_image_info;
22struct objc_object;
23struct objc_selector;
24
25using Class = objc_class *;
26using id = objc_object *;
27using SEL = objc_selector *;
28
29using ObjCMsgSendTy = id (*)(id, SEL, ...);
30using ObjCReadClassPairTy = Class (*)(Class, const objc_image_info *);
31using SelRegisterNameTy = SEL (*)(const char *);
32
33enum class ObjCRegistrationAPI { Uninitialized, Unavailable, Initialized };
34
35ObjCRegistrationAPI ObjCRegistrationAPIState =
36    ObjCRegistrationAPI::Uninitialized;
37ObjCMsgSendTy objc_msgSend = nullptr;
38ObjCReadClassPairTy objc_readClassPair = nullptr;
39SelRegisterNameTy sel_registerName = nullptr;
40
41} // end anonymous namespace
42
43namespace llvm {
44namespace orc {
45
46template <typename FnTy>
47static Error setUpObjCRegAPIFunc(FnTy &Target, sys::DynamicLibrary &LibObjC,
48                                 const char *Name) {
49  if (void *Addr = LibObjC.getAddressOfSymbol(Name))
50    Target = reinterpret_cast<FnTy>(Addr);
51  else
52    return make_error<StringError>(
53        (Twine("Could not find address for ") + Name).str(),
54        inconvertibleErrorCode());
55  return Error::success();
56}
57
58Error enableObjCRegistration(const char *PathToLibObjC) {
59  // If we've already tried to initialize then just bail out.
60  if (ObjCRegistrationAPIState != ObjCRegistrationAPI::Uninitialized)
61    return Error::success();
62
63  ObjCRegistrationAPIState = ObjCRegistrationAPI::Unavailable;
64
65  std::string ErrMsg;
66  auto LibObjC =
67      sys::DynamicLibrary::getPermanentLibrary(PathToLibObjC, &ErrMsg);
68
69  if (!LibObjC.isValid())
70    return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
71
72  if (auto Err = setUpObjCRegAPIFunc(objc_msgSend, LibObjC, "objc_msgSend"))
73    return Err;
74  if (auto Err = setUpObjCRegAPIFunc(objc_readClassPair, LibObjC,
75                                     "objc_readClassPair"))
76    return Err;
77  if (auto Err =
78          setUpObjCRegAPIFunc(sel_registerName, LibObjC, "sel_registerName"))
79    return Err;
80
81  ObjCRegistrationAPIState = ObjCRegistrationAPI::Initialized;
82  return Error::success();
83}
84
85bool objCRegistrationEnabled() {
86  return ObjCRegistrationAPIState == ObjCRegistrationAPI::Initialized;
87}
88
89void MachOJITDylibInitializers::runModInits() const {
90  for (const auto &ModInit : ModInitSections) {
91    for (uint64_t I = 0; I != ModInit.NumPtrs; ++I) {
92      auto *InitializerAddr = jitTargetAddressToPointer<uintptr_t *>(
93          ModInit.Address + (I * sizeof(uintptr_t)));
94      auto *Initializer =
95          jitTargetAddressToFunction<void (*)()>(*InitializerAddr);
96      Initializer();
97    }
98  }
99}
100
101void MachOJITDylibInitializers::registerObjCSelectors() const {
102  assert(objCRegistrationEnabled() && "ObjC registration not enabled.");
103
104  for (const auto &ObjCSelRefs : ObjCSelRefsSections) {
105    for (uint64_t I = 0; I != ObjCSelRefs.NumPtrs; ++I) {
106      auto SelEntryAddr = ObjCSelRefs.Address + (I * sizeof(uintptr_t));
107      const auto *SelName =
108          *jitTargetAddressToPointer<const char **>(SelEntryAddr);
109      auto Sel = sel_registerName(SelName);
110      *jitTargetAddressToPointer<SEL *>(SelEntryAddr) = Sel;
111    }
112  }
113}
114
115Error MachOJITDylibInitializers::registerObjCClasses() const {
116  assert(objCRegistrationEnabled() && "ObjC registration not enabled.");
117
118  struct ObjCClassCompiled {
119    void *Metaclass;
120    void *Parent;
121    void *Cache1;
122    void *Cache2;
123    void *Data;
124  };
125
126  auto *ImageInfo =
127      jitTargetAddressToPointer<const objc_image_info *>(ObjCImageInfoAddr);
128  auto ClassSelector = sel_registerName("class");
129
130  for (const auto &ObjCClassList : ObjCClassListSections) {
131    for (uint64_t I = 0; I != ObjCClassList.NumPtrs; ++I) {
132      auto ClassPtrAddr = ObjCClassList.Address + (I * sizeof(uintptr_t));
133      auto Cls = *jitTargetAddressToPointer<Class *>(ClassPtrAddr);
134      auto *ClassCompiled =
135          *jitTargetAddressToPointer<ObjCClassCompiled **>(ClassPtrAddr);
136      objc_msgSend(reinterpret_cast<id>(ClassCompiled->Parent), ClassSelector);
137      auto Registered = objc_readClassPair(Cls, ImageInfo);
138
139      // FIXME: Improve diagnostic by reporting the failed class's name.
140      if (Registered != Cls)
141        return make_error<StringError>("Unable to register Objective-C class",
142                                       inconvertibleErrorCode());
143    }
144  }
145  return Error::success();
146}
147
148MachOPlatform::MachOPlatform(
149    ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
150    std::unique_ptr<MemoryBuffer> StandardSymbolsObject)
151    : ES(ES), ObjLinkingLayer(ObjLinkingLayer),
152      StandardSymbolsObject(std::move(StandardSymbolsObject)) {
153  ObjLinkingLayer.addPlugin(std::make_unique<InitScraperPlugin>(*this));
154}
155
156Error MachOPlatform::setupJITDylib(JITDylib &JD) {
157  auto ObjBuffer = MemoryBuffer::getMemBuffer(
158      StandardSymbolsObject->getMemBufferRef(), false);
159  return ObjLinkingLayer.add(JD, std::move(ObjBuffer));
160}
161
162Error MachOPlatform::notifyAdding(JITDylib &JD, const MaterializationUnit &MU) {
163  const auto &InitSym = MU.getInitializerSymbol();
164  if (!InitSym)
165    return Error::success();
166
167  RegisteredInitSymbols[&JD].add(InitSym,
168                                 SymbolLookupFlags::WeaklyReferencedSymbol);
169  LLVM_DEBUG({
170    dbgs() << "MachOPlatform: Registered init symbol " << *InitSym << " for MU "
171           << MU.getName() << "\n";
172  });
173  return Error::success();
174}
175
176Error MachOPlatform::notifyRemoving(JITDylib &JD, VModuleKey K) {
177  llvm_unreachable("Not supported yet");
178}
179
180Expected<MachOPlatform::InitializerSequence>
181MachOPlatform::getInitializerSequence(JITDylib &JD) {
182
183  LLVM_DEBUG({
184    dbgs() << "MachOPlatform: Building initializer sequence for "
185           << JD.getName() << "\n";
186  });
187
188  std::vector<JITDylib *> DFSLinkOrder;
189
190  while (true) {
191
192    DenseMap<JITDylib *, SymbolLookupSet> NewInitSymbols;
193
194    ES.runSessionLocked([&]() {
195      DFSLinkOrder = getDFSLinkOrder(JD);
196
197      for (auto *InitJD : DFSLinkOrder) {
198        auto RISItr = RegisteredInitSymbols.find(InitJD);
199        if (RISItr != RegisteredInitSymbols.end()) {
200          NewInitSymbols[InitJD] = std::move(RISItr->second);
201          RegisteredInitSymbols.erase(RISItr);
202        }
203      }
204    });
205
206    if (NewInitSymbols.empty())
207      break;
208
209    LLVM_DEBUG({
210      dbgs() << "MachOPlatform: Issuing lookups for new init symbols: "
211                "(lookup may require multiple rounds)\n";
212      for (auto &KV : NewInitSymbols)
213        dbgs() << "  \"" << KV.first->getName() << "\": " << KV.second << "\n";
214    });
215
216    // Outside the lock, issue the lookup.
217    if (auto R = lookupInitSymbols(JD.getExecutionSession(), NewInitSymbols))
218      ; // Nothing to do in the success case.
219    else
220      return R.takeError();
221  }
222
223  LLVM_DEBUG({
224    dbgs() << "MachOPlatform: Init symbol lookup complete, building init "
225              "sequence\n";
226  });
227
228  // Lock again to collect the initializers.
229  InitializerSequence FullInitSeq;
230  {
231    std::lock_guard<std::mutex> Lock(InitSeqsMutex);
232    for (auto *InitJD : reverse(DFSLinkOrder)) {
233      LLVM_DEBUG({
234        dbgs() << "MachOPlatform: Appending inits for \"" << InitJD->getName()
235               << "\" to sequence\n";
236      });
237      auto ISItr = InitSeqs.find(InitJD);
238      if (ISItr != InitSeqs.end()) {
239        FullInitSeq.emplace_back(InitJD, std::move(ISItr->second));
240        InitSeqs.erase(ISItr);
241      }
242    }
243  }
244
245  return FullInitSeq;
246}
247
248Expected<MachOPlatform::DeinitializerSequence>
249MachOPlatform::getDeinitializerSequence(JITDylib &JD) {
250  std::vector<JITDylib *> DFSLinkOrder = getDFSLinkOrder(JD);
251
252  DeinitializerSequence FullDeinitSeq;
253  {
254    std::lock_guard<std::mutex> Lock(InitSeqsMutex);
255    for (auto *DeinitJD : DFSLinkOrder) {
256      FullDeinitSeq.emplace_back(DeinitJD, MachOJITDylibDeinitializers());
257    }
258  }
259
260  return FullDeinitSeq;
261}
262
263std::vector<JITDylib *> MachOPlatform::getDFSLinkOrder(JITDylib &JD) {
264  std::vector<JITDylib *> Result, WorkStack({&JD});
265  DenseSet<JITDylib *> Visited;
266
267  while (!WorkStack.empty()) {
268    auto *NextJD = WorkStack.back();
269    WorkStack.pop_back();
270    if (Visited.count(NextJD))
271      continue;
272    Visited.insert(NextJD);
273    Result.push_back(NextJD);
274    NextJD->withLinkOrderDo([&](const JITDylibSearchOrder &LO) {
275      for (auto &KV : LO)
276        WorkStack.push_back(KV.first);
277    });
278  }
279
280  return Result;
281}
282
283void MachOPlatform::registerInitInfo(
284    JITDylib &JD, JITTargetAddress ObjCImageInfoAddr,
285    MachOJITDylibInitializers::SectionExtent ModInits,
286    MachOJITDylibInitializers::SectionExtent ObjCSelRefs,
287    MachOJITDylibInitializers::SectionExtent ObjCClassList) {
288  std::lock_guard<std::mutex> Lock(InitSeqsMutex);
289
290  auto &InitSeq = InitSeqs[&JD];
291
292  InitSeq.setObjCImageInfoAddr(ObjCImageInfoAddr);
293
294  if (ModInits.Address)
295    InitSeq.addModInitsSection(std::move(ModInits));
296
297  if (ObjCSelRefs.Address)
298    InitSeq.addObjCSelRefsSection(std::move(ObjCSelRefs));
299
300  if (ObjCClassList.Address)
301    InitSeq.addObjCClassListSection(std::move(ObjCClassList));
302}
303
304static Expected<MachOJITDylibInitializers::SectionExtent>
305getSectionExtent(jitlink::LinkGraph &G, StringRef SectionName) {
306  auto *Sec = G.findSectionByName(SectionName);
307  if (!Sec)
308    return MachOJITDylibInitializers::SectionExtent();
309  jitlink::SectionRange R(*Sec);
310  if (R.getSize() % G.getPointerSize() != 0)
311    return make_error<StringError>(SectionName + " section size is not a "
312                                                 "multiple of the pointer size",
313                                   inconvertibleErrorCode());
314  return MachOJITDylibInitializers::SectionExtent(
315      R.getStart(), R.getSize() / G.getPointerSize());
316}
317
318void MachOPlatform::InitScraperPlugin::modifyPassConfig(
319    MaterializationResponsibility &MR, const Triple &TT,
320    jitlink::PassConfiguration &Config) {
321
322  Config.PrePrunePasses.push_back([this, &MR](jitlink::LinkGraph &G) -> Error {
323    JITLinkSymbolVector InitSectionSymbols;
324    preserveInitSectionIfPresent(InitSectionSymbols, G, "__mod_init_func");
325    preserveInitSectionIfPresent(InitSectionSymbols, G, "__objc_selrefs");
326    preserveInitSectionIfPresent(InitSectionSymbols, G, "__objc_classlist");
327
328    if (!InitSymbolDeps.empty()) {
329      std::lock_guard<std::mutex> Lock(InitScraperMutex);
330      InitSymbolDeps[&MR] = std::move(InitSectionSymbols);
331    }
332
333    if (auto Err = processObjCImageInfo(G, MR))
334      return Err;
335
336    return Error::success();
337  });
338
339  Config.PostFixupPasses.push_back([this, &JD = MR.getTargetJITDylib()](
340                                       jitlink::LinkGraph &G) -> Error {
341    MachOJITDylibInitializers::SectionExtent ModInits, ObjCSelRefs,
342        ObjCClassList;
343
344    JITTargetAddress ObjCImageInfoAddr = 0;
345    if (auto *ObjCImageInfoSec = G.findSectionByName("__objc_image_info")) {
346      if (auto Addr = jitlink::SectionRange(*ObjCImageInfoSec).getStart()) {
347        ObjCImageInfoAddr = Addr;
348        dbgs() << "Recorded __objc_imageinfo @ " << formatv("{0:x16}", Addr);
349      }
350    }
351
352    // Record __mod_init_func.
353    if (auto ModInitsOrErr = getSectionExtent(G, "__mod_init_func"))
354      ModInits = std::move(*ModInitsOrErr);
355    else
356      return ModInitsOrErr.takeError();
357
358    // Record __objc_selrefs.
359    if (auto ObjCSelRefsOrErr = getSectionExtent(G, "__objc_selrefs"))
360      ObjCSelRefs = std::move(*ObjCSelRefsOrErr);
361    else
362      return ObjCSelRefsOrErr.takeError();
363
364    // Record __objc_classlist.
365    if (auto ObjCClassListOrErr = getSectionExtent(G, "__objc_classlist"))
366      ObjCClassList = std::move(*ObjCClassListOrErr);
367    else
368      return ObjCClassListOrErr.takeError();
369
370    // Dump the scraped inits.
371    LLVM_DEBUG({
372      dbgs() << "MachOPlatform: Scraped " << G.getName() << " init sections:\n";
373      dbgs() << "  __objc_selrefs: ";
374      if (ObjCSelRefs.NumPtrs)
375        dbgs() << ObjCSelRefs.NumPtrs << " pointer(s) at "
376               << formatv("{0:x16}", ObjCSelRefs.Address) << "\n";
377      else
378        dbgs() << "none\n";
379
380      dbgs() << "  __objc_classlist: ";
381      if (ObjCClassList.NumPtrs)
382        dbgs() << ObjCClassList.NumPtrs << " pointer(s) at "
383               << formatv("{0:x16}", ObjCClassList.Address) << "\n";
384      else
385        dbgs() << "none\n";
386
387      dbgs() << "  __mod_init_func: ";
388      if (ModInits.NumPtrs)
389        dbgs() << ModInits.NumPtrs << " pointer(s) at "
390               << formatv("{0:x16}", ModInits.Address) << "\n";
391      else
392        dbgs() << "none\n";
393    });
394
395    MP.registerInitInfo(JD, ObjCImageInfoAddr, std::move(ModInits),
396                        std::move(ObjCSelRefs), std::move(ObjCClassList));
397
398    return Error::success();
399  });
400}
401
402ObjectLinkingLayer::Plugin::LocalDependenciesMap
403MachOPlatform::InitScraperPlugin::getSyntheticSymbolLocalDependencies(
404    MaterializationResponsibility &MR) {
405  std::lock_guard<std::mutex> Lock(InitScraperMutex);
406  auto I = InitSymbolDeps.find(&MR);
407  if (I != InitSymbolDeps.end()) {
408    LocalDependenciesMap Result;
409    Result[MR.getInitializerSymbol()] = std::move(I->second);
410    InitSymbolDeps.erase(&MR);
411    return Result;
412  }
413  return LocalDependenciesMap();
414}
415
416void MachOPlatform::InitScraperPlugin::preserveInitSectionIfPresent(
417    JITLinkSymbolVector &Symbols, jitlink::LinkGraph &G,
418    StringRef SectionName) {
419  if (auto *Sec = G.findSectionByName(SectionName)) {
420    auto SecBlocks = Sec->blocks();
421    if (!llvm::empty(SecBlocks))
422      Symbols.push_back(
423          &G.addAnonymousSymbol(**SecBlocks.begin(), 0, 0, false, true));
424  }
425}
426
427Error MachOPlatform::InitScraperPlugin::processObjCImageInfo(
428    jitlink::LinkGraph &G, MaterializationResponsibility &MR) {
429
430  // If there's an ObjC imagine info then either
431  //   (1) It's the first __objc_imageinfo we've seen in this JITDylib. In
432  //       this case we name and record it.
433  // OR
434  //   (2) We already have a recorded __objc_imageinfo for this JITDylib,
435  //       in which case we just verify it.
436  auto *ObjCImageInfo = G.findSectionByName("__objc_imageinfo");
437  if (!ObjCImageInfo)
438    return Error::success();
439
440  auto ObjCImageInfoBlocks = ObjCImageInfo->blocks();
441
442  // Check that the section is not empty if present.
443  if (llvm::empty(ObjCImageInfoBlocks))
444    return make_error<StringError>("Empty __objc_imageinfo section in " +
445                                       G.getName(),
446                                   inconvertibleErrorCode());
447
448  // Check that there's only one block in the section.
449  if (std::next(ObjCImageInfoBlocks.begin()) != ObjCImageInfoBlocks.end())
450    return make_error<StringError>("Multiple blocks in __objc_imageinfo "
451                                   "section in " +
452                                       G.getName(),
453                                   inconvertibleErrorCode());
454
455  // Check that the __objc_imageinfo section is unreferenced.
456  // FIXME: We could optimize this check if Symbols had a ref-count.
457  for (auto &Sec : G.sections()) {
458    if (&Sec != ObjCImageInfo)
459      for (auto *B : Sec.blocks())
460        for (auto &E : B->edges())
461          if (E.getTarget().isDefined() &&
462              &E.getTarget().getBlock().getSection() == ObjCImageInfo)
463            return make_error<StringError>("__objc_imageinfo is referenced "
464                                           "within file " +
465                                               G.getName(),
466                                           inconvertibleErrorCode());
467  }
468
469  auto &ObjCImageInfoBlock = **ObjCImageInfoBlocks.begin();
470  auto *ObjCImageInfoData = ObjCImageInfoBlock.getContent().data();
471  auto Version = support::endian::read32(ObjCImageInfoData, G.getEndianness());
472  auto Flags =
473      support::endian::read32(ObjCImageInfoData + 4, G.getEndianness());
474
475  // Lock the mutex while we verify / update the ObjCImageInfos map.
476  std::lock_guard<std::mutex> Lock(InitScraperMutex);
477
478  auto ObjCImageInfoItr = ObjCImageInfos.find(&MR.getTargetJITDylib());
479  if (ObjCImageInfoItr != ObjCImageInfos.end()) {
480    // We've already registered an __objc_imageinfo section. Verify the
481    // content of this new section matches, then delete it.
482    if (ObjCImageInfoItr->second.first != Version)
483      return make_error<StringError>(
484          "ObjC version in " + G.getName() +
485              " does not match first registered version",
486          inconvertibleErrorCode());
487    if (ObjCImageInfoItr->second.second != Flags)
488      return make_error<StringError>("ObjC flags in " + G.getName() +
489                                         " do not match first registered flags",
490                                     inconvertibleErrorCode());
491
492    // __objc_imageinfo is valid. Delete the block.
493    for (auto *S : ObjCImageInfo->symbols())
494      G.removeDefinedSymbol(*S);
495    G.removeBlock(ObjCImageInfoBlock);
496  } else {
497    // We haven't registered an __objc_imageinfo section yet. Register and
498    // move on. The section should already be marked no-dead-strip.
499    ObjCImageInfos[&MR.getTargetJITDylib()] = std::make_pair(Version, Flags);
500  }
501
502  return Error::success();
503}
504
505} // End namespace orc.
506} // End namespace llvm.
507