diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h @@ -1852,6 +1852,25 @@ 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 +Expected createAnonymousPointer(LinkGraph &G, Section &PointerSection, + Symbol *InitialTarget = nullptr, + uint64_t InitialAddend = 0); + +/// 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. +Expected createAnonymousPointerJumpStub(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol); + /// Base case for edge-visitors where the visitor-list is empty. inline void visitEdge(LinkGraph &G, Block *B, Edge &E) {} diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h @@ -594,9 +594,10 @@ /// alignment: 64-bit /// alignment-offset: 0 /// address: highest allowable (~7U) -inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, - Symbol *InitialTarget = nullptr, - uint64_t InitialAddend = 0) { +inline Symbol &createAnonymousPointer_aarch64(LinkGraph &G, + Section &PointerSection, + Symbol *InitialTarget = nullptr, + uint64_t InitialAddend = 0) { auto &B = G.createContentBlock(PointerSection, NullPointerContent, orc::ExecutorAddr(~uint64_t(7)), 8, 0); if (InitialTarget) @@ -610,8 +611,9 @@ /// alignment: 32-bit /// alignment-offset: 0 /// address: highest allowable: (~11U) -inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, - Symbol &PointerSymbol) { +inline Block &createPointerJumpStubBlock_aarch64(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { auto &B = G.createContentBlock(StubSection, PointerJumpStubContent, orc::ExecutorAddr(~uint64_t(11)), 1, 0); B.addEdge(Page21, 0, PointerSymbol, 0); @@ -622,12 +624,12 @@ /// 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. -inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, - Section &StubSection, - Symbol &PointerSymbol) { +/// The stub block will be created by createPointerJumpStubBlock_aarch64. +inline Symbol &createAnonymousPointerJumpStub_aarch64(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { return G.addAnonymousSymbol( - createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, + createPointerJumpStubBlock_aarch64(G, StubSection, PointerSymbol), 0, sizeof(PointerJumpStubContent), true, false); } @@ -678,7 +680,7 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointer(G, getGOTSection(G), &Target); + return createAnonymousPointer_aarch64(G, getGOTSection(G), &Target); } private: @@ -713,8 +715,8 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointerJumpStub(G, getStubsSection(G), - GOT.getEntryForTarget(G, Target)); + return createAnonymousPointerJumpStub_aarch64( + G, getStubsSection(G), GOT.getEntryForTarget(G, Target)); } public: diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/i386.h b/llvm/include/llvm/ExecutionEngine/JITLink/i386.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/i386.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/i386.h @@ -312,9 +312,10 @@ /// alignment: 32-bit /// alignment-offset: 0 /// address: highest allowable (~7U) -inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, - Symbol *InitialTarget = nullptr, - uint64_t InitialAddend = 0) { +inline Symbol &createAnonymousPointer_i386(LinkGraph &G, + Section &PointerSection, + Symbol *InitialTarget = nullptr, + uint64_t InitialAddend = 0) { auto &B = G.createContentBlock(PointerSection, NullPointerContent, orc::ExecutorAddr(), 8, 0); if (InitialTarget) @@ -328,8 +329,9 @@ /// alignment: 8-bit /// alignment-offset: 0 /// address: highest allowable: (~5U) -inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, - Symbol &PointerSymbol) { +inline Block &createPointerJumpStubBlock_i386(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { auto &B = G.createContentBlock(StubSection, PointerJumpStubContent, orc::ExecutorAddr(), 8, 0); B.addEdge(Pointer32, @@ -344,12 +346,12 @@ /// an anonymous symbol pointing to it. Return the anonymous symbol. /// /// The stub block will be created by createPointerJumpStubBlock. -inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, - Section &StubSection, - Symbol &PointerSymbol) { +inline Symbol &createAnonymousPointerJumpStub_i386(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { return G.addAnonymousSymbol( - createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true, - false); + createPointerJumpStubBlock_i386(G, StubSection, PointerSymbol), 0, 6, + true, false); } /// Global Offset Table Builder. @@ -385,7 +387,7 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointer(G, getGOTSection(G), &Target); + return createAnonymousPointer_i386(G, getGOTSection(G), &Target); } private: @@ -422,8 +424,8 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointerJumpStub(G, getStubsSection(G), - GOT.getEntryForTarget(G, Target)); + return createAnonymousPointerJumpStub_i386( + G, getStubsSection(G), GOT.getEntryForTarget(G, Target)); } public: diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h @@ -288,9 +288,10 @@ /// The pointer block will have the following default values: /// alignment: PointerSize /// alignment-offset: 0 -inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, - Symbol *InitialTarget = nullptr, - uint64_t InitialAddend = 0) { +inline Symbol &createAnonymousPointer_loongarch(LinkGraph &G, + Section &PointerSection, + Symbol *InitialTarget = nullptr, + uint64_t InitialAddend = 0) { auto &B = G.createContentBlock(PointerSection, getGOTEntryBlockContent(G), orc::ExecutorAddr(), G.getPointerSize(), 0); if (InitialTarget) @@ -301,9 +302,9 @@ /// Create a jump stub that jumps via the pointer at the given symbol and /// an anonymous symbol pointing to it. Return the anonymous symbol. -inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, - Section &StubSection, - Symbol &PointerSymbol) { +inline Symbol &createAnonymousPointerJumpStub_loongarch(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { Block &StubContentBlock = G.createContentBlock( StubSection, getStubBlockContent(G), orc::ExecutorAddr(), 4, 0); StubContentBlock.addEdge(Page20, 0, PointerSymbol, 0); @@ -341,7 +342,7 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointer(G, getGOTSection(G), &Target); + return createAnonymousPointer_loongarch(G, getGOTSection(G), &Target); } private: @@ -376,8 +377,8 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointerJumpStub(G, getStubsSection(G), - GOT.getEntryForTarget(G, Target)); + return createAnonymousPointerJumpStub_loongarch( + G, getStubsSection(G), GOT.getEntryForTarget(G, Target)); } public: diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h b/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h @@ -540,9 +540,10 @@ /// alignment: 64-bit /// alignment-offset: 0 /// address: highest allowable (~7U) -inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, - Symbol *InitialTarget = nullptr, - uint64_t InitialAddend = 0) { +inline Symbol &createAnonymousPointer_x86_64(LinkGraph &G, + Section &PointerSection, + Symbol *InitialTarget = nullptr, + uint64_t InitialAddend = 0) { auto &B = G.createContentBlock(PointerSection, NullPointerContent, orc::ExecutorAddr(~uint64_t(7)), 8, 0); if (InitialTarget) @@ -556,11 +557,12 @@ /// alignment: 8-bit /// alignment-offset: 0 /// address: highest allowable: (~5U) -inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, - Symbol &PointerSymbol) { +inline Block &createPointerJumpStubBlock_x86_64(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; } @@ -568,12 +570,12 @@ /// an anonymous symbol pointing to it. Return the anonymous symbol. /// /// The stub block will be created by createPointerJumpStubBlock. -inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, - Section &StubSection, - Symbol &PointerSymbol) { +inline Symbol &createAnonymousPointerJumpStub_x86_64(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { return G.addAnonymousSymbol( - createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true, - false); + createPointerJumpStubBlock_x86_64(G, StubSection, PointerSymbol), 0, 6, + true, false); } /// Global Offset Table Builder. @@ -621,7 +623,7 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointer(G, getGOTSection(G), &Target); + return createAnonymousPointer_x86_64(G, getGOTSection(G), &Target); } private: @@ -658,8 +660,8 @@ } Symbol &createEntry(LinkGraph &G, Symbol &Target) { - return createAnonymousPointerJumpStub(G, getStubsSection(G), - GOT.getEntryForTarget(G, Target)); + return createAnonymousPointerJumpStub_x86_64( + G, getStubsSection(G), GOT.getEntryForTarget(G, Target)); } public: diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp --- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp @@ -12,6 +12,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" @@ -416,6 +420,50 @@ " is not aligned to " + Twine(N) + " bytes"); } +Expected createAnonymousPointer(LinkGraph &G, Section &PointerSection, + Symbol *InitialTarget, + uint64_t InitialAddend) { + switch (G.getTargetTriple().getArch()) { + case Triple::aarch64: + return aarch64::createAnonymousPointer_aarch64( + G, PointerSection, InitialTarget, InitialAddend); + case Triple::x86_64: + return x86_64::createAnonymousPointer_x86_64(G, PointerSection, + InitialTarget, InitialAddend); + case Triple::x86: + return i386::createAnonymousPointer_i386(G, PointerSection, InitialTarget, + InitialAddend); + case Triple::loongarch32: + case Triple::loongarch64: + return loongarch::createAnonymousPointer_loongarch( + G, PointerSection, InitialTarget, InitialAddend); + default: + return make_error("Unsupported architecture"); + } +} + +Expected createAnonymousPointerJumpStub(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol) { + switch (G.getTargetTriple().getArch()) { + case Triple::aarch64: + return aarch64::createAnonymousPointerJumpStub_aarch64(G, StubSection, + PointerSymbol); + case Triple::x86_64: + return x86_64::createAnonymousPointerJumpStub_x86_64(G, StubSection, + PointerSymbol); + case Triple::x86: + return i386::createAnonymousPointerJumpStub_i386(G, StubSection, + PointerSymbol); + case Triple::loongarch32: + case Triple::loongarch64: + return loongarch::createAnonymousPointerJumpStub_loongarch(G, StubSection, + PointerSymbol); + default: + return make_error("Unsupported architecture"); + } +} + Expected> createLinkGraphFromObject(MemoryBufferRef ObjectBuffer) { auto Magic = identify_magic(ObjectBuffer.getBuffer()); diff --git a/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp --- a/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ExecutionUtils.cpp @@ -553,7 +553,7 @@ Expected> 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); @@ -573,7 +573,7 @@ // Create __imp_ symbol jitlink::Symbol &Ptr = - jitlink::x86_64::createAnonymousPointer(*G, Sec, &Target); + jitlink::x86_64::createAnonymousPointer_x86_64(*G, Sec, &Target); auto NameCopy = G->allocateContent(Twine(getImpPrefix()) + *KV.first); StringRef NameCopyRef = StringRef(NameCopy.data(), NameCopy.size()); Ptr.setName(NameCopyRef); @@ -583,7 +583,7 @@ // Create PLT stub // FIXME: check PLT stub of data symbol is not accessed jitlink::Block &StubBlock = - jitlink::x86_64::createPointerJumpStubBlock(*G, Sec, Ptr); + jitlink::x86_64::createPointerJumpStubBlock_x86_64(*G, Sec, Ptr); G->addDefinedSymbol(StubBlock, 0, *KV.first, StubBlock.getSize(), jitlink::Linkage::Strong, jitlink::Scope::Default, true, false); diff --git a/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt @@ -11,6 +11,7 @@ AArch32Tests.cpp EHFrameSupportTests.cpp LinkGraphTests.cpp + StubsTests.cpp ) target_link_libraries(JITLinkTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp b/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp @@ -0,0 +1,174 @@ +//===------ 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 +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 PointerSym = createAnonymousPointer(G, PointersSec, &FuncSymbol); + 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 StubSym = createAnonymousPointerJumpStub(G, StubsSec, *PointerSym); + EXPECT_FALSE(errorToBool(StubSym.takeError())); + return {*PointerSym, *StubSym}; +} + +TEST(StubsTest, StubsGeneration_x86_64) { + const char PointerJumpStubContent[6] = { + static_cast(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(PointerJumpStubContent)); +} + +TEST(StubsTest, StubsGeneration_aarch64) { + const char PointerJumpStubContent[12] = { + 0x10, 0x00, 0x00, (char)0x90u, // ADRP x16, @page21 + 0x10, 0x02, 0x40, (char)0xf9u, // LDR x16, [x16, @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(PointerJumpStubContent)); +} + +TEST(StubsTest, StubsGeneration_i386) { + const char PointerJumpStubContent[6] = { + static_cast(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(PointerJumpStubContent)); +} + +TEST(StubsTest, StubsGeneration_loongarch32) { + const char PointerJumpStubContent[12] = { + 0x14, + 0x00, + 0x00, + 0x1a, // pcalau12i $t8, %page20(imm) + static_cast(0x94), + 0x02, + static_cast(0x80), + 0x28, // ld.d $t8, $t8, %pageoff12(imm) + static_cast(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(PointerJumpStubContent)); +} + +TEST(StubsTest, StubsGeneration_loongarch64) { + const char PointerJumpStubContent[12] = { + 0x14, + 0x00, + 0x00, + 0x1a, // pcalau12i $t8, %page20(imm) + static_cast(0x94), + 0x02, + static_cast(0xc0), + 0x28, // ld.d $t8, $t8, %pageoff12(imm) + static_cast(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(PointerJumpStubContent)); +} \ No newline at end of file