[llvm] [JITLink][AArch32] Multi-stub support for armv7/thumbv7 (PR #78371)
Stefan Gränitz via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 16 16:36:56 PST 2024
https://github.com/weliveindetail created https://github.com/llvm/llvm-project/pull/78371
None
>From 6e3b317fcf8a34c4fe36df51105172cda08281c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 16 Jan 2024 22:33:02 +0100
Subject: [PATCH 1/3] [llvm-jitlink] Refactor GOT and stubs registration (NFC)
---
llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp | 35 +++-----
llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp | 79 ++++++++-----------
.../tools/llvm-jitlink/llvm-jitlink-macho.cpp | 30 ++-----
llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 30 +++++++
llvm/tools/llvm-jitlink/llvm-jitlink.h | 10 +++
5 files changed, 89 insertions(+), 95 deletions(-)
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
index 283e655205d780..5271fdb5565904 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
@@ -108,34 +108,17 @@ Error registerCOFFGraphInfo(Session &S, LinkGraph &G) {
if (Sym->getAddress() > LastSym->getAddress())
LastSym = Sym;
- if (isGOTSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in GOT section",
- inconvertibleErrorCode());
-
- // If this is a GOT symbol with size (i.e. not the GOT start symbol)
- // then add it to the GOT entry info table.
- if (Sym->getSize() != 0) {
- if (auto TS = getCOFFGOTTarget(G, Sym->getBlock()))
- FileInfo.GOTEntryInfos[TS->getName()] = {
- Sym->getSymbolContent(), Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return TS.takeError();
+ if (isGOTSection || isStubsSection) {
+ if (isGOTSection) {
+ // Skip the GOT start symbol
+ if (Sym->getSize() != 0)
+ if (Error E = FileInfo.registerGOTEntry(G, *Sym, getCOFFGOTTarget))
+ return E;
+ } else {
+ if (Error E = FileInfo.registerStubEntry(G, *Sym, getCOFFStubTarget))
+ return E;
}
SectionContainsContent = true;
- } else if (isStubsSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in Stub section",
- inconvertibleErrorCode());
-
- if (auto TS = getCOFFStubTarget(G, Sym->getBlock()))
- FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
- Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return TS.takeError();
- SectionContainsContent = true;
}
if (Sym->hasName()) {
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
index a02468758b33f6..c6b4218aad7af8 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp
@@ -72,13 +72,29 @@ static Expected<Symbol &> getELFStubTarget(LinkGraph &G, Block &B) {
return getELFGOTTarget(G, GOTSym.getBlock());
}
-static Expected<std::string> getELFAArch32StubTargetName(LinkGraph &G,
- Block &B) {
+static Expected<Symbol &> getELFAArch32StubTarget(LinkGraph &G, Block &B) {
auto E = getFirstRelocationEdge(G, B);
if (!E)
return E.takeError();
- Symbol &StubTarget = E->getTarget();
- return StubTarget.getName().str();
+ return E->getTarget();
+}
+
+enum SectionType { GOT, Stubs, AArch32Stubs, Other };
+
+static Error registerSymbol(LinkGraph &G, Symbol &Sym, Session::FileInfo &FI,
+ SectionType SecType) {
+ switch (SecType) {
+ case GOT:
+ if (Sym.getSize() == 0)
+ return Error::success(); // Skip the GOT start symbol
+ return FI.registerGOTEntry(G, Sym, getELFGOTTarget);
+ case Stubs:
+ return FI.registerStubEntry(G, Sym, getELFStubTarget);
+ case AArch32Stubs:
+ return FI.registerStubEntry(G, Sym, getELFAArch32StubTarget);
+ case Other:
+ return Error::success();
+ }
}
namespace llvm {
@@ -113,9 +129,16 @@ Error registerELFGraphInfo(Session &S, LinkGraph &G) {
"\"",
inconvertibleErrorCode());
- bool isGOTSection = isELFGOTSection(Sec);
- bool isStubsSection = isELFStubsSection(Sec);
- bool isAArch32StubsSection = isELFAArch32StubsSection(Sec);
+ SectionType SecType;
+ if (isELFGOTSection(Sec)) {
+ SecType = GOT;
+ } else if (isELFStubsSection(Sec)) {
+ SecType = Stubs;
+ } else if (isELFAArch32StubsSection(Sec)) {
+ SecType = AArch32Stubs;
+ } else {
+ SecType = Other;
+ }
bool SectionContainsContent = false;
bool SectionContainsZeroFill = false;
@@ -128,45 +151,9 @@ Error registerELFGraphInfo(Session &S, LinkGraph &G) {
if (Sym->getAddress() > LastSym->getAddress())
LastSym = Sym;
- if (isGOTSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in GOT section",
- inconvertibleErrorCode());
-
- // If this is a GOT symbol with size (i.e. not the GOT start symbol)
- // then add it to the GOT entry info table.
- if (Sym->getSize() != 0) {
- if (auto TS = getELFGOTTarget(G, Sym->getBlock()))
- FileInfo.GOTEntryInfos[TS->getName()] = {
- Sym->getSymbolContent(), Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return TS.takeError();
- }
- SectionContainsContent = true;
- } else if (isStubsSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in Stub section",
- inconvertibleErrorCode());
-
- if (auto TS = getELFStubTarget(G, Sym->getBlock()))
- FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
- Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return TS.takeError();
- SectionContainsContent = true;
- } else if (isAArch32StubsSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in Stub section",
- inconvertibleErrorCode());
-
- if (auto Name = getELFAArch32StubTargetName(G, Sym->getBlock()))
- FileInfo.StubInfos[*Name] = {Sym->getSymbolContent(),
- Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return Name.takeError();
+ if (SecType != Other) {
+ if (Error Err = registerSymbol(G, *Sym, FileInfo, SecType))
+ return Err;
SectionContainsContent = true;
}
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
index 7dcadd94c2365a..2c60c802293a1e 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-macho.cpp
@@ -111,29 +111,13 @@ Error registerMachOGraphInfo(Session &S, LinkGraph &G) {
FirstSym = Sym;
if (Sym->getAddress() > LastSym->getAddress())
LastSym = Sym;
- if (isGOTSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in GOT section",
- inconvertibleErrorCode());
-
- if (auto TS = getMachOGOTTarget(G, Sym->getBlock()))
- FileInfo.GOTEntryInfos[TS->getName()] = {Sym->getSymbolContent(),
- Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return TS.takeError();
- SectionContainsContent = true;
- } else if (isStubsSection) {
- if (Sym->isSymbolZeroFill())
- return make_error<StringError>("zero-fill atom in Stub section",
- inconvertibleErrorCode());
-
- if (auto TS = getMachOStubTarget(G, Sym->getBlock()))
- FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
- Sym->getAddress().getValue(),
- Sym->getTargetFlags()};
- else
- return TS.takeError();
+ if (isGOTSection || isStubsSection) {
+ Error Err =
+ isGOTSection
+ ? FileInfo.registerGOTEntry(G, *Sym, getMachOGOTTarget)
+ : FileInfo.registerStubEntry(G, *Sym, getMachOStubTarget);
+ if (Err)
+ return Err;
SectionContainsContent = true;
} else if (Sym->hasName()) {
if (Sym->isSymbolZeroFill()) {
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index aa032c97485fbf..8c18610313ce8f 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -1183,6 +1183,36 @@ Error Session::loadAndLinkDynamicLibrary(JITDylib &JD, StringRef LibPath) {
return Error::success();
}
+Error Session::FileInfo::registerGOTEntry(
+ 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 TS = GetSymbolTarget(G, Sym.getBlock());
+ if (!TS)
+ return TS.takeError();
+ GOTEntryInfos[TS->getName()] = {Sym.getSymbolContent(),
+ Sym.getAddress().getValue(),
+ Sym.getTargetFlags()};
+ return Error::success();
+}
+
+Error Session::FileInfo::registerStubEntry(
+ 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 TS = GetSymbolTarget(G, Sym.getBlock());
+ if (!TS)
+ return TS.takeError();
+ StubInfos[TS->getName()] = {Sym.getSymbolContent(),
+ Sym.getAddress().getValue(),
+ Sym.getTargetFlags()};
+ return Error::success();
+}
+
Expected<Session::FileInfo &> Session::findFileInfo(StringRef FileName) {
auto FileInfoItr = FileInfos.find(FileName);
if (FileInfoItr == FileInfos.end())
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h
index 3ff406b7b82dfd..93a00266b15043 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.h
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h
@@ -51,6 +51,16 @@ struct Session {
StringMap<MemoryRegionInfo> SectionInfos;
StringMap<MemoryRegionInfo> StubInfos;
StringMap<MemoryRegionInfo> GOTEntryInfos;
+
+ using Symbol = jitlink::Symbol;
+ using LinkGraph = jitlink::LinkGraph;
+ using GetSymbolTargetFunction =
+ unique_function<Expected<Symbol &>(LinkGraph &G, jitlink::Block &)>;
+
+ Error registerGOTEntry(LinkGraph &G, Symbol &Sym,
+ GetSymbolTargetFunction GetSymbolTarget);
+ Error registerStubEntry(LinkGraph &G, Symbol &Sym,
+ GetSymbolTargetFunction GetSymbolTarget);
};
using DynLibJDMap = std::map<std::string, orc::JITDylib *>;
>From 3104039c376190ff78edc84b0de5a9cc4ef69230 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:35:54 +0100
Subject: [PATCH 2/3] [llvm-jitlink] Allow optional index argument in
jitlink-check stub_addr() expressions
---
.../llvm/ExecutionEngine/RuntimeDyldChecker.h | 2 +-
.../RuntimeDyld/RuntimeDyldChecker.cpp | 21 +++++--
.../RuntimeDyld/RuntimeDyldCheckerImpl.h | 3 +-
llvm/tools/llvm-jitlink/llvm-jitlink-elf.cpp | 2 +-
llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 55 ++++++++++++++++---
llvm/tools/llvm-jitlink/llvm-jitlink.h | 8 ++-
llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp | 15 +++--
7 files changed, 81 insertions(+), 25 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h b/llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
index 80e4bbf494339c..55cc16834976d0 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, int64_t StubIndex)>;
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 7fadbdd6a1fff2..801496b8b91035 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 stub index parameter
+ int64_t StubIndex = 0;
+ if (RemainingExpr.starts_with(",")) {
+ RemainingExpr = RemainingExpr.substr(1).ltrim();
+ EvalResult Number;
+ std::tie(Number, RemainingExpr) = evalNumberExpr(RemainingExpr);
+ StubIndex = Number.getValue();
+ }
+
if (!RemainingExpr.starts_with(")"))
return std::make_pair(
unexpectedToken(RemainingExpr, Expr, "expected ')'"), "");
@@ -408,7 +417,7 @@ class RuntimeDyldCheckerExprEval {
uint64_t StubAddr;
std::string ErrorMsg;
std::tie(StubAddr, ErrorMsg) = Checker.getStubOrGOTAddrFor(
- StubContainerName, Symbol, PCtx.IsInsideLoad, IsStubAddr);
+ StubContainerName, Symbol, StubIndex, PCtx.IsInsideLoad, IsStubAddr);
if (ErrorMsg != "")
return std::make_pair(EvalResult(ErrorMsg), "");
@@ -985,11 +994,13 @@ 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 {
+ StringRef StubContainerName, StringRef SymbolName, int64_t StubIndex,
+ bool IsInsideLoad, bool IsStubAddr) const {
- auto StubInfo = IsStubAddr ? GetStubInfo(StubContainerName, SymbolName)
- : GetGOTInfo(StubContainerName, SymbolName);
+ assert((StubIndex == 0 || IsStubAddr) && "Indexing only supported for stubs");
+ auto StubInfo = IsStubAddr
+ ? GetStubInfo(StubContainerName, SymbolName, StubIndex)
+ : 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 9f44a9389f4734..0f43e877318895 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;
+ int64_t StubIndex, 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 c6b4218aad7af8..1f0fca2202a0ef 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 8c18610313ce8f..68f39b880c69c9 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();
}
@@ -1236,7 +1266,8 @@ Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
}
Expected<Session::MemoryRegionInfo &>
-Session::findStubInfo(StringRef FileName, StringRef TargetName) {
+Session::findStubInfo(StringRef FileName, StringRef TargetName,
+ uint64_t StubIndex) {
auto FI = findFileInfo(FileName);
if (!FI)
return FI.takeError();
@@ -1246,7 +1277,12 @@ Session::findStubInfo(StringRef FileName, StringRef TargetName) {
"\" registered for file \"" + FileName +
"\"",
inconvertibleErrorCode());
- return StubInfoItr->second;
+ if (StubIndex >= StubInfoItr->second.size())
+ return make_error<StringError>(
+ "no stub for \"" + TargetName + " with index " + Twine(StubIndex) +
+ "\" registered for file \"" + FileName + "\"",
+ inconvertibleErrorCode());
+ return StubInfoItr->second[StubIndex];
}
Expected<Session::MemoryRegionInfo &>
@@ -2015,8 +2051,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,
+ int64_t StubIndex) {
+ return S.findStubInfo(FileName, SectionName, StubIndex);
};
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 93a00266b15043..46346bfde5e957 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 *>;
@@ -73,8 +75,8 @@ struct Session {
Expected<FileInfo &> findFileInfo(StringRef FileName);
Expected<MemoryRegionInfo &> findSectionInfo(StringRef FileName,
StringRef SectionName);
- Expected<MemoryRegionInfo &> findStubInfo(StringRef FileName,
- StringRef TargetName);
+ Expected<MemoryRegionInfo &>
+ findStubInfo(StringRef FileName, StringRef TargetName, uint64_t StubIndex);
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 107b555a99faa4..266e24e067aa4f 100644
--- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -925,9 +925,10 @@ static int linkAndVerify() {
return SecInfo;
};
- auto GetStubInfo = [&Dyld, &StubMap](StringRef StubContainer,
- StringRef SymbolName)
- -> Expected<RuntimeDyldChecker::MemoryRegionInfo> {
+ auto GetStubInfo =
+ [&Dyld, &StubMap](
+ StringRef StubContainer, StringRef SymbolName,
+ int64_t StubIndex) -> Expected<RuntimeDyldChecker::MemoryRegionInfo> {
if (!StubMap.count(StubContainer))
return make_error<StringError>("Stub container not found: " +
StubContainer,
@@ -947,6 +948,11 @@ static int linkAndVerify() {
return StubMemInfo;
};
+ auto GetGOTInfo = [&GetStubInfo](StringRef StubContainer,
+ StringRef SymbolName) {
+ return GetStubInfo(StubContainer, SymbolName, 0);
+ };
+
// 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 eac1e83064319faa0a977d049cc3abd8f7f8c5e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Sat, 13 Jan 2024 23:58:36 +0100
Subject: [PATCH 3/3] [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 | 39 ++++++
.../JITLink/AArch32/ELF_stubs_multi.s | 50 +++++++
4 files changed, 215 insertions(+), 58 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 7765208b5e3dfe..eb5c4bf916e355 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -318,64 +318,31 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
}
-/// 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 8153c97deff628..49e0548c5ccd6a 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"
@@ -678,27 +679,127 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
}
}
+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 00000000000000..90b1c5b7a3593e
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_stubs_arm.s
@@ -0,0 +1,39 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -triple=armv7-linux-gnueabi -arm-add-build-attributes \
+# RUN: -filetype=obj -o %t/elf_stubs.o %s
+# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 \
+# RUN: -slab-allocate 10Kb -slab-page-size 4096 \
+# RUN: -abs ext_arm=0x76bbe880 \
+# RUN: -check %s %t/elf_stubs.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.
+#
+# jitlink-check: decode_operand(test_external_call, 0) = stub_addr(elf_stubs.o, ext_arm) - (test_external_call + 8)
+# jitlink-check: decode_operand(test_external_jump, 0) = stub_addr(elf_stubs.o, ext_arm) - (test_external_jump + 8)
+ .globl test_external_call
+ .type test_external_call,%function
+ .p2align 2
+test_external_call:
+ bl ext_arm
+ .size test_external_call, .-test_external_call
+
+ .globl test_external_jump
+ .type test_external_jump,%function
+ .p2align 2
+test_external_jump:
+ b ext_arm
+ .size test_external_jump, .-test_external_jump
+
+# Empty main function for jitlink to be happy
+ .globl main
+ .type main,%function
+ .p2align 2
+main:
+ bx lr
+ .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 00000000000000..602bf256add624
--- /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 10-byte stub is at index 0:
+# jitlink-check: decode_operand(test_stub_thumb, 0) = stub_addr(out.o, ext, 0) - (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 12-bytes stub is at index 1:
+# jitlink-check: decode_operand(test_stub_arm, 0) = stub_addr(out.o, ext, 1) - (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` function, as long as it
+# returns (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
More information about the llvm-commits
mailing list