mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-18 09:29:31 +00:00
9ab9fd58bd
The RTDyldMemoryManager::getSymbolAddressInProcess method accepts a linker-mangled symbol name, but it calls through to dlsym to do the lookup (via DynamicLibrary::SearchForAddressOfSymbol), and dlsym expects an unmangled symbol name. Historically we've attempted to "demangle" by removing leading '_'s on all platforms, and fallen back to an extra search if that failed. That's broken, as it can cause symbols to resolve incorrectly on platforms that don't do mangling if you query '_foo' and the process also happens to contain a 'foo'. Fix this by demangling conditionally based on the host platform. That's safe here because this function is specifically for symbols in the host process, so the usual cross-process JIT looking concerns don't apply. M unittests/ExecutionEngine/ExecutionEngineTest.cpp M lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@262657 91177308-0d34-0410-b5e6-96231b3b80d8
159 lines
5.7 KiB
C++
159 lines
5.7 KiB
C++
//===- ExecutionEngineTest.cpp - Unit tests for ExecutionEngine -----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ExecutionEngine/Interpreter.h"
|
|
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/DynamicLibrary.h"
|
|
#include "llvm/Support/ManagedStatic.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
class ExecutionEngineTest : public testing::Test {
|
|
private:
|
|
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
|
|
|
|
protected:
|
|
ExecutionEngineTest() {
|
|
auto Owner = make_unique<Module>("<main>", getGlobalContext());
|
|
M = Owner.get();
|
|
Engine.reset(EngineBuilder(std::move(Owner)).setErrorStr(&Error).create());
|
|
}
|
|
|
|
void SetUp() override {
|
|
ASSERT_TRUE(Engine.get() != nullptr) << "EngineBuilder returned error: '"
|
|
<< Error << "'";
|
|
}
|
|
|
|
GlobalVariable *NewExtGlobal(Type *T, const Twine &Name) {
|
|
return new GlobalVariable(*M, T, false, // Not constant.
|
|
GlobalValue::ExternalLinkage, nullptr, Name);
|
|
}
|
|
|
|
std::string Error;
|
|
Module *M; // Owned by ExecutionEngine.
|
|
std::unique_ptr<ExecutionEngine> Engine;
|
|
};
|
|
|
|
TEST_F(ExecutionEngineTest, ForwardGlobalMapping) {
|
|
GlobalVariable *G1 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
|
|
int32_t Mem1 = 3;
|
|
Engine->addGlobalMapping(G1, &Mem1);
|
|
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1));
|
|
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable("Global1"));
|
|
int32_t Mem2 = 4;
|
|
Engine->updateGlobalMapping(G1, &Mem2);
|
|
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));
|
|
Engine->updateGlobalMapping(G1, nullptr);
|
|
EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G1));
|
|
Engine->updateGlobalMapping(G1, &Mem2);
|
|
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));
|
|
|
|
GlobalVariable *G2 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
|
|
EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G2))
|
|
<< "The NULL return shouldn't depend on having called"
|
|
<< " updateGlobalMapping(..., NULL)";
|
|
// Check that update...() can be called before add...().
|
|
Engine->updateGlobalMapping(G2, &Mem1);
|
|
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G2));
|
|
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1))
|
|
<< "A second mapping shouldn't affect the first.";
|
|
}
|
|
|
|
TEST_F(ExecutionEngineTest, ReverseGlobalMapping) {
|
|
GlobalVariable *G1 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
|
|
|
|
int32_t Mem1 = 3;
|
|
Engine->addGlobalMapping(G1, &Mem1);
|
|
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));
|
|
int32_t Mem2 = 4;
|
|
Engine->updateGlobalMapping(G1, &Mem2);
|
|
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
|
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));
|
|
|
|
GlobalVariable *G2 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2");
|
|
Engine->updateGlobalMapping(G2, &Mem1);
|
|
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));
|
|
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));
|
|
Engine->updateGlobalMapping(G1, nullptr);
|
|
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1))
|
|
<< "Removing one mapping doesn't affect a different one.";
|
|
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem2));
|
|
Engine->updateGlobalMapping(G2, &Mem2);
|
|
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
|
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem2))
|
|
<< "Once a mapping is removed, we can point another GV at the"
|
|
<< " now-free address.";
|
|
}
|
|
|
|
TEST_F(ExecutionEngineTest, ClearModuleMappings) {
|
|
GlobalVariable *G1 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
|
|
|
|
int32_t Mem1 = 3;
|
|
Engine->addGlobalMapping(G1, &Mem1);
|
|
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));
|
|
|
|
Engine->clearGlobalMappingsFromModule(M);
|
|
|
|
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
|
|
|
GlobalVariable *G2 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2");
|
|
// After clearing the module mappings, we can assign a new GV to the
|
|
// same address.
|
|
Engine->addGlobalMapping(G2, &Mem1);
|
|
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));
|
|
}
|
|
|
|
TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) {
|
|
GlobalVariable *G1 =
|
|
NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1");
|
|
int32_t Mem1 = 3;
|
|
Engine->addGlobalMapping(G1, &Mem1);
|
|
// Make sure the reverse mapping is enabled.
|
|
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));
|
|
// When the GV goes away, the ExecutionEngine should remove any
|
|
// mappings that refer to it.
|
|
G1->eraseFromParent();
|
|
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
|
}
|
|
|
|
TEST_F(ExecutionEngineTest, LookupWithMangledAndDemangledSymbol) {
|
|
int x;
|
|
int _x;
|
|
llvm::sys::DynamicLibrary::AddSymbol("x", &x);
|
|
llvm::sys::DynamicLibrary::AddSymbol("_x", &_x);
|
|
|
|
// RTDyldMemoryManager::getSymbolAddressInProcess expects a mangled symbol,
|
|
// but DynamicLibrary is a wrapper for dlsym, which expects the unmangled C
|
|
// symbol name. This test verifies that getSymbolAddressInProcess strips the
|
|
// leading '_' on Darwin, but not on other platforms.
|
|
#ifdef __APPLE__
|
|
EXPECT_EQ(reinterpret_cast<uint64_t>(&x),
|
|
RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
|
|
#else
|
|
EXPECT_EQ(reinterpret_cast<uint64_t>(&_x),
|
|
RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
|
|
#endif
|
|
}
|
|
|
|
}
|