[JITLink] Refactor and expand DWARF pointer encoding support.
Adds support for pointer encodings commonly used in large/static models, including non-pcrel, sdata/udata8, indirect, and omit. Also refactors pointer-encoding handling to consolidate error generation inside common functions, rather than callees of those functions.
This commit is contained in:
@@ -20,10 +20,12 @@ namespace llvm {
|
||||
namespace jitlink {
|
||||
|
||||
EHFrameEdgeFixer::EHFrameEdgeFixer(StringRef EHFrameSectionName,
|
||||
unsigned PointerSize, Edge::Kind Delta64,
|
||||
Edge::Kind Delta32, Edge::Kind NegDelta32)
|
||||
unsigned PointerSize, Edge::Kind Pointer32,
|
||||
Edge::Kind Pointer64, Edge::Kind Delta32,
|
||||
Edge::Kind Delta64, Edge::Kind NegDelta32)
|
||||
: EHFrameSectionName(EHFrameSectionName), PointerSize(PointerSize),
|
||||
Delta64(Delta64), Delta32(Delta32), NegDelta32(NegDelta32) {}
|
||||
Pointer32(Pointer32), Pointer64(Pointer64), Delta32(Delta32),
|
||||
Delta64(Delta64), NegDelta32(NegDelta32) {}
|
||||
|
||||
Error EHFrameEdgeFixer::operator()(LinkGraph &G) {
|
||||
auto *EHFrame = G.findSectionByName(EHFrameSectionName);
|
||||
@@ -84,10 +86,7 @@ Error EHFrameEdgeFixer::operator()(LinkGraph &G) {
|
||||
|
||||
Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Processing block at " << formatv("{0:x16}", B.getAddress())
|
||||
<< "\n";
|
||||
});
|
||||
LLVM_DEBUG(dbgs() << " Processing block at " << B.getAddress() << "\n");
|
||||
|
||||
// eh-frame should not contain zero-fill blocks.
|
||||
if (B.isZeroFill())
|
||||
@@ -121,7 +120,7 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Processing CFI record at "
|
||||
<< formatv("{0:x16}", B.getAddress() + RecordStartOffset) << "\n";
|
||||
<< (B.getAddress() + RecordStartOffset) << "\n";
|
||||
});
|
||||
|
||||
// Get the record length.
|
||||
@@ -156,7 +155,7 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
|
||||
if (CIEDelta == 0) {
|
||||
if (auto Err = processCIE(PC, B, RecordStartOffset,
|
||||
CIEDeltaFieldOffset + RecordRemaining,
|
||||
CIEDeltaFieldOffset))
|
||||
CIEDeltaFieldOffset, BlockEdges))
|
||||
return Err;
|
||||
} else {
|
||||
if (auto Err = processFDE(PC, B, RecordStartOffset,
|
||||
@@ -175,7 +174,8 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
|
||||
|
||||
Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
|
||||
size_t RecordOffset, size_t RecordLength,
|
||||
size_t CIEDeltaFieldOffset) {
|
||||
size_t CIEDeltaFieldOffset,
|
||||
const BlockEdgeMap &BlockEdges) {
|
||||
|
||||
LLVM_DEBUG(dbgs() << " Record is CIE\n");
|
||||
|
||||
@@ -234,67 +234,60 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
|
||||
if (auto Err = RecordReader.skip(1))
|
||||
return Err;
|
||||
|
||||
uint64_t AugmentationDataLength = 0;
|
||||
if (auto Err = RecordReader.readULEB128(AugmentationDataLength))
|
||||
return Err;
|
||||
if (AugInfo->AugmentationDataPresent) {
|
||||
|
||||
uint32_t AugmentationDataStartOffset = RecordReader.getOffset();
|
||||
CIEInfo.AugmentationDataPresent = true;
|
||||
|
||||
uint8_t *NextField = &AugInfo->Fields[0];
|
||||
while (uint8_t Field = *NextField++) {
|
||||
switch (Field) {
|
||||
case 'L': {
|
||||
CIEInfo.FDEsHaveLSDAField = true;
|
||||
uint8_t LSDAPointerEncoding;
|
||||
if (auto Err = RecordReader.readInteger(LSDAPointerEncoding))
|
||||
return Err;
|
||||
if (!isSupportedPointerEncoding(LSDAPointerEncoding))
|
||||
return make_error<JITLinkError>(
|
||||
"Unsupported LSDA pointer encoding " +
|
||||
formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
CIEInfo.LSDAPointerEncoding = LSDAPointerEncoding;
|
||||
break;
|
||||
}
|
||||
case 'P': {
|
||||
uint8_t PersonalityPointerEncoding = 0;
|
||||
if (auto Err = RecordReader.readInteger(PersonalityPointerEncoding))
|
||||
return Err;
|
||||
if (PersonalityPointerEncoding !=
|
||||
(dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel |
|
||||
dwarf::DW_EH_PE_sdata4))
|
||||
return make_error<JITLinkError>(
|
||||
"Unspported personality pointer "
|
||||
"encoding " +
|
||||
formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
uint32_t PersonalityPointerAddress;
|
||||
if (auto Err = RecordReader.readInteger(PersonalityPointerAddress))
|
||||
return Err;
|
||||
break;
|
||||
}
|
||||
case 'R': {
|
||||
uint8_t FDEPointerEncoding;
|
||||
if (auto Err = RecordReader.readInteger(FDEPointerEncoding))
|
||||
return Err;
|
||||
if (!isSupportedPointerEncoding(FDEPointerEncoding))
|
||||
return make_error<JITLinkError>(
|
||||
"Unsupported FDE pointer encoding " +
|
||||
formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
CIEInfo.FDEPointerEncoding = FDEPointerEncoding;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Invalid augmentation string field");
|
||||
uint64_t AugmentationDataLength = 0;
|
||||
if (auto Err = RecordReader.readULEB128(AugmentationDataLength))
|
||||
return Err;
|
||||
|
||||
uint32_t AugmentationDataStartOffset = RecordReader.getOffset();
|
||||
|
||||
uint8_t *NextField = &AugInfo->Fields[0];
|
||||
while (uint8_t Field = *NextField++) {
|
||||
switch (Field) {
|
||||
case 'L':
|
||||
CIEInfo.LSDAPresent = true;
|
||||
if (auto PE = readPointerEncoding(RecordReader, B, "LSDA"))
|
||||
CIEInfo.LSDAEncoding = *PE;
|
||||
else
|
||||
return PE.takeError();
|
||||
break;
|
||||
case 'P': {
|
||||
auto PersonalityPointerEncoding =
|
||||
readPointerEncoding(RecordReader, B, "personality");
|
||||
if (!PersonalityPointerEncoding)
|
||||
return PersonalityPointerEncoding.takeError();
|
||||
if (auto Err =
|
||||
getOrCreateEncodedPointerEdge(
|
||||
PC, BlockEdges, *PersonalityPointerEncoding, RecordReader,
|
||||
B, RecordOffset + RecordReader.getOffset(), "personality")
|
||||
.takeError())
|
||||
return Err;
|
||||
break;
|
||||
}
|
||||
case 'R':
|
||||
if (auto PE = readPointerEncoding(RecordReader, B, "address")) {
|
||||
CIEInfo.AddressEncoding = *PE;
|
||||
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()));
|
||||
} else
|
||||
return PE.takeError();
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Invalid augmentation string field");
|
||||
}
|
||||
}
|
||||
|
||||
if (RecordReader.getOffset() - AugmentationDataStartOffset >
|
||||
AugmentationDataLength)
|
||||
return make_error<JITLinkError>("Read past the end of the augmentation "
|
||||
"data while parsing fields");
|
||||
}
|
||||
|
||||
if (RecordReader.getOffset() - AugmentationDataStartOffset >
|
||||
AugmentationDataLength)
|
||||
return make_error<JITLinkError>("Read past the end of the augmentation "
|
||||
"data while parsing fields");
|
||||
|
||||
assert(!PC.CIEInfos.count(CIESymbol.getAddress()) &&
|
||||
"Multiple CIEs recorded at the same address?");
|
||||
PC.CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);
|
||||
@@ -306,7 +299,7 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
|
||||
size_t RecordOffset, size_t RecordLength,
|
||||
size_t CIEDeltaFieldOffset,
|
||||
uint32_t CIEDelta,
|
||||
BlockEdgeMap &BlockEdges) {
|
||||
const BlockEdgeMap &BlockEdges) {
|
||||
LLVM_DEBUG(dbgs() << " Record is FDE\n");
|
||||
|
||||
orc::ExecutorAddr RecordAddress = B.getAddress() + RecordOffset;
|
||||
@@ -334,8 +327,8 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Adding edge at "
|
||||
<< formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset)
|
||||
<< " to CIE at: " << formatv("{0:x16}", CIEAddress) << "\n";
|
||||
<< (RecordAddress + CIEDeltaFieldOffset)
|
||||
<< " to CIE at: " << CIEAddress << "\n";
|
||||
});
|
||||
if (auto CIEInfoOrErr = PC.findCIEInfo(CIEAddress))
|
||||
CIEInfo = *CIEInfoOrErr;
|
||||
@@ -347,8 +340,8 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
|
||||
} else {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Already has edge at "
|
||||
<< formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset)
|
||||
<< " to CIE at " << formatv("{0:x16}", CIEAddress) << "\n";
|
||||
<< (RecordAddress + CIEDeltaFieldOffset) << " to CIE at "
|
||||
<< CIEAddress << "\n";
|
||||
});
|
||||
auto &EI = CIEEdgeItr->second;
|
||||
if (EI.Addend)
|
||||
@@ -363,107 +356,41 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Process the PC-Begin field.
|
||||
Block *PCBeginBlock = nullptr;
|
||||
orc::ExecutorAddrDiff PCBeginFieldOffset = RecordReader.getOffset();
|
||||
auto PCEdgeItr = BlockEdges.find(RecordOffset + PCBeginFieldOffset);
|
||||
if (PCEdgeItr == BlockEdges.end()) {
|
||||
auto PCBeginPtrInfo =
|
||||
readEncodedPointer(CIEInfo->FDEPointerEncoding,
|
||||
RecordAddress + PCBeginFieldOffset, RecordReader);
|
||||
if (!PCBeginPtrInfo)
|
||||
return PCBeginPtrInfo.takeError();
|
||||
orc::ExecutorAddr PCBegin = PCBeginPtrInfo->first;
|
||||
Edge::Kind PCBeginEdgeKind = PCBeginPtrInfo->second;
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Adding edge at "
|
||||
<< (RecordAddress + PCBeginFieldOffset) << " to PC at "
|
||||
<< formatv("{0:x16}", PCBegin) << "\n";
|
||||
});
|
||||
auto PCBeginSym = getOrCreateSymbol(PC, PCBegin);
|
||||
if (!PCBeginSym)
|
||||
return PCBeginSym.takeError();
|
||||
B.addEdge(PCBeginEdgeKind, RecordOffset + PCBeginFieldOffset, *PCBeginSym,
|
||||
0);
|
||||
PCBeginBlock = &PCBeginSym->getBlock();
|
||||
} else {
|
||||
auto &EI = PCEdgeItr->second;
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Already has edge at "
|
||||
<< formatv("{0:x16}", RecordAddress + PCBeginFieldOffset)
|
||||
<< " to PC at " << formatv("{0:x16}", EI.Target->getAddress());
|
||||
if (EI.Addend)
|
||||
dbgs() << " + " << formatv("{0:x16}", EI.Addend);
|
||||
dbgs() << "\n";
|
||||
});
|
||||
|
||||
// Make sure the existing edge points at a defined block.
|
||||
if (!EI.Target->isDefined()) {
|
||||
auto EdgeAddr = RecordAddress + PCBeginFieldOffset;
|
||||
return make_error<JITLinkError>("FDE edge at " +
|
||||
formatv("{0:x16}", EdgeAddr) +
|
||||
" points at external block");
|
||||
}
|
||||
PCBeginBlock = &EI.Target->getBlock();
|
||||
if (auto Err = RecordReader.skip(
|
||||
getPointerEncodingDataSize(CIEInfo->FDEPointerEncoding)))
|
||||
return Err;
|
||||
}
|
||||
|
||||
// Process the PC-Begin field.
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Processing PC-begin at "
|
||||
<< (RecordAddress + RecordReader.getOffset()) << "\n";
|
||||
});
|
||||
if (auto PCBegin = getOrCreateEncodedPointerEdge(
|
||||
PC, BlockEdges, CIEInfo->AddressEncoding, RecordReader, B,
|
||||
RecordReader.getOffset(), "PC begin")) {
|
||||
assert(*PCBegin && "PC-begin symbol not set");
|
||||
// Add a keep-alive edge from the FDE target to the FDE to ensure that the
|
||||
// FDE is kept alive if its target is.
|
||||
assert(PCBeginBlock && "PC-begin block not recorded");
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Adding keep-alive edge from target at "
|
||||
<< formatv("{0:x16}", PCBeginBlock->getAddress()) << " to FDE at "
|
||||
<< formatv("{0:x16}", RecordAddress) << "\n";
|
||||
<< (*PCBegin)->getBlock().getAddress() << " to FDE at "
|
||||
<< RecordAddress << "\n";
|
||||
});
|
||||
PCBeginBlock->addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
|
||||
}
|
||||
(*PCBegin)->getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
|
||||
} else
|
||||
return PCBegin.takeError();
|
||||
|
||||
// Skip over the PC range size field.
|
||||
if (auto Err = RecordReader.skip(
|
||||
getPointerEncodingDataSize(CIEInfo->FDEPointerEncoding)))
|
||||
if (auto Err = skipEncodedPointer(CIEInfo->AddressEncoding, RecordReader))
|
||||
return Err;
|
||||
|
||||
if (CIEInfo->FDEsHaveLSDAField) {
|
||||
if (CIEInfo->AugmentationDataPresent) {
|
||||
uint64_t AugmentationDataSize;
|
||||
if (auto Err = RecordReader.readULEB128(AugmentationDataSize))
|
||||
return Err;
|
||||
|
||||
orc::ExecutorAddrDiff LSDAFieldOffset = RecordReader.getOffset();
|
||||
auto LSDAEdgeItr = BlockEdges.find(RecordOffset + LSDAFieldOffset);
|
||||
if (LSDAEdgeItr == BlockEdges.end()) {
|
||||
auto LSDAPointerInfo =
|
||||
readEncodedPointer(CIEInfo->LSDAPointerEncoding,
|
||||
RecordAddress + LSDAFieldOffset, RecordReader);
|
||||
if (!LSDAPointerInfo)
|
||||
return LSDAPointerInfo.takeError();
|
||||
orc::ExecutorAddr LSDA = LSDAPointerInfo->first;
|
||||
Edge::Kind LSDAEdgeKind = LSDAPointerInfo->second;
|
||||
auto LSDASym = getOrCreateSymbol(PC, LSDA);
|
||||
if (!LSDASym)
|
||||
return LSDASym.takeError();
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Adding edge at "
|
||||
<< formatv("{0:x16}", RecordAddress + LSDAFieldOffset)
|
||||
<< " to LSDA at " << formatv("{0:x16}", LSDA) << "\n";
|
||||
});
|
||||
B.addEdge(LSDAEdgeKind, RecordOffset + LSDAFieldOffset, *LSDASym, 0);
|
||||
} else {
|
||||
LLVM_DEBUG({
|
||||
auto &EI = LSDAEdgeItr->second;
|
||||
dbgs() << " Already has edge at "
|
||||
<< formatv("{0:x16}", RecordAddress + LSDAFieldOffset)
|
||||
<< " to LSDA at " << formatv("{0:x16}", EI.Target->getAddress());
|
||||
if (EI.Addend)
|
||||
dbgs() << " + " << formatv("{0:x16}", EI.Addend);
|
||||
dbgs() << "\n";
|
||||
});
|
||||
if (auto Err = RecordReader.skip(AugmentationDataSize))
|
||||
if (CIEInfo->LSDAPresent)
|
||||
if (auto Err = getOrCreateEncodedPointerEdge(
|
||||
PC, BlockEdges, CIEInfo->LSDAEncoding, RecordReader, B,
|
||||
RecordReader.getOffset(), "LSDA")
|
||||
.takeError())
|
||||
return Err;
|
||||
}
|
||||
} else {
|
||||
LLVM_DEBUG(dbgs() << " Record does not have LSDA field.\n");
|
||||
}
|
||||
@@ -512,108 +439,155 @@ EHFrameEdgeFixer::parseAugmentationString(BinaryStreamReader &RecordReader) {
|
||||
return std::move(AugInfo);
|
||||
}
|
||||
|
||||
bool EHFrameEdgeFixer::isSupportedPointerEncoding(uint8_t PointerEncoding) {
|
||||
Expected<uint8_t> EHFrameEdgeFixer::readPointerEncoding(BinaryStreamReader &R,
|
||||
Block &InBlock,
|
||||
const char *FieldName) {
|
||||
using namespace dwarf;
|
||||
|
||||
// We only support PC-rel for now.
|
||||
if ((PointerEncoding & 0x70) != DW_EH_PE_pcrel)
|
||||
return false;
|
||||
uint8_t PointerEncoding;
|
||||
if (auto Err = R.readInteger(PointerEncoding))
|
||||
return Err;
|
||||
|
||||
// readEncodedPointer does not handle indirect.
|
||||
if (PointerEncoding & DW_EH_PE_indirect)
|
||||
return false;
|
||||
|
||||
// Supported datatypes.
|
||||
bool Supported = true;
|
||||
switch (PointerEncoding & 0xf) {
|
||||
case DW_EH_PE_absptr:
|
||||
case DW_EH_PE_udata4:
|
||||
case DW_EH_PE_udata8:
|
||||
case DW_EH_PE_sdata4:
|
||||
case DW_EH_PE_sdata8:
|
||||
return true;
|
||||
case DW_EH_PE_uleb128:
|
||||
case DW_EH_PE_udata2:
|
||||
case DW_EH_PE_sleb128:
|
||||
case DW_EH_PE_sdata2:
|
||||
Supported = false;
|
||||
break;
|
||||
}
|
||||
if (Supported) {
|
||||
switch (PointerEncoding & 0x70) {
|
||||
case DW_EH_PE_textrel:
|
||||
case DW_EH_PE_datarel:
|
||||
case DW_EH_PE_funcrel:
|
||||
case DW_EH_PE_aligned:
|
||||
Supported = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (Supported)
|
||||
return PointerEncoding;
|
||||
|
||||
return make_error<JITLinkError>("Unsupported pointer encoding " +
|
||||
formatv("{0:x2}", PointerEncoding) + " for " +
|
||||
FieldName + "in CFI record at " +
|
||||
formatv("{0:x16}", InBlock.getAddress()));
|
||||
}
|
||||
|
||||
unsigned EHFrameEdgeFixer::getPointerEncodingDataSize(uint8_t PointerEncoding) {
|
||||
Error EHFrameEdgeFixer::skipEncodedPointer(uint8_t PointerEncoding,
|
||||
BinaryStreamReader &RecordReader) {
|
||||
using namespace dwarf;
|
||||
|
||||
assert(isSupportedPointerEncoding(PointerEncoding) &&
|
||||
"Unsupported pointer encoding");
|
||||
// Switch absptr to corresponding udata encoding.
|
||||
if ((PointerEncoding & 0xf) == DW_EH_PE_absptr)
|
||||
PointerEncoding |= (PointerSize == 8) ? DW_EH_PE_udata8 : DW_EH_PE_udata4;
|
||||
|
||||
switch (PointerEncoding & 0xf) {
|
||||
case DW_EH_PE_absptr:
|
||||
return PointerSize;
|
||||
case DW_EH_PE_udata4:
|
||||
case DW_EH_PE_sdata4:
|
||||
return 4;
|
||||
if (auto Err = RecordReader.skip(4))
|
||||
return Err;
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
case DW_EH_PE_sdata8:
|
||||
return 8;
|
||||
if (auto Err = RecordReader.skip(8))
|
||||
return Err;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unsupported encoding");
|
||||
llvm_unreachable("Unrecognized encoding");
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<std::pair<orc::ExecutorAddr, Edge::Kind>>
|
||||
EHFrameEdgeFixer::readEncodedPointer(uint8_t PointerEncoding,
|
||||
orc::ExecutorAddr PointerFieldAddress,
|
||||
BinaryStreamReader &RecordReader) {
|
||||
assert(isSupportedPointerEncoding(PointerEncoding) &&
|
||||
"Unsupported pointer encoding");
|
||||
|
||||
Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
|
||||
ParseContext &PC, const BlockEdgeMap &BlockEdges, uint8_t PointerEncoding,
|
||||
BinaryStreamReader &RecordReader, Block &BlockToFix,
|
||||
size_t PointerFieldOffset, const char *FieldName) {
|
||||
using namespace dwarf;
|
||||
|
||||
// Isolate data type, remap absptr to udata4 or udata8. This relies on us
|
||||
// having verified that the graph uses 32-bit or 64-bit pointers only at the
|
||||
// start of this pass.
|
||||
uint8_t EffectiveType = PointerEncoding & 0xf;
|
||||
if (EffectiveType == DW_EH_PE_absptr)
|
||||
EffectiveType = (PointerSize == 8) ? DW_EH_PE_udata8 : DW_EH_PE_udata4;
|
||||
if (PointerEncoding == DW_EH_PE_omit)
|
||||
return nullptr;
|
||||
|
||||
orc::ExecutorAddr Addr;
|
||||
Edge::Kind PointerEdgeKind = Edge::Invalid;
|
||||
switch (EffectiveType) {
|
||||
// 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()) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Existing edge at "
|
||||
<< (BlockToFix.getAddress() + PointerFieldOffset) << " to "
|
||||
<< FieldName << " at " << EdgeI->second.Target->getAddress();
|
||||
if (EdgeI->second.Target->hasName())
|
||||
dbgs() << " (" << EdgeI->second.Target->getName() << ")";
|
||||
dbgs() << "\n";
|
||||
});
|
||||
if (auto Err = skipEncodedPointer(PointerEncoding, RecordReader))
|
||||
return Err;
|
||||
return EdgeI->second.Target;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch absptr to corresponding udata encoding.
|
||||
if ((PointerEncoding & 0xf) == DW_EH_PE_absptr)
|
||||
PointerEncoding |= (PointerSize == 8) ? DW_EH_PE_udata8 : DW_EH_PE_udata4;
|
||||
|
||||
// We need to create an edge. Start by reading the field value.
|
||||
uint64_t FieldValue;
|
||||
bool Is64Bit = false;
|
||||
switch (PointerEncoding & 0xf) {
|
||||
case DW_EH_PE_udata4: {
|
||||
uint32_t Val;
|
||||
if (auto Err = RecordReader.readInteger(Val))
|
||||
return std::move(Err);
|
||||
Addr = PointerFieldAddress + Val;
|
||||
PointerEdgeKind = Delta32;
|
||||
break;
|
||||
}
|
||||
case DW_EH_PE_udata8: {
|
||||
uint64_t Val;
|
||||
if (auto Err = RecordReader.readInteger(Val))
|
||||
return std::move(Err);
|
||||
Addr = PointerFieldAddress + Val;
|
||||
PointerEdgeKind = Delta64;
|
||||
FieldValue = Val;
|
||||
break;
|
||||
}
|
||||
case DW_EH_PE_sdata4: {
|
||||
int32_t Val;
|
||||
uint32_t Val;
|
||||
if (auto Err = RecordReader.readInteger(Val))
|
||||
return std::move(Err);
|
||||
Addr = PointerFieldAddress + Val;
|
||||
PointerEdgeKind = Delta32;
|
||||
FieldValue = Val;
|
||||
break;
|
||||
}
|
||||
case DW_EH_PE_sdata8: {
|
||||
int64_t Val;
|
||||
if (auto Err = RecordReader.readInteger(Val))
|
||||
case DW_EH_PE_udata8:
|
||||
case DW_EH_PE_sdata8:
|
||||
Is64Bit = true;
|
||||
if (auto Err = RecordReader.readInteger(FieldValue))
|
||||
return std::move(Err);
|
||||
Addr = PointerFieldAddress + Val;
|
||||
PointerEdgeKind = Delta64;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Unsupported encoding");
|
||||
}
|
||||
|
||||
if (PointerEdgeKind == Edge::Invalid)
|
||||
return make_error<JITLinkError>(
|
||||
"Unspported edge kind for encoded pointer at " +
|
||||
formatv("{0:x}", PointerFieldAddress));
|
||||
// Find the edge target and edge kind to use.
|
||||
orc::ExecutorAddr Target;
|
||||
Edge::Kind PtrEdgeKind = Edge::Invalid;
|
||||
if ((PointerEncoding & 0x70) == DW_EH_PE_pcrel) {
|
||||
Target = BlockToFix.getAddress() + PointerFieldOffset;
|
||||
PtrEdgeKind = Is64Bit ? Delta64 : Delta32;
|
||||
} else
|
||||
PtrEdgeKind = Is64Bit ? Pointer64 : Pointer32;
|
||||
Target += FieldValue;
|
||||
|
||||
return std::make_pair(Addr, Delta64);
|
||||
// Find or create a symbol to point the edge at.
|
||||
auto TargetSym = getOrCreateSymbol(PC, Target);
|
||||
if (!TargetSym)
|
||||
return TargetSym.takeError();
|
||||
BlockToFix.addEdge(PtrEdgeKind, PointerFieldOffset, *TargetSym, 0);
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Adding edge at "
|
||||
<< (BlockToFix.getAddress() + PointerFieldOffset) << " to "
|
||||
<< FieldName << " at " << TargetSym->getAddress();
|
||||
if (TargetSym->hasName())
|
||||
dbgs() << " (" << TargetSym->getName() << ")";
|
||||
dbgs() << "\n";
|
||||
});
|
||||
|
||||
return &*TargetSym;
|
||||
}
|
||||
|
||||
Expected<Symbol &> EHFrameEdgeFixer::getOrCreateSymbol(ParseContext &PC,
|
||||
|
||||
@@ -25,8 +25,12 @@ namespace jitlink {
|
||||
/// edges.
|
||||
class EHFrameEdgeFixer {
|
||||
public:
|
||||
/// Create an eh-frame edge fixer.
|
||||
/// If a given edge-kind is not supported on the target architecture then
|
||||
/// Edge::Invalid should be used.
|
||||
EHFrameEdgeFixer(StringRef EHFrameSectionName, unsigned PointerSize,
|
||||
Edge::Kind Delta64, Edge::Kind Delta32,
|
||||
Edge::Kind Pointer32, Edge::Kind Pointer64,
|
||||
Edge::Kind Delta32, Edge::Kind Delta64,
|
||||
Edge::Kind NegDelta32);
|
||||
Error operator()(LinkGraph &G);
|
||||
|
||||
@@ -42,9 +46,10 @@ private:
|
||||
CIEInformation() = default;
|
||||
CIEInformation(Symbol &CIESymbol) : CIESymbol(&CIESymbol) {}
|
||||
Symbol *CIESymbol = nullptr;
|
||||
bool FDEsHaveLSDAField = false;
|
||||
uint8_t FDEPointerEncoding = 0;
|
||||
uint8_t LSDAPointerEncoding = 0;
|
||||
bool AugmentationDataPresent = false;
|
||||
bool LSDAPresent = false;
|
||||
uint8_t LSDAEncoding = 0;
|
||||
uint8_t AddressEncoding = 0;
|
||||
};
|
||||
|
||||
struct EdgeTarget {
|
||||
@@ -77,28 +82,33 @@ private:
|
||||
|
||||
Error processBlock(ParseContext &PC, Block &B);
|
||||
Error processCIE(ParseContext &PC, Block &B, size_t RecordOffset,
|
||||
size_t RecordLength, size_t CIEDeltaFieldOffset);
|
||||
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, BlockEdgeMap &BlockEdges);
|
||||
uint32_t CIEDelta, const BlockEdgeMap &BlockEdges);
|
||||
|
||||
Expected<AugmentationInfo>
|
||||
parseAugmentationString(BinaryStreamReader &RecordReader);
|
||||
|
||||
static bool isSupportedPointerEncoding(uint8_t PointerEncoding);
|
||||
unsigned getPointerEncodingDataSize(uint8_t PointerEncoding);
|
||||
Expected<std::pair<orc::ExecutorAddr, Edge::Kind>>
|
||||
readEncodedPointer(uint8_t PointerEncoding,
|
||||
orc::ExecutorAddr PointerFieldAddress,
|
||||
BinaryStreamReader &RecordReader);
|
||||
Expected<uint8_t> readPointerEncoding(BinaryStreamReader &RecordReader,
|
||||
Block &InBlock, const char *FieldName);
|
||||
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);
|
||||
|
||||
Expected<Symbol &> getOrCreateSymbol(ParseContext &PC,
|
||||
orc::ExecutorAddr Addr);
|
||||
|
||||
StringRef EHFrameSectionName;
|
||||
unsigned PointerSize;
|
||||
Edge::Kind Delta64;
|
||||
Edge::Kind Pointer32;
|
||||
Edge::Kind Pointer64;
|
||||
Edge::Kind Delta32;
|
||||
Edge::Kind Delta64;
|
||||
Edge::Kind NegDelta32;
|
||||
};
|
||||
|
||||
|
||||
@@ -381,9 +381,9 @@ void link_ELF_x86_64(std::unique_ptr<LinkGraph> G,
|
||||
if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {
|
||||
|
||||
Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
|
||||
Config.PrePrunePasses.push_back(
|
||||
EHFrameEdgeFixer(".eh_frame", x86_64::PointerSize, x86_64::Delta64,
|
||||
x86_64::Delta32, x86_64::NegDelta32));
|
||||
Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
|
||||
".eh_frame", x86_64::PointerSize, x86_64::Pointer32, x86_64::Pointer64,
|
||||
x86_64::Delta32, x86_64::Delta64, x86_64::NegDelta32));
|
||||
Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
|
||||
|
||||
// Construct a JITLinker and run the link function.
|
||||
|
||||
@@ -716,7 +716,8 @@ void link_MachO_arm64(std::unique_ptr<LinkGraph> G,
|
||||
Config.PrePrunePasses.push_back(
|
||||
DWARFRecordSectionSplitter("__TEXT,__eh_frame"));
|
||||
Config.PrePrunePasses.push_back(
|
||||
EHFrameEdgeFixer("__TEXT,__eh_frame", 8, Delta64, Delta32, NegDelta32));
|
||||
EHFrameEdgeFixer("__TEXT,__eh_frame", 8, Pointer32, Pointer64, Delta32,
|
||||
Delta64, NegDelta32));
|
||||
|
||||
// Add an in-place GOT/Stubs pass.
|
||||
Config.PostPrunePasses.push_back(
|
||||
|
||||
@@ -510,7 +510,8 @@ LinkGraphPassFunction createEHFrameSplitterPass_MachO_x86_64() {
|
||||
|
||||
LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_x86_64() {
|
||||
return EHFrameEdgeFixer("__TEXT,__eh_frame", x86_64::PointerSize,
|
||||
x86_64::Delta64, x86_64::Delta32, x86_64::NegDelta32);
|
||||
x86_64::Pointer32, x86_64::Pointer64, x86_64::Delta32,
|
||||
x86_64::Delta64, x86_64::NegDelta32);
|
||||
}
|
||||
|
||||
} // end namespace jitlink
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
# CHECK: Processing CFI record at
|
||||
# CHECK: Record is FDE
|
||||
# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
|
||||
# CHECK: Already has edge at {{.*}} to PC at {{.*}}
|
||||
# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
|
||||
# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
|
||||
# CHECK: Already has edge at {{.*}} to LSDA at {{.*}}
|
||||
# CHECK: Existing edge at {{.*}} to LSDA at {{.*}}
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.globl _main
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
# REQUIRES: asserts
|
||||
# UNSUPPORTED: system-windows
|
||||
# RUN: llvm-mc -triple=x86_64-pc-linux-gnu -large-code-model \
|
||||
# RUN: -filetype=obj -o %t %s
|
||||
# RUN: llvm-jitlink -debug-only=jitlink -noexec %t 2>&1 | FileCheck %s
|
||||
#
|
||||
# Check handling of pointer encodings for personality functions when compiling
|
||||
# with `-mcmodel=large -static`.
|
||||
#
|
||||
|
||||
# CHECK: Record is CIE
|
||||
# CHECK-NEXT: edge at {{.*}} to personality at {{.*}} (DW.ref.__gxx_personality_v0)
|
||||
# CHECK: Record is CIE
|
||||
# CHECK-NEXT: edge at {{.*}} to personality at {{.*}} (__gxx_personality_v0)
|
||||
|
||||
.text
|
||||
.file "eh.cpp"
|
||||
|
||||
.globl main
|
||||
.p2align 4, 0x90
|
||||
.type main,@function
|
||||
main:
|
||||
xorl %eax, %eax
|
||||
retq
|
||||
.Lfunc_end_main:
|
||||
.size main, .Lfunc_end_main-main
|
||||
|
||||
# pe_absptr uses absptr encoding for __gxx_personality_v0
|
||||
.text
|
||||
.globl pe_absptr
|
||||
.p2align 4, 0x90
|
||||
.type pe_absptr,@function
|
||||
pe_absptr:
|
||||
.Lfunc_begin0:
|
||||
.cfi_startproc
|
||||
.cfi_personality 0, __gxx_personality_v0
|
||||
.cfi_lsda 0, .Lexception0
|
||||
pushq %rax
|
||||
.cfi_def_cfa_offset 16
|
||||
movabsq $__cxa_allocate_exception, %rax
|
||||
movl $4, %edi
|
||||
callq *%rax
|
||||
movl $42, (%rax)
|
||||
.Ltmp0:
|
||||
movabsq $_ZTIi, %rsi
|
||||
movabsq $__cxa_throw, %rcx
|
||||
movq %rax, %rdi
|
||||
xorl %edx, %edx
|
||||
callq *%rcx
|
||||
.Ltmp1:
|
||||
.LBB0_2:
|
||||
.Ltmp2:
|
||||
movabsq $__cxa_begin_catch, %rcx
|
||||
movq %rax, %rdi
|
||||
callq *%rcx
|
||||
movabsq $__cxa_end_catch, %rax
|
||||
popq %rcx
|
||||
.cfi_def_cfa_offset 8
|
||||
jmpq *%rax
|
||||
.Lfunc_end0:
|
||||
.size pe_absptr, .Lfunc_end0-pe_absptr
|
||||
.cfi_endproc
|
||||
.section .gcc_except_table,"a",@progbits
|
||||
.p2align 2
|
||||
GCC_except_table0:
|
||||
.Lexception0:
|
||||
.byte 255 # @LPStart Encoding = omit
|
||||
.byte 0 # @TType Encoding = absptr
|
||||
.uleb128 .Lttbase0-.Lttbaseref0
|
||||
.Lttbaseref0:
|
||||
.byte 1 # Call site Encoding = uleb128
|
||||
.uleb128 .Lcst_end0-.Lcst_begin0
|
||||
.Lcst_begin0:
|
||||
.uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
|
||||
.uleb128 .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
|
||||
.byte 0 # has no landing pad
|
||||
.byte 0 # On action: cleanup
|
||||
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
|
||||
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
|
||||
.uleb128 .Ltmp2-.Lfunc_begin0 # jumps to .Ltmp2
|
||||
.byte 1 # On action: 1
|
||||
.uleb128 .Ltmp1-.Lfunc_begin0 # >> Call Site 3 <<
|
||||
.uleb128 .Lfunc_end0-.Ltmp1 # Call between .Ltmp1 and .Lfunc_end0
|
||||
.byte 0 # has no landing pad
|
||||
.byte 0 # On action: cleanup
|
||||
.Lcst_end0:
|
||||
.byte 1 # >> Action Record 1 <<
|
||||
# Catch TypeInfo 1
|
||||
.byte 0 # No further actions
|
||||
.p2align 2
|
||||
# >> Catch TypeInfos <<
|
||||
.quad _ZTIi # TypeInfo 1
|
||||
.Lttbase0:
|
||||
.p2align 2
|
||||
# -- End function
|
||||
|
||||
# pe_indir_pcrel_sdata8 uses 0x9C -- Indirect, pc-rel, sdata8 encoding to
|
||||
# DW.ref.__gxx_personality_v0
|
||||
.text
|
||||
.globl pe_indir_pcrel_sdata8
|
||||
.p2align 4, 0x90
|
||||
.type pe_indir_pcrel_sdata8,@function
|
||||
pe_indir_pcrel_sdata8:
|
||||
.Lfunc_begin1:
|
||||
.cfi_startproc
|
||||
.cfi_personality 156, DW.ref.__gxx_personality_v0
|
||||
.cfi_lsda 28, .Lexception1
|
||||
pushq %r14
|
||||
.cfi_def_cfa_offset 16
|
||||
pushq %rbx
|
||||
.cfi_def_cfa_offset 24
|
||||
pushq %rax
|
||||
.cfi_def_cfa_offset 32
|
||||
.cfi_offset %rbx, -24
|
||||
.cfi_offset %r14, -16
|
||||
.L1$pb:
|
||||
leaq .L1$pb(%rip), %rax
|
||||
movabsq $_GLOBAL_OFFSET_TABLE_-.L1$pb, %rbx
|
||||
addq %rax, %rbx
|
||||
movabsq $__cxa_allocate_exception@GOT, %rax
|
||||
movl $4, %edi
|
||||
callq *(%rbx,%rax)
|
||||
movl $42, (%rax)
|
||||
.Ltmp4:
|
||||
movabsq $_ZTIi@GOT, %rcx
|
||||
movq (%rbx,%rcx), %rsi
|
||||
movabsq $__cxa_throw@GOT, %rcx
|
||||
movq %rax, %rdi
|
||||
xorl %edx, %edx
|
||||
movq %rbx, %r14
|
||||
callq *(%rbx,%rcx)
|
||||
.Ltmp5:
|
||||
.LBB1_2:
|
||||
.Ltmp6:
|
||||
movabsq $__cxa_begin_catch@GOT, %rcx
|
||||
movq %rax, %rdi
|
||||
callq *(%r14,%rcx)
|
||||
movabsq $__cxa_end_catch@GOT, %rax
|
||||
movq %r14, %rcx
|
||||
addq $8, %rsp
|
||||
.cfi_def_cfa_offset 24
|
||||
popq %rbx
|
||||
.cfi_def_cfa_offset 16
|
||||
popq %r14
|
||||
.cfi_def_cfa_offset 8
|
||||
jmpq *(%rcx,%rax)
|
||||
.Lfunc_end1:
|
||||
.size pe_indir_pcrel_sdata8, .Lfunc_end1-pe_indir_pcrel_sdata8
|
||||
.cfi_endproc
|
||||
.section .gcc_except_table,"a",@progbits
|
||||
.p2align 2
|
||||
GCC_except_table1:
|
||||
.Lexception1:
|
||||
.byte 255 # @LPStart Encoding = omit
|
||||
.byte 156 # @TType Encoding = indirect pcrel sdata8
|
||||
.uleb128 .Lttbase1-.Lttbaseref1
|
||||
.Lttbaseref1:
|
||||
.byte 1 # Call site Encoding = uleb128
|
||||
.uleb128 .Lcst_end1-.Lcst_begin1
|
||||
.Lcst_begin1:
|
||||
.uleb128 .Lfunc_begin1-.Lfunc_begin1 # >> Call Site 1 <<
|
||||
.uleb128 .Ltmp4-.Lfunc_begin1 # Call between .Lfunc_begin1 and .Ltmp4
|
||||
.byte 0 # has no landing pad
|
||||
.byte 0 # On action: cleanup
|
||||
.uleb128 .Ltmp4-.Lfunc_begin1 # >> Call Site 2 <<
|
||||
.uleb128 .Ltmp5-.Ltmp4 # Call between .Ltmp4 and .Ltmp5
|
||||
.uleb128 .Ltmp6-.Lfunc_begin1 # jumps to .Ltmp6
|
||||
.byte 1 # On action: 1
|
||||
.uleb128 .Ltmp5-.Lfunc_begin1 # >> Call Site 3 <<
|
||||
.uleb128 .Lfunc_end1-.Ltmp5 # Call between .Ltmp5 and .Lfunc_end1
|
||||
.byte 0 # has no landing pad
|
||||
.byte 0 # On action: cleanup
|
||||
.Lcst_end1:
|
||||
.byte 1 # >> Action Record 1 <<
|
||||
# Catch TypeInfo 1
|
||||
.byte 0 # No further actions
|
||||
.p2align 2
|
||||
# >> Catch TypeInfos <<
|
||||
.Ltmp7: # TypeInfo 1
|
||||
.quad .L_ZTIi.DW.stub-.Ltmp7
|
||||
.Lttbase1:
|
||||
.p2align 2
|
||||
# -- End function
|
||||
|
||||
.data
|
||||
.p2align 3
|
||||
.L_ZTIi.DW.stub:
|
||||
.quad _ZTIi
|
||||
.hidden DW.ref.__gxx_personality_v0
|
||||
.weak DW.ref.__gxx_personality_v0
|
||||
|
||||
.section .data.DW.ref.__gxx_personality_v0,"aGw",@progbits,DW.ref.__gxx_personality_v0,comdat
|
||||
.p2align 3
|
||||
.type DW.ref.__gxx_personality_v0,@object
|
||||
.size DW.ref.__gxx_personality_v0, 8
|
||||
DW.ref.__gxx_personality_v0:
|
||||
.quad __gxx_personality_v0
|
||||
|
||||
.ident "clang version 13.0.1"
|
||||
.section ".note.GNU-stack","",@progbits
|
||||
.addrsig
|
||||
.addrsig_sym __gxx_personality_v0
|
||||
.addrsig_sym _ZTIi
|
||||
Reference in New Issue
Block a user