1 //===- ExecutionEngineTest.cpp - Unit tests for ExecutionEngine -----------===// 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/ADT/STLExtras.h" 10 #include "llvm/ExecutionEngine/Interpreter.h" 11 #include "llvm/ExecutionEngine/RTDyldMemoryManager.h" 12 #include "llvm/IR/DerivedTypes.h" 13 #include "llvm/IR/GlobalVariable.h" 14 #include "llvm/IR/LLVMContext.h" 15 #include "llvm/IR/Module.h" 16 #include "llvm/Support/DynamicLibrary.h" 17 #include "llvm/Support/ManagedStatic.h" 18 #include "gtest/gtest.h" 19 20 using namespace llvm; 21 22 namespace { 23 24 class ExecutionEngineTest : public testing::Test { 25 private: 26 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 27 28 protected: 29 ExecutionEngineTest() { 30 auto Owner = std::make_unique<Module>("<main>", Context); 31 M = Owner.get(); 32 Engine.reset(EngineBuilder(std::move(Owner)).setErrorStr(&Error).create()); 33 } 34 35 void SetUp() override { 36 ASSERT_TRUE(Engine.get() != nullptr) << "EngineBuilder returned error: '" 37 << Error << "'"; 38 } 39 40 GlobalVariable *NewExtGlobal(Type *T, const Twine &Name) { 41 return new GlobalVariable(*M, T, false, // Not constant. 42 GlobalValue::ExternalLinkage, nullptr, Name); 43 } 44 45 std::string Error; 46 LLVMContext Context; 47 Module *M; // Owned by ExecutionEngine. 48 std::unique_ptr<ExecutionEngine> Engine; 49 }; 50 51 TEST_F(ExecutionEngineTest, ForwardGlobalMapping) { 52 GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); 53 int32_t Mem1 = 3; 54 Engine->addGlobalMapping(G1, &Mem1); 55 EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1)); 56 EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable("Global1")); 57 int32_t Mem2 = 4; 58 Engine->updateGlobalMapping(G1, &Mem2); 59 EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1)); 60 Engine->updateGlobalMapping(G1, nullptr); 61 EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G1)); 62 Engine->updateGlobalMapping(G1, &Mem2); 63 EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1)); 64 65 GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); 66 EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G2)) 67 << "The NULL return shouldn't depend on having called" 68 << " updateGlobalMapping(..., NULL)"; 69 // Check that update...() can be called before add...(). 70 Engine->updateGlobalMapping(G2, &Mem1); 71 EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G2)); 72 EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1)) 73 << "A second mapping shouldn't affect the first."; 74 } 75 76 TEST_F(ExecutionEngineTest, ReverseGlobalMapping) { 77 GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); 78 79 int32_t Mem1 = 3; 80 Engine->addGlobalMapping(G1, &Mem1); 81 EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1)); 82 int32_t Mem2 = 4; 83 Engine->updateGlobalMapping(G1, &Mem2); 84 EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); 85 EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2)); 86 87 GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2"); 88 Engine->updateGlobalMapping(G2, &Mem1); 89 EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1)); 90 EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2)); 91 Engine->updateGlobalMapping(G1, nullptr); 92 EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1)) 93 << "Removing one mapping doesn't affect a different one."; 94 EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem2)); 95 Engine->updateGlobalMapping(G2, &Mem2); 96 EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); 97 EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem2)) 98 << "Once a mapping is removed, we can point another GV at the" 99 << " now-free address."; 100 } 101 102 TEST_F(ExecutionEngineTest, ClearModuleMappings) { 103 GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); 104 105 int32_t Mem1 = 3; 106 Engine->addGlobalMapping(G1, &Mem1); 107 EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1)); 108 109 Engine->clearGlobalMappingsFromModule(M); 110 111 EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); 112 113 GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2"); 114 // After clearing the module mappings, we can assign a new GV to the 115 // same address. 116 Engine->addGlobalMapping(G2, &Mem1); 117 EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1)); 118 } 119 120 TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) { 121 GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1"); 122 int32_t Mem1 = 3; 123 Engine->addGlobalMapping(G1, &Mem1); 124 // Make sure the reverse mapping is enabled. 125 EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1)); 126 // When the GV goes away, the ExecutionEngine should remove any 127 // mappings that refer to it. 128 G1->eraseFromParent(); 129 EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1)); 130 } 131 132 TEST_F(ExecutionEngineTest, LookupWithMangledAndDemangledSymbol) { 133 int x; 134 int _x; 135 llvm::sys::DynamicLibrary::AddSymbol("x", &x); 136 llvm::sys::DynamicLibrary::AddSymbol("_x", &_x); 137 138 // RTDyldMemoryManager::getSymbolAddressInProcess expects a mangled symbol, 139 // but DynamicLibrary is a wrapper for dlsym, which expects the unmangled C 140 // symbol name. This test verifies that getSymbolAddressInProcess strips the 141 // leading '_' on Darwin, but not on other platforms. 142 #ifdef __APPLE__ 143 EXPECT_EQ(reinterpret_cast<uint64_t>(&x), 144 RTDyldMemoryManager::getSymbolAddressInProcess("_x")); 145 #else 146 EXPECT_EQ(reinterpret_cast<uint64_t>(&_x), 147 RTDyldMemoryManager::getSymbolAddressInProcess("_x")); 148 #endif 149 } 150 151 } 152