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