diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h b/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h index 33eee7a75f390a..49e1ce7278ffe7 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/EHFrameSupport.h @@ -21,6 +21,68 @@ namespace llvm { namespace jitlink { +/// Inspect an eh-frame CFI record. +class EHFrameCFIBlockInspector { +public: + /// Identify CFI record type and edges based on number and order of edges + /// in the given block only. This assumes that the block contains one CFI + /// record that has already been split out and fixed by the + /// DWARFRecordSplitter and EHFrameEdgeFixer passes. + /// + /// Zero or one outgoing edges: Record is CIE. If present, edge points to + /// personality. + /// + /// Two or three outgoing edges: Record is an FDE. First edge points to CIE, + /// second to PC-begin, third (if present) to LSDA. + /// + /// It is illegal to call this function on a block with four or more edges. + static EHFrameCFIBlockInspector FromEdgeScan(Block &B); + + /// Returns true if this frame is an FDE, false for a CIE. + bool isFDE() const { return CIEEdge != nullptr; } + + /// Returns true if this frame is a CIE, false for an FDE. + bool isCIE() const { return CIEEdge == nullptr; } + + /// If this is a CIE record, returns the Edge pointing at the personality + /// function, if any. + /// It is illegal to call this method on FDE records. + Edge *getPersonalityEdge() const { + assert(isCIE() && "CFI record is not a CIE"); + return PersonalityEdge; + } + + /// If this is an FDE record, returns the Edge pointing to the CIE. + /// If this is a CIE record, returns null. + /// + /// The result is not valid if any modification has been made to the block + /// after parsing. + Edge *getCIEEdge() const { return CIEEdge; } + + /// If this is an FDE record, returns the Edge pointing at the PC-begin + /// symbol. + /// If this a CIE record, returns null. + Edge *getPCBeginEdge() const { return PCBeginEdge; } + + /// If this is an FDE record, returns the Edge pointing at the LSDA, if any. + /// It is illegal to call this method on CIE records. + Edge *getLSDAEdge() const { + assert(isFDE() && "CFI record is not an FDE"); + return LSDAEdge; + } + +private: + EHFrameCFIBlockInspector(Edge *PersonalityEdge); + EHFrameCFIBlockInspector(Edge &CIEEdge, Edge &PCBeginEdge, Edge *LSDAEdge); + + Edge *CIEEdge = nullptr; + Edge *PCBeginEdge = nullptr; + union { + Edge *PersonalityEdge; + Edge *LSDAEdge; + }; +}; + /// Supports registration/deregistration of EH-frames in a target process. class EHFrameRegistrar { public: diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h b/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h index 6f2ff012697ddb..31721bf999ec18 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h @@ -38,6 +38,14 @@ createLinkGraphFromMachOObject_arm64(MemoryBufferRef ObjectBuffer); void link_MachO_arm64(std::unique_ptr G, std::unique_ptr Ctx); +/// Returns a pass suitable for splitting __eh_frame sections in MachO/x86-64 +/// objects. +LinkGraphPassFunction createEHFrameSplitterPass_MachO_arm64(); + +/// Returns a pass suitable for fixing missing edges in an __eh_frame section +/// in a MachO/x86-64 object. +LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_arm64(); + } // end namespace jitlink } // end namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h index f2c3fba7bcde9f..11fbbbd579d00c 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h @@ -244,8 +244,11 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { return Error::success(); } +/// aarch64 pointer size. +constexpr uint64_t PointerSize = 8; + /// AArch64 null pointer content. -extern const uint8_t NullGOTEntryContent[8]; +extern const uint8_t NullGOTEntryContent[PointerSize]; /// AArch64 PLT stub content. extern const uint8_t StubContent[8]; diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp index 389fd14c0f29a5..b0d79276367d7a 100644 --- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp @@ -85,6 +85,32 @@ Error EHFrameEdgeFixer::operator()(LinkGraph &G) { return Error::success(); } +static Expected readCFIRecordLength(const Block &B, + BinaryStreamReader &R) { + uint32_t Length; + if (auto Err = R.readInteger(Length)) + return Err; + + // If Length < 0xffffffff then use the regular length field, otherwise + // read the extended length field. + if (Length != 0xffffffff) + return Length; + + uint64_t ExtendedLength; + if (auto Err = R.readInteger(ExtendedLength)) + return Err; + + if (ExtendedLength > std::numeric_limits::max()) + return make_error( + "In CFI record at " + + formatv("{0:x}", B.getAddress() + R.getOffset() - 12) + + ", extended length of " + formatv("{0:x}", ExtendedLength) + + " exceeds address-range max (" + + formatv("{0:x}", std::numeric_limits::max())); + + return ExtendedLength; +} + Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) { LLVM_DEBUG(dbgs() << " Processing block at " << B.getAddress() << "\n"); @@ -125,24 +151,11 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) { }); // Get the record length. - size_t RecordRemaining; - { - uint32_t Length; - if (auto Err = BlockReader.readInteger(Length)) - return Err; - // If Length < 0xffffffff then use the regular length field, otherwise - // read the extended length field. - if (Length != 0xffffffff) - RecordRemaining = Length; - else { - uint64_t ExtendedLength; - if (auto Err = BlockReader.readInteger(ExtendedLength)) - return Err; - RecordRemaining = ExtendedLength; - } - } + Expected RecordRemaining = readCFIRecordLength(B, BlockReader); + if (!RecordRemaining) + return RecordRemaining.takeError(); - if (BlockReader.bytesRemaining() < RecordRemaining) + if (BlockReader.bytesRemaining() < *RecordRemaining) return make_error( "Incomplete CFI record at " + formatv("{0:x16}", B.getAddress() + RecordStartOffset)); @@ -155,19 +168,19 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) { if (CIEDelta == 0) { if (auto Err = processCIE(PC, B, RecordStartOffset, - CIEDeltaFieldOffset + RecordRemaining, + CIEDeltaFieldOffset + *RecordRemaining, CIEDeltaFieldOffset, BlockEdges)) return Err; } else { if (auto Err = processFDE(PC, B, RecordStartOffset, - CIEDeltaFieldOffset + RecordRemaining, + CIEDeltaFieldOffset + *RecordRemaining, CIEDeltaFieldOffset, CIEDelta, BlockEdges)) return Err; } // Move to the next record. BlockReader.setOffset(RecordStartOffset + CIEDeltaFieldOffset + - RecordRemaining); + *RecordRemaining); } return Error::success(); @@ -358,14 +371,25 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B, 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. - LLVM_DEBUG({ - dbgs() << " Adding keep-alive edge from target at " - << (*PCBegin)->getBlock().getAddress() << " to FDE at " - << RecordAddress << "\n"; - }); - (*PCBegin)->getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0); + if ((*PCBegin)->isDefined()) { + // 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 " + << (*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 " + << RecordAddress << ", which points to " + << ((*PCBegin)->isExternal() ? "external" : "absolute") + << " symbol \"" << (*PCBegin)->getName() + << "\" -- FDE must be kept alive manually or it will be " + << "dead stripped.\n"; + }); + } } else return PCBegin.takeError(); @@ -639,6 +663,31 @@ Error InProcessEHFrameRegistrar::deregisterEHFrames( EHFrameSection.size()); } +EHFrameCFIBlockInspector EHFrameCFIBlockInspector::FromEdgeScan(Block &B) { + if (B.edges_empty()) + return EHFrameCFIBlockInspector(nullptr); + if (B.edges_size() == 1) + return EHFrameCFIBlockInspector(&*B.edges().begin()); + SmallVector Es; + for (auto &E : B.edges()) + Es.push_back(&E); + assert(Es.size() >= 2 && Es.size() <= 3 && "Unexpected number of edges"); + llvm::sort(Es, [](const Edge *LHS, const Edge *RHS) { + return LHS->getOffset() < RHS->getOffset(); + }); + return EHFrameCFIBlockInspector(*Es[0], *Es[1], + Es.size() == 3 ? Es[2] : nullptr); + return EHFrameCFIBlockInspector(nullptr); +} + +EHFrameCFIBlockInspector::EHFrameCFIBlockInspector(Edge *PersonalityEdge) + : PersonalityEdge(PersonalityEdge) {} + +EHFrameCFIBlockInspector::EHFrameCFIBlockInspector(Edge &CIEEdge, + Edge &PCBeginEdge, + Edge *LSDAEdge) + : CIEEdge(&CIEEdge), PCBeginEdge(&PCBeginEdge), LSDAEdge(LSDAEdge) {} + LinkGraphPassFunction createEHFrameRecorderPass(const Triple &TT, StoreFrameRangeFunction StoreRangeAddress) { diff --git a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp index 04194318498fd9..d243a1ff01cd92 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp @@ -563,11 +563,8 @@ void link_MachO_arm64(std::unique_ptr G, // Add eh-frame passses. // FIXME: Prune eh-frames for which compact-unwind is available once // we support compact-unwind registration with libunwind. - Config.PrePrunePasses.push_back( - DWARFRecordSectionSplitter("__TEXT,__eh_frame")); - Config.PrePrunePasses.push_back(EHFrameEdgeFixer( - "__TEXT,__eh_frame", 8, aarch64::Pointer32, aarch64::Pointer64, - aarch64::Delta32, aarch64::Delta64, aarch64::NegDelta32)); + Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_arm64()); + Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_arm64()); // Add an in-place GOT/Stubs pass. Config.PostPrunePasses.push_back(buildTables_MachO_arm64); @@ -580,5 +577,16 @@ void link_MachO_arm64(std::unique_ptr G, MachOJITLinker_arm64::link(std::move(Ctx), std::move(G), std::move(Config)); } +LinkGraphPassFunction createEHFrameSplitterPass_MachO_arm64() { + return DWARFRecordSectionSplitter("__TEXT,__eh_frame"); +} + +LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_arm64() { + return EHFrameEdgeFixer("__TEXT,__eh_frame", aarch64::PointerSize, + aarch64::Pointer32, aarch64::Pointer64, + aarch64::Delta32, aarch64::Delta64, + aarch64::NegDelta32); +} + } // end namespace jitlink } // end namespace llvm diff --git a/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt index 3cbb3e774e9e64..8162cbf39ff34d 100644 --- a/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/JITLink/CMakeLists.txt @@ -9,6 +9,7 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_unittest(JITLinkTests + EHFrameSupportTests.cpp LinkGraphTests.cpp ) diff --git a/llvm/unittests/ExecutionEngine/JITLink/EHFrameSupportTests.cpp b/llvm/unittests/ExecutionEngine/JITLink/EHFrameSupportTests.cpp new file mode 100644 index 00000000000000..3be932aa77cd6c --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JITLink/EHFrameSupportTests.cpp @@ -0,0 +1,246 @@ +//===----- EHFrameSupportTests.cpp - Unit tests for eh-frame support ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/MachO_arm64.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::jitlink; + +// TestObjectBytes contains a MachO arm64 object file with three symbols: foo, +// bar, and main, with corresponding FDEs. It was generated with: +// +// % cat foo.cpp +// extern "C" void e(); +// extern "C" void a() { +// try { +// e(); +// } catch (int x) { +// } +// } +// extern "C" void b() noexcept {} +// extern "C" void c() noexcept {} +// +// % clang++ --target=arm64-apple-darwin -femit-dwarf-unwind=always -c -o foo.o +// \ +// foo.c +// % xxd -i foo.o + +static const uint8_t TestObjectBytes[] = { + 0xcf, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xb8, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67, 0x63, 0x63, 0x5f, 0x65, 0x78, + 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x5f, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xd8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, 0x75, 0x6e, + 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x5f, 0x4c, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb0, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x65, 0x68, + 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x40, 0x04, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x05, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x83, 0x00, 0xd1, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, + 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x14, 0x10, 0x00, 0x00, 0x14, + 0xe8, 0x03, 0x01, 0xaa, 0xe0, 0x07, 0x00, 0xf9, 0xe8, 0x07, 0x00, 0xb9, + 0x01, 0x00, 0x00, 0x14, 0xe8, 0x07, 0x40, 0xb9, 0x08, 0x05, 0x00, 0x71, + 0xe8, 0x07, 0x9f, 0x1a, 0x68, 0x01, 0x00, 0x37, 0x01, 0x00, 0x00, 0x14, + 0xe0, 0x07, 0x40, 0xf9, 0x00, 0x00, 0x00, 0x94, 0x08, 0x00, 0x40, 0xb9, + 0xe8, 0x03, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x14, + 0xfd, 0x7b, 0x41, 0xa9, 0xff, 0x83, 0x00, 0x91, 0xc0, 0x03, 0x5f, 0xd6, + 0xe0, 0x07, 0x40, 0xf9, 0x00, 0x00, 0x00, 0x94, 0xc0, 0x03, 0x5f, 0xd6, + 0xc0, 0x03, 0x5f, 0xd6, 0xff, 0x9b, 0x11, 0x01, 0x08, 0x0c, 0x04, 0x18, + 0x01, 0x10, 0x58, 0x00, 0x00, 0x01, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x7a, 0x52, 0x00, 0x01, 0x78, 0x1e, 0x01, 0x10, 0x0c, 0x1f, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xe4, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x50, 0x4c, + 0x52, 0x00, 0x01, 0x78, 0x1e, 0x07, 0x9b, 0x9d, 0xff, 0xff, 0xff, 0x10, + 0x10, 0x0c, 0x1f, 0x00, 0x38, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x44, 0x0e, 0x20, 0x48, 0x0c, 0x1d, 0x10, 0x9e, 0x01, 0x9d, 0x02, + 0x0a, 0x02, 0x48, 0x0c, 0x1f, 0x20, 0x48, 0x0e, 0x00, 0xde, 0xdd, 0x44, + 0x0b, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x2d, + 0x4c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x2d, 0x40, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x2d, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x2d, + 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x7d, 0x40, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x85, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x1e, 0x85, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0e, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1e, 0x74, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x0e, 0x63, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x7d, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1e, 0x38, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1e, + 0x1c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x00, 0x00, + 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x0e, 0x03, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x5a, 0x54, 0x49, 0x69, 0x00, + 0x5f, 0x5f, 0x5f, 0x63, 0x78, 0x61, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, + 0x5f, 0x63, 0x61, 0x74, 0x63, 0x68, 0x00, 0x5f, 0x5f, 0x5f, 0x63, 0x78, + 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x63, 0x68, 0x00, + 0x5f, 0x5f, 0x55, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x52, 0x65, 0x73, + 0x75, 0x6d, 0x65, 0x00, 0x5f, 0x65, 0x00, 0x5f, 0x63, 0x00, 0x5f, 0x62, + 0x00, 0x5f, 0x61, 0x00, 0x6c, 0x74, 0x6d, 0x70, 0x33, 0x00, 0x6c, 0x74, + 0x6d, 0x70, 0x32, 0x00, 0x6c, 0x74, 0x6d, 0x70, 0x31, 0x00, 0x5f, 0x5f, + 0x5f, 0x67, 0x78, 0x78, 0x5f, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x76, 0x30, 0x00, 0x6c, 0x74, 0x6d, 0x70, + 0x30, 0x00, 0x47, 0x43, 0x43, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, + 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x30, 0x00}; + +llvm::MemoryBufferRef + TestObject(StringRef(reinterpret_cast(TestObjectBytes), + sizeof(TestObjectBytes)), + "foo.o"); + +TEST(EHFrameCFIBlockInspector, BasicSuccessCase) { + // Create a LinkGraph from the test object above and verify that + // (1) There are two CIEs -- one with a personality function and one + // without. + // (2) There are three FDEs -- two attached to the CIE with no + // personality, one attached to the CIE with a personality. + // (3) Each FDE has an edge pointing to the CIE at the correct offset. + // (4) Each function has exactly one FDE pointing at it. + + auto G = cantFail(createLinkGraphFromMachOObject_arm64(TestObject)); + cantFail(createEHFrameSplitterPass_MachO_arm64()(*G)); + cantFail(createEHFrameEdgeFixerPass_MachO_arm64()(*G)); + + auto *EHFrame = G->findSectionByName("__TEXT,__eh_frame"); + assert(EHFrame && "eh-frame missing?"); + + SmallVector CIEs; + for (auto *B : EHFrame->blocks()) { + auto CFIBI = EHFrameCFIBlockInspector::FromEdgeScan(*B); + if (CFIBI.isCIE()) { + CIEs.push_back(B); + // If this CIE has an edge, check that getPersonalityEdge returns it. + if (B->edges_size() != 0) + EXPECT_TRUE(!!CFIBI.getPersonalityEdge()); + } + } + ASSERT_EQ(CIEs.size(), 2U); + + // Make sure that the CIE with no edges is CIEs[0]. + if (CIEs[1]->edges_empty()) + std::swap(CIEs[0], CIEs[1]); + + EXPECT_TRUE(CIEs[0]->edges_empty()); + EXPECT_EQ(CIEs[1]->edges_size(), 1U); + + std::set Targets; + for (auto *B : EHFrame->blocks()) { + auto CFIBI = EHFrameCFIBlockInspector::FromEdgeScan(*B); + if (CFIBI.isFDE()) { + ASSERT_TRUE(!!CFIBI.getCIEEdge()); + ASSERT_TRUE(CFIBI.getCIEEdge()->getTarget().isDefined()); + auto &CIE = CFIBI.getCIEEdge()->getTarget().getBlock(); + ASSERT_TRUE(&CIE == CIEs[0] || &CIE == CIEs[1]); + + ASSERT_TRUE(!!CFIBI.getPCBeginEdge()); + auto &PCBeginTarget = CFIBI.getPCBeginEdge()->getTarget(); + ASSERT_TRUE(PCBeginTarget.hasName()); + Targets.insert(PCBeginTarget.getName()); + + // If the FDE points at CIEs[0] (the CIE without a personality) then it + // should not have an LSDA. If it points to CIEs[1] then it should have + // an LSDA. + if (&CIE == CIEs[0]) + EXPECT_EQ(CFIBI.getLSDAEdge(), nullptr); + else + EXPECT_NE(CFIBI.getLSDAEdge(), nullptr); + } + } + + EXPECT_EQ(Targets.size(), 3U) << "Unexpected number of FDEs"; + EXPECT_EQ(Targets.count("_a"), 1U); + EXPECT_EQ(Targets.count("_b"), 1U); + EXPECT_EQ(Targets.count("_c"), 1U); +} + +TEST(EHFrameCFIBlockInspector, ExternalPCBegin) { + // Check that we don't crash if we transform the target of an FDE into an + // external symbol before running edge-fixing. + auto G = cantFail(createLinkGraphFromMachOObject_arm64(TestObject)); + + // Make '_a' external. + for (auto *Sym : G->defined_symbols()) + if (Sym->hasName() && Sym->getName() == "_a") { + G->makeExternal(*Sym); + break; + } + + // Run the splitter and edge-fixer passes. + cantFail(createEHFrameSplitterPass_MachO_arm64()(*G)); + cantFail(createEHFrameEdgeFixerPass_MachO_arm64()(*G)); +}