Introduces COFFVCRuntimeBootstrapper that loads/initialize vc runtime libraries. In COFF, we *must* jit-link vc runtime libraries as COFF relocation types have no proper way to deal with out-of-reach data symbols ragardless of linking mode. (even dynamic version msvcrt.lib have tons of static data symbols that must be jit-linked) This class tries to load vc runtime library files from msvc installations with an option to override the path.
There are some complications when dealing with static version of vc runtimes. First, they need static initializers to be ran that requires COFFPlatform support but orc runtime will not be usable before vc runtimes are fully initialized. (as orc runtime will use msvc stl libraries) COFFPlatform that will be introduced in a following up patch will collect static initializers and run them manually in host before boostrapping itself. So, the user will have to do the following.
1. Create COFFPlatform that addes static initializer collecting passes.
2. LoadVCRuntime
3. InitializeVCRuntime
4. COFFPlatform.bootstrap()
Second, the internal crt initialization function had to be reimplemented in orc side. There are other ways of doing this, but this is the simplest implementation that makes platform fully responsible for static initializer. The complication comes from the fact that crt initialization functions (such as acrt_initialize or dllmain_crt_process_attach) actually run all static initializers by traversing from `__xi_a` symbol to `__xi_z`. This requires symbols to be contiguously allocated in sections alphabetically sorted in memory, which is not possible right now and not practical in jit setting. We might ignore emission of `__xi_a` and `__xi_z` symbol and allocate them ourselves, but we have to take extra care after orc runtime boostrap has been done -- as that point orc runtime should be the one running the static initializers.
Reviewed By: lhames
Differential Revision: https://reviews.llvm.org/D130456
--- /dev/null
+//===----- COFFVCRuntimeSupport.h -- VC runtime support in ORC --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Utilities for loading and initializaing vc runtime in Orc.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_COFFCRUNTIMESUPPORT_H
+#define LLVM_EXECUTIONENGINE_ORC_COFFCRUNTIMESUPPORT_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
+
+#include <future>
+#include <memory>
+#include <thread>
+#include <vector>
+
+namespace llvm {
+namespace orc {
+
+/// Bootstraps the vc runtime within jitdylibs.
+class COFFVCRuntimeBootstrapper {
+public:
+ /// Try to create a COFFVCRuntimeBootstrapper instance. An optional
+ /// RuntimePath can be given to specify the location of directory that
+ /// contains all vc runtime library files such as ucrt.lib and msvcrt.lib. If
+ /// not path was given, it will try to search the MSVC toolchain and Windows
+ /// SDK installation and use the found library files automatically.
+ ///
+ /// Note that depending on the build setting, a different library
+ /// file must be used. In general, if vc runtime was statically linked to the
+ /// object file that is to be jit-linked, LoadStaticVCRuntime and
+ /// InitializeStaticVCRuntime must be used with libcmt.lib, libucrt.lib,
+ /// libvcruntimelib. If vc runtime was dynamically linked LoadDynamicVCRuntime
+ /// must be used along with msvcrt.lib, ucrt.lib, vcruntime.lib.
+ ///
+ /// More information is on:
+ /// https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
+ static Expected<std::unique_ptr<COFFVCRuntimeBootstrapper>>
+ Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
+ const char *RuntimePath = nullptr);
+
+ /// Adds symbol definitions of static version of msvc runtime libraries.
+ Expected<std::vector<std::string>>
+ loadStaticVCRuntime(JITDylib &JD, bool DebugVersion = false);
+
+ /// Runs the initializer of static version of msvc runtime libraries.
+ /// This must be called before calling any functions requiring c runtime (e.g.
+ /// printf) within the jit session. Note that proper initialization of vc
+ /// runtime requires ability of running static initializers. Cosider setting
+ /// up COFFPlatform.
+ Error initializeStaticVCRuntime(JITDylib &JD);
+
+ /// Adds symbol definitions of dynamic versino of msvc runtie libraries.
+ Expected<std::vector<std::string>>
+ loadDynamicVCRuntime(JITDylib &JD, bool DebugVersion = false);
+
+private:
+ COFFVCRuntimeBootstrapper(ExecutionSession &ES,
+ ObjectLinkingLayer &ObjLinkingLayer,
+ const char *RuntimePath);
+
+ ExecutionSession &ES;
+ ObjectLinkingLayer &ObjLinkingLayer;
+ std::string RuntimePath;
+
+ struct MSVCToolchainPath {
+ SmallString<256> VCToolchainLib;
+ SmallString<256> UCRTSdkLib;
+ };
+
+ static Expected<MSVCToolchainPath> getMSVCToolchainPath();
+ Error loadVCRuntime(JITDylib &JD, std::vector<std::string> &ImportedLibraries,
+ ArrayRef<StringRef> VCLibs, ArrayRef<StringRef> UCRTLibs);
+};
+
+} // namespace orc
+} // namespace llvm
+
+#endif
virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) = 0;
+ // TODO: move this to ORC runtime.
+ /// Run function with a int (*)(void) signature.
+ virtual Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) = 0;
+
+ // TODO: move this to ORC runtime.
+ /// Run function with a int (*)(int) signature.
+ virtual Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr,
+ int Arg) = 0;
+
/// Run a wrapper function in the executor. The given WFRHandler will be
/// called on the result when it is returned.
///
llvm_unreachable("Unsupported");
}
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override {
+ llvm_unreachable("Unsupported");
+ }
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override {
+ llvm_unreachable("Unsupported");
+ }
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override {
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override;
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override;
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override;
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override;
extern const char *DeregisterEHFrameSectionWrapperName;
extern const char *RunAsMainWrapperName;
+extern const char *RunAsVoidFunctionWrapperName;
+extern const char *RunAsIntFunctionWrapperName;
using SPSSimpleExecutorDylibManagerOpenSignature =
shared::SPSExpected<uint64_t>(shared::SPSExecutorAddr, shared::SPSString,
using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddr,
shared::SPSSequence<shared::SPSString>);
-
+using SPSRunAsVoidFunctionSignature = int32_t(shared::SPSExecutorAddr);
+using SPSRunAsIntFunctionSignature = int32_t(shared::SPSExecutorAddr, int32_t);
} // end namespace rt
} // end namespace orc
} // end namespace llvm
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override;
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override;
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override;
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override;
std::unique_ptr<EPCGenericDylibManager> DylibMgr;
ExecutorAddr RunAsMainAddr;
+ ExecutorAddr RunAsVoidFunctionAddr;
+ ExecutorAddr RunAsIntFunctionAddr;
uint64_t NextSeqNo = 0;
PendingCallWrapperResultsMap PendingCallWrapperResults;
int runAsMain(int (*Main)(int, char *[]), ArrayRef<std::string> Args,
Optional<StringRef> ProgramName = None);
+int runAsVoidFunction(int (*Func)(void));
+int runAsIntFunction(int (*Func)(int), int Arg);
+
} // end namespace orc
} // end namespace llvm
endif()
add_llvm_component_library(LLVMOrcJIT
+ COFFVCRuntimeSupport.cpp
CompileOnDemandLayer.cpp
CompileUtils.cpp
Core.cpp
Object
OrcShared
OrcTargetProcess
+ WindowsDriver
MC
MCDisassembler
Passes
--- /dev/null
+//===------- COFFVCRuntimeSupport.cpp - VC runtime support in ORC ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h"
+
+#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/WindowsDriver/MSVCPaths.h"
+
+#define DEBUG_TYPE "orc"
+
+using namespace llvm;
+using namespace llvm::orc;
+using namespace llvm::orc::shared;
+
+Expected<std::unique_ptr<COFFVCRuntimeBootstrapper>>
+COFFVCRuntimeBootstrapper::Create(ExecutionSession &ES,
+ ObjectLinkingLayer &ObjLinkingLayer,
+ const char *RuntimePath) {
+ return std::unique_ptr<COFFVCRuntimeBootstrapper>(
+ new COFFVCRuntimeBootstrapper(ES, ObjLinkingLayer, RuntimePath));
+}
+
+COFFVCRuntimeBootstrapper::COFFVCRuntimeBootstrapper(
+ ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
+ const char *RuntimePath)
+ : ES(ES), ObjLinkingLayer(ObjLinkingLayer) {
+ if (RuntimePath)
+ this->RuntimePath = RuntimePath;
+}
+
+Expected<std::vector<std::string>>
+COFFVCRuntimeBootstrapper::loadStaticVCRuntime(JITDylib &JD,
+ bool DebugVersion) {
+ StringRef VCLibs[] = {"libvcruntime.lib", "libcmt.lib", "libcpmt.lib"};
+ StringRef UCRTLibs[] = {"libucrt.lib"};
+ std::vector<std::string> ImportedLibraries;
+ if (auto Err = loadVCRuntime(JD, ImportedLibraries, makeArrayRef(VCLibs),
+ makeArrayRef(UCRTLibs)))
+ return Err;
+ return ImportedLibraries;
+}
+
+Expected<std::vector<std::string>>
+COFFVCRuntimeBootstrapper::loadDynamicVCRuntime(JITDylib &JD,
+ bool DebugVersion) {
+ StringRef VCLibs[] = {"vcruntime.lib", "msvcrt.lib", "msvcprt.lib"};
+ StringRef UCRTLibs[] = {"ucrt.lib"};
+ std::vector<std::string> ImportedLibraries;
+ if (auto Err = loadVCRuntime(JD, ImportedLibraries, makeArrayRef(VCLibs),
+ makeArrayRef(UCRTLibs)))
+ return Err;
+ return ImportedLibraries;
+}
+
+Error COFFVCRuntimeBootstrapper::loadVCRuntime(
+ JITDylib &JD, std::vector<std::string> &ImportedLibraries,
+ ArrayRef<StringRef> VCLibs, ArrayRef<StringRef> UCRTLibs) {
+ MSVCToolchainPath Path;
+ if (!RuntimePath.empty()) {
+ Path.UCRTSdkLib = RuntimePath;
+ Path.VCToolchainLib = RuntimePath;
+ } else {
+ auto ToolchainPath = getMSVCToolchainPath();
+ if (!ToolchainPath)
+ return ToolchainPath.takeError();
+ Path = *ToolchainPath;
+ }
+ LLVM_DEBUG({
+ dbgs() << "Using VC toolchain pathes\n";
+ dbgs() << " VC toolchain path: " << Path.VCToolchainLib << "\n";
+ dbgs() << " UCRT path: " << Path.UCRTSdkLib << "\n";
+ });
+
+ auto LoadLibrary = [&](SmallString<256> LibPath, StringRef LibName) -> Error {
+ sys::path::append(LibPath, LibName);
+
+ auto G = StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer,
+ LibPath.c_str());
+ if (!G)
+ return G.takeError();
+
+ for (auto &Lib : (*G)->getImportedDynamicLibraries())
+ ImportedLibraries.push_back(Lib);
+
+ JD.addGenerator(std::move(*G));
+
+ return Error::success();
+ };
+ for (auto &Lib : UCRTLibs)
+ if (auto Err = LoadLibrary(Path.UCRTSdkLib, Lib))
+ return Err;
+
+ for (auto &Lib : VCLibs)
+ if (auto Err = LoadLibrary(Path.VCToolchainLib, Lib))
+ return Err;
+ ImportedLibraries.push_back("ntdll.dll");
+ ImportedLibraries.push_back("Kernel32.dll");
+
+ return Error::success();
+}
+
+Error COFFVCRuntimeBootstrapper::initializeStaticVCRuntime(JITDylib &JD) {
+ ExecutorAddr jit_scrt_initialize, jit_scrt_dllmain_before_initialize_c,
+ jit_scrt_initialize_type_info,
+ jit_scrt_initialize_default_local_stdio_options;
+ if (auto Err = lookupAndRecordAddrs(
+ ES, LookupKind::Static, makeJITDylibSearchOrder(&JD),
+ {{ES.intern("__scrt_initialize_crt"), &jit_scrt_initialize},
+ {ES.intern("__scrt_dllmain_before_initialize_c"),
+ &jit_scrt_dllmain_before_initialize_c},
+ {ES.intern("?__scrt_initialize_type_info@@YAXXZ"),
+ &jit_scrt_initialize_type_info},
+ {ES.intern("__scrt_initialize_default_local_stdio_options"),
+ &jit_scrt_initialize_default_local_stdio_options}}))
+ return Err;
+
+ auto RunVoidInitFunc = [&](ExecutorAddr Addr) -> Error {
+ if (auto Res = ES.getExecutorProcessControl().runAsVoidFunction(Addr))
+ return Error::success();
+ else
+ return Res.takeError();
+ };
+
+ auto R =
+ ES.getExecutorProcessControl().runAsIntFunction(jit_scrt_initialize, 0);
+ if (!R)
+ return R.takeError();
+
+ if (auto Err = RunVoidInitFunc(jit_scrt_dllmain_before_initialize_c))
+ return Err;
+
+ if (auto Err = RunVoidInitFunc(jit_scrt_initialize_type_info))
+ return Err;
+
+ if (auto Err =
+ RunVoidInitFunc(jit_scrt_initialize_default_local_stdio_options))
+ return Err;
+
+ SymbolAliasMap Alias;
+ Alias[ES.intern("__run_after_c_init")] = {
+ ES.intern("__scrt_dllmain_after_initialize_c"), JITSymbolFlags::Exported};
+ if (auto Err = JD.define(symbolAliases(Alias)))
+ return Err;
+
+ return Error::success();
+}
+
+Expected<COFFVCRuntimeBootstrapper::MSVCToolchainPath>
+COFFVCRuntimeBootstrapper::getMSVCToolchainPath() {
+ std::string VCToolChainPath;
+ ToolsetLayout VSLayout;
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS = vfs::getRealFileSystem();
+ if (!findVCToolChainViaCommandLine(*VFS, None, None, None, VCToolChainPath,
+ VSLayout) &&
+ !findVCToolChainViaEnvironment(*VFS, VCToolChainPath, VSLayout) &&
+ !findVCToolChainViaSetupConfig(*VFS, VCToolChainPath, VSLayout) &&
+ !findVCToolChainViaRegistry(VCToolChainPath, VSLayout))
+ return make_error<StringError>("Couldn't find msvc toolchain.",
+ inconvertibleErrorCode());
+
+ std::string UniversalCRTSdkPath;
+ std::string UCRTVersion;
+ if (!getUniversalCRTSdkDir(*VFS, None, None, None, UniversalCRTSdkPath,
+ UCRTVersion))
+ return make_error<StringError>("Couldn't find universal sdk.",
+ inconvertibleErrorCode());
+
+ MSVCToolchainPath ToolchainPath;
+ SmallString<256> VCToolchainLib(VCToolChainPath);
+ sys::path::append(VCToolchainLib, "lib", "x64");
+ ToolchainPath.VCToolchainLib = VCToolchainLib;
+
+ SmallString<256> UCRTSdkLib(UniversalCRTSdkPath);
+ sys::path::append(UCRTSdkLib, "Lib", UCRTVersion, "ucrt", "x64");
+ ToolchainPath.UCRTSdkLib = UCRTSdkLib;
+ return ToolchainPath;
+}
return orc::runAsMain(MainFnAddr.toPtr<MainTy>(), Args);
}
+Expected<int32_t>
+SelfExecutorProcessControl::runAsVoidFunction(ExecutorAddr VoidFnAddr) {
+ using VoidTy = int (*)();
+ return orc::runAsVoidFunction(VoidFnAddr.toPtr<VoidTy>());
+}
+
+Expected<int32_t>
+SelfExecutorProcessControl::runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) {
+ using IntTy = int (*)(int);
+ return orc::runAsIntFunction(IntFnAddr.toPtr<IntTy>(), Arg);
+}
+
void SelfExecutorProcessControl::callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler SendResult,
ArrayRef<char> ArgBuffer) {
"__llvm_orc_bootstrap_deregister_ehframe_section_wrapper";
const char *RunAsMainWrapperName = "__llvm_orc_bootstrap_run_as_main_wrapper";
+const char *RunAsVoidFunctionWrapperName =
+ "__llvm_orc_bootstrap_run_as_void_function_wrapper";
+const char *RunAsIntFunctionWrapperName =
+ "__llvm_orc_bootstrap_run_as_int_function_wrapper";
} // end namespace rt
} // end namespace orc
return Result;
}
+Expected<int32_t> SimpleRemoteEPC::runAsVoidFunction(ExecutorAddr VoidFnAddr) {
+ int32_t Result = 0;
+ if (auto Err = callSPSWrapper<rt::SPSRunAsVoidFunctionSignature>(
+ RunAsVoidFunctionAddr, Result, ExecutorAddr(VoidFnAddr)))
+ return std::move(Err);
+ return Result;
+}
+
+Expected<int32_t> SimpleRemoteEPC::runAsIntFunction(ExecutorAddr IntFnAddr,
+ int Arg) {
+ int32_t Result = 0;
+ if (auto Err = callSPSWrapper<rt::SPSRunAsIntFunctionSignature>(
+ RunAsIntFunctionAddr, Result, ExecutorAddr(IntFnAddr), Arg))
+ return std::move(Err);
+ return Result;
+}
+
void SimpleRemoteEPC::callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) {
if (auto Err = getBootstrapSymbols(
{{JDI.JITDispatchContext, ExecutorSessionObjectName},
{JDI.JITDispatchFunction, DispatchFnName},
- {RunAsMainAddr, rt::RunAsMainWrapperName}}))
+ {RunAsMainAddr, rt::RunAsMainWrapperName},
+ {RunAsVoidFunctionAddr, rt::RunAsVoidFunctionWrapperName},
+ {RunAsIntFunctionAddr, rt::RunAsIntFunctionWrapperName}}))
return Err;
if (auto DM =
.release();
}
+static llvm::orc::shared::CWrapperFunctionResult
+runAsVoidFunctionWrapper(const char *ArgData, size_t ArgSize) {
+ return WrapperFunction<rt::SPSRunAsVoidFunctionSignature>::handle(
+ ArgData, ArgSize,
+ [](ExecutorAddr MainAddr) -> int32_t {
+ return runAsVoidFunction(MainAddr.toPtr<int32_t (*)(void)>());
+ })
+ .release();
+}
+
+static llvm::orc::shared::CWrapperFunctionResult
+runAsIntFunctionWrapper(const char *ArgData, size_t ArgSize) {
+ return WrapperFunction<rt::SPSRunAsIntFunctionSignature>::handle(
+ ArgData, ArgSize,
+ [](ExecutorAddr MainAddr, int32_t Arg) -> int32_t {
+ return runAsIntFunction(MainAddr.toPtr<int32_t (*)(int32_t)>(),
+ Arg);
+ })
+ .release();
+}
+
void addTo(StringMap<ExecutorAddr> &M) {
M[rt::MemoryWriteUInt8sWrapperName] = ExecutorAddr::fromPtr(
&writeUIntsWrapper<tpctypes::UInt8Write,
M[rt::DeregisterEHFrameSectionWrapperName] =
ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionWrapper);
M[rt::RunAsMainWrapperName] = ExecutorAddr::fromPtr(&runAsMainWrapper);
+ M[rt::RunAsVoidFunctionWrapperName] =
+ ExecutorAddr::fromPtr(&runAsVoidFunctionWrapper);
+ M[rt::RunAsIntFunctionWrapperName] =
+ ExecutorAddr::fromPtr(&runAsIntFunctionWrapper);
}
} // end namespace rt_bootstrap
return Main(Args.size() + !!ProgramName, ArgV.data());
}
+int runAsVoidFunction(int (*Func)(void)) { return Func(); }
+
+int runAsIntFunction(int (*Func)(int), int Arg) { return Func(Arg); }
+
} // End namespace orc.
} // End namespace llvm.