Skip to content

Commit

Permalink
[JITLink] Add public APIs for getting stub creation functions.
Browse files Browse the repository at this point in the history
Creating stubs in JITLink require creating architecture-specific edges. In order to allow user to create stubs in cross-architecture manner, this patch exposes these stub creations functions by returning "stub creators" for given triple.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D155557
  • Loading branch information
sunho committed Aug 5, 2023
1 parent a330759 commit 5c98617
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 2 deletions.
25 changes: 25 additions & 0 deletions llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
Expand Down Expand Up @@ -1851,6 +1852,30 @@ Error makeTargetOutOfRangeError(const LinkGraph &G, const Block &B,
Error makeAlignmentError(llvm::orc::ExecutorAddr Loc, uint64_t Value, int N,
const Edge &E);

/// Creates a new pointer block in the given section and returns an
/// Anonymous symobl pointing to it.
///
/// The pointer block will have the following default values:
/// alignment: PointerSize
/// alignment-offset: 0
/// address: highest allowable
using AnonymousPointerCreator = unique_function<Expected<Symbol &>(
LinkGraph &G, Section &PointerSection, Symbol *InitialTarget,
uint64_t InitialAddend)>;

/// Get target-specific AnonymousPointerCreator
AnonymousPointerCreator getAnonymousPointerCreator(const Triple &TT);

/// Create a jump stub that jumps via the pointer at the given symbol and
/// an anonymous symbol pointing to it. Return the anonymous symbol.
///
/// The stub block will be created by createPointerJumpStubBlock.
using PointerJumpStubCreator = unique_function<Expected<Symbol &>(
LinkGraph &G, Section &StubSection, Symbol &PointerSymbol)>;

/// Get target-specific PointerJumpStubCreator
PointerJumpStubCreator getPointerJumpStubCreator(const Triple &TT);

/// Base case for edge-visitors where the visitor-list is empty.
inline void visitEdge(LinkGraph &G, Block *B, Edge &E) {}

Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection,
Symbol &PointerSymbol) {
auto &B = G.createContentBlock(StubSection, PointerJumpStubContent,
orc::ExecutorAddr(~uint64_t(5)), 1, 0);
B.addEdge(Delta32, 2, PointerSymbol, -4);
B.addEdge(BranchPCRel32, 2, PointerSymbol, 0);
return B;
}

Expand Down
36 changes: 36 additions & 0 deletions llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#include "llvm/ExecutionEngine/JITLink/COFF.h"
#include "llvm/ExecutionEngine/JITLink/ELF.h"
#include "llvm/ExecutionEngine/JITLink/MachO.h"
#include "llvm/ExecutionEngine/JITLink/aarch64.h"
#include "llvm/ExecutionEngine/JITLink/i386.h"
#include "llvm/ExecutionEngine/JITLink/loongarch.h"
#include "llvm/ExecutionEngine/JITLink/x86_64.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
Expand Down Expand Up @@ -417,6 +421,38 @@ Error makeAlignmentError(llvm::orc::ExecutorAddr Loc, uint64_t Value, int N,
" is not aligned to " + Twine(N) + " bytes");
}

AnonymousPointerCreator getAnonymousPointerCreator(const Triple &TT) {
switch (TT.getArch()) {
case Triple::aarch64:
return aarch64::createAnonymousPointer;
case Triple::x86_64:
return x86_64::createAnonymousPointer;
case Triple::x86:
return i386::createAnonymousPointer;
case Triple::loongarch32:
case Triple::loongarch64:
return loongarch::createAnonymousPointer;
default:
return nullptr;
}
}

PointerJumpStubCreator getPointerJumpStubCreator(const Triple &TT) {
switch (TT.getArch()) {
case Triple::aarch64:
return aarch64::createAnonymousPointerJumpStub;
case Triple::x86_64:
return x86_64::createAnonymousPointerJumpStub;
case Triple::x86:
return i386::createAnonymousPointerJumpStub;
case Triple::loongarch32:
case Triple::loongarch64:
return loongarch::createAnonymousPointerJumpStub;
default:
return nullptr;
}
}

Expected<std::unique_ptr<LinkGraph>>
createLinkGraphFromObject(MemoryBufferRef ObjectBuffer) {
auto Magic = identify_magic(ObjectBuffer.getBuffer());
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ DLLImportDefinitionGenerator::getTargetEndianness(const Triple &TT) {
Expected<std::unique_ptr<jitlink::LinkGraph>>
DLLImportDefinitionGenerator::createStubsGraph(const SymbolMap &Resolved) {
Triple TT = ES.getTargetTriple();
auto PointerSize = getTargetEndianness(TT);
auto PointerSize = getTargetPointerSize(TT);
if (!PointerSize)
return PointerSize.takeError();
auto Endianness = getTargetEndianness(TT);
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_llvm_unittest(JITLinkTests
JITLinkMocks.cpp
LinkGraphTests.cpp
MemoryManagerErrorTests.cpp
StubsTests.cpp
)

target_link_libraries(JITLinkTests PRIVATE LLVMTestingSupport)
Expand Down
179 changes: 179 additions & 0 deletions llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//===------ StubsTests.cpp - Unit tests for generic stub generation -------===//
//
// 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/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/ExecutionEngine/JITLink/aarch64.h"
#include "llvm/ExecutionEngine/JITLink/i386.h"
#include "llvm/ExecutionEngine/JITLink/loongarch.h"
#include "llvm/ExecutionEngine/JITLink/x86_64.h"
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Memory.h"

#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"

using namespace llvm;
using namespace llvm::jitlink;

static std::pair<Symbol &, Symbol &>
GenerateStub(LinkGraph &G, size_t PointerSize, Edge::Kind PointerEdgeKind) {
auto &FuncSymbol = G.addAbsoluteSymbol("Func", orc::ExecutorAddr(0x2000), 0,
Linkage::Strong, Scope::Default, true);

// Create a section for pointer symbols.
auto &PointersSec =
G.createSection("__pointers", orc::MemProt::Read | orc::MemProt::Write);

// Create a section for jump stubs symbols.
auto &StubsSec =
G.createSection("__stubs", orc::MemProt::Read | orc::MemProt::Write);

auto AnonymousPtrCreator = getAnonymousPointerCreator(G.getTargetTriple());
EXPECT_TRUE(AnonymousPtrCreator);

auto PointerSym = AnonymousPtrCreator(G, PointersSec, &FuncSymbol, 0);
EXPECT_FALSE(errorToBool(PointerSym.takeError()));
EXPECT_EQ(std::distance(PointerSym->getBlock().edges().begin(),
PointerSym->getBlock().edges().end()),
1U);
auto &DeltaEdge = *PointerSym->getBlock().edges().begin();
EXPECT_EQ(DeltaEdge.getKind(), PointerEdgeKind);
EXPECT_EQ(&DeltaEdge.getTarget(), &FuncSymbol);
EXPECT_EQ(PointerSym->getBlock().getSize(), PointerSize);
EXPECT_TRUE(all_of(PointerSym->getBlock().getContent(),
[](char x) { return x == 0; }));

auto PtrJumpStubCreator = getPointerJumpStubCreator(G.getTargetTriple());
EXPECT_TRUE(PtrJumpStubCreator);
auto StubSym = PtrJumpStubCreator(G, StubsSec, *PointerSym);
EXPECT_FALSE(errorToBool(StubSym.takeError()));
return {*PointerSym, *StubSym};
}

TEST(StubsTest, StubsGeneration_x86_64) {
const char PointerJumpStubContent[6] = {
static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};
LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,
getGenericEdgeKindName);
auto [PointerSym, StubSym] = GenerateStub(G, 8U, x86_64::Pointer64);

EXPECT_EQ(std::distance(StubSym.getBlock().edges().begin(),
StubSym.getBlock().edges().end()),
1U);
auto &JumpEdge = *StubSym.getBlock().edges().begin();
EXPECT_EQ(JumpEdge.getKind(), x86_64::BranchPCRel32);
EXPECT_EQ(&JumpEdge.getTarget(), &PointerSym);
EXPECT_EQ(StubSym.getBlock().getContent(),
ArrayRef<char>(PointerJumpStubContent));
}

TEST(StubsTest, StubsGeneration_aarch64) {
const char PointerJumpStubContent[12] = {
0x10, 0x00, 0x00, (char)0x90u, // ADRP x16, <imm>@page21
0x10, 0x02, 0x40, (char)0xf9u, // LDR x16, [x16, <imm>@pageoff12]
0x00, 0x02, 0x1f, (char)0xd6u // BR x16
};
LinkGraph G("foo", Triple("aarch64-linux-gnu"), 8, support::little,
getGenericEdgeKindName);
auto [PointerSym, StubSym] = GenerateStub(G, 8U, aarch64::Pointer64);

EXPECT_EQ(std::distance(StubSym.getBlock().edges().begin(),
StubSym.getBlock().edges().end()),
2U);
auto &AdrpHighEdge = *StubSym.getBlock().edges().begin();
auto &LdrEdge = *++StubSym.getBlock().edges().begin();
EXPECT_EQ(AdrpHighEdge.getKind(), aarch64::Page21);
EXPECT_EQ(&AdrpHighEdge.getTarget(), &PointerSym);
EXPECT_EQ(LdrEdge.getKind(), aarch64::PageOffset12);
EXPECT_EQ(&LdrEdge.getTarget(), &PointerSym);
EXPECT_EQ(StubSym.getBlock().getContent(),
ArrayRef<char>(PointerJumpStubContent));
}

TEST(StubsTest, StubsGeneration_i386) {
const char PointerJumpStubContent[6] = {
static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};
LinkGraph G("foo", Triple("i386-unknown-linux-gnu"), 4, support::little,
getGenericEdgeKindName);
auto [PointerSym, StubSym] = GenerateStub(G, 4U, i386::Pointer32);

EXPECT_EQ(std::distance(StubSym.getBlock().edges().begin(),
StubSym.getBlock().edges().end()),
1U);
auto &JumpEdge = *StubSym.getBlock().edges().begin();
EXPECT_EQ(JumpEdge.getKind(), i386::Pointer32);
EXPECT_EQ(&JumpEdge.getTarget(), &PointerSym);
EXPECT_EQ(StubSym.getBlock().getContent(),
ArrayRef<char>(PointerJumpStubContent));
}

TEST(StubsTest, StubsGeneration_loongarch32) {
const char PointerJumpStubContent[12] = {
0x14,
0x00,
0x00,
0x1a, // pcalau12i $t8, %page20(imm)
static_cast<char>(0x94),
0x02,
static_cast<char>(0x80),
0x28, // ld.d $t8, $t8, %pageoff12(imm)
static_cast<char>(0x80),
0x02,
0x00,
0x4c // jr $t8
};
LinkGraph G("foo", Triple("loongarch32"), 4, support::little,
getGenericEdgeKindName);
auto [PointerSym, StubSym] = GenerateStub(G, 4U, loongarch::Pointer32);

EXPECT_EQ(std::distance(StubSym.getBlock().edges().begin(),
StubSym.getBlock().edges().end()),
2U);
auto &PageHighEdge = *StubSym.getBlock().edges().begin();
auto &PageLowEdge = *++StubSym.getBlock().edges().begin();
EXPECT_EQ(PageHighEdge.getKind(), loongarch::Page20);
EXPECT_EQ(&PageHighEdge.getTarget(), &PointerSym);
EXPECT_EQ(PageLowEdge.getKind(), loongarch::PageOffset12);
EXPECT_EQ(&PageLowEdge.getTarget(), &PointerSym);
EXPECT_EQ(StubSym.getBlock().getContent(),
ArrayRef<char>(PointerJumpStubContent));
}

TEST(StubsTest, StubsGeneration_loongarch64) {
const char PointerJumpStubContent[12] = {
0x14,
0x00,
0x00,
0x1a, // pcalau12i $t8, %page20(imm)
static_cast<char>(0x94),
0x02,
static_cast<char>(0xc0),
0x28, // ld.d $t8, $t8, %pageoff12(imm)
static_cast<char>(0x80),
0x02,
0x00,
0x4c // jr $t8
};
LinkGraph G("foo", Triple("loongarch64"), 8, support::little,
getGenericEdgeKindName);
auto [PointerSym, StubSym] = GenerateStub(G, 8U, loongarch::Pointer64);

EXPECT_EQ(std::distance(StubSym.getBlock().edges().begin(),
StubSym.getBlock().edges().end()),
2U);
auto &PageHighEdge = *StubSym.getBlock().edges().begin();
auto &PageLowEdge = *++StubSym.getBlock().edges().begin();
EXPECT_EQ(PageHighEdge.getKind(), loongarch::Page20);
EXPECT_EQ(&PageHighEdge.getTarget(), &PointerSym);
EXPECT_EQ(PageLowEdge.getKind(), loongarch::PageOffset12);
EXPECT_EQ(&PageLowEdge.getTarget(), &PointerSym);
EXPECT_EQ(StubSym.getBlock().getContent(),
ArrayRef<char>(PointerJumpStubContent));
}

0 comments on commit 5c98617

Please sign in to comment.