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