-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[JITLink][RISCV] Implement eh_frame handling #68253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-backend-risc-v ChangesThis requires adding a This PR depends on #66707 and #68252; only the top-most commit should be reviewed here. Patch is 29.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/68253.diff 11 Files Affected:
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/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index 86249591a9be053..f036445497cafd5 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -126,83 +126,71 @@ 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);
+ }
}
- CIEInfosMap CIEInfos;
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) {
+ const BlockEdgesInfo &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;
@@ -268,7 +256,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;
@@ -279,7 +267,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;
@@ -302,69 +290,54 @@ 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");
+ const BlockEdgesInfo &BlockEdges) {
+ 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.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) -
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, 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(
@@ -375,14 +348,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()
@@ -409,7 +382,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();
@@ -520,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;
@@ -531,10 +504,10 @@ 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 "
+ dbgs() << " Existing edge at "
<< (BlockToFix.getAddress() + PointerFieldOffset) << " to "
<< FieldName << " at " << EdgeI->second.Target->getAddress();
if (EdgeI->second.Target->hasName())
@@ -545,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.
@@ -596,7 +573,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..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 {
@@ -81,12 +85,10 @@ class EHFrameEdgeFixer {
};
Error processBlock(ParseContext &PC, Block &B);
- Error processCIE(ParseContext &PC, Block &B, size_t RecordOffset,
- size_t RecordLength, size_t CIEDeltaFieldOffset,
- const BlockEdgeMap &BlockEdges);
- Error processFDE(ParseContext &PC, Block &B, size_t RecordOffset,
- size_t RecordLength, size_t CIEDeltaFieldOffset,
- uint32_t CIEDelta, const BlockEdgeMap &BlockEdges);
+ Error processCIE(ParseContext &PC, Block &B, size_t CIEDeltaFieldOffset,
+ const BlockEdgesInfo &BlockEdges);
+ Error processFDE(ParseContext &PC, Block &B, size_t CIEDeltaFieldOffset,
+ uint32_t CIEDelta, const BlockEdgesInfo &BlockEdges);
Expected<AugmentationInfo>
parseAugmentationString(BinaryStreamReader &RecordReader);
@@ -96,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);
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/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 mai...
[truncated]
|
I'm also not sure if there are any guidelines but for me this is definitely fine. Thanks! |
This requires adding a NegDelta32 edge kind that cannot be mapped to existing relocations. Co-authored-by: Job Noorman <[email protected]>
This reverts commit 123223a as the test is now passing.
9237e00
to
4af1848
Compare
It seems after this change we started to fail on
|
First, sorry for the slow response and breaking the builds with expensive checks in the first place. I believe the report is actually fully correct and we cannot assume the edges are ordered by their offset. The problem should be fixed by #78849. |
This requires adding a
NegDelta32
edge kind that cannot be mapped to existing relocations.