https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/77313
From ab1fa3773766072882666a16682e985bbd9cd72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Thu, 18 Jan 2024 17:26:33 +0100 Subject: [PATCH 1/9] [JITLink][AArch32] Add GOT builder and implement R_ARM_GOT_PREL relocations for ELF --- .../llvm/ExecutionEngine/JITLink/aarch32.h | 17 ++++- .../ExecutionEngine/JITLink/ELF_aarch32.cpp | 7 ++ llvm/lib/ExecutionEngine/JITLink/aarch32.cpp | 65 +++++++++++++++---- .../JITLink/AArch32/ELF_relocations_data.s | 50 +++++++++++--- 4 files changed, 118 insertions(+), 21 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h index 7765208b5e3dfec..0968a093279bfbf 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h @@ -41,7 +41,10 @@ enum EdgeKind_aarch32 : Edge::Kind { /// Absolute 32-bit value relocation Data_Pointer32, - LastDataRelocation = Data_Pointer32, + /// Create GOT entry and store offset + Data_RequestGOTAndTransformToDelta32, + + LastDataRelocation = Data_RequestGOTAndTransformToDelta32, /// /// Relocations of class Arm (covers fixed-width 4-byte instruction subset) @@ -318,6 +321,18 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, llvm_unreachable("Relocation must be of class Data, Arm or Thumb"); } +/// Populate a Global Offset Table from edges that request it. +class GOTBuilder : public TableManager<GOTBuilder> { +public: + static StringRef getSectionName() { return "$__GOT"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E); + Symbol &createEntry(LinkGraph &G, Symbol &Target); + +private: + Section *GOTSection = nullptr; +}; + /// Stubs builder for v7 emits non-position-independent Thumb stubs. /// /// Right now we only have one default stub kind, but we want to extend this diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp index b862a7ba2acc922..2553cd70a576912 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp @@ -35,6 +35,8 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) { switch (ELFType) { case ELF::R_ARM_ABS32: return aarch32::Data_Pointer32; + case ELF::R_ARM_GOT_PREL: + return aarch32::Data_RequestGOTAndTransformToDelta32; case ELF::R_ARM_REL32: return aarch32::Data_Delta32; case ELF::R_ARM_CALL: @@ -71,6 +73,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) { return ELF::R_ARM_REL32; case aarch32::Data_Pointer32: return ELF::R_ARM_ABS32; + case aarch32::Data_RequestGOTAndTransformToDelta32: + return ELF::R_ARM_GOT_PREL; case aarch32::Arm_Call: return ELF::R_ARM_CALL; case aarch32::Arm_Jump24: @@ -222,6 +226,9 @@ Error buildTables_ELF_aarch32(LinkGraph &G) { StubsManagerType StubsManager; visitExistingEdges(G, StubsManager); + aarch32::GOTBuilder GOT; + visitExistingEdges(G, GOT); + return Error::success(); } diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp index 8153c97deff6289..eb51a4896ddcc66 100644 --- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp @@ -395,6 +395,7 @@ Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset, switch (Kind) { case Data_Delta32: case Data_Pointer32: + case Data_RequestGOTAndTransformToDelta32: return SignExtend64<32>(support::endian::read32(FixupPtr, Endian)); default: return make_error<JITLinkError>( @@ -464,15 +465,6 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) { char *BlockWorkingMem = B.getAlreadyMutableContent().data(); char *FixupPtr = BlockWorkingMem + E.getOffset(); - auto Write32 = [FixupPtr, Endian = G.getEndianness()](int64_t Value) { - assert(isInt<32>(Value) && "Must be in signed 32-bit range"); - uint32_t Imm = static_cast<int32_t>(Value); - if (LLVM_LIKELY(Endian == endianness::little)) - endian::write32<endianness::little>(FixupPtr, Imm); - else - endian::write32<endianness::big>(FixupPtr, Imm); - }; - Edge::Kind Kind = E.getKind(); uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue(); int64_t Addend = E.getAddend(); @@ -487,16 +479,24 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) { int64_t Value = TargetAddress - FixupAddress + Addend; if (!isInt<32>(Value)) return makeTargetOutOfRangeError(G, B, E); - Write32(Value); + if (LLVM_LIKELY(G.getEndianness() == endianness::little)) + endian::write32le(FixupPtr, Value); + else + endian::write32be(FixupPtr, Value); return Error::success(); } case Data_Pointer32: { int64_t Value = TargetAddress + Addend; - if (!isInt<32>(Value)) + if (!isUInt<32>(Value)) return makeTargetOutOfRangeError(G, B, E); - Write32(Value); + if (LLVM_LIKELY(G.getEndianness() == endianness::little)) + endian::write32le(FixupPtr, Value); + else + endian::write32be(FixupPtr, Value); return Error::success(); } + case Data_RequestGOTAndTransformToDelta32: + llvm_unreachable("Should be transformed"); default: return make_error<JITLinkError>( "In graph " + G.getName() + ", section " + B.getSection().getName() + @@ -678,6 +678,46 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E, } } +const uint8_t GOTEntryInit[] = { + 0x00, 0x00, 0x00, 0x00, +}; + +/// Create a new node in the link-graph for the given pointer value. +template <size_t Size> +static Block &allocPointer(LinkGraph &G, Section &S, const uint8_t (&Content)[Size]) { + static_assert(Size == 4, "Pointers are 32-bit"); + constexpr uint64_t Alignment = 4; + ArrayRef<char> Init(reinterpret_cast<const char *>(Content), Size); + return G.createContentBlock(S, Init, orc::ExecutorAddr(), Alignment, 0); +} + +Symbol &GOTBuilder::createEntry(LinkGraph &G, Symbol &Target) { + if (!GOTSection) + GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read); + Block &B = allocPointer(G, *GOTSection, GOTEntryInit); + constexpr int64_t GOTEntryAddend = 0; + B.addEdge(Data_Pointer32, 0, Target, GOTEntryAddend); + return G.addAnonymousSymbol(B, 0, B.getSize(), false, false); +} + +bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) { + Edge::Kind KindToSet = Edge::Invalid; + switch (E.getKind()) { + case aarch32::Data_RequestGOTAndTransformToDelta32: { + KindToSet = aarch32::Data_Delta32; + break; + } + default: + return false; + } + LLVM_DEBUG(dbgs() << " Transforming " << G.getEdgeKindName(E.getKind()) << " edge at " + << B->getFixupAddress(E) << " (" << B->getAddress() << " + " + << formatv("{0:x}", E.getOffset()) << ") into " << G.getEdgeKindName(KindToSet) << "\n"); + E.setKind(KindToSet); + E.setTarget(getEntryForTarget(G, E.getTarget())); + return true; +} + const uint8_t Thumbv7ABS[] = { 0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000 ; lower 16-bit 0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000 ; upper 16-bit @@ -709,6 +749,7 @@ const char *getEdgeKindName(Edge::Kind K) { switch (K) { KIND_NAME_CASE(Data_Delta32) KIND_NAME_CASE(Data_Pointer32) + KIND_NAME_CASE(Data_RequestGOTAndTransformToDelta32) KIND_NAME_CASE(Arm_Call) KIND_NAME_CASE(Arm_Jump24) KIND_NAME_CASE(Arm_MovwAbsNC) diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s index 379d35fe4902cb0..f91a4733c40eee0 100644 --- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s +++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s @@ -1,12 +1,13 @@ -# RUN: llvm-mc -triple=armv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t_armv7.o %s -# RUN: llvm-objdump -r %t_armv7.o | FileCheck --check-prefix=CHECK-TYPE %s +# RUN: rm -rf %t && mkdir -p %t/armv7 && mkdir -p %t/thumbv7 +# RUN: llvm-mc -triple=armv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t/armv7/out.o %s +# RUN: llvm-objdump -r %t/armv7/out.o | FileCheck --check-prefix=CHECK-TYPE %s # RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb -slab-page-size 4096 \ -# RUN: -abs target=0x76bbe88f -check %s %t_armv7.o +# RUN: -abs target=0x76bbe88f -check %s %t/armv7/out.o -# RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t_thumbv7.o %s -# RUN: llvm-objdump -r %t_thumbv7.o | FileCheck --check-prefix=CHECK-TYPE %s +# RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t/thumbv7/out.o %s +# RUN: llvm-objdump -r %t/thumbv7/out.o | FileCheck --check-prefix=CHECK-TYPE %s # RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb -slab-page-size 4096 \ -# RUN: -abs target=0x76bbe88f -check %s %t_thumbv7.o +# RUN: -abs target=0x76bbe88f -check %s %t/thumbv7/out.o .data .global target @@ -28,10 +29,43 @@ rel32: .word target - . .size rel32, .-rel32 -# Empty main function for jitlink to be happy +# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_GOT_PREL target +# +# The GOT entry contains the absolute address of the external: +# jitlink-check: *{4}(got_addr(out.o, target)) = target +# +# The embedded offset value contains the offset to the GOT entry relative to pc. +# The +12 accounts for the ARM branch offset (8) and the .LPC offset (4), which +# is stored as initial addend inline. +# FIXME: We shouldn't need to substract the 64-bit sign-extension manually. +# jitlink-check: *{4}got_prel_offset = got_addr(out.o, target) - (got_prel + 12) - 0xffffffff00000000 + .globl got_prel + .type got_prel,%function + .p2align 2 + .code 32 +got_prel: + ldr r0, .LCPI +.LPC: + ldr r0, [pc, r0] + ldr r0, [r0] + bx lr +# Actual relocation site is on the embedded offset value: + .globl got_prel_offset +got_prel_offset: +.LCPI: + .long target(GOT_PREL)-((.LPC+8)-.LCPI) + .size got_prel_offset, .-got_prel_offset + .size got_prel, .-got_prel + +# This test is executable with any 4-byte external target: +# > echo "unsigned target = 42;" | clang -target armv7-linux-gnueabihf -o target.o -c -xc - +# > llvm-jitlink target.o armv7/out.o +# .globl main .type main, %function .p2align 2 main: - bx lr + push {lr} + bl got_prel + pop {pc} .size main, .-main From 8b607588a28b54c70609be0d7bc160f732797db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Fri, 19 Jan 2024 19:17:36 +0100 Subject: [PATCH 2/9] fixup! [JITLink][AArch32] Add GOT builder and implement R_ARM_GOT_PREL relocations for ELF --- llvm/lib/ExecutionEngine/JITLink/aarch32.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp index eb51a4896ddcc66..111527a39e06ecd 100644 --- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp @@ -679,12 +679,16 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E, } const uint8_t GOTEntryInit[] = { - 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, + 0x00, + 0x00, }; /// Create a new node in the link-graph for the given pointer value. template <size_t Size> -static Block &allocPointer(LinkGraph &G, Section &S, const uint8_t (&Content)[Size]) { +static Block &allocPointer(LinkGraph &G, Section &S, + const uint8_t (&Content)[Size]) { static_assert(Size == 4, "Pointers are 32-bit"); constexpr uint64_t Alignment = 4; ArrayRef<char> Init(reinterpret_cast<const char *>(Content), Size); @@ -710,9 +714,11 @@ bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) { default: return false; } - LLVM_DEBUG(dbgs() << " Transforming " << G.getEdgeKindName(E.getKind()) << " edge at " - << B->getFixupAddress(E) << " (" << B->getAddress() << " + " - << formatv("{0:x}", E.getOffset()) << ") into " << G.getEdgeKindName(KindToSet) << "\n"); + LLVM_DEBUG(dbgs() << " Transforming " << G.getEdgeKindName(E.getKind()) + << " edge at " << B->getFixupAddress(E) << " (" + << B->getAddress() << " + " + << formatv("{0:x}", E.getOffset()) << ") into " + << G.getEdgeKindName(KindToSet) << "\n"); E.setKind(KindToSet); E.setTarget(getEntryForTarget(G, E.getTarget())); return true; From e19bf04dd167179833093878d66779df683ed994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Sat, 13 Jan 2024 23:35:54 +0100 Subject: [PATCH 3/9] [llvm-jitlink] Allow optional stub-kind filter in stub_addr() expressions --- .../llvm/ExecutionEngine/RuntimeDyldChecker.h | 2 +- .../RuntimeDyld/RuntimeDyldChecker.cpp | 27 ++++-- .../RuntimeDyld/RuntimeDyldCheckerImpl.h | 3 +- llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp | 2 +- llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 86 +++++++++++++++++-- llvm/tools/llvm-jitlink/llvm-jitlink.h | 7 +- llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp | 11 ++- 7 files changed, 114 insertions(+), 24 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h index 80e4bbf494339cf..034c134a13731d3 100644 --- a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h +++ b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h @@ -154,7 +154,7 @@ class RuntimeDyldChecker { using GetSectionInfoFunction = std::function<Expected<MemoryRegionInfo>( StringRef FileName, StringRef SectionName)>; using GetStubInfoFunction = std::function<Expected<MemoryRegionInfo>( - StringRef StubContainer, StringRef TargetName)>; + StringRef StubContainer, StringRef TargetName, StringRef StubKindFilter)>; using GetGOTInfoFunction = std::function<Expected<MemoryRegionInfo>( StringRef GOTContainer, StringRef TargetName)>; diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp index 7fadbdd6a1fff2b..11fb21a9c1c0a70 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp @@ -400,6 +400,15 @@ class RuntimeDyldCheckerExprEval { StringRef Symbol; std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); + // Parse optional parameter to filter by stub kind + StringRef KindNameFilter; + if (RemainingExpr.starts_with(",")) { + RemainingExpr = RemainingExpr.substr(1).ltrim(); + size_t ClosingBracket = RemainingExpr.find(")"); + KindNameFilter = RemainingExpr.substr(0, ClosingBracket); + RemainingExpr = RemainingExpr.substr(ClosingBracket); + } + if (!RemainingExpr.starts_with(")")) return std::make_pair( unexpectedToken(RemainingExpr, Expr, "expected ')'"), ""); @@ -407,8 +416,9 @@ class RuntimeDyldCheckerExprEval { uint64_t StubAddr; std::string ErrorMsg; - std::tie(StubAddr, ErrorMsg) = Checker.getStubOrGOTAddrFor( - StubContainerName, Symbol, PCtx.IsInsideLoad, IsStubAddr); + std::tie(StubAddr, ErrorMsg) = + Checker.getStubOrGOTAddrFor(StubContainerName, Symbol, KindNameFilter, + PCtx.IsInsideLoad, IsStubAddr); if (ErrorMsg != "") return std::make_pair(EvalResult(ErrorMsg), ""); @@ -985,11 +995,14 @@ std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getSectionAddr( } std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getStubOrGOTAddrFor( - StringRef StubContainerName, StringRef SymbolName, bool IsInsideLoad, - bool IsStubAddr) const { - - auto StubInfo = IsStubAddr ? GetStubInfo(StubContainerName, SymbolName) - : GetGOTInfo(StubContainerName, SymbolName); + StringRef StubContainerName, StringRef SymbolName, StringRef StubKindFilter, + bool IsInsideLoad, bool IsStubAddr) const { + + assert((StubKindFilter.empty() || IsStubAddr) && + "Kind name filter only supported for stubs"); + auto StubInfo = + IsStubAddr ? GetStubInfo(StubContainerName, SymbolName, StubKindFilter) + : GetGOTInfo(StubContainerName, SymbolName); if (!StubInfo) { std::string ErrMsg; diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h index 9f44a9389f47344..bda554e9e5b673c 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldCheckerImpl.h @@ -64,7 +64,8 @@ class RuntimeDyldCheckerImpl { std::pair<uint64_t, std::string> getStubOrGOTAddrFor(StringRef StubContainerName, StringRef Symbol, - bool IsInsideLoad, bool IsStubAddr) const; + StringRef StubKindFilter, bool IsInsideLoad, + bool IsStubAddr) const; std::optional<uint64_t> getSectionLoadAddress(void *LocalAddr) const; diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp index c6b4218aad7af86..1f0fca2202a0ef7 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp @@ -91,7 +91,7 @@ static Error registerSymbol(LinkGraph &G, Symbol &Sym, Session::FileInfo &FI, case Stubs: return FI.registerStubEntry(G, Sym, getELFStubTarget); case AArch32Stubs: - return FI.registerStubEntry(G, Sym, getELFAArch32StubTarget); + return FI.registerMultiStubEntry(G, Sym, getELFAArch32StubTarget); case Other: return Error::success(); } diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp index 8c18610313ce8f4..d233ebdb5a3a8dc 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -331,8 +331,12 @@ operator<<(raw_ostream &OS, const Session::FileInfo &FI) { OS << " Section \"" << SIKV.first() << "\": " << SIKV.second << "\n"; for (auto &GOTKV : FI.GOTEntryInfos) OS << " GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n"; - for (auto &StubKV : FI.StubInfos) - OS << " Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n"; + for (auto &StubKVs : FI.StubInfos) { + OS << " Stubs \"" << StubKVs.first() << "\":"; + for (auto MemRegion : StubKVs.second) + OS << " " << MemRegion; + OS << "\n"; + } return OS; } @@ -1207,9 +1211,35 @@ Error Session::FileInfo::registerStubEntry( auto TS = GetSymbolTarget(G, Sym.getBlock()); if (!TS) return TS.takeError(); - StubInfos[TS->getName()] = {Sym.getSymbolContent(), - Sym.getAddress().getValue(), - Sym.getTargetFlags()}; + + SmallVector<MemoryRegionInfo> &Entry = StubInfos[TS->getName()]; + Entry.insert(Entry.begin(), + {Sym.getSymbolContent(), Sym.getAddress().getValue(), + Sym.getTargetFlags()}); + return Error::success(); +} + +Error Session::FileInfo::registerMultiStubEntry( + LinkGraph &G, Symbol &Sym, GetSymbolTargetFunction GetSymbolTarget) { + if (Sym.isSymbolZeroFill()) + return make_error<StringError>("Unexpected zero-fill symbol in section " + + Sym.getBlock().getSection().getName(), + inconvertibleErrorCode()); + + auto Target = GetSymbolTarget(G, Sym.getBlock()); + if (!Target) + return Target.takeError(); + + SmallVector<MemoryRegionInfo> &Entry = StubInfos[Target->getName()]; + Entry.emplace_back(Sym.getSymbolContent(), Sym.getAddress().getValue(), + Sym.getTargetFlags()); + + // Let's keep stubs ordered by ascending address. + std::sort(Entry.begin(), Entry.end(), + [](const MemoryRegionInfo &L, const MemoryRegionInfo &R) { + return L.getTargetAddress() < R.getTargetAddress(); + }); + return Error::success(); } @@ -1235,8 +1265,14 @@ Session::findSectionInfo(StringRef FileName, StringRef SectionName) { return SecInfoItr->second; } +static StringRef detectStubKind(const Session::MemoryRegionInfo &Stub) { + // Implement acutal stub kind detection + return ""; +} + Expected<Session::MemoryRegionInfo &> -Session::findStubInfo(StringRef FileName, StringRef TargetName) { +Session::findStubInfo(StringRef FileName, StringRef TargetName, + StringRef KindNameFilter) { auto FI = findFileInfo(FileName); if (!FI) return FI.takeError(); @@ -1246,7 +1282,38 @@ Session::findStubInfo(StringRef FileName, StringRef TargetName) { "\" registered for file \"" + FileName + "\"", inconvertibleErrorCode()); - return StubInfoItr->second; + auto &StubsForTarget = StubInfoItr->second; + assert(!StubsForTarget.empty() && "At least 1 stub in each entry"); + if (KindNameFilter.empty() && StubsForTarget.size() == 1) + return StubsForTarget[0]; // Regular single-stub match + + std::string KindsStr; + SmallVector<MemoryRegionInfo *, 1> Matches; + Regex KindNameMatcher(KindNameFilter.empty() ? ".*" : KindNameFilter); + for (MemoryRegionInfo &Stub : StubsForTarget) { + StringRef Kind = detectStubKind(Stub); + if (KindNameMatcher.match(Kind)) + Matches.push_back(&Stub); + KindsStr += "\"" + (Kind.empty() ? "<unknown>" : Kind.str()) + "\", "; + } + if (Matches.empty()) + return make_error<StringError>( + "\"" + TargetName + "\" has " + Twine(StubsForTarget.size()) + + " stubs in file \"" + FileName + + "\", but none of them matches the stub-kind filter \"" + + KindNameFilter + "\" (all encountered kinds are " + + StringRef(KindsStr.data(), KindsStr.size() - 2) + ").", + inconvertibleErrorCode()); + if (Matches.size() > 1) + return make_error<StringError>( + "\"" + TargetName + "\" has " + Twine(Matches.size()) + + " candidate stubs in file \"" + FileName + + "\". Please refine stub-kind filter \"" + KindNameFilter + + "\" for disambiguation (encountered kinds are " + + StringRef(KindsStr.data(), KindsStr.size() - 2) + ").", + inconvertibleErrorCode()); + + return *Matches[0]; } Expected<Session::MemoryRegionInfo &> @@ -2015,8 +2082,9 @@ static Error runChecks(Session &S, Triple TT, SubtargetFeatures Features) { return S.findSectionInfo(FileName, SectionName); }; - auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) { - return S.findStubInfo(FileName, SectionName); + auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName, + StringRef KindNameFilter) { + return S.findStubInfo(FileName, SectionName, KindNameFilter); }; auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) { diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h index 93a00266b150436..e09c15adace20ec 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink.h +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h @@ -49,7 +49,7 @@ struct Session { struct FileInfo { StringMap<MemoryRegionInfo> SectionInfos; - StringMap<MemoryRegionInfo> StubInfos; + StringMap<SmallVector<MemoryRegionInfo, 1>> StubInfos; StringMap<MemoryRegionInfo> GOTEntryInfos; using Symbol = jitlink::Symbol; @@ -61,6 +61,8 @@ struct Session { GetSymbolTargetFunction GetSymbolTarget); Error registerStubEntry(LinkGraph &G, Symbol &Sym, GetSymbolTargetFunction GetSymbolTarget); + Error registerMultiStubEntry(LinkGraph &G, Symbol &Sym, + GetSymbolTargetFunction GetSymbolTarget); }; using DynLibJDMap = std::map<std::string, orc::JITDylib *>; @@ -74,7 +76,8 @@ struct Session { Expected<MemoryRegionInfo &> findSectionInfo(StringRef FileName, StringRef SectionName); Expected<MemoryRegionInfo &> findStubInfo(StringRef FileName, - StringRef TargetName); + StringRef TargetName, + StringRef KindNameFilter); Expected<MemoryRegionInfo &> findGOTEntryInfo(StringRef FileName, StringRef TargetName); diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp index 107b555a99faa40..4cb76f4347422b6 100644 --- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -926,7 +926,8 @@ static int linkAndVerify() { }; auto GetStubInfo = [&Dyld, &StubMap](StringRef StubContainer, - StringRef SymbolName) + StringRef SymbolName, + StringRef KindNameFilter) -> Expected<RuntimeDyldChecker::MemoryRegionInfo> { if (!StubMap.count(StubContainer)) return make_error<StringError>("Stub container not found: " + @@ -947,6 +948,11 @@ static int linkAndVerify() { return StubMemInfo; }; + auto GetGOTInfo = [&GetStubInfo](StringRef StubContainer, + StringRef SymbolName) { + return GetStubInfo(StubContainer, SymbolName, ""); + }; + // We will initialize this below once we have the first object file and can // know the endianness. std::unique_ptr<RuntimeDyldChecker> Checker; @@ -977,8 +983,7 @@ static int linkAndVerify() { if (!Checker) Checker = std::make_unique<RuntimeDyldChecker>( - IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, - GetStubInfo, + IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo, Obj.isLittleEndian() ? llvm::endianness::little : llvm::endianness::big, TheTriple, MCPU, SubtargetFeatures(), dbgs()); From 559cf1218c87e304dcb474b7f1de5bc13d537247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Sat, 13 Jan 2024 23:58:36 +0100 Subject: [PATCH 4/9] [JITLink][AArch32] Multi-stub support for armv7/thumbv7 --- .../llvm/ExecutionEngine/JITLink/aarch32.h | 59 ++------- llvm/lib/ExecutionEngine/JITLink/aarch32.cpp | 125 ++++++++++++++++-- .../JITLink/AArch32/ELF_stubs_arm.s | 53 ++++++++ .../JITLink/AArch32/ELF_stubs_multi.s | 50 +++++++ llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 46 ++++++- 5 files changed, 274 insertions(+), 59 deletions(-) create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s create mode 100644 llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h index 0968a093279bfbf..725f9a7eeb88896 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h @@ -333,64 +333,31 @@ class GOTBuilder : public TableManager<GOTBuilder> { Section *GOTSection = nullptr; }; -/// Stubs builder for v7 emits non-position-independent Thumb stubs. -/// -/// Right now we only have one default stub kind, but we want to extend this -/// and allow creation of specific kinds in the future (e.g. branch range -/// extension or interworking). -/// -/// Let's keep it simple for the moment and not wire this through a GOT. -/// -class StubsManager_v7 : public TableManager<StubsManager_v7> { +/// Stubs builder for v7 emits non-position-independent Arm and Thumb stubs. +class StubsManager_v7 { public: StubsManager_v7() = default; /// Name of the object file section that will contain all our stubs. static StringRef getSectionName() { - return "__llvm_jitlink_aarch32_STUBS_Thumbv7"; + return "__llvm_jitlink_aarch32_STUBS_v7"; } /// Implements link-graph traversal via visitExistingEdges(). - bool visitEdge(LinkGraph &G, Block *B, Edge &E) { - if (E.getTarget().isDefined()) - return false; - - switch (E.getKind()) { - case Thumb_Call: - case Thumb_Jump24: { - DEBUG_WITH_TYPE("jitlink", { - dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " - << B->getFixupAddress(E) << " (" << B->getAddress() << " + " - << formatv("{0:x}", E.getOffset()) << ")\n"; - }); - E.setTarget(this->getEntryForTarget(G, E.getTarget())); - return true; - } - } - return false; - } - - /// Create a branch range extension stub with Thumb encoding for v7 CPUs. - Symbol &createEntry(LinkGraph &G, Symbol &Target); + bool visitEdge(LinkGraph &G, Block *B, Edge &E); private: - /// Create a new node in the link-graph for the given stub template. - template <size_t Size> - Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size], - uint64_t Alignment) { - ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size); - return G.createContentBlock(getStubsSection(G), Template, - orc::ExecutorAddr(), Alignment, 0); - } - - /// Get or create the object file section that will contain all our stubs. - Section &getStubsSection(LinkGraph &G) { - if (!StubsSection) - StubsSection = &G.createSection(getSectionName(), - orc::MemProt::Read | orc::MemProt::Exec); - return *StubsSection; + // Two slots per external: Arm and Thumb + using StubMapEntry = std::tuple<Symbol *, Symbol *>; + + Symbol *&getStubSymbolSlot(StringRef Name, bool Thumb) { + StubMapEntry &Stubs = StubMap.try_emplace(Name).first->second; + if (Thumb) + return std::get<1>(Stubs); + return std::get<0>(Stubs); } + DenseMap<StringRef, StubMapEntry> StubMap; Section *StubsSection = nullptr; }; diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp index 111527a39e06ecd..7f7b460b4b36968 100644 --- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ManagedStatic.h" @@ -724,27 +725,127 @@ bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) { return true; } +const uint8_t Armv7ABS[] = { + 0x00, 0xc0, 0x00, 0xe3, // movw r12, #0x0000 ; lower 16-bit + 0x00, 0xc0, 0x40, 0xe3, // movt r12, #0x0000 ; upper 16-bit + 0x1c, 0xff, 0x2f, 0xe1 // bx r12 +}; + const uint8_t Thumbv7ABS[] = { 0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000 ; lower 16-bit 0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000 ; upper 16-bit 0x60, 0x47 // bx r12 }; -Symbol &StubsManager_v7::createEntry(LinkGraph &G, Symbol &Target) { +/// Create a new node in the link-graph for the given stub template. +template <size_t Size> +static Block &allocStub(LinkGraph &G, Section &S, const uint8_t (&Code)[Size]) { constexpr uint64_t Alignment = 4; - Block &B = addStub(G, Thumbv7ABS, Alignment); - LLVM_DEBUG({ - const char *StubPtr = B.getContent().data(); - HalfWords Reg12 = encodeRegMovtT1MovwT3(12); - assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) && - checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) && - "Linker generated stubs may only corrupt register r12 (IP)"); - }); + ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size); + return G.createContentBlock(S, Template, orc::ExecutorAddr(), Alignment, 0); +} + +static Block &createStubThumbv7(LinkGraph &G, Section &S, Symbol &Target) { + Block &B = allocStub(G, S, Thumbv7ABS); B.addEdge(Thumb_MovwAbsNC, 0, Target, 0); B.addEdge(Thumb_MovtAbs, 4, Target, 0); - Symbol &Stub = G.addAnonymousSymbol(B, 0, B.getSize(), true, false); - Stub.setTargetFlags(ThumbSymbol); - return Stub; + + [[maybe_unused]] const char *StubPtr = B.getContent().data(); + [[maybe_unused]] HalfWords Reg12 = encodeRegMovtT1MovwT3(12); + assert(checkRegister<Thumb_MovwAbsNC>(StubPtr, Reg12) && + checkRegister<Thumb_MovtAbs>(StubPtr + 4, Reg12) && + "Linker generated stubs may only corrupt register r12 (IP)"); + return B; +} + +static Block &createStubArmv7(LinkGraph &G, Section &S, Symbol &Target) { + Block &B = allocStub(G, S, Armv7ABS); + B.addEdge(Arm_MovwAbsNC, 0, Target, 0); + B.addEdge(Arm_MovtAbs, 4, Target, 0); + + [[maybe_unused]] const char *StubPtr = B.getContent().data(); + [[maybe_unused]] uint32_t Reg12 = encodeRegMovtA1MovwA2(12); + assert(checkRegister<Arm_MovwAbsNC>(StubPtr, Reg12) && + checkRegister<Arm_MovtAbs>(StubPtr + 4, Reg12) && + "Linker generated stubs may only corrupt register r12 (IP)"); + return B; +} + +static bool needsStub(const Edge &E) { + Symbol &Target = E.getTarget(); + + // Create stubs for external branch targets. + if (!Target.isDefined()) { + switch (E.getKind()) { + case Arm_Call: + case Arm_Jump24: + case Thumb_Call: + case Thumb_Jump24: + return true; + default: + return false; + } + } + + // For local targets, create interworking stubs if we switch Arm/Thumb with an + // instruction that cannot switch the instruction set state natively. + bool TargetIsThumb = Target.getTargetFlags() & ThumbSymbol; + switch (E.getKind()) { + case Arm_Jump24: + return TargetIsThumb; // Branch to Thumb needs interworking stub + case Thumb_Jump24: + return !TargetIsThumb; // Branch to Arm needs interworking stub + default: + break; + } + + return false; +} + +bool StubsManager_v7::visitEdge(LinkGraph &G, Block *B, Edge &E) { + if (!needsStub(E)) + return false; + + // Stub Arm/Thumb follows instruction set state at relocation site. + // TODO: We may reduce them at relaxation time and reuse freed slots. + bool MakeThumb = (E.getKind() > LastArmRelocation); + LLVM_DEBUG(dbgs() << " Preparing " << (MakeThumb ? "Thumb" : "Arm") + << " stub for " << G.getEdgeKindName(E.getKind()) + << " edge at " << B->getFixupAddress(E) << " (" + << B->getAddress() << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"); + + Symbol &Target = E.getTarget(); + assert(Target.hasName() && "Edge cannot point to anonymous target"); + Symbol *&StubSymbol = getStubSymbolSlot(Target.getName(), MakeThumb); + + if (!StubSymbol) { + if (!StubsSection) + StubsSection = &G.createSection(getSectionName(), + orc::MemProt::Read | orc::MemProt::Exec); + Block &B = MakeThumb ? createStubThumbv7(G, *StubsSection, Target) + : createStubArmv7(G, *StubsSection, Target); + StubSymbol = &G.addAnonymousSymbol(B, 0, B.getSize(), true, false); + if (MakeThumb) + StubSymbol->setTargetFlags(ThumbSymbol); + + LLVM_DEBUG({ + dbgs() << " Created " << (MakeThumb ? "Thumb" : "Arm") << " entry for " + << Target.getName() << " in " << StubsSection->getName() << ": " + << *StubSymbol << "\n"; + }); + } + + assert(MakeThumb == (StubSymbol->getTargetFlags() & ThumbSymbol) && + "Instruction set states of stub and relocation site should be equal"); + LLVM_DEBUG({ + dbgs() << " Using " << (MakeThumb ? "Thumb" : "Arm") << " entry " + << *StubSymbol << " in " + << StubSymbol->getBlock().getSection().getName() << "\n"; + }); + + E.setTarget(*StubSymbol); + return true; } const char *getEdgeKindName(Edge::Kind K) { diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s new file mode 100644 index 000000000000000..fb2e0eb2c0bf249 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s @@ -0,0 +1,53 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \ +# RUN: -filetype=obj -o %t/out.o %s +# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \ +# RUN: -slab-allocate 10Kb -slab-page-size 4096 \ +# RUN: -abs ext=0x76bbe880 \ +# RUN: -check %s %t/out.o + + .text + .syntax unified + +# Check that calls/jumps to external functions trigger the generation of +# branch-range extension stubs. These stubs don't follow the default PLT model +# where the branch-target address is loaded from a GOT entry. Instead, they +# hard-code it in the immediate field. + +# The external function ext will return to the caller directly. +# jitlink-check: decode_operand(test_arm_jump, 0) = stub_addr(out.o, ext) - (test_arm_jump + 8) + .globl test_arm_jump + .type test_arm_jump,%function + .p2align 2 +test_arm_jump: + b ext + .size test_arm_jump, .-test_arm_jump + +# The branch-with-link sets the LR register so that the external function ext +# returns to us. We have to save the register (push) and return to main manually +# (pop). This adds the +4 offset for the bl instruction we decode: +# jitlink-check: decode_operand(test_arm_call + 4, 0) = stub_addr(out.o, ext) - (test_arm_call + 8) - 4 + .globl test_arm_call + .type test_arm_call,%function + .p2align 2 +test_arm_call: + push {lr} + bl ext + pop {pc} + .size test_arm_call, .-test_arm_call + +# This test is executable with both, Arm and Thumb `ext` functions. It only has +# to return with `bx lr`. For example: +# > echo "void ext() {}" | clang -target armv7-linux-gnueabihf -o ext-arm.o -c -xc - +# > llvm-jitlink ext-arm.o out.o +# + .globl main + .type main,%function + .p2align 2 +main: + push {lr} + bl test_arm_call + bl test_arm_jump + movw r0, #0 + pop {pc} + .size main, .-main diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s new file mode 100644 index 000000000000000..d575f114dcba1e7 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_multi.s @@ -0,0 +1,50 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \ +# RUN: -filetype=obj -o %t/out.o %s +# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \ +# RUN: -slab-allocate=10Kb -slab-page-size=4096 \ +# RUN: -abs ext=0x76bbe880 -check %s %t/out.o + + .text + .syntax unified + +# Check that a single external symbol can have multiple stubs. We access them +# with the extra stub-index argument to stub_addr(). Stubs are sorted by +# ascending size (because the default memory manager lays out blocks by size). + +# Thumb relocation site emits thumb stub +# jitlink-check: decode_operand(test_stub_thumb, 0) = stub_addr(out.o, ext, thumb) - (test_stub_thumb + 4) + .globl test_stub_thumb + .type test_stub_thumb,%function + .p2align 1 + .code 16 + .thumb_func +test_stub_thumb: + b ext + .size test_stub_thumb, .-test_stub_thumb + +# Arm relocation site emits arm stub +# jitlink-check: decode_operand(test_stub_arm, 0) = stub_addr(out.o, ext, arm) - (test_stub_arm + 8) + .globl test_stub_arm + .type test_stub_arm,%function + .p2align 2 + .code 32 +test_stub_arm: + b ext + .size test_stub_arm, .-test_stub_arm + +# This test is executable with both, Arm and Thumb `ext` functions. It only has +# to return (directly to main) with `bx lr`. For example: +# > echo "void ext() {}" | clang -target armv7-linux-gnueabihf -o ext-arm.o -c -xc - +# > llvm-jitlink ext-arm.o out.o +# + .globl main + .type main,%function + .p2align 2 +main: + push {lr} + bl test_stub_arm + bl test_stub_thumb + movw r0, #0 + pop {pc} + .size main, .-main diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp index d233ebdb5a3a8dc..c0554108818f68b 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -1265,8 +1265,52 @@ Session::findSectionInfo(StringRef FileName, StringRef SectionName) { return SecInfoItr->second; } +class MemoryMatcher { +public: + MemoryMatcher(ArrayRef<char> Content) + : Pos(Content.data()), End(Pos + Content.size()) {} + + template <typename MaskType> bool matchMask(MaskType Mask) { + if (Mask == (Mask & *reinterpret_cast<const MaskType *>(Pos))) { + Pos += sizeof(MaskType); + return true; + } + return false; + } + + template <typename ValueType> bool matchEqual(ValueType Value) { + if (Value == *reinterpret_cast<const ValueType *>(Pos)) { + Pos += sizeof(ValueType); + return true; + } + return false; + } + + bool done() const { return Pos == End; } + +private: + const char *Pos; + const char *End; +}; + static StringRef detectStubKind(const Session::MemoryRegionInfo &Stub) { - // Implement acutal stub kind detection + constexpr uint32_t Armv7MovWTle = 0xe300c000; + constexpr uint32_t Armv7BxR12le = 0xe12fff1c; + constexpr uint32_t Thumbv7MovWTle = 0x0c00f240; + constexpr uint16_t Thumbv7BxR12le = 0x4760; + + MemoryMatcher M(Stub.getContent()); + if (M.matchMask(Thumbv7MovWTle)) { + if (M.matchMask(Thumbv7MovWTle)) + if (M.matchEqual(Thumbv7BxR12le)) + if (M.done()) + return "thumbv7_abs_le"; + } else if (M.matchMask(Armv7MovWTle)) { + if (M.matchMask(Armv7MovWTle)) + if (M.matchEqual(Armv7BxR12le)) + if (M.done()) + return "armv7_abs_le"; + } return ""; } From 7215c81f693ec1caaac5006e1d269623a2068bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Sat, 20 Jan 2024 11:13:45 +0100 Subject: [PATCH 5/9] [clang-repl] Fix linker error on ARM Error in gold linker is: PLT offset too large, try linking with --long-plt --- clang/tools/clang-repl/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt index 2ccbe292fd49e09..8589e37418354e7 100644 --- a/clang/tools/clang-repl/CMakeLists.txt +++ b/clang/tools/clang-repl/CMakeLists.txt @@ -22,3 +22,7 @@ clang_target_link_libraries(clang-repl PRIVATE if(CLANG_PLUGIN_SUPPORT) export_executable_symbols_for_plugins(clang-repl) endif() + +if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM") + target_link_options(clang-repl PRIVATE LINKER:--long-plt) +endif() From e1ce5b6c12f8c85c641d99ecc94a7ae02e4ce4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Sat, 20 Jan 2024 16:12:46 +0100 Subject: [PATCH 6/9] [JITLink][AArch32] Implement R_ARM_TARGET1 Prepare a configuration switch anddefault to R_ARM_ABS32 --- llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h | 2 ++ llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp | 8 ++++++-- llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp | 7 ++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h index 725f9a7eeb88896..98525b9f69b97e2 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h @@ -135,6 +135,8 @@ enum class StubsFlavor { struct ArmConfig { bool J1J2BranchEncoding = false; StubsFlavor Stubs = StubsFlavor::Unsupported; + // We might want a --target1-rel linker switch in the long term. + bool Target1Rel = false; }; /// Obtain the sub-arch configuration for a given Arm CPU model. diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp index 2553cd70a576912..ed7d58da2dc111e 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp @@ -31,7 +31,8 @@ namespace llvm { namespace jitlink { /// Translate from ELF relocation type to JITLink-internal edge kind. -Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) { +Expected<aarch32::EdgeKind_aarch32> +getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &ArmCfg) { switch (ELFType) { case ELF::R_ARM_ABS32: return aarch32::Data_Pointer32; @@ -47,6 +48,9 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) { return aarch32::Arm_MovwAbsNC; case ELF::R_ARM_MOVT_ABS: return aarch32::Arm_MovtAbs; + case ELF::R_ARM_TARGET1: + return (ArmCfg.Target1Rel) ? aarch32::Data_Delta32 + : aarch32::Data_Pointer32; case ELF::R_ARM_THM_CALL: return aarch32::Thumb_Call; case ELF::R_ARM_THM_JUMP24: @@ -171,7 +175,7 @@ class ELFLinkGraphBuilder_aarch32 inconvertibleErrorCode()); uint32_t Type = Rel.getType(false); - Expected<aarch32::EdgeKind_aarch32> Kind = getJITLinkEdgeKind(Type); + Expected<aarch32::EdgeKind_aarch32> Kind = getJITLinkEdgeKind(Type, ArmCfg); if (!Kind) return Kind.takeError(); diff --git a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp index 26c773b8dc3a715..46792e9e397b6ae 100644 --- a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp +++ b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp @@ -39,7 +39,7 @@ struct MutableWord { namespace llvm { namespace jitlink { -Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType); +Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &Cfg); Expected<uint32_t> getELFRelocationType(Edge::Kind Kind); } // namespace jitlink @@ -47,7 +47,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind); TEST(AArch32_ELF, EdgeKinds) { // Fails: Invalid ELF type -> JITLink kind - Expected<uint32_t> ErrKind = getJITLinkEdgeKind(ELF::R_ARM_NONE); + aarch32::ArmConfig Cfg; + Expected<uint32_t> ErrKind = getJITLinkEdgeKind(ELF::R_ARM_NONE, Cfg); EXPECT_TRUE(errorToBool(ErrKind.takeError())); // Fails: Invalid JITLink kind -> ELF type @@ -59,7 +60,7 @@ TEST(AArch32_ELF, EdgeKinds) { EXPECT_FALSE(errorToBool(ELFType.takeError())) << "Failed to translate JITLink kind -> ELF type"; - Expected<Edge::Kind> JITLinkKind = getJITLinkEdgeKind(*ELFType); + Expected<Edge::Kind> JITLinkKind = getJITLinkEdgeKind(*ELFType, Cfg); EXPECT_FALSE(errorToBool(JITLinkKind.takeError())) << "Failed to translate ELF type -> JITLink kind"; From 14ec373340abce8655f59e85cc1dfe3c37b13a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Sat, 20 Jan 2024 18:06:14 +0100 Subject: [PATCH 7/9] [JITLink][AArch32] Implement R_ARM_NONE --- llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h | 12 +++++++++--- llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp | 4 ++++ llvm/lib/ExecutionEngine/JITLink/aarch32.cpp | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h index 98525b9f69b97e2..7be0380d4a7c3e7 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h @@ -101,7 +101,11 @@ enum EdgeKind_aarch32 : Edge::Kind { Thumb_MovtPrel, LastThumbRelocation = Thumb_MovtPrel, - LastRelocation = LastThumbRelocation, + + /// No-op relocation + None, + + LastRelocation = None, }; /// Flags enum for AArch32-specific symbol properties @@ -293,7 +297,8 @@ inline Expected<int64_t> readAddend(LinkGraph &G, Block &B, if (Kind <= LastThumbRelocation) return readAddendThumb(G, B, Offset, Kind, ArmCfg); - llvm_unreachable("Relocation must be of class Data, Arm or Thumb"); + assert(Kind == None && "Not associated with a relocation class"); + return 0; } /// Helper function to apply the fixup for Data-class relocations. @@ -320,7 +325,8 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, if (Kind <= LastThumbRelocation) return applyFixupThumb(G, B, E, ArmCfg); - llvm_unreachable("Relocation must be of class Data, Arm or Thumb"); + assert(Kind == None && "Not associated with a relocation class"); + return Error::success(); } /// Populate a Global Offset Table from edges that request it. diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp index ed7d58da2dc111e..15c209e1ebe5bf6 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp @@ -48,6 +48,8 @@ getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &ArmCfg) { return aarch32::Arm_MovwAbsNC; case ELF::R_ARM_MOVT_ABS: return aarch32::Arm_MovtAbs; + case ELF::R_ARM_NONE: + return aarch32::None; case ELF::R_ARM_TARGET1: return (ArmCfg.Target1Rel) ? aarch32::Data_Delta32 : aarch32::Data_Pointer32; @@ -99,6 +101,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) { return ELF::R_ARM_THM_MOVW_PREL_NC; case aarch32::Thumb_MovtPrel: return ELF::R_ARM_THM_MOVT_PREL; + case aarch32::None: + return ELF::R_ARM_NONE; } return make_error<JITLinkError>(formatv("Invalid aarch32 edge {0:d}: ", diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp index 7f7b460b4b36968..9508cde07b42a65 100644 --- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp @@ -867,6 +867,7 @@ const char *getEdgeKindName(Edge::Kind K) { KIND_NAME_CASE(Thumb_MovtAbs) KIND_NAME_CASE(Thumb_MovwPrelNC) KIND_NAME_CASE(Thumb_MovtPrel) + KIND_NAME_CASE(None) default: return getGenericEdgeKindName(K); } From 1fc8d948dd7728810d1ff579a4e0ef087a1b3519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Sat, 20 Jan 2024 16:00:51 +0100 Subject: [PATCH 8/9] [JITLink][AArch32] Implement R_ARM_PREL31 and stop skipping .ARM.exidx sections (wip) --- .../llvm/ExecutionEngine/JITLink/aarch32.h | 2 ++ .../ExecutionEngine/JITLink/ELF_aarch32.cpp | 13 ++++++++ llvm/lib/ExecutionEngine/JITLink/aarch32.cpp | 21 +++++++++++-- .../JITLink/AArch32/ELF_relocations_data.s | 30 +++++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h index 7be0380d4a7c3e7..dc75394c7283672 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h @@ -41,6 +41,8 @@ enum EdgeKind_aarch32 : Edge::Kind { /// Absolute 32-bit value relocation Data_Pointer32, + Data_PRel31, + /// Create GOT entry and store offset Data_RequestGOTAndTransformToDelta32, diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp index 15c209e1ebe5bf6..563dc498d7737eb 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp @@ -50,6 +50,8 @@ getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &ArmCfg) { return aarch32::Arm_MovtAbs; case ELF::R_ARM_NONE: return aarch32::None; + case ELF::R_ARM_PREL31: + return aarch32::Data_PRel31; case ELF::R_ARM_TARGET1: return (ArmCfg.Target1Rel) ? aarch32::Data_Delta32 : aarch32::Data_Pointer32; @@ -79,6 +81,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) { return ELF::R_ARM_REL32; case aarch32::Data_Pointer32: return ELF::R_ARM_ABS32; + case aarch32::Data_PRel31: + return ELF::R_ARM_PREL31; case aarch32::Data_RequestGOTAndTransformToDelta32: return ELF::R_ARM_GOT_PREL; case aarch32::Arm_Call: @@ -147,6 +151,15 @@ class ELFLinkGraphBuilder_aarch32 // Handling ABI for the Arm® Architecture -> Index table entries if (Sect.sh_type == ELF::SHT_ARM_EXIDX) return true; + // Skip .ARM.extab sections. They are only ever referenced from .ARM.exidx, + // i.e. when unwind instructions don't fit into 4 bytes. + if (Sect.sh_type == ELF::SHT_PROGBITS && (Sect.sh_flags & ELF::SHF_ALLOC)) { + if (Sect.sh_name && Sect.sh_name < Base::SectionStringTab.size()) { + StringRef SectionName = Base::SectionStringTab.data() + Sect.sh_name; + if (SectionName.starts_with(".ARM.extab")) + return true; + } + } return false; } diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp index 9508cde07b42a65..96dff7656f17d63 100644 --- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp @@ -398,6 +398,8 @@ Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset, case Data_Pointer32: case Data_RequestGOTAndTransformToDelta32: return SignExtend64<32>(support::endian::read32(FixupPtr, Endian)); + case Data_PRel31: + return SignExtend64<31>(support::endian::read32(FixupPtr, Endian)); default: return make_error<JITLinkError>( "In graph " + G.getName() + ", section " + B.getSection().getName() + @@ -472,9 +474,8 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) { Symbol &TargetSymbol = E.getTarget(); uint64_t TargetAddress = TargetSymbol.getAddress().getValue(); - // Regular data relocations have size 4, alignment 1 and write the full 32-bit - // result to the place; no need for overflow checking. There are three - // exceptions: R_ARM_ABS8, R_ARM_ABS16, R_ARM_PREL31 + // Data relocations have alignment 1, size 4 (except R_ARM_ABS8 and + // R_ARM_ABS16) and write the full 32-bit result (except R_ARM_PREL31). switch (Kind) { case Data_Delta32: { int64_t Value = TargetAddress - FixupAddress + Addend; @@ -496,6 +497,19 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) { endian::write32be(FixupPtr, Value); return Error::success(); } + case Data_PRel31: { + int64_t Value = TargetAddress - FixupAddress + Addend; + if (!isInt<31>(Value)) + return makeTargetOutOfRangeError(G, B, E); + if (LLVM_LIKELY(G.getEndianness() == endianness::little)) { + uint32_t MSB = endian::read32le(FixupPtr) & 0x80000000; + endian::write32le(FixupPtr, MSB | (Value & ~0x80000000)); + } else { + uint32_t MSB = endian::read32be(FixupPtr) & 0x80000000; + endian::write32be(FixupPtr, MSB | (Value & ~0x80000000)); + } + return Error::success(); + } case Data_RequestGOTAndTransformToDelta32: llvm_unreachable("Should be transformed"); default: @@ -856,6 +870,7 @@ const char *getEdgeKindName(Edge::Kind K) { switch (K) { KIND_NAME_CASE(Data_Delta32) KIND_NAME_CASE(Data_Pointer32) + KIND_NAME_CASE(Data_PRel31) KIND_NAME_CASE(Data_RequestGOTAndTransformToDelta32) KIND_NAME_CASE(Arm_Call) KIND_NAME_CASE(Arm_Jump24) diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s index f91a4733c40eee0..82092965d5bb307 100644 --- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s +++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s @@ -57,6 +57,35 @@ got_prel_offset: .size got_prel_offset, .-got_prel_offset .size got_prel, .-got_prel +# Dummy personality routine. It will be referenced from .ARM.exidx +# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_NONE __aeabi_unwind_cpp_pr0 + .globl __aeabi_unwind_cpp_pr0 + .type __aeabi_unwind_cpp_pr0,%function + .align 2 +__aeabi_unwind_cpp_pr0: + bx lr + +# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_PREL31 .text +# +# Used in the .ARM.exidx exception tables. The linker must preserve the most- +# significant bit. It denotes whether the field is an inline entry (1) or a +# relocation (0). +# +# jitlink-check: *{4}(section_addr(out.o, .ARM.exidx)) = (prel31 - (section_addr(out.o, .ARM.exidx))) & 0x7fffffff + .globl prel31 + .type prel31,%function + .align 2 +prel31: + .fnstart + .save {r11, lr} + push {r11, lr} + .setfp r11, sp + mov r11, sp + pop {r11, lr} + mov pc, lr + .size prel31,.-prel31 + .fnend + # This test is executable with any 4-byte external target: # > echo "unsigned target = 42;" | clang -target armv7-linux-gnueabihf -o target.o -c -xc - # > llvm-jitlink target.o armv7/out.o @@ -67,5 +96,6 @@ got_prel_offset: main: push {lr} bl got_prel + bl prel31 pop {pc} .size main, .-main From b7431fc5f62147e48261e5d0fcfa7c390efb855c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graen...@gmail.com> Date: Mon, 8 Jan 2024 15:06:33 +0100 Subject: [PATCH 9/9] [Orc] Make JITLink default in LLJIT for ELF-based ARM targets --- llvm/lib/ExecutionEngine/Orc/LLJIT.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp index e259c393d07e031..833dcb9d5bf2e72 100644 --- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp @@ -734,6 +734,12 @@ Error LLJITBuilderState::prepareForConstruction() { case Triple::aarch64: UseJITLink = !TT.isOSBinFormatCOFF(); break; + case Triple::arm: + case Triple::armeb: + case Triple::thumb: + case Triple::thumbeb: + UseJITLink = TT.isOSBinFormatELF(); + break; case Triple::x86_64: UseJITLink = !TT.isOSBinFormatCOFF(); break; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits