[llvm] [JITLink][RISCV] Implement eh_frame handling (PR #68253)
Jonas Hahnfeld via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 4 13:12:24 PDT 2023
https://github.com/hahnjo created https://github.com/llvm/llvm-project/pull/68253
This requires adding a `NegDelta32` edge kind that cannot be mapped to existing relocations.
---
This PR depends on https://github.com/llvm/llvm-project/pull/66707 and https://github.com/llvm/llvm-project/pull/68252; only the top-most commit should be reviewed here.
>From 2b437c3520da880cbc4745f92fc096ee405f8ae1 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hahnjo at hahnjo.de>
Date: Mon, 18 Sep 2023 21:53:21 +0200
Subject: [PATCH 1/5] [JITLink][EHFrameSupport] Remove unused variable
---
llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index 86249591a9be053..63ba765c8620ed1 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -138,7 +138,6 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
BlockEdges[E.getOffset()] = EdgeTarget(E);
}
- CIEInfosMap CIEInfos;
BinaryStreamReader BlockReader(
StringRef(B.getContent().data(), B.getContent().size()),
PC.G.getEndianness());
>From 6fa388c69f3317c03a55b018427159fdba1c7607 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hahnjo at hahnjo.de>
Date: Mon, 18 Sep 2023 21:22:53 +0200
Subject: [PATCH 2/5] [JITLink][EHFrameSupport] Error on existing edge at CIE
pointer field
---
.../JITLink/EHFrameSupport.cpp | 48 +++++++------------
1 file changed, 17 insertions(+), 31 deletions(-)
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index 63ba765c8620ed1..7b2af45ad3ce666 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -325,40 +325,26 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
{
// Process the CIE pointer field.
auto CIEEdgeItr = BlockEdges.find(RecordOffset + CIEDeltaFieldOffset);
+ if (CIEEdgeItr != BlockEdges.end())
+ return make_error<JITLinkError>(
+ "CIE pointer field already has edge at " +
+ formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset));
+
orc::ExecutorAddr CIEAddress =
RecordAddress + orc::ExecutorAddrDiff(CIEDeltaFieldOffset) -
orc::ExecutorAddrDiff(CIEDelta);
- if (CIEEdgeItr == BlockEdges.end()) {
-
- LLVM_DEBUG({
- dbgs() << " Adding edge at "
- << (RecordAddress + CIEDeltaFieldOffset)
- << " to CIE at: " << CIEAddress << "\n";
- });
- if (auto CIEInfoOrErr = PC.findCIEInfo(CIEAddress))
- CIEInfo = *CIEInfoOrErr;
- else
- return CIEInfoOrErr.takeError();
- assert(CIEInfo->CIESymbol && "CIEInfo has no CIE symbol set");
- B.addEdge(NegDelta32, RecordOffset + CIEDeltaFieldOffset,
- *CIEInfo->CIESymbol, 0);
- } else {
- LLVM_DEBUG({
- dbgs() << " Already has edge at "
- << (RecordAddress + CIEDeltaFieldOffset) << " to CIE at "
- << CIEAddress << "\n";
- });
- auto &EI = CIEEdgeItr->second;
- if (EI.Addend)
- return make_error<JITLinkError>(
- "CIE edge at " +
- formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset) +
- " has non-zero addend");
- if (auto CIEInfoOrErr = PC.findCIEInfo(EI.Target->getAddress()))
- CIEInfo = *CIEInfoOrErr;
- else
- return CIEInfoOrErr.takeError();
- }
+ LLVM_DEBUG({
+ dbgs() << " Adding edge at "
+ << (RecordAddress + CIEDeltaFieldOffset)
+ << " to CIE at: " << CIEAddress << "\n";
+ });
+ if (auto CIEInfoOrErr = PC.findCIEInfo(CIEAddress))
+ CIEInfo = *CIEInfoOrErr;
+ else
+ return CIEInfoOrErr.takeError();
+ assert(CIEInfo->CIESymbol && "CIEInfo has no CIE symbol set");
+ B.addEdge(NegDelta32, RecordOffset + CIEDeltaFieldOffset,
+ *CIEInfo->CIESymbol, 0);
}
// Process the PC-Begin field.
>From a73e2159064a58accf5fc72c9955114dc6cb07cf Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hahnjo at hahnjo.de>
Date: Mon, 18 Sep 2023 23:49:04 +0200
Subject: [PATCH 3/5] [JITLink] Simplify CFI processing in EHFrameEdgeFixer
The code expects DWARFRecordSectionSplitter to split each CFI record
into its own block, so remove loop over possibly multiple entries in
one block.
---
.../JITLink/EHFrameSupport.cpp | 101 +++++++-----------
.../JITLink/EHFrameSupportImpl.h | 6 +-
.../JITLink/AArch64/ELF_ehframe.s | 21 ++--
.../JITLink/AArch64/MachO_ehframe.s | 14 ++-
.../LoongArch/ELF_loongarch64_ehframe.s | 21 ++--
.../JITLink/ppc64/ELF_ppc64_ehframe.s | 14 ++-
6 files changed, 71 insertions(+), 106 deletions(-)
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index 7b2af45ad3ce666..c095b13e8c30c56 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -141,67 +141,49 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
BinaryStreamReader BlockReader(
StringRef(B.getContent().data(), B.getContent().size()),
PC.G.getEndianness());
- while (!BlockReader.empty()) {
- size_t RecordStartOffset = BlockReader.getOffset();
- LLVM_DEBUG({
- dbgs() << " Processing CFI record at "
- << (B.getAddress() + RecordStartOffset) << "\n";
- });
-
- // Get the record length.
- Expected<size_t> RecordRemaining = readCFIRecordLength(B, BlockReader);
- if (!RecordRemaining)
- return RecordRemaining.takeError();
-
- if (BlockReader.bytesRemaining() < *RecordRemaining)
- return make_error<JITLinkError>(
- "Incomplete CFI record at " +
- formatv("{0:x16}", B.getAddress() + RecordStartOffset));
+ // Get the record length.
+ Expected<size_t> RecordRemaining = readCFIRecordLength(B, BlockReader);
+ if (!RecordRemaining)
+ return RecordRemaining.takeError();
+
+ // We expect DWARFRecordSectionSplitter to split each CFI record into its own
+ // block.
+ if (BlockReader.bytesRemaining() != *RecordRemaining)
+ return make_error<JITLinkError>("Incomplete CFI record at " +
+ formatv("{0:x16}", B.getAddress()));
+
+ // Read the CIE delta for this record.
+ uint64_t CIEDeltaFieldOffset = BlockReader.getOffset();
+ uint32_t CIEDelta;
+ if (auto Err = BlockReader.readInteger(CIEDelta))
+ return Err;
- // Read the CIE delta for this record.
- uint64_t CIEDeltaFieldOffset = BlockReader.getOffset() - RecordStartOffset;
- uint32_t CIEDelta;
- if (auto Err = BlockReader.readInteger(CIEDelta))
+ if (CIEDelta == 0) {
+ if (auto Err = processCIE(PC, B, CIEDeltaFieldOffset, BlockEdges))
+ return Err;
+ } else {
+ if (auto Err = processFDE(PC, B, CIEDeltaFieldOffset, CIEDelta, BlockEdges))
return Err;
-
- if (CIEDelta == 0) {
- if (auto Err = processCIE(PC, B, RecordStartOffset,
- CIEDeltaFieldOffset + *RecordRemaining,
- CIEDeltaFieldOffset, BlockEdges))
- return Err;
- } else {
- if (auto Err = processFDE(PC, B, RecordStartOffset,
- CIEDeltaFieldOffset + *RecordRemaining,
- CIEDeltaFieldOffset, CIEDelta, BlockEdges))
- return Err;
- }
-
- // Move to the next record.
- BlockReader.setOffset(RecordStartOffset + CIEDeltaFieldOffset +
- *RecordRemaining);
}
return Error::success();
}
Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
- size_t RecordOffset, size_t RecordLength,
size_t CIEDeltaFieldOffset,
const BlockEdgeMap &BlockEdges) {
- LLVM_DEBUG(dbgs() << " Record is CIE\n");
+ LLVM_DEBUG(dbgs() << " Record is CIE\n");
- auto RecordContent = B.getContent().slice(RecordOffset, RecordLength);
BinaryStreamReader RecordReader(
- StringRef(RecordContent.data(), RecordContent.size()),
+ StringRef(B.getContent().data(), B.getContent().size()),
PC.G.getEndianness());
// Skip past the CIE delta field: we've already processed this far.
RecordReader.setOffset(CIEDeltaFieldOffset + 4);
- auto &CIESymbol =
- PC.G.addAnonymousSymbol(B, RecordOffset, RecordLength, false, false);
+ auto &CIESymbol = PC.G.addAnonymousSymbol(B, 0, B.getSize(), false, false);
CIEInformation CIEInfo(CIESymbol);
uint8_t Version = 0;
@@ -267,7 +249,7 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
if (auto Err =
getOrCreateEncodedPointerEdge(
PC, BlockEdges, *PersonalityPointerEncoding, RecordReader,
- B, RecordOffset + RecordReader.getOffset(), "personality")
+ B, RecordReader.getOffset(), "personality")
.takeError())
return Err;
break;
@@ -278,7 +260,7 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
if (CIEInfo.AddressEncoding == dwarf::DW_EH_PE_omit)
return make_error<JITLinkError>(
"Invalid address encoding DW_EH_PE_omit in CIE at " +
- formatv("{0:x}", (B.getAddress() + RecordOffset).getValue()));
+ formatv("{0:x}", B.getAddress().getValue()));
} else
return PE.takeError();
break;
@@ -301,30 +283,27 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
}
Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
- size_t RecordOffset, size_t RecordLength,
size_t CIEDeltaFieldOffset,
uint32_t CIEDelta,
const BlockEdgeMap &BlockEdges) {
- LLVM_DEBUG(dbgs() << " Record is FDE\n");
+ LLVM_DEBUG(dbgs() << " Record is FDE\n");
- orc::ExecutorAddr RecordAddress = B.getAddress() + RecordOffset;
+ orc::ExecutorAddr RecordAddress = B.getAddress();
- auto RecordContent = B.getContent().slice(RecordOffset, RecordLength);
BinaryStreamReader RecordReader(
- StringRef(RecordContent.data(), RecordContent.size()),
+ StringRef(B.getContent().data(), B.getContent().size()),
PC.G.getEndianness());
// Skip past the CIE delta field: we've already read this far.
RecordReader.setOffset(CIEDeltaFieldOffset + 4);
- auto &FDESymbol =
- PC.G.addAnonymousSymbol(B, RecordOffset, RecordLength, false, false);
+ auto &FDESymbol = PC.G.addAnonymousSymbol(B, 0, B.getSize(), false, false);
CIEInformation *CIEInfo = nullptr;
{
// Process the CIE pointer field.
- auto CIEEdgeItr = BlockEdges.find(RecordOffset + CIEDeltaFieldOffset);
+ auto CIEEdgeItr = BlockEdges.find(CIEDeltaFieldOffset);
if (CIEEdgeItr != BlockEdges.end())
return make_error<JITLinkError>(
"CIE pointer field already has edge at " +
@@ -334,8 +313,7 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
RecordAddress + orc::ExecutorAddrDiff(CIEDeltaFieldOffset) -
orc::ExecutorAddrDiff(CIEDelta);
LLVM_DEBUG({
- dbgs() << " Adding edge at "
- << (RecordAddress + CIEDeltaFieldOffset)
+ dbgs() << " Adding edge at " << (RecordAddress + CIEDeltaFieldOffset)
<< " to CIE at: " << CIEAddress << "\n";
});
if (auto CIEInfoOrErr = PC.findCIEInfo(CIEAddress))
@@ -343,13 +321,12 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
else
return CIEInfoOrErr.takeError();
assert(CIEInfo->CIESymbol && "CIEInfo has no CIE symbol set");
- B.addEdge(NegDelta32, RecordOffset + CIEDeltaFieldOffset,
- *CIEInfo->CIESymbol, 0);
+ B.addEdge(NegDelta32, CIEDeltaFieldOffset, *CIEInfo->CIESymbol, 0);
}
// Process the PC-Begin field.
LLVM_DEBUG({
- dbgs() << " Processing PC-begin at "
+ dbgs() << " Processing PC-begin at "
<< (RecordAddress + RecordReader.getOffset()) << "\n";
});
if (auto PCBegin = getOrCreateEncodedPointerEdge(
@@ -360,14 +337,14 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
// Add a keep-alive edge from the FDE target to the FDE to ensure that the
// FDE is kept alive if its target is.
LLVM_DEBUG({
- dbgs() << " Adding keep-alive edge from target at "
+ dbgs() << " Adding keep-alive edge from target at "
<< (*PCBegin)->getBlock().getAddress() << " to FDE at "
<< RecordAddress << "\n";
});
(*PCBegin)->getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
} else {
LLVM_DEBUG({
- dbgs() << " WARNING: Not adding keep-alive edge to FDE at "
+ dbgs() << " WARNING: Not adding keep-alive edge to FDE at "
<< RecordAddress << ", which points to "
<< ((*PCBegin)->isExternal() ? "external" : "absolute")
<< " symbol \"" << (*PCBegin)->getName()
@@ -394,7 +371,7 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
.takeError())
return Err;
} else {
- LLVM_DEBUG(dbgs() << " Record does not have LSDA field.\n");
+ LLVM_DEBUG(dbgs() << " Record does not have LSDA field.\n");
}
return Error::success();
@@ -519,7 +496,7 @@ Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
auto EdgeI = BlockEdges.find(PointerFieldOffset);
if (EdgeI != BlockEdges.end()) {
LLVM_DEBUG({
- dbgs() << " Existing edge at "
+ dbgs() << " Existing edge at "
<< (BlockToFix.getAddress() + PointerFieldOffset) << " to "
<< FieldName << " at " << EdgeI->second.Target->getAddress();
if (EdgeI->second.Target->hasName())
@@ -581,7 +558,7 @@ Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
BlockToFix.addEdge(PtrEdgeKind, PointerFieldOffset, *TargetSym, 0);
LLVM_DEBUG({
- dbgs() << " Adding edge at "
+ dbgs() << " Adding edge at "
<< (BlockToFix.getAddress() + PointerFieldOffset) << " to "
<< FieldName << " at " << TargetSym->getAddress();
if (TargetSym->hasName())
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
index 55cf7fc63ee7957..cc3c24cc85ead03 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
@@ -81,11 +81,9 @@ class EHFrameEdgeFixer {
};
Error processBlock(ParseContext &PC, Block &B);
- Error processCIE(ParseContext &PC, Block &B, size_t RecordOffset,
- size_t RecordLength, size_t CIEDeltaFieldOffset,
+ Error processCIE(ParseContext &PC, Block &B, size_t CIEDeltaFieldOffset,
const BlockEdgeMap &BlockEdges);
- Error processFDE(ParseContext &PC, Block &B, size_t RecordOffset,
- size_t RecordLength, size_t CIEDeltaFieldOffset,
+ Error processFDE(ParseContext &PC, Block &B, size_t CIEDeltaFieldOffset,
uint32_t CIEDelta, const BlockEdgeMap &BlockEdges);
Expected<AugmentationInfo>
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe.s
index 770cd60a1d70d76..716c4e5abd3fb9d 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_ehframe.s
@@ -13,20 +13,17 @@
# CHECK: Extracted {{.*}} section = .eh_frame
# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is CIE
+# CHECK: Record is CIE
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is FDE
-# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
-# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
-# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is FDE
-# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
-# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
-# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
.text
.globl main
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_ehframe.s b/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_ehframe.s
index 5f821276288617c..4bc006a9a7a2a9a 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_ehframe.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_ehframe.s
@@ -13,15 +13,13 @@
# CHECK: Extracted {{.*}} section = __TEXT,__eh_frame
# CHECK: EHFrameEdgeFixer: Processing __TEXT,__eh_frame in "{{.*}}"...
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is CIE
+# CHECK: Record is CIE
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is FDE
-# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
-# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
-# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
-# CHECK: Existing edge at {{.*}} to LSDA at {{.*}}
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Existing edge at {{.*}} to LSDA at {{.*}}
.section __TEXT,__text,regular,pure_instructions
.globl _main
diff --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s
index 4a1a1649508eb62..3eb3cba11da262b 100644
--- a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s
@@ -13,20 +13,17 @@
# CHECK: Extracted {{.*}} section = .eh_frame
# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is CIE
+# CHECK: Record is CIE
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is FDE
-# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
-# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
-# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is FDE
-# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
-# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
-# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
.text
.globl main
diff --git a/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64_ehframe.s b/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64_ehframe.s
index 3b84768ce786654..13e9006b7a8a817 100644
--- a/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64_ehframe.s
+++ b/llvm/test/ExecutionEngine/JITLink/ppc64/ELF_ppc64_ehframe.s
@@ -16,15 +16,13 @@
# CHECK: Extracted {{.*}} section = .eh_frame
# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is CIE
+# CHECK: Record is CIE
# CHECK: Processing block at
-# CHECK: Processing CFI record at
-# CHECK: Record is FDE
-# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
-# CHECK: Processing PC-begin at
-# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
-# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Processing PC-begin at
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
.text
.abiversion 2
>From a9d51108f86fed6c799aa6bd1d7055e0eddb3b8d Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hahnjo at hahnjo.de>
Date: Wed, 4 Oct 2023 21:09:46 +0200
Subject: [PATCH 4/5] [JITLink] Allow multiple relocations at same offset in
EHFrameEdgeFixer
The pass only requires that it can determine a uniquely identified
target at some offsets. Multiple relocations at the same offset are
fine otherwise and will be required when adding exception handling
support for RISC-V.
---
.../JITLink/EHFrameSupport.cpp | 45 ++++++++++++-------
.../JITLink/EHFrameSupportImpl.h | 16 ++++---
2 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index c095b13e8c30c56..f036445497cafd5 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -126,16 +126,23 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
}
// Find the offsets of any existing edges from this block.
- BlockEdgeMap BlockEdges;
+ BlockEdgesInfo BlockEdges;
for (auto &E : B.edges())
if (E.isRelocation()) {
- if (BlockEdges.count(E.getOffset()))
- return make_error<JITLinkError>(
- "Multiple relocations at offset " +
- formatv("{0:x16}", E.getOffset()) + " in " + EHFrameSectionName +
- " block at address " + formatv("{0:x16}", B.getAddress()));
-
- BlockEdges[E.getOffset()] = EdgeTarget(E);
+ // Check if we already saw more than one relocation at this offset.
+ if (BlockEdges.Multiple.contains(E.getOffset()))
+ continue;
+
+ // Otherwise check if we previously had exactly one relocation at this
+ // offset. If so, we now have a second one and move it from the TargetMap
+ // into the Multiple set.
+ auto It = BlockEdges.TargetMap.find(E.getOffset());
+ if (It != BlockEdges.TargetMap.end()) {
+ BlockEdges.TargetMap.erase(It);
+ BlockEdges.Multiple.insert(E.getOffset());
+ } else {
+ BlockEdges.TargetMap[E.getOffset()] = EdgeTarget(E);
+ }
}
BinaryStreamReader BlockReader(
@@ -172,7 +179,7 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
size_t CIEDeltaFieldOffset,
- const BlockEdgeMap &BlockEdges) {
+ const BlockEdgesInfo &BlockEdges) {
LLVM_DEBUG(dbgs() << " Record is CIE\n");
@@ -285,7 +292,7 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
size_t CIEDeltaFieldOffset,
uint32_t CIEDelta,
- const BlockEdgeMap &BlockEdges) {
+ const BlockEdgesInfo &BlockEdges) {
LLVM_DEBUG(dbgs() << " Record is FDE\n");
orc::ExecutorAddr RecordAddress = B.getAddress();
@@ -303,11 +310,15 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
{
// Process the CIE pointer field.
- auto CIEEdgeItr = BlockEdges.find(CIEDeltaFieldOffset);
- if (CIEEdgeItr != BlockEdges.end())
+ auto CIEEdgeItr = BlockEdges.TargetMap.find(CIEDeltaFieldOffset);
+ if (CIEEdgeItr != BlockEdges.TargetMap.end())
return make_error<JITLinkError>(
"CIE pointer field already has edge at " +
formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset));
+ if (BlockEdges.Multiple.contains(CIEDeltaFieldOffset))
+ return make_error<JITLinkError>(
+ "CIE pointer field already has multiple edges at " +
+ formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset));
orc::ExecutorAddr CIEAddress =
RecordAddress + orc::ExecutorAddrDiff(CIEDeltaFieldOffset) -
@@ -482,7 +493,7 @@ Error EHFrameEdgeFixer::skipEncodedPointer(uint8_t PointerEncoding,
}
Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
- ParseContext &PC, const BlockEdgeMap &BlockEdges, uint8_t PointerEncoding,
+ ParseContext &PC, const BlockEdgesInfo &BlockEdges, uint8_t PointerEncoding,
BinaryStreamReader &RecordReader, Block &BlockToFix,
size_t PointerFieldOffset, const char *FieldName) {
using namespace dwarf;
@@ -493,8 +504,8 @@ Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
// If there's already an edge here then just skip the encoded pointer and
// return the edge's target.
{
- auto EdgeI = BlockEdges.find(PointerFieldOffset);
- if (EdgeI != BlockEdges.end()) {
+ auto EdgeI = BlockEdges.TargetMap.find(PointerFieldOffset);
+ if (EdgeI != BlockEdges.TargetMap.end()) {
LLVM_DEBUG({
dbgs() << " Existing edge at "
<< (BlockToFix.getAddress() + PointerFieldOffset) << " to "
@@ -507,6 +518,10 @@ Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
return std::move(Err);
return EdgeI->second.Target;
}
+
+ if (BlockEdges.Multiple.contains(PointerFieldOffset))
+ return make_error<JITLinkError>("Multiple relocations at offset " +
+ formatv("{0:x16}", PointerFieldOffset));
}
// Switch absptr to corresponding udata encoding.
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
index cc3c24cc85ead03..49fbf650e7a775e 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
@@ -60,7 +60,11 @@ class EHFrameEdgeFixer {
Edge::AddendT Addend = 0;
};
- using BlockEdgeMap = DenseMap<Edge::OffsetT, EdgeTarget>;
+ struct BlockEdgesInfo {
+ DenseMap<Edge::OffsetT, EdgeTarget> TargetMap;
+ DenseSet<Edge::OffsetT> Multiple;
+ };
+
using CIEInfosMap = DenseMap<orc::ExecutorAddr, CIEInformation>;
struct ParseContext {
@@ -82,9 +86,9 @@ class EHFrameEdgeFixer {
Error processBlock(ParseContext &PC, Block &B);
Error processCIE(ParseContext &PC, Block &B, size_t CIEDeltaFieldOffset,
- const BlockEdgeMap &BlockEdges);
+ const BlockEdgesInfo &BlockEdges);
Error processFDE(ParseContext &PC, Block &B, size_t CIEDeltaFieldOffset,
- uint32_t CIEDelta, const BlockEdgeMap &BlockEdges);
+ uint32_t CIEDelta, const BlockEdgesInfo &BlockEdges);
Expected<AugmentationInfo>
parseAugmentationString(BinaryStreamReader &RecordReader);
@@ -94,9 +98,9 @@ class EHFrameEdgeFixer {
Error skipEncodedPointer(uint8_t PointerEncoding,
BinaryStreamReader &RecordReader);
Expected<Symbol *> getOrCreateEncodedPointerEdge(
- ParseContext &PC, const BlockEdgeMap &BlockEdges, uint8_t PointerEncoding,
- BinaryStreamReader &RecordReader, Block &BlockToFix,
- size_t PointerFieldOffset, const char *FieldName);
+ ParseContext &PC, const BlockEdgesInfo &BlockEdges,
+ uint8_t PointerEncoding, BinaryStreamReader &RecordReader,
+ Block &BlockToFix, size_t PointerFieldOffset, const char *FieldName);
Expected<Symbol &> getOrCreateSymbol(ParseContext &PC,
orc::ExecutorAddr Addr);
>From 9237e0032ed19782a30f1a1a8d54e1a24bf4015b Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hahnjo at hahnjo.de>
Date: Wed, 4 Oct 2023 21:27:14 +0200
Subject: [PATCH 5/5] [JITLink][RISCV] Implement eh_frame handling
This requires adding a NegDelta32 edge kind that cannot be mapped to
existing relocations.
Co-authored-by: Job Noorman <jnoorman at igalia.com>
---
.../llvm/ExecutionEngine/JITLink/riscv.h | 6 +
.../lib/ExecutionEngine/JITLink/ELF_riscv.cpp | 16 +++
llvm/lib/ExecutionEngine/JITLink/riscv.cpp | 2 +
.../JITLink/RISCV/ELF_ehframe.s | 74 +++++++++++++
.../JITLink/RISCV/ELF_ehframe.test | 104 ++++++++++++++++++
5 files changed, 202 insertions(+)
create mode 100644 llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s
create mode 100644 llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h
index cb66289180880ce..a31f7d73b099f45 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h
@@ -214,6 +214,12 @@ enum EdgeKind_riscv : Edge::Kind {
/// Linker relaxation will use this to ensure all code sequences are properly
/// aligned and then remove these edges from the graph.
AlignRelaxable,
+
+ /// 32-bit negative delta.
+ ///
+ /// Fixup expression:
+ /// Fixup <- Fixup - Target + Addend
+ NegDelta32,
};
/// Returns a string name for the given riscv edge. For debugging purposes
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp
index 35816ea66cf9bc3..d0701ba08bd9194 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp
@@ -11,10 +11,12 @@
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
+#include "EHFrameSupportImpl.h"
#include "ELFLinkGraphBuilder.h"
#include "JITLinkGeneric.h"
#include "PerGraphGOTAndPLTStubsBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/ExecutionEngine/JITLink/riscv.h"
#include "llvm/Object/ELF.h"
@@ -456,6 +458,13 @@ class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> {
case AlignRelaxable:
// Ignore when the relaxation pass did not run
break;
+ case NegDelta32: {
+ int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
+ if (LLVM_UNLIKELY(!isInRangeForImm(Value, 32)))
+ return makeTargetOutOfRangeError(G, B, E);
+ *(little32_t *)FixupPtr = static_cast<uint32_t>(Value);
+ break;
+ }
}
return Error::success();
}
@@ -958,6 +967,13 @@ void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
PassConfiguration Config;
const Triple &TT = G->getTargetTriple();
if (Ctx->shouldAddDefaultTargetPasses(TT)) {
+
+ Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
+ Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
+ ".eh_frame", G->getPointerSize(), Edge::Invalid, Edge::Invalid,
+ Edge::Invalid, Edge::Invalid, NegDelta32));
+ Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
+
if (auto MarkLive = Ctx->getMarkLivePass(TT))
Config.PrePrunePasses.push_back(std::move(MarkLive));
else
diff --git a/llvm/lib/ExecutionEngine/JITLink/riscv.cpp b/llvm/lib/ExecutionEngine/JITLink/riscv.cpp
index a78843b1614795e..a4e4daef97fb5d6 100644
--- a/llvm/lib/ExecutionEngine/JITLink/riscv.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/riscv.cpp
@@ -82,6 +82,8 @@ const char *getEdgeKindName(Edge::Kind K) {
return "CallRelaxable";
case AlignRelaxable:
return "AlignRelaxable";
+ case NegDelta32:
+ return "NegDelta32";
}
return getGenericEdgeKindName(K);
}
diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s
new file mode 100644
index 000000000000000..9173bf7c3d95e4a
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s
@@ -0,0 +1,74 @@
+# REQUIRES: asserts
+
+# RUN: llvm-mc -triple=riscv32-linux-gnu -mattr=+relax -filetype=obj -o %t.32.o %s
+# RUN: llvm-jitlink -noexec -phony-externals -debug-only=jitlink %t.32.o 2>&1 | \
+# RUN: FileCheck %s
+
+# RUN: llvm-mc -triple=riscv64-linux-gnu -mattr=+relax -filetype=obj -o %t.64.o %s
+# RUN: llvm-jitlink -noexec -phony-externals -debug-only=jitlink %t.64.o 2>&1 | \
+# RUN: FileCheck %s
+
+# Check that splitting of eh-frame sections works.
+#
+# CHECK: DWARFRecordSectionSplitter: Processing .eh_frame...
+# CHECK: Processing block at
+# CHECK: Processing CFI record at
+# CHECK: Extracted {{.*}} section = .eh_frame
+# CHECK: Processing CFI record at
+# CHECK: Extracted {{.*}} section = .eh_frame
+# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
+# CHECK: Processing block at
+# CHECK: Record is CIE
+# CHECK: Processing block at
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK: Processing block at
+# CHECK: Record is FDE
+# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+
+## This is "int main { throw 1; }" compiled for riscv32. We use the 32-bit
+## version because it is also legal for riscv64.
+ .text
+ .globl main
+ .p2align 1
+ .type main, at function
+main:
+ .cfi_startproc
+ addi sp, sp, -16
+ .cfi_def_cfa_offset 16
+ sw ra, 12(sp)
+ .cfi_offset ra, -4
+ li a0, 4
+ call __cxa_allocate_exception
+ li a1, 1
+ sw a1, 0(a0)
+ lga a1, _ZTIi
+ li a2, 0
+ call __cxa_throw
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .cfi_endproc
+
+ .globl dup
+ .p2align 1
+ .type dup, at function
+dup:
+ .cfi_startproc
+ addi sp, sp, -16
+ .cfi_def_cfa_offset 16
+ sw ra, 12(sp)
+ .cfi_offset ra, -4
+ li a0, 4
+ call __cxa_allocate_exception
+ li a1, 1
+ sw a1, 0(a0)
+ lga a1, _ZTIi
+ li a2, 0
+ call __cxa_throw
+.Lfunc_end1:
+ .size dup, .Lfunc_end1-dup
+ .cfi_endproc
diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test
new file mode 100644
index 000000000000000..95666d2e232b70d
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test
@@ -0,0 +1,104 @@
+# RUN: yaml2obj -DELFCLASS=ELFCLASS32 -o %t.32.o %s
+# RUN: llvm-jitlink -noexec -check %s %t.32.o
+# RUN: yaml2obj -DELFCLASS=ELFCLASS64 -o %t.64.o %s
+# RUN: llvm-jitlink -noexec -check %s %t.64.o
+
+### Compiled from the following code with -mattr=+relax to force relocations for
+### address_range and DW_CFA_advance_loc (both needed for .balign).
+## .text
+## .globl main
+## .p2align 1
+## .type main, at function
+## main:
+## .cfi_startproc
+## .balign 8
+## addi sp, sp, -16
+## cfa_advance_loc:
+## .cfi_def_cfa_offset 16
+## nop
+## main_end:
+## .size main, main_end-main
+## .cfi_endproc
+
+--- !ELF
+FileHeader:
+ Class: [[ELFCLASS]]
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_RISCV
+ SectionHeaderStringTable: .strtab
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x8
+ Content: 13000000130101FF13000000
+ - Name: .eh_frame
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ AddressAlign: 0x8
+ Content: 1000000000000000017A5200017801011B0C02001000000018000000000000000000000000400E10
+ - Name: .rela.text
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .text
+ Relocations:
+ - Type: R_RISCV_ALIGN
+ Addend: 4
+ - Name: .rela.eh_frame
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .eh_frame
+ Relocations:
+ - Offset: 0x1C
+ Symbol: main
+ Type: R_RISCV_32_PCREL
+ - Offset: 0x20
+ Symbol: main_end
+ Type: R_RISCV_ADD32
+ - Offset: 0x20
+ Symbol: main
+ Type: R_RISCV_SUB32
+ - Offset: 0x25
+ Symbol: cfa_advance_loc
+ Type: R_RISCV_SET6
+ - Offset: 0x25
+ Symbol: main
+ Type: R_RISCV_SUB6
+ - Type: SectionHeaderTable
+ Sections:
+ - Name: .strtab
+ - Name: .text
+ - Name: .rela.text
+ - Name: .eh_frame
+ - Name: .rela.eh_frame
+ - Name: .symtab
+Symbols:
+ - Name: cfa_advance_loc
+ Section: .text
+ Value: 0x8
+ - Name: main_end
+ Section: .text
+ Value: 0xC
+ - Name: main
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Size: 0xC
+ - Name: eh_frame
+ Type: STT_SECTION
+ Binding: STB_GLOBAL
+ Section: .eh_frame
+ Size: 0x28
+...
+
+## CIE_pointer
+# jitlink-check: *{4}(eh_frame + 0x1c) = main - (eh_frame + 0x1c)
+## address_range
+# jitlink-check: *{4}(eh_frame + 0x20) = main_end - main
+## DW_CFA_advance_loc
+# jitlink-check: (*{1}(eh_frame + 0x25)) & 0x3f = cfa_advance_loc - main
More information about the llvm-commits
mailing list