[ORC][ORC_RT][COFF] Support dynamic VC runtime.

Supports dynamic VC runtime. It implements atexits handling which is required to load msvcrt.lib successfully. (the object file containing atexit symbol somehow resolves to static vc runtim symbols) It also default to dynamic vc runtime which tends to be more robust.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D132525
This commit is contained in:
sunho 2022-09-10 15:25:49 +09:00
parent bc24e6ab7c
commit 73c4033987
16 changed files with 235 additions and 64 deletions

View File

@ -13,6 +13,7 @@ set(ORC_COMMON_SOURCES
set(ALL_ORC_SOURCES
${ORC_COMMON_SOURCES}
coff_platform.cpp
coff_platform.per_jd.cpp
elfnix_platform.cpp
macho_ehframe_registration.cpp
macho_platform.cpp
@ -128,7 +129,12 @@ else() # not Apple
set(ORC_SOURCES
${ORC_COMMON_SOURCES}
coff_platform.cpp
coff_platform.per_jd.cpp
)
if (MSVC)
set(ORC_CFLAGS "${ORC_CFLAGS} /MD")
endif()
else()
set(ORC_BUILD_TYPE STATIC)

View File

@ -81,6 +81,7 @@ private:
size_t LinkedAgainstRefCount = 0;
size_t DlRefCount = 0;
std::vector<JITDylibState *> Deps;
std::vector<void (*)(void)> AtExits;
XtorSection CInitSection; // XIA~XIZ
XtorSection CXXInitSection; // XCA~XCZ
XtorSection CPreTermSection; // XPA~XPZ
@ -94,6 +95,7 @@ private:
public:
static void initialize();
static COFFPlatformRuntimeState &get();
static bool isInitialized() { return CPS; }
static void destroy();
COFFPlatformRuntimeState() = default;
@ -113,6 +115,8 @@ public:
Error registerJITDylib(std::string Name, void *Header);
Error deregisterJITDylib(void *Header);
Error registerAtExit(ExecutorAddr HeaderAddr, void (*AtExit)(void));
Error registerObjectSections(
ExecutorAddr HeaderAddr,
std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs,
@ -410,7 +414,10 @@ Error COFFPlatformRuntimeState::dlcloseDeinitialize(JITDylibState &JDS) {
JDS.Name.c_str());
});
// FIXME: call atexits.
// Run atexits
for (auto AtExit : JDS.AtExits)
AtExit();
JDS.AtExits.clear();
// Run static terminators.
JDS.CPreTermSection.RunAllNewAndFlush();
@ -454,8 +461,7 @@ Error COFFPlatformRuntimeState::registerObjectSections(
auto I = JDStates.find(HeaderAddr.toPtr<void *>());
if (I == JDStates.end()) {
std::ostringstream ErrStream;
ErrStream << "Attempted to register unrecognized header "
<< HeaderAddr.getValue();
ErrStream << "Unrecognized header " << HeaderAddr.getValue();
return make_error<StringError>(ErrStream.str());
}
auto &JDState = I->second;
@ -499,7 +505,7 @@ Error COFFPlatformRuntimeState::deregisterObjectSections(
auto I = JDStates.find(HeaderAddr.toPtr<void *>());
if (I == JDStates.end()) {
std::ostringstream ErrStream;
ErrStream << "Attempted to register unrecognized header "
ErrStream << "Attempted to deregister unrecognized header "
<< HeaderAddr.getValue();
return make_error<StringError>(ErrStream.str());
}
@ -549,6 +555,19 @@ Error COFFPlatformRuntimeState::deregisterBlockRange(ExecutorAddr HeaderAddr,
return Error::success();
}
Error COFFPlatformRuntimeState::registerAtExit(ExecutorAddr HeaderAddr,
void (*AtExit)(void)) {
std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex);
auto I = JDStates.find(HeaderAddr.toPtr<void *>());
if (I == JDStates.end()) {
std::ostringstream ErrStream;
ErrStream << "Unrecognized header " << HeaderAddr.getValue();
return make_error<StringError>(ErrStream.str());
}
I->second.AtExits.push_back(AtExit);
return Error::success();
}
void COFFPlatformRuntimeState::initialize() {
assert(!CPS && "COFFPlatformRuntimeState should be null");
CPS = new COFFPlatformRuntimeState();
@ -691,6 +710,32 @@ ORC_RT_INTERFACE void __stdcall __orc_rt_coff_cxx_throw_exception(
_countof(parameters), parameters);
}
//------------------------------------------------------------------------------
// COFF atexits
//------------------------------------------------------------------------------
typedef int (*OnExitFunction)(void);
typedef void (*AtExitFunction)(void);
ORC_RT_INTERFACE OnExitFunction __orc_rt_coff_onexit(void *Header,
OnExitFunction Func) {
if (auto Err = COFFPlatformRuntimeState::get().registerAtExit(
ExecutorAddr::fromPtr(Header), (void (*)(void))Func)) {
consumeError(std::move(Err));
return nullptr;
}
return Func;
}
ORC_RT_INTERFACE int __orc_rt_coff_atexit(void *Header, AtExitFunction Func) {
if (auto Err = COFFPlatformRuntimeState::get().registerAtExit(
ExecutorAddr::fromPtr(Header), (void (*)(void))Func)) {
consumeError(std::move(Err));
return -1;
}
return 0;
}
//------------------------------------------------------------------------------
// COFF Run Program
//------------------------------------------------------------------------------

View File

@ -0,0 +1,31 @@
//===- coff_platform.per_jd.cpp -------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains code that will be loaded per each JITDylib.
//
//===----------------------------------------------------------------------===//
#include "compiler.h"
ORC_RT_INTERFACE void __orc_rt_coff_per_jd_marker() {}
typedef int (*OnExitFunction)(void);
typedef void (*AtExitFunction)(void);
extern "C" void *__ImageBase;
ORC_RT_INTERFACE OnExitFunction __orc_rt_coff_onexit(void *Header,
OnExitFunction Func);
ORC_RT_INTERFACE int __orc_rt_coff_atexit(void *Header, AtExitFunction Func);
ORC_RT_INTERFACE OnExitFunction
__orc_rt_coff_onexit_per_jd(OnExitFunction Func) {
return __orc_rt_coff_onexit(&__ImageBase, Func);
}
ORC_RT_INTERFACE int __orc_rt_coff_atexit_per_jd(AtExitFunction Func) {
return __orc_rt_coff_atexit(&__ImageBase, Func);
}

View File

@ -1,9 +1,13 @@
#include <stdio.h>
#include <stdlib.h>
void Dtor() { printf("destructor\n"); }
int Ctor() {
printf("constructor\n");
atexit(Dtor);
return 0;
}
#pragma section(".CRT$XIV", long, read)
__declspec(allocate(".CRT$XIV")) int (*i1)(void) = Ctor;
__declspec(allocate(".CRT$XIV")) int (*i1)(void) = Ctor;

View File

@ -1,4 +1,4 @@
// RUN: %clang -c -o %t %s
// RUN: %clang_cl -MD -c -o %t %s
// RUN: %llvm_jitlink %t 2>&1 | FileCheck %s
// CHECK: Hello, world!

View File

@ -1,4 +1,4 @@
// RUN: %clangxx -c -o %t %s
// RUN: %clang_cl -MD -c -o %t %s
// RUN: %llvm_jitlink %t 2>&1 | FileCheck %s
// CHECK: Hello, world!

View File

@ -1,4 +1,4 @@
// RUN: %clang -c -o %t %s
// RUN: %clang_cl -MD -c -o %t %s
// RUN: %llvm_jitlink %t 2>&1 | FileCheck %s
// CHECK: init1
// CHECK-NEXT: init2

View File

@ -1,4 +1,4 @@
// RUN: %clang -c -o %t %s
// RUN: %clang_cl -MD -c -o %t %s
// RUN: %llvm_jitlink %t 2>&1 | FileCheck %s
// CHECK: init1
// CHECK-NEXT: init2

View File

@ -1,4 +1,4 @@
// RUN: %clangxx -c -o %t %s
// RUN: %clang_cl -EHsc -MD -c -o %t %s
// RUN: %llvm_jitlink %t
extern "C" __declspec(dllimport) void llvm_jitlink_setTestResultOverride(

View File

@ -1,4 +1,4 @@
// RUN: %clangxx -c -o %t %s
// RUN: %clang_cl -MD -c -o %t %s
// RUN: %llvm_jitlink %t
extern "C" __declspec(dllimport) void llvm_jitlink_setTestResultOverride(

View File

@ -0,0 +1,18 @@
// RUN: %clang_cl -MD -c -o %t %s
// RUN: %llvm_jitlink %t 2>&1 | FileCheck %s
// CHECK: Entering main
// CHECK-NEXT: Meow
#include <stdio.h>
#include <stdlib.h>
void meow() {
printf("Meow\n");
fflush(stdout);
}
int main(int argc, char *argv[]) {
atexit(meow);
printf("Entering main\n");
return 0;
}

View File

@ -1,10 +1,9 @@
// XFAIL: *
// Test that __orc_rt_coff_jit_dlopen and __orc_rt_coff_jit_dlclose work as
// expected for a straightforward dlopen; dlclose sequence: first the
// constructors should be run.
//
// RUN: %clang -c -o %t.inits.o %p/Inputs/standalone-dylib.c
// RUN: %clang -c -o %t.test.o %s
// RUN: %clang_cl -MD -c -o %t.inits.o %p/Inputs/standalone-dylib.c
// RUN: %clang_cl -MD -c -o %t.test.o %s
// RUN: %llvm_jitlink \
// RUN: -alias dlopen=__orc_rt_coff_jit_dlopen \
// RUN: -alias dlclose=__orc_rt_coff_jit_dlclose \
@ -12,11 +11,12 @@
// CHECK: entering main
// CHECK-NEXT: constructor
// CHECK-NEXT: destructor
// CHECK-NEXT: leaving main
#include <stdio.h>
__declspec(dllimport) void *dlopen(const char *path, int mode);
__declspec(dllimport) int dlclose(void *handle);
void *dlopen(const char *path, int mode);
int dlclose(void *handle);
int main(int argc, char *argv[]) {
printf("entering main\n");

View File

@ -37,6 +37,9 @@ config.substitutions.append(
config.substitutions.append(
('%clangxx ',
build_invocation(config.cxx_mode_flags + [config.target_cflags])))
config.substitutions.append(
('%clang_cl ',
build_invocation(['--driver-mode=cl'] + [config.target_cflags])))
if config.host_os == 'Windows':
config.substitutions.append(
('%llvm_jitlink', (llvm_jitlink + ' -orc-runtime=' +

View File

@ -42,7 +42,8 @@ public:
static Expected<std::unique_ptr<COFFPlatform>>
Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD, const char *OrcRuntimePath,
LoadDynamicLibrary LoadDynLibrary, const char *VCRuntimePath = nullptr,
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime = false,
const char *VCRuntimePath = nullptr,
Optional<SymbolAliasMap> RuntimeAliases = None);
Error bootstrap(JITDylib &PlatformJD);
@ -141,11 +142,10 @@ private:
static bool supportedTarget(const Triple &TT);
COFFPlatform(
ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD,
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
LoadDynamicLibrary LoadDynLibrary, const char *VCRuntimePath, Error &Err);
COFFPlatform(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD, const char *OrcRuntimePath,
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
const char *VCRuntimePath, Error &Err);
// Associate COFFPlatform JIT-side runtime support functions with handlers.
Error associateRuntimeSupportFunctions(JITDylib &PlatformJD);
@ -164,6 +164,8 @@ private:
// Build dependency graph of a JITDylib
Expected<JITDylibDepMap> buildJDDepMap(JITDylib &JD);
Expected<MemoryBufferRef> getPerJDObjectFile();
// Implements rt_pushInitializers by making repeat async lookups for
// initializer symbols (each lookup may spawn more initializer symbols if
// it pulls in new materializers, e.g. from objects in a static library).
@ -181,6 +183,9 @@ private:
LoadDynamicLibrary LoadDynLibrary;
std::unique_ptr<COFFVCRuntimeBootstrapper> VCRuntimeBootstrap;
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer;
std::unique_ptr<object::Archive> OrcRuntimeArchive;
bool StaticVCRuntime;
SymbolStringPtr COFFHeaderStartSymbol;

View File

@ -9,6 +9,7 @@
#include "llvm/ExecutionEngine/Orc/COFFPlatform.h"
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
#include "llvm/Object/COFF.h"
@ -161,7 +162,7 @@ namespace orc {
Expected<std::unique_ptr<COFFPlatform>>
COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD, const char *OrcRuntimePath,
LoadDynamicLibrary LoadDynLibrary,
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
const char *VCRuntimePath,
Optional<SymbolAliasMap> RuntimeAliases) {
auto &EPC = ES.getExecutorProcessControl();
@ -192,23 +193,44 @@ COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
JITSymbolFlags::Exported}}})))
return std::move(Err);
// Create a generator for the ORC runtime archive.
auto OrcRuntimeArchiveGenerator =
StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer, OrcRuntimePath);
if (!OrcRuntimeArchiveGenerator)
return OrcRuntimeArchiveGenerator.takeError();
PlatformJD.addToLinkOrder(HostFuncJD);
// Create the instance.
Error Err = Error::success();
auto P = std::unique_ptr<COFFPlatform>(new COFFPlatform(
ES, ObjLinkingLayer, PlatformJD, std::move(*OrcRuntimeArchiveGenerator),
std::move(LoadDynLibrary), VCRuntimePath, Err));
ES, ObjLinkingLayer, PlatformJD, OrcRuntimePath,
std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, Err));
if (Err)
return std::move(Err);
return std::move(P);
}
Expected<MemoryBufferRef> COFFPlatform::getPerJDObjectFile() {
auto PerJDObj = OrcRuntimeArchive->findSym("__orc_rt_coff_per_jd_marker");
if (!PerJDObj)
return PerJDObj.takeError();
if (!*PerJDObj)
return make_error<StringError>("Could not find per jd object file",
inconvertibleErrorCode());
auto Buffer = (*PerJDObj)->getAsBinary();
if (!Buffer)
return Buffer.takeError();
return (*Buffer)->getMemoryBufferRef();
}
static void addAliases(ExecutionSession &ES, SymbolAliasMap &Aliases,
ArrayRef<std::pair<const char *, const char *>> AL) {
for (auto &KV : AL) {
auto AliasName = ES.intern(KV.first);
assert(!Aliases.count(AliasName) && "Duplicate symbol name in alias map");
Aliases[std::move(AliasName)] = {ES.intern(KV.second),
JITSymbolFlags::Exported};
}
}
Error COFFPlatform::setupJITDylib(JITDylib &JD) {
if (auto Err = JD.define(std::make_unique<COFFHeaderMaterializationUnit>(
*this, COFFHeaderStartSymbol)))
@ -217,15 +239,36 @@ Error COFFPlatform::setupJITDylib(JITDylib &JD) {
if (auto Err = ES.lookup({&JD}, COFFHeaderStartSymbol).takeError())
return Err;
// Define the CXX aliases.
SymbolAliasMap CXXAliases;
addAliases(ES, CXXAliases, requiredCXXAliases());
if (auto Err = JD.define(symbolAliases(std::move(CXXAliases))))
return std::move(Err);
auto PerJDObj = getPerJDObjectFile();
if (!PerJDObj)
return PerJDObj.takeError();
auto I = getObjectFileInterface(ES, *PerJDObj);
if (!I)
return I.takeError();
if (auto Err = ObjLinkingLayer.add(
JD, MemoryBuffer::getMemBuffer(*PerJDObj, false), std::move(*I)))
return Err;
if (!Bootstrapping) {
auto ImportedLibs = VCRuntimeBootstrap->loadStaticVCRuntime(JD);
auto ImportedLibs = StaticVCRuntime
? VCRuntimeBootstrap->loadStaticVCRuntime(JD)
: VCRuntimeBootstrap->loadDynamicVCRuntime(JD);
if (!ImportedLibs)
return ImportedLibs.takeError();
for (auto &Lib : *ImportedLibs)
if (auto Err = LoadDynLibrary(JD, Lib))
return Err;
if (auto Err = VCRuntimeBootstrap->initializeStaticVCRuntime(JD))
return Err;
if (StaticVCRuntime)
if (auto Err = VCRuntimeBootstrap->initializeStaticVCRuntime(JD))
return Err;
}
JD.addGenerator(DLLImportDefinitionGenerator::Create(ES, ObjLinkingLayer));
@ -265,19 +308,8 @@ Error COFFPlatform::notifyRemoving(ResourceTracker &RT) {
llvm_unreachable("Not supported yet");
}
static void addAliases(ExecutionSession &ES, SymbolAliasMap &Aliases,
ArrayRef<std::pair<const char *, const char *>> AL) {
for (auto &KV : AL) {
auto AliasName = ES.intern(KV.first);
assert(!Aliases.count(AliasName) && "Duplicate symbol name in alias map");
Aliases[std::move(AliasName)] = {ES.intern(KV.second),
JITSymbolFlags::Exported};
}
}
SymbolAliasMap COFFPlatform::standardPlatformAliases(ExecutionSession &ES) {
SymbolAliasMap Aliases;
addAliases(ES, Aliases, requiredCXXAliases());
addAliases(ES, Aliases, standardRuntimeUtilityAliases());
return Aliases;
}
@ -286,7 +318,8 @@ ArrayRef<std::pair<const char *, const char *>>
COFFPlatform::requiredCXXAliases() {
static const std::pair<const char *, const char *> RequiredCXXAliases[] = {
{"_CxxThrowException", "__orc_rt_coff_cxx_throw_exception"},
};
{"_onexit", "__orc_rt_coff_onexit_per_jd"},
{"atexit", "__orc_rt_coff_atexit_per_jd"}};
return ArrayRef<std::pair<const char *, const char *>>(RequiredCXXAliases);
}
@ -315,16 +348,37 @@ bool COFFPlatform::supportedTarget(const Triple &TT) {
}
}
COFFPlatform::COFFPlatform(
ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD,
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
LoadDynamicLibrary LoadDynLibrary, const char *VCRuntimePath, Error &Err)
COFFPlatform::COFFPlatform(ExecutionSession &ES,
ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD, const char *OrcRuntimePath,
LoadDynamicLibrary LoadDynLibrary,
bool StaticVCRuntime, const char *VCRuntimePath,
Error &Err)
: ES(ES), ObjLinkingLayer(ObjLinkingLayer),
LoadDynLibrary(std::move(LoadDynLibrary)),
StaticVCRuntime(StaticVCRuntime),
COFFHeaderStartSymbol(ES.intern("__ImageBase")) {
ErrorAsOutParameter _(&Err);
// Create a generator for the ORC runtime archive.
auto OrcRuntimeArchiveGenerator =
StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer, OrcRuntimePath);
if (!OrcRuntimeArchiveGenerator) {
Err = std::move(OrcRuntimeArchiveGenerator.takeError());
return;
}
auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath);
if (!ArchiveBuffer) {
Err = createFileError(OrcRuntimePath, ArchiveBuffer.getError());
return;
}
OrcRuntimeArchiveBuffer = std::move(*ArchiveBuffer);
OrcRuntimeArchive =
std::make_unique<object::Archive>(*OrcRuntimeArchiveBuffer, Err);
if (Err)
return;
Bootstrapping.store(true);
ObjLinkingLayer.addPlugin(std::make_unique<COFFPlatformPlugin>(*this));
@ -337,10 +391,12 @@ COFFPlatform::COFFPlatform(
}
VCRuntimeBootstrap = std::move(*VCRT);
for (auto &Lib : OrcRuntimeGenerator->getImportedDynamicLibraries())
for (auto &Lib : (*OrcRuntimeArchiveGenerator)->getImportedDynamicLibraries())
DylibsToPreload.insert(Lib);
auto ImportedLibs = VCRuntimeBootstrap->loadStaticVCRuntime(PlatformJD);
auto ImportedLibs =
StaticVCRuntime ? VCRuntimeBootstrap->loadStaticVCRuntime(PlatformJD)
: VCRuntimeBootstrap->loadDynamicVCRuntime(PlatformJD);
if (!ImportedLibs) {
Err = ImportedLibs.takeError();
return;
@ -349,14 +405,14 @@ COFFPlatform::COFFPlatform(
for (auto &Lib : *ImportedLibs)
DylibsToPreload.insert(Lib);
PlatformJD.addGenerator(std::move(*OrcRuntimeArchiveGenerator));
// PlatformJD hasn't been set up by the platform yet (since we're creating
// the platform now), so set it up.
if (auto E2 = setupJITDylib(PlatformJD)) {
Err = std::move(E2);
return;
}
PlatformJD.addGenerator(std::move(OrcRuntimeGenerator));
}
Error COFFPlatform::bootstrap(JITDylib &PlatformJD) {
@ -364,8 +420,9 @@ Error COFFPlatform::bootstrap(JITDylib &PlatformJD) {
if (auto Err = LoadDynLibrary(PlatformJD, Lib))
return Err;
if (auto Err = VCRuntimeBootstrap->initializeStaticVCRuntime(PlatformJD))
return Err;
if (StaticVCRuntime)
if (auto Err = VCRuntimeBootstrap->initializeStaticVCRuntime(PlatformJD))
return Err;
// Associate wrapper function tags with JIT-side function implementations.
if (auto Err = associateRuntimeSupportFunctions(PlatformJD)) {
@ -636,13 +693,6 @@ Error COFFPlatform::bootstrapCOFFRuntime(JITDylib &PlatformJD) {
}))
return Err;
// Run static initializers collected in bootstrap stage.
for (auto KV : JDBootstrapStates) {
auto &JDBState = KV.second;
if (auto Err = runBootstrapInitializers(JDBState))
return Err;
}
// Call bootstrap functions
if (auto Err = ES.callSPSWrapper<void()>(orc_rt_coff_platform_bootstrap))
return Err;
@ -664,6 +714,13 @@ Error COFFPlatform::bootstrapCOFFRuntime(JITDylib &PlatformJD) {
return Err;
}
// Run static initializers collected in bootstrap stage.
for (auto KV : JDBootstrapStates) {
auto &JDBState = KV.second;
if (auto Err = runBootstrapInitializers(JDBState))
return Err;
}
return Error::success();
}

View File

@ -1197,10 +1197,12 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
ExitOnErr(loadProcessSymbols(*this));
else {
// This symbol is used in testcases.
ExitOnErr(MainJD->define(absoluteSymbols(
auto &TestResultJD = ES.createBareJITDylib("<TestResultJD>");
ExitOnErr(TestResultJD.define(absoluteSymbols(
{{ES.intern("llvm_jitlink_setTestResultOverride"),
{pointerToJITTargetAddress(llvm_jitlink_setTestResultOverride),
JITSymbolFlags::Exported}}})));
MainJD->addToLinkOrder(TestResultJD);
}
ExitOnErr(loadDylibs(*this));