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