[mach-o] make __unwind_info defer to __eh_frame when necessary.

Not all situations are representable in the compressed __unwind_info format,
and when this happens the entry needs to point to the more general __eh_frame
description.

Just x86_64 implementation for now.

rdar://problem/18208653

llvm-svn: 219836
This commit is contained in:
Tim Northover 2014-10-15 19:32:21 +00:00
parent 78206c3576
commit 1cc4fb76da
11 changed files with 237 additions and 53 deletions

View File

@ -168,6 +168,7 @@ public:
void addReference(Reference::KindNamespace ns, Reference::KindArch arch,
Reference::KindValue kindValue, uint64_t off,
const Atom *target, Reference::Addend a) {
assert(target && "trying to create reference to nothing");
_references.push_back(SimpleReference(ns, arch, kindValue, off, target, a));
}

View File

@ -142,6 +142,17 @@ int64_t ArchHandler::readS64(bool swap, const uint8_t *addr) {
return read64(swap, *reinterpret_cast<const uint64_t*>(addr));
}
bool ArchHandler::isDwarfCIE(bool swap, const DefinedAtom *atom) {
assert(atom->contentType() == DefinedAtom::typeCFI);
uint32_t size = read32(swap, *(uint32_t *)atom->rawContent().data());
uint32_t idOffset = sizeof(uint32_t);
if (size == 0xffffffffU)
idOffset += sizeof(uint64_t);
return read32(swap, *(uint32_t *)(atom->rawContent().data() + idOffset)) == 0;
}
} // namespace mach_o
} // namespace lld

View File

@ -70,11 +70,21 @@ public:
/// section.
virtual Reference::KindValue imageOffsetKindIndirect() = 0;
/// Architecture specific compact unwind type that signals __eh_frame should
/// actually be used.
virtual uint32_t dwarfCompactUnwindType() = 0;
/// Reference from an __eh_frame FDE atom to the function it's
/// describing. Usually pointer-sized and PC-relative, but differs in whether
/// it needs to be in relocatable objects.
virtual Reference::KindValue unwindRefToFunctionKind() = 0;
/// Reference from an __unwind_info entry of dwarfCompactUnwindType to the
/// required __eh_frame entry. On current architectures, the low 24 bits
/// represent the offset of the function's FDE entry from the start of
/// __eh_frame.
virtual Reference::KindValue unwindRefToEhFrameKind() = 0;
/// Used by normalizedFromAtoms() to know where to generated rebasing and
/// binding info in final executables.
virtual bool isPointer(const Reference &) = 0;
@ -148,6 +158,7 @@ public:
/// Copy raw content then apply all fixup References on an Atom.
virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) = 0;
@ -196,6 +207,9 @@ public:
llvm_unreachable("shims only support on arm");
}
/// Does a given unwind-cfi atom represent a CIE (as opposed to an FDE).
static bool isDwarfCIE(bool swap, const DefinedAtom *atom);
struct ReferenceInfo {
Reference::KindArch arch;
uint16_t kind;

View File

@ -52,6 +52,15 @@ public:
return invalid;
}
Reference::KindValue unwindRefToEhFrameKind() override {
return invalid;
}
uint32_t dwarfCompactUnwindType() override {
// FIXME
return -1;
}
std::error_code getReferenceInfo(const normalized::Relocation &reloc,
const DefinedAtom *inAtom,
uint32_t offsetInAtom,
@ -75,6 +84,7 @@ public:
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) override;
@ -994,6 +1004,7 @@ void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *location,
void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom,
bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) {
// Copy raw bytes.

View File

@ -95,6 +95,15 @@ public:
return invalid;
}
Reference::KindValue unwindRefToEhFrameKind() override {
return invalid;
}
uint32_t dwarfCompactUnwindType() override {
// FIXME
return -1;
}
std::error_code getReferenceInfo(const normalized::Relocation &reloc,
const DefinedAtom *inAtom,
uint32_t offsetInAtom,
@ -122,6 +131,7 @@ public:
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) override;
@ -457,11 +467,10 @@ std::error_code ArchHandler_arm64::getPairReferenceInfo(
}
}
void ArchHandler_arm64::generateAtomContent(const DefinedAtom &atom,
bool relocatable,
FindAddressForAtom findAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) {
void ArchHandler_arm64::generateAtomContent(
const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) {
// Copy raw bytes.
memcpy(atomContentBuffer, atom.rawContent().data(), atom.size());
// Apply fix-ups.

View File

@ -55,6 +55,15 @@ public:
return delta32;
}
Reference::KindValue unwindRefToEhFrameKind() override {
return invalid;
}
uint32_t dwarfCompactUnwindType() override {
return 0x04000000U;
}
std::error_code getReferenceInfo(const normalized::Relocation &reloc,
const DefinedAtom *inAtom,
uint32_t offsetInAtom,
@ -78,6 +87,7 @@ public:
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) override;
@ -386,6 +396,7 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom,
bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) {
// Copy raw bytes.

View File

@ -88,6 +88,13 @@ public:
return unwindFDEToFunction;
}
Reference::KindValue unwindRefToEhFrameKind() override {
return unwindInfoToEhFrame;
}
uint32_t dwarfCompactUnwindType() override {
return 0x04000000U;
}
const StubInfo &stubInfo() override { return _sStubInfo; }
@ -126,6 +133,7 @@ public:
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBase,
uint8_t *atomContentBuffer) override;
@ -172,7 +180,8 @@ private:
/// final image (typically personality function).
unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
/// relocatable object (yay for implicit contracts!).
unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
/// refer to __eh_frame entry.
};
Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
@ -181,7 +190,8 @@ private:
void applyFixupFinal(const Reference &ref, uint8_t *location,
uint64_t fixupAddress, uint64_t targetAddress,
uint64_t inAtomAddress, uint64_t imageBaseAddress);
uint64_t inAtomAddress, uint64_t imageBaseAddress,
FindAddressForAtom findSectionAddress);
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
uint64_t fixupAddress,
@ -210,6 +220,7 @@ const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon),
LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot),
LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
LLD_KIND_STRING_END
};
@ -424,11 +435,10 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
}
}
void ArchHandler_x86_64::generateAtomContent(const DefinedAtom &atom,
bool relocatable,
FindAddressForAtom findAddress,
uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) {
void ArchHandler_x86_64::generateAtomContent(
const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
uint8_t *atomContentBuffer) {
// Copy raw bytes.
memcpy(atomContentBuffer, atom.rawContent().data(), atom.size());
// Apply fix-ups.
@ -447,17 +457,15 @@ void ArchHandler_x86_64::generateAtomContent(const DefinedAtom &atom,
} else {
applyFixupFinal(*ref, &atomContentBuffer[offset],
fixupAddress, targetAddress,
atomAddress, imageBaseAddress);
atomAddress, imageBaseAddress, findSectionAddress);
}
}
}
void ArchHandler_x86_64::applyFixupFinal(const Reference &ref,
uint8_t *location,
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress,
uint64_t imageBaseAddress) {
void ArchHandler_x86_64::applyFixupFinal(
const Reference &ref, uint8_t *location, uint64_t fixupAddress,
uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress,
FindAddressForAtom findSectionAddress) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::x86_64);
@ -507,6 +515,13 @@ void ArchHandler_x86_64::applyFixupFinal(const Reference &ref,
case imageOffsetGot:
write32(*loc32, _swap, (targetAddress - imageBaseAddress) + ref.addend());
return;
case unwindInfoToEhFrame: {
uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
assert(val < 0xffffffU && "offset in __eh_frame too large");
uint32_t encoding = read32(_swap, *loc32) & 0xff000000U;
write32(*loc32, _swap, encoding | val);
return;
}
case invalid:
// Fall into llvm_unreachable().
break;
@ -568,7 +583,8 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
return;
case imageOffset:
case imageOffsetGot:
llvm_unreachable("image offset implies __unwind_info");
case unwindInfoToEhFrame:
llvm_unreachable("fixup implies __unwind_info");
return;
case unwindFDEToFunction:
// Do nothing for now
@ -658,6 +674,7 @@ void ArchHandler_x86_64::appendSectionRelocations(
X86_64_RELOC_UNSIGNED | rLength8 );
return;
case unwindFDEToFunction:
case unwindInfoToEhFrame:
return;
case ripRel32GotLoadNowLea:
llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");

View File

@ -39,9 +39,26 @@ struct CompactUnwindEntry {
const Atom *rangeStart;
const Atom *personalityFunction;
const Atom *lsdaLocation;
const Atom *ehFrame;
uint32_t rangeLength;
// There are 3 types of compact unwind entry, distinguished by the encoding
// value: 0 indicates a function with no unwind info;
// _archHandler.dwarfCompactUnwindType() indicates that the entry defers to
// __eh_frame, and that the ehFrame entry will be valid; any other value is a
// real compact unwind entry -- personalityFunction will be set and
// lsdaLocation may be.
uint32_t encoding;
CompactUnwindEntry(const DefinedAtom *function)
: rangeStart(function), personalityFunction(nullptr),
lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(function->size()),
encoding(0) {}
CompactUnwindEntry()
: rangeStart(nullptr), personalityFunction(nullptr),
lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(0), encoding(0) {}
};
struct UnwindInfoPage {
@ -212,12 +229,23 @@ public:
uint32_t pagePos = curPageOffset + headerSize;
for (auto &entry : page.entries) {
addImageReference(pagePos, entry.rangeStart);
write32(reinterpret_cast<int32_t *>(_contents.data() + pagePos)[1], _swap,
entry.encoding);
if ((entry.encoding & 0x0f000000U) ==
_archHandler.dwarfCompactUnwindType())
addEhFrameReference(pagePos + sizeof(uint32_t), entry.ehFrame);
pagePos += 2 * sizeof(uint32_t);
}
}
void addEhFrameReference(uint32_t offset, const Atom *dest,
Reference::Addend addend = 0) {
addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
_archHandler.unwindRefToEhFrameKind(), offset, dest, addend);
}
void addImageReference(uint32_t offset, const Atom *dest,
Reference::Addend addend = 0) {
addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
@ -253,15 +281,18 @@ private:
void perform(std::unique_ptr<MutableFile> &mergedFile) override {
DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n");
// First collect all __compact_unwind entries, addressable by the function
// it's referring to.
std::map<const Atom *, CompactUnwindEntry> unwindLocs;
std::map<const Atom *, const Atom *> dwarfFrames;
std::vector<const Atom *> personalities;
uint32_t numLSDAs = 0;
// First collect all __compact_unwind and __eh_frame entries, addressable by
// the function referred to.
collectCompactUnwindEntries(mergedFile, unwindLocs, personalities,
numLSDAs);
collectDwarfFrameEntries(mergedFile, dwarfFrames);
// FIXME: if there are more than 4 personality functions then we need to
// defer to DWARF info for the ones we don't put in the list. They should
// also probably be sorted by frequency.
@ -270,8 +301,8 @@ private:
// Now sort the entries by final address and fixup the compact encoding to
// its final form (i.e. set personality function bits & create DWARF
// references where needed).
std::vector<CompactUnwindEntry> unwindInfos =
createUnwindInfoEntries(mergedFile, unwindLocs, personalities);
std::vector<CompactUnwindEntry> unwindInfos = createUnwindInfoEntries(
mergedFile, unwindLocs, personalities, dwarfFrames);
// Finally, we can start creating pages based on these entries.
@ -348,7 +379,7 @@ private:
}
CompactUnwindEntry extractCompactUnwindEntry(const DefinedAtom *atom) {
CompactUnwindEntry entry = {nullptr, nullptr, nullptr, 0, 0};
CompactUnwindEntry entry;
for (const Reference *ref : *atom) {
switch (ref->offsetInAtom()) {
@ -376,6 +407,28 @@ private:
return entry;
}
void
collectDwarfFrameEntries(std::unique_ptr<MutableFile> &mergedFile,
std::map<const Atom *, const Atom *> &dwarfFrames) {
for (const DefinedAtom *ehFrameAtom : mergedFile->defined()) {
if (ehFrameAtom->contentType() != DefinedAtom::typeCFI ||
ArchHandler::isDwarfCIE(_swap, ehFrameAtom))
continue;
auto functionRef = std::find_if(ehFrameAtom->begin(), ehFrameAtom->end(),
[&](const Reference *ref) {
return ref->kindNamespace() == Reference::KindNamespace::mach_o &&
ref->kindArch() == _archHandler.kindArch() &&
ref->kindValue() == _archHandler.unwindRefToFunctionKind();
});
if (functionRef != ehFrameAtom->end()) {
const Atom *functionAtom = functionRef->target();
dwarfFrames.insert(std::make_pair(functionAtom, ehFrameAtom));
}
}
}
/// Every atom defined in __TEXT,__text needs an entry in the final
/// __unwind_info section (in order). These comes from two sources:
/// + Input __compact_unwind sections where possible (after adding the
@ -385,7 +438,8 @@ private:
std::vector<CompactUnwindEntry> createUnwindInfoEntries(
const std::unique_ptr<MutableFile> &mergedFile,
const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
const std::vector<const Atom *> &personalities) {
const std::vector<const Atom *> &personalities,
const std::map<const Atom *, const Atom *> &dwarfFrames) {
std::vector<CompactUnwindEntry> unwindInfos;
DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n");
@ -396,8 +450,8 @@ private:
if (atom->contentType() != DefinedAtom::typeCode)
continue;
unwindInfos.push_back(
finalizeUnwindInfoEntryForAtom(atom, unwindLocs, personalities));
unwindInfos.push_back(finalizeUnwindInfoEntryForAtom(
atom, unwindLocs, personalities, dwarfFrames));
DEBUG(llvm::dbgs() << " Entry for " << atom->name()
<< ", final encoding="
@ -411,20 +465,32 @@ private:
CompactUnwindEntry finalizeUnwindInfoEntryForAtom(
const DefinedAtom *function,
const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
const std::vector<const Atom *> &personalities) {
const std::vector<const Atom *> &personalities,
const std::map<const Atom *, const Atom *> &dwarfFrames) {
auto unwindLoc = unwindLocs.find(function);
// FIXME: we should synthesize a DWARF compact unwind entry before claiming
// there's no unwind if a __compact_unwind atom doesn't exist.
CompactUnwindEntry entry;
if (unwindLoc == unwindLocs.end()) {
CompactUnwindEntry entry;
memset(&entry, 0, sizeof(CompactUnwindEntry));
// Default entry has correct encoding (0 => no unwind), but we need to
// synthesise the function.
entry.rangeStart = function;
entry.rangeLength = function->size();
return entry;
} else
entry = unwindLoc->second;
// If there's no __compact_unwind entry, or it explicitly says to use
// __eh_frame, we need to try and fill in the correct DWARF atom.
if (entry.encoding == _archHandler.dwarfCompactUnwindType() ||
entry.encoding == 0) {
auto dwarfFrame = dwarfFrames.find(function);
if (dwarfFrame != dwarfFrames.end()) {
entry.encoding = _archHandler.dwarfCompactUnwindType();
entry.ehFrame = dwarfFrame->second;
}
}
CompactUnwindEntry entry = unwindLoc->second;
auto personality = std::find(personalities.begin(), personalities.end(),
entry.personalityFunction);
uint32_t personalityIdx = personality == personalities.end()

View File

@ -556,6 +556,14 @@ void Util::copySectionContent(NormalizedFile &file) {
return pos->second;
};
auto sectionAddrForAtom = [&] (const Atom &atom) -> uint64_t {
for (const SectionInfo *sectInfo : _sectionInfos)
for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets)
if (atomInfo.atom == &atom)
return sectInfo->address;
llvm_unreachable("atom not assigned to section");
};
for (SectionInfo *si : _sectionInfos) {
if (si->type == llvm::MachO::S_ZEROFILL)
continue;
@ -567,6 +575,7 @@ void Util::copySectionContent(NormalizedFile &file) {
uint8_t *atomContent = reinterpret_cast<uint8_t*>
(&sectionContent[ai.offsetInSection]);
_archHandler.generateAtomContent(*ai.atom, r, addrForAtom,
sectionAddrForAtom,
_context.baseAddress(), atomContent);
}
}

View File

@ -621,17 +621,6 @@ bool isDebugInfoSection(const Section &section) {
return section.segmentName.equals("__DWARF");
}
static bool isCIE(bool swap, const DefinedAtom *atom) {
assert(atom->contentType() == DefinedAtom::typeCFI);
uint32_t size = read32(swap, *(uint32_t *)atom->rawContent().data());
uint32_t idOffset = sizeof(uint32_t);
if (size == 0xffffffffU)
idOffset += sizeof(uint64_t);
return read32(swap, *(uint32_t *)(atom->rawContent().data() + idOffset)) == 0;
}
static int64_t readSPtr(bool is64, bool swap, const uint8_t *addr) {
if (is64)
return read64(swap, *reinterpret_cast<const uint64_t *>(addr));
@ -662,7 +651,7 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile,
[&](MachODefinedAtom *atom, uint64_t offset) -> void {
assert(atom->contentType() == DefinedAtom::typeCFI);
if (isCIE(swap, atom))
if (ArchHandler::isDwarfCIE(swap, atom))
return;
// Compiler wasn't lazy and actually told us what it meant.

View File

@ -13,14 +13,17 @@
# CHECK: Personality functions: (count = 1)
# CHECK: personality[1]: 0x00001000
# CHECK: Top level indices: (count = 2)
# CHECK: [0]: function offset=0x00000f7e, 2nd level page offset=0x00000040, LSDA offset=0x00000038
# CHECK: [1]: function offset=0x00000f80, 2nd level page offset=0x00000000, LSDA offset=0x00000040
# CHECK: [0]: function offset=0x00000efb, 2nd level page offset=0x00000040, LSDA offset=0x00000038
# CHECK: [1]: function offset=0x00000f00, 2nd level page offset=0x00000000, LSDA offset=0x00000040
# CHECK: LSDA descriptors:
# CHECK: [0]: function offset=0x00000f7e, LSDA offset=0x00000f80
# CHECK: [0]: function offset=0x00000efb, LSDA offset=0x00000f00
# CHECK: Second level indices:
# CHECK: Second level index[0]: offset in section=0x00000040, base function offset=0x00000f7e
# CHECK: [0]: function offset=0x00000f7e, encoding=0x51000000
# CHECK: [1]: function offset=0x00000f7f, encoding=0x01000000
# CHECK: Second level index[0]: offset in section=0x00000040, base function offset=0x00000efb
# CHECK: [0]: function offset=0x00000efb, encoding=0x51000000
# CHECK: [1]: function offset=0x00000efc, encoding=0x01000000
# CHECK: [2]: function offset=0x00000efd, encoding=0x04000018
# CHECK: [3]: function offset=0x00000efe, encoding=0x04000040
# CHECK: [4]: function offset=0x00000eff, encoding=0x00000000
--- !native
path: '<linker-internal>'
@ -53,6 +56,40 @@ defined-atoms:
- kind: pointer64Anon
offset: 0
target: _main
- type: compact-unwind
content: [ C1, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00,
00, 00, 00, 04, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00 ]
references:
- kind: pointer64Anon
offset: 0
target: _needsDwarfButNoCompactUnwind
# Generic x86_64 CIE:
- type: unwind-cfi
content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00,
01, 78, 10, 01, 10, 0C, 07, 08, 90, 01, 00, 00 ]
- type: unwind-cfi
content: [ 24, 00, 00, 00, 1C, 00, 00, 00, C8, FE, FF, FF,
FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00,
00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
00, 00, 00, 00 ]
references:
- kind: unwindFDEToFunction
offset: 8
target: _needsDwarfButNoCompactUnwind
- type: unwind-cfi
content: [ 24, 00, 00, 00, 44, 00, 00, 00, C8, FE, FF, FF,
FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00,
00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
00, 00, 00, 00 ]
references:
- kind: unwindFDEToFunction
offset: 8
target: _needsDwarfSaysCompactUnwind
- name: __Z3barv
scope: global
@ -64,6 +101,15 @@ defined-atoms:
- kind: branch32
offset: 9
target: __Z3barv
- name: _needsDwarfButNoCompactUnwind
scope: global
content: [ C3 ]
- name: _needsDwarfSaysCompactUnwind
scope: global
content: [ C3 ]
- name: _noUnwindData
scope: global
content: [ C3 ]
shared-library-atoms:
- name: ___gxx_personality_v0