EPCIndirectionUtils.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. //===------- EPCIndirectionUtils.cpp -- EPC based indirection APIs --------===//
  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. #include "llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h"
  9. #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
  10. #include "llvm/Support/MathExtras.h"
  11. #include <future>
  12. using namespace llvm;
  13. using namespace llvm::orc;
  14. namespace llvm {
  15. namespace orc {
  16. class EPCIndirectionUtilsAccess {
  17. public:
  18. using IndirectStubInfo = EPCIndirectionUtils::IndirectStubInfo;
  19. using IndirectStubInfoVector = EPCIndirectionUtils::IndirectStubInfoVector;
  20. static Expected<IndirectStubInfoVector>
  21. getIndirectStubs(EPCIndirectionUtils &EPCIU, unsigned NumStubs) {
  22. return EPCIU.getIndirectStubs(NumStubs);
  23. };
  24. };
  25. } // end namespace orc
  26. } // end namespace llvm
  27. namespace {
  28. class EPCTrampolinePool : public TrampolinePool {
  29. public:
  30. EPCTrampolinePool(EPCIndirectionUtils &EPCIU);
  31. Error deallocatePool();
  32. protected:
  33. Error grow() override;
  34. using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc;
  35. EPCIndirectionUtils &EPCIU;
  36. unsigned TrampolineSize = 0;
  37. unsigned TrampolinesPerPage = 0;
  38. std::vector<FinalizedAlloc> TrampolineBlocks;
  39. };
  40. class EPCIndirectStubsManager : public IndirectStubsManager,
  41. private EPCIndirectionUtilsAccess {
  42. public:
  43. EPCIndirectStubsManager(EPCIndirectionUtils &EPCIU) : EPCIU(EPCIU) {}
  44. Error deallocateStubs();
  45. Error createStub(StringRef StubName, JITTargetAddress StubAddr,
  46. JITSymbolFlags StubFlags) override;
  47. Error createStubs(const StubInitsMap &StubInits) override;
  48. JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override;
  49. JITEvaluatedSymbol findPointer(StringRef Name) override;
  50. Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override;
  51. private:
  52. using StubInfo = std::pair<IndirectStubInfo, JITSymbolFlags>;
  53. std::mutex ISMMutex;
  54. EPCIndirectionUtils &EPCIU;
  55. StringMap<StubInfo> StubInfos;
  56. };
  57. EPCTrampolinePool::EPCTrampolinePool(EPCIndirectionUtils &EPCIU)
  58. : EPCIU(EPCIU) {
  59. auto &EPC = EPCIU.getExecutorProcessControl();
  60. auto &ABI = EPCIU.getABISupport();
  61. TrampolineSize = ABI.getTrampolineSize();
  62. TrampolinesPerPage =
  63. (EPC.getPageSize() - ABI.getPointerSize()) / TrampolineSize;
  64. }
  65. Error EPCTrampolinePool::deallocatePool() {
  66. std::promise<MSVCPError> DeallocResultP;
  67. auto DeallocResultF = DeallocResultP.get_future();
  68. EPCIU.getExecutorProcessControl().getMemMgr().deallocate(
  69. std::move(TrampolineBlocks),
  70. [&](Error Err) { DeallocResultP.set_value(std::move(Err)); });
  71. return DeallocResultF.get();
  72. }
  73. Error EPCTrampolinePool::grow() {
  74. using namespace jitlink;
  75. assert(AvailableTrampolines.empty() &&
  76. "Grow called with trampolines still available");
  77. auto ResolverAddress = EPCIU.getResolverBlockAddress();
  78. assert(ResolverAddress && "Resolver address can not be null");
  79. auto &EPC = EPCIU.getExecutorProcessControl();
  80. auto PageSize = EPC.getPageSize();
  81. auto Alloc = SimpleSegmentAlloc::Create(
  82. EPC.getMemMgr(), nullptr,
  83. {{MemProt::Read | MemProt::Exec, {PageSize, Align(PageSize)}}});
  84. if (!Alloc)
  85. return Alloc.takeError();
  86. unsigned NumTrampolines = TrampolinesPerPage;
  87. auto SegInfo = Alloc->getSegInfo(MemProt::Read | MemProt::Exec);
  88. EPCIU.getABISupport().writeTrampolines(SegInfo.WorkingMem.data(),
  89. SegInfo.Addr.getValue(),
  90. ResolverAddress, NumTrampolines);
  91. for (unsigned I = 0; I < NumTrampolines; ++I)
  92. AvailableTrampolines.push_back(SegInfo.Addr.getValue() +
  93. (I * TrampolineSize));
  94. auto FA = Alloc->finalize();
  95. if (!FA)
  96. return FA.takeError();
  97. TrampolineBlocks.push_back(std::move(*FA));
  98. return Error::success();
  99. }
  100. Error EPCIndirectStubsManager::createStub(StringRef StubName,
  101. JITTargetAddress StubAddr,
  102. JITSymbolFlags StubFlags) {
  103. StubInitsMap SIM;
  104. SIM[StubName] = std::make_pair(StubAddr, StubFlags);
  105. return createStubs(SIM);
  106. }
  107. Error EPCIndirectStubsManager::createStubs(const StubInitsMap &StubInits) {
  108. auto AvailableStubInfos = getIndirectStubs(EPCIU, StubInits.size());
  109. if (!AvailableStubInfos)
  110. return AvailableStubInfos.takeError();
  111. {
  112. std::lock_guard<std::mutex> Lock(ISMMutex);
  113. unsigned ASIdx = 0;
  114. for (auto &SI : StubInits) {
  115. auto &A = (*AvailableStubInfos)[ASIdx++];
  116. StubInfos[SI.first()] = std::make_pair(A, SI.second.second);
  117. }
  118. }
  119. auto &MemAccess = EPCIU.getExecutorProcessControl().getMemoryAccess();
  120. switch (EPCIU.getABISupport().getPointerSize()) {
  121. case 4: {
  122. unsigned ASIdx = 0;
  123. std::vector<tpctypes::UInt32Write> PtrUpdates;
  124. for (auto &SI : StubInits)
  125. PtrUpdates.push_back(
  126. {ExecutorAddr((*AvailableStubInfos)[ASIdx++].PointerAddress),
  127. static_cast<uint32_t>(SI.second.first)});
  128. return MemAccess.writeUInt32s(PtrUpdates);
  129. }
  130. case 8: {
  131. unsigned ASIdx = 0;
  132. std::vector<tpctypes::UInt64Write> PtrUpdates;
  133. for (auto &SI : StubInits)
  134. PtrUpdates.push_back(
  135. {ExecutorAddr((*AvailableStubInfos)[ASIdx++].PointerAddress),
  136. static_cast<uint64_t>(SI.second.first)});
  137. return MemAccess.writeUInt64s(PtrUpdates);
  138. }
  139. default:
  140. return make_error<StringError>("Unsupported pointer size",
  141. inconvertibleErrorCode());
  142. }
  143. }
  144. JITEvaluatedSymbol EPCIndirectStubsManager::findStub(StringRef Name,
  145. bool ExportedStubsOnly) {
  146. std::lock_guard<std::mutex> Lock(ISMMutex);
  147. auto I = StubInfos.find(Name);
  148. if (I == StubInfos.end())
  149. return nullptr;
  150. return {I->second.first.StubAddress, I->second.second};
  151. }
  152. JITEvaluatedSymbol EPCIndirectStubsManager::findPointer(StringRef Name) {
  153. std::lock_guard<std::mutex> Lock(ISMMutex);
  154. auto I = StubInfos.find(Name);
  155. if (I == StubInfos.end())
  156. return nullptr;
  157. return {I->second.first.PointerAddress, I->second.second};
  158. }
  159. Error EPCIndirectStubsManager::updatePointer(StringRef Name,
  160. JITTargetAddress NewAddr) {
  161. JITTargetAddress PtrAddr = 0;
  162. {
  163. std::lock_guard<std::mutex> Lock(ISMMutex);
  164. auto I = StubInfos.find(Name);
  165. if (I == StubInfos.end())
  166. return make_error<StringError>("Unknown stub name",
  167. inconvertibleErrorCode());
  168. PtrAddr = I->second.first.PointerAddress;
  169. }
  170. auto &MemAccess = EPCIU.getExecutorProcessControl().getMemoryAccess();
  171. switch (EPCIU.getABISupport().getPointerSize()) {
  172. case 4: {
  173. tpctypes::UInt32Write PUpdate(ExecutorAddr(PtrAddr), NewAddr);
  174. return MemAccess.writeUInt32s(PUpdate);
  175. }
  176. case 8: {
  177. tpctypes::UInt64Write PUpdate(ExecutorAddr(PtrAddr), NewAddr);
  178. return MemAccess.writeUInt64s(PUpdate);
  179. }
  180. default:
  181. return make_error<StringError>("Unsupported pointer size",
  182. inconvertibleErrorCode());
  183. }
  184. }
  185. } // end anonymous namespace.
  186. namespace llvm {
  187. namespace orc {
  188. EPCIndirectionUtils::ABISupport::~ABISupport() = default;
  189. Expected<std::unique_ptr<EPCIndirectionUtils>>
  190. EPCIndirectionUtils::Create(ExecutorProcessControl &EPC) {
  191. const auto &TT = EPC.getTargetTriple();
  192. switch (TT.getArch()) {
  193. default:
  194. return make_error<StringError>(
  195. std::string("No EPCIndirectionUtils available for ") + TT.str(),
  196. inconvertibleErrorCode());
  197. case Triple::aarch64:
  198. case Triple::aarch64_32:
  199. return CreateWithABI<OrcAArch64>(EPC);
  200. case Triple::x86:
  201. return CreateWithABI<OrcI386>(EPC);
  202. case Triple::loongarch64:
  203. return CreateWithABI<OrcLoongArch64>(EPC);
  204. case Triple::mips:
  205. return CreateWithABI<OrcMips32Be>(EPC);
  206. case Triple::mipsel:
  207. return CreateWithABI<OrcMips32Le>(EPC);
  208. case Triple::mips64:
  209. case Triple::mips64el:
  210. return CreateWithABI<OrcMips64>(EPC);
  211. case Triple::riscv64:
  212. return CreateWithABI<OrcRiscv64>(EPC);
  213. case Triple::x86_64:
  214. if (TT.getOS() == Triple::OSType::Win32)
  215. return CreateWithABI<OrcX86_64_Win32>(EPC);
  216. else
  217. return CreateWithABI<OrcX86_64_SysV>(EPC);
  218. }
  219. }
  220. Error EPCIndirectionUtils::cleanup() {
  221. auto &MemMgr = EPC.getMemMgr();
  222. auto Err = MemMgr.deallocate(std::move(IndirectStubAllocs));
  223. if (TP)
  224. Err = joinErrors(std::move(Err),
  225. static_cast<EPCTrampolinePool &>(*TP).deallocatePool());
  226. if (ResolverBlock)
  227. Err =
  228. joinErrors(std::move(Err), MemMgr.deallocate(std::move(ResolverBlock)));
  229. return Err;
  230. }
  231. Expected<JITTargetAddress>
  232. EPCIndirectionUtils::writeResolverBlock(JITTargetAddress ReentryFnAddr,
  233. JITTargetAddress ReentryCtxAddr) {
  234. using namespace jitlink;
  235. assert(ABI && "ABI can not be null");
  236. auto ResolverSize = ABI->getResolverCodeSize();
  237. auto Alloc =
  238. SimpleSegmentAlloc::Create(EPC.getMemMgr(), nullptr,
  239. {{MemProt::Read | MemProt::Exec,
  240. {ResolverSize, Align(EPC.getPageSize())}}});
  241. if (!Alloc)
  242. return Alloc.takeError();
  243. auto SegInfo = Alloc->getSegInfo(MemProt::Read | MemProt::Exec);
  244. ResolverBlockAddr = SegInfo.Addr.getValue();
  245. ABI->writeResolverCode(SegInfo.WorkingMem.data(), ResolverBlockAddr,
  246. ReentryFnAddr, ReentryCtxAddr);
  247. auto FA = Alloc->finalize();
  248. if (!FA)
  249. return FA.takeError();
  250. ResolverBlock = std::move(*FA);
  251. return ResolverBlockAddr;
  252. }
  253. std::unique_ptr<IndirectStubsManager>
  254. EPCIndirectionUtils::createIndirectStubsManager() {
  255. return std::make_unique<EPCIndirectStubsManager>(*this);
  256. }
  257. TrampolinePool &EPCIndirectionUtils::getTrampolinePool() {
  258. if (!TP)
  259. TP = std::make_unique<EPCTrampolinePool>(*this);
  260. return *TP;
  261. }
  262. LazyCallThroughManager &EPCIndirectionUtils::createLazyCallThroughManager(
  263. ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) {
  264. assert(!LCTM &&
  265. "createLazyCallThroughManager can not have been called before");
  266. LCTM = std::make_unique<LazyCallThroughManager>(ES, ErrorHandlerAddr,
  267. &getTrampolinePool());
  268. return *LCTM;
  269. }
  270. EPCIndirectionUtils::EPCIndirectionUtils(ExecutorProcessControl &EPC,
  271. std::unique_ptr<ABISupport> ABI)
  272. : EPC(EPC), ABI(std::move(ABI)) {
  273. assert(this->ABI && "ABI can not be null");
  274. assert(EPC.getPageSize() > getABISupport().getStubSize() &&
  275. "Stubs larger than one page are not supported");
  276. }
  277. Expected<EPCIndirectionUtils::IndirectStubInfoVector>
  278. EPCIndirectionUtils::getIndirectStubs(unsigned NumStubs) {
  279. using namespace jitlink;
  280. std::lock_guard<std::mutex> Lock(EPCUIMutex);
  281. // If there aren't enough stubs available then allocate some more.
  282. if (NumStubs > AvailableIndirectStubs.size()) {
  283. auto NumStubsToAllocate = NumStubs;
  284. auto PageSize = EPC.getPageSize();
  285. auto StubBytes = alignTo(NumStubsToAllocate * ABI->getStubSize(), PageSize);
  286. NumStubsToAllocate = StubBytes / ABI->getStubSize();
  287. auto PtrBytes =
  288. alignTo(NumStubsToAllocate * ABI->getPointerSize(), PageSize);
  289. auto StubProt = MemProt::Read | MemProt::Exec;
  290. auto PtrProt = MemProt::Read | MemProt::Write;
  291. auto Alloc = SimpleSegmentAlloc::Create(
  292. EPC.getMemMgr(), nullptr,
  293. {{StubProt, {static_cast<size_t>(StubBytes), Align(PageSize)}},
  294. {PtrProt, {static_cast<size_t>(PtrBytes), Align(PageSize)}}});
  295. if (!Alloc)
  296. return Alloc.takeError();
  297. auto StubSeg = Alloc->getSegInfo(StubProt);
  298. auto PtrSeg = Alloc->getSegInfo(PtrProt);
  299. ABI->writeIndirectStubsBlock(StubSeg.WorkingMem.data(),
  300. StubSeg.Addr.getValue(),
  301. PtrSeg.Addr.getValue(), NumStubsToAllocate);
  302. auto FA = Alloc->finalize();
  303. if (!FA)
  304. return FA.takeError();
  305. IndirectStubAllocs.push_back(std::move(*FA));
  306. auto StubExecutorAddr = StubSeg.Addr;
  307. auto PtrExecutorAddr = PtrSeg.Addr;
  308. for (unsigned I = 0; I != NumStubsToAllocate; ++I) {
  309. AvailableIndirectStubs.push_back(IndirectStubInfo(
  310. StubExecutorAddr.getValue(), PtrExecutorAddr.getValue()));
  311. StubExecutorAddr += ABI->getStubSize();
  312. PtrExecutorAddr += ABI->getPointerSize();
  313. }
  314. }
  315. assert(NumStubs <= AvailableIndirectStubs.size() &&
  316. "Sufficient stubs should have been allocated above");
  317. IndirectStubInfoVector Result;
  318. while (NumStubs--) {
  319. Result.push_back(AvailableIndirectStubs.back());
  320. AvailableIndirectStubs.pop_back();
  321. }
  322. return std::move(Result);
  323. }
  324. static JITTargetAddress reentry(JITTargetAddress LCTMAddr,
  325. JITTargetAddress TrampolineAddr) {
  326. auto &LCTM = *jitTargetAddressToPointer<LazyCallThroughManager *>(LCTMAddr);
  327. std::promise<JITTargetAddress> LandingAddrP;
  328. auto LandingAddrF = LandingAddrP.get_future();
  329. LCTM.resolveTrampolineLandingAddress(
  330. TrampolineAddr,
  331. [&](JITTargetAddress Addr) { LandingAddrP.set_value(Addr); });
  332. return LandingAddrF.get();
  333. }
  334. Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU) {
  335. auto &LCTM = EPCIU.getLazyCallThroughManager();
  336. return EPCIU
  337. .writeResolverBlock(pointerToJITTargetAddress(&reentry),
  338. pointerToJITTargetAddress(&LCTM))
  339. .takeError();
  340. }
  341. } // end namespace orc
  342. } // end namespace llvm