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 llvm {
19 namespace orc {
20 
21 MachOPlatform::MachOPlatform(
22     ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
23     std::unique_ptr<MemoryBuffer> StandardSymbolsObject)
24     : ES(ES), ObjLinkingLayer(ObjLinkingLayer),
25       StandardSymbolsObject(std::move(StandardSymbolsObject)) {
26   ObjLinkingLayer.addPlugin(std::make_unique<InitScraperPlugin>(*this));
27 }
28 
29 Error MachOPlatform::setupJITDylib(JITDylib &JD) {
30   auto ObjBuffer = MemoryBuffer::getMemBuffer(
31       StandardSymbolsObject->getMemBufferRef(), false);
32   return ObjLinkingLayer.add(JD, std::move(ObjBuffer));
33 }
34 
35 Error MachOPlatform::notifyAdding(ResourceTracker &RT,
36                                   const MaterializationUnit &MU) {
37   auto &JD = RT.getJITDylib();
38   const auto &InitSym = MU.getInitializerSymbol();
39   if (!InitSym)
40     return Error::success();
41 
42   RegisteredInitSymbols[&JD].add(InitSym,
43                                  SymbolLookupFlags::WeaklyReferencedSymbol);
44   LLVM_DEBUG({
45     dbgs() << "MachOPlatform: Registered init symbol " << *InitSym << " for MU "
46            << MU.getName() << "\n";
47   });
48   return Error::success();
49 }
50 
51 Error MachOPlatform::notifyRemoving(ResourceTracker &RT) {
52   llvm_unreachable("Not supported yet");
53 }
54 
55 Expected<MachOPlatform::InitializerSequence>
56 MachOPlatform::getInitializerSequence(JITDylib &JD) {
57 
58   LLVM_DEBUG({
59     dbgs() << "MachOPlatform: Building initializer sequence for "
60            << JD.getName() << "\n";
61   });
62 
63   std::vector<JITDylibSP> DFSLinkOrder;
64 
65   while (true) {
66 
67     DenseMap<JITDylib *, SymbolLookupSet> NewInitSymbols;
68 
69     ES.runSessionLocked([&]() {
70       DFSLinkOrder = JD.getDFSLinkOrder();
71 
72       for (auto &InitJD : DFSLinkOrder) {
73         auto RISItr = RegisteredInitSymbols.find(InitJD.get());
74         if (RISItr != RegisteredInitSymbols.end()) {
75           NewInitSymbols[InitJD.get()] = std::move(RISItr->second);
76           RegisteredInitSymbols.erase(RISItr);
77         }
78       }
79     });
80 
81     if (NewInitSymbols.empty())
82       break;
83 
84     LLVM_DEBUG({
85       dbgs() << "MachOPlatform: Issuing lookups for new init symbols: "
86                 "(lookup may require multiple rounds)\n";
87       for (auto &KV : NewInitSymbols)
88         dbgs() << "  \"" << KV.first->getName() << "\": " << KV.second << "\n";
89     });
90 
91     // Outside the lock, issue the lookup.
92     if (auto R = lookupInitSymbols(JD.getExecutionSession(), NewInitSymbols))
93       ; // Nothing to do in the success case.
94     else
95       return R.takeError();
96   }
97 
98   LLVM_DEBUG({
99     dbgs() << "MachOPlatform: Init symbol lookup complete, building init "
100               "sequence\n";
101   });
102 
103   // Lock again to collect the initializers.
104   InitializerSequence FullInitSeq;
105   {
106     std::lock_guard<std::mutex> Lock(InitSeqsMutex);
107     for (auto &InitJD : reverse(DFSLinkOrder)) {
108       LLVM_DEBUG({
109         dbgs() << "MachOPlatform: Appending inits for \"" << InitJD->getName()
110                << "\" to sequence\n";
111       });
112       auto ISItr = InitSeqs.find(InitJD.get());
113       if (ISItr != InitSeqs.end()) {
114         FullInitSeq.emplace_back(InitJD.get(), std::move(ISItr->second));
115         InitSeqs.erase(ISItr);
116       }
117     }
118   }
119 
120   return FullInitSeq;
121 }
122 
123 Expected<MachOPlatform::DeinitializerSequence>
124 MachOPlatform::getDeinitializerSequence(JITDylib &JD) {
125   std::vector<JITDylibSP> DFSLinkOrder = JD.getDFSLinkOrder();
126 
127   DeinitializerSequence FullDeinitSeq;
128   {
129     std::lock_guard<std::mutex> Lock(InitSeqsMutex);
130     for (auto &DeinitJD : DFSLinkOrder) {
131       FullDeinitSeq.emplace_back(DeinitJD.get(), MachOJITDylibDeinitializers());
132     }
133   }
134 
135   return FullDeinitSeq;
136 }
137 
138 void MachOPlatform::registerInitInfo(JITDylib &JD,
139                                      JITTargetAddress ObjCImageInfoAddr,
140                                      ExecutorAddressRange ModInits,
141                                      ExecutorAddressRange ObjCSelRefs,
142                                      ExecutorAddressRange ObjCClassList) {
143   std::lock_guard<std::mutex> Lock(InitSeqsMutex);
144 
145   auto &InitSeq = InitSeqs[&JD];
146 
147   InitSeq.setObjCImageInfoAddr(ObjCImageInfoAddr);
148 
149   if (ModInits.StartAddress)
150     InitSeq.addModInitsSection(std::move(ModInits));
151 
152   if (ObjCSelRefs.StartAddress)
153     InitSeq.addObjCSelRefsSection(std::move(ObjCSelRefs));
154 
155   if (ObjCClassList.StartAddress)
156     InitSeq.addObjCClassListSection(std::move(ObjCClassList));
157 }
158 
159 static Expected<ExecutorAddressRange> getSectionExtent(jitlink::LinkGraph &G,
160                                                        StringRef SectionName) {
161   auto *Sec = G.findSectionByName(SectionName);
162   if (!Sec)
163     return ExecutorAddressRange();
164   jitlink::SectionRange R(*Sec);
165   if (R.getSize() % G.getPointerSize() != 0)
166     return make_error<StringError>(SectionName + " section size is not a "
167                                                  "multiple of the pointer size",
168                                    inconvertibleErrorCode());
169   return ExecutorAddressRange(ExecutorAddress(R.getStart()),
170                               ExecutorAddress(R.getEnd()));
171 }
172 
173 void MachOPlatform::InitScraperPlugin::modifyPassConfig(
174     MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
175     jitlink::PassConfiguration &Config) {
176 
177   if (!MR.getInitializerSymbol())
178     return;
179 
180   Config.PrePrunePasses.push_back([this, &MR](jitlink::LinkGraph &G) -> Error {
181     JITLinkSymbolSet InitSectionSyms;
182     preserveInitSectionIfPresent(InitSectionSyms, G, "__DATA,__mod_init_func");
183     preserveInitSectionIfPresent(InitSectionSyms, G, "__DATA,__objc_selrefs");
184     preserveInitSectionIfPresent(InitSectionSyms, G, "__DATA,__objc_classlist");
185 
186     if (!InitSectionSyms.empty()) {
187       std::lock_guard<std::mutex> Lock(InitScraperMutex);
188       InitSymbolDeps[&MR] = std::move(InitSectionSyms);
189     }
190 
191     if (auto Err = processObjCImageInfo(G, MR))
192       return Err;
193 
194     return Error::success();
195   });
196 
197   Config.PostFixupPasses.push_back([this, &JD = MR.getTargetJITDylib()](
198                                        jitlink::LinkGraph &G) -> Error {
199     ExecutorAddressRange ModInits, ObjCSelRefs, ObjCClassList;
200 
201     JITTargetAddress ObjCImageInfoAddr = 0;
202     if (auto *ObjCImageInfoSec =
203             G.findSectionByName("__DATA,__objc_image_info")) {
204       if (auto Addr = jitlink::SectionRange(*ObjCImageInfoSec).getStart())
205         ObjCImageInfoAddr = Addr;
206     }
207 
208     // Record __mod_init_func.
209     if (auto ModInitsOrErr = getSectionExtent(G, "__DATA,__mod_init_func"))
210       ModInits = std::move(*ModInitsOrErr);
211     else
212       return ModInitsOrErr.takeError();
213 
214     // Record __objc_selrefs.
215     if (auto ObjCSelRefsOrErr = getSectionExtent(G, "__DATA,__objc_selrefs"))
216       ObjCSelRefs = std::move(*ObjCSelRefsOrErr);
217     else
218       return ObjCSelRefsOrErr.takeError();
219 
220     // Record __objc_classlist.
221     if (auto ObjCClassListOrErr =
222             getSectionExtent(G, "__DATA,__objc_classlist"))
223       ObjCClassList = std::move(*ObjCClassListOrErr);
224     else
225       return ObjCClassListOrErr.takeError();
226 
227     // Dump the scraped inits.
228     LLVM_DEBUG({
229       dbgs() << "MachOPlatform: Scraped " << G.getName() << " init sections:\n";
230       dbgs() << "  __objc_selrefs: ";
231       auto NumObjCSelRefs = ObjCSelRefs.size().getValue() / sizeof(uintptr_t);
232       if (NumObjCSelRefs)
233         dbgs() << NumObjCSelRefs << " pointer(s) at "
234                << formatv("{0:x16}", ObjCSelRefs.StartAddress.getValue())
235                << "\n";
236       else
237         dbgs() << "none\n";
238 
239       dbgs() << "  __objc_classlist: ";
240       auto NumObjCClasses = ObjCClassList.size().getValue() / sizeof(uintptr_t);
241       if (NumObjCClasses)
242         dbgs() << NumObjCClasses << " pointer(s) at "
243                << formatv("{0:x16}", ObjCClassList.StartAddress.getValue())
244                << "\n";
245       else
246         dbgs() << "none\n";
247 
248       dbgs() << "  __mod_init_func: ";
249       auto NumModInits = ModInits.size().getValue() / sizeof(uintptr_t);
250       if (NumModInits)
251         dbgs() << NumModInits << " pointer(s) at "
252                << formatv("{0:x16}", ModInits.StartAddress.getValue()) << "\n";
253       else
254         dbgs() << "none\n";
255     });
256 
257     MP.registerInitInfo(JD, ObjCImageInfoAddr, std::move(ModInits),
258                         std::move(ObjCSelRefs), std::move(ObjCClassList));
259 
260     return Error::success();
261   });
262 }
263 
264 ObjectLinkingLayer::Plugin::SyntheticSymbolDependenciesMap
265 MachOPlatform::InitScraperPlugin::getSyntheticSymbolDependencies(
266     MaterializationResponsibility &MR) {
267   std::lock_guard<std::mutex> Lock(InitScraperMutex);
268   auto I = InitSymbolDeps.find(&MR);
269   if (I != InitSymbolDeps.end()) {
270     SyntheticSymbolDependenciesMap Result;
271     Result[MR.getInitializerSymbol()] = std::move(I->second);
272     InitSymbolDeps.erase(&MR);
273     return Result;
274   }
275   return SyntheticSymbolDependenciesMap();
276 }
277 
278 void MachOPlatform::InitScraperPlugin::preserveInitSectionIfPresent(
279     JITLinkSymbolSet &Symbols, jitlink::LinkGraph &G, StringRef SectionName) {
280   if (auto *Sec = G.findSectionByName(SectionName)) {
281     auto SecBlocks = Sec->blocks();
282     if (!llvm::empty(SecBlocks))
283       Symbols.insert(
284           &G.addAnonymousSymbol(**SecBlocks.begin(), 0, 0, false, true));
285   }
286 }
287 
288 Error MachOPlatform::InitScraperPlugin::processObjCImageInfo(
289     jitlink::LinkGraph &G, MaterializationResponsibility &MR) {
290 
291   // If there's an ObjC imagine info then either
292   //   (1) It's the first __objc_imageinfo we've seen in this JITDylib. In
293   //       this case we name and record it.
294   // OR
295   //   (2) We already have a recorded __objc_imageinfo for this JITDylib,
296   //       in which case we just verify it.
297   auto *ObjCImageInfo = G.findSectionByName("__objc_imageinfo");
298   if (!ObjCImageInfo)
299     return Error::success();
300 
301   auto ObjCImageInfoBlocks = ObjCImageInfo->blocks();
302 
303   // Check that the section is not empty if present.
304   if (llvm::empty(ObjCImageInfoBlocks))
305     return make_error<StringError>("Empty __objc_imageinfo section in " +
306                                        G.getName(),
307                                    inconvertibleErrorCode());
308 
309   // Check that there's only one block in the section.
310   if (std::next(ObjCImageInfoBlocks.begin()) != ObjCImageInfoBlocks.end())
311     return make_error<StringError>("Multiple blocks in __objc_imageinfo "
312                                    "section in " +
313                                        G.getName(),
314                                    inconvertibleErrorCode());
315 
316   // Check that the __objc_imageinfo section is unreferenced.
317   // FIXME: We could optimize this check if Symbols had a ref-count.
318   for (auto &Sec : G.sections()) {
319     if (&Sec != ObjCImageInfo)
320       for (auto *B : Sec.blocks())
321         for (auto &E : B->edges())
322           if (E.getTarget().isDefined() &&
323               &E.getTarget().getBlock().getSection() == ObjCImageInfo)
324             return make_error<StringError>("__objc_imageinfo is referenced "
325                                            "within file " +
326                                                G.getName(),
327                                            inconvertibleErrorCode());
328   }
329 
330   auto &ObjCImageInfoBlock = **ObjCImageInfoBlocks.begin();
331   auto *ObjCImageInfoData = ObjCImageInfoBlock.getContent().data();
332   auto Version = support::endian::read32(ObjCImageInfoData, G.getEndianness());
333   auto Flags =
334       support::endian::read32(ObjCImageInfoData + 4, G.getEndianness());
335 
336   // Lock the mutex while we verify / update the ObjCImageInfos map.
337   std::lock_guard<std::mutex> Lock(InitScraperMutex);
338 
339   auto ObjCImageInfoItr = ObjCImageInfos.find(&MR.getTargetJITDylib());
340   if (ObjCImageInfoItr != ObjCImageInfos.end()) {
341     // We've already registered an __objc_imageinfo section. Verify the
342     // content of this new section matches, then delete it.
343     if (ObjCImageInfoItr->second.first != Version)
344       return make_error<StringError>(
345           "ObjC version in " + G.getName() +
346               " does not match first registered version",
347           inconvertibleErrorCode());
348     if (ObjCImageInfoItr->second.second != Flags)
349       return make_error<StringError>("ObjC flags in " + G.getName() +
350                                          " do not match first registered flags",
351                                      inconvertibleErrorCode());
352 
353     // __objc_imageinfo is valid. Delete the block.
354     for (auto *S : ObjCImageInfo->symbols())
355       G.removeDefinedSymbol(*S);
356     G.removeBlock(ObjCImageInfoBlock);
357   } else {
358     // We haven't registered an __objc_imageinfo section yet. Register and
359     // move on. The section should already be marked no-dead-strip.
360     ObjCImageInfos[&MR.getTargetJITDylib()] = std::make_pair(Version, Flags);
361   }
362 
363   return Error::success();
364 }
365 
366 } // End namespace orc.
367 } // End namespace llvm.
368