583 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			583 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -------------*- C++ -*-===//
 | |
| //
 | |
| //                             The LLVM Linker
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| ///
 | |
| /// \file A pass to convert MachO's __compact_unwind sections into the final
 | |
| /// __unwind_info format used during runtime. See
 | |
| /// mach-o/compact_unwind_encoding.h for more details on the formats involved.
 | |
| ///
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "ArchHandler.h"
 | |
| #include "File.h"
 | |
| #include "MachONormalizedFileBinaryUtils.h"
 | |
| #include "MachOPasses.h"
 | |
| #include "lld/Core/DefinedAtom.h"
 | |
| #include "lld/Core/File.h"
 | |
| #include "lld/Core/LLVM.h"
 | |
| #include "lld/Core/Reference.h"
 | |
| #include "lld/Core/Simple.h"
 | |
| #include "llvm/ADT/DenseMap.h"
 | |
| #include "llvm/Support/Debug.h"
 | |
| #include "llvm/Support/Format.h"
 | |
| 
 | |
| #define DEBUG_TYPE "macho-compact-unwind"
 | |
| 
 | |
| namespace lld {
 | |
| namespace mach_o {
 | |
| 
 | |
| namespace {
 | |
| 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 {
 | |
|   ArrayRef<CompactUnwindEntry> entries;
 | |
| };
 | |
| }
 | |
| 
 | |
| class UnwindInfoAtom : public SimpleDefinedAtom {
 | |
| public:
 | |
|   UnwindInfoAtom(ArchHandler &archHandler, const File &file, bool isBig,
 | |
|                  std::vector<const Atom *> &personalities,
 | |
|                  std::vector<uint32_t> &commonEncodings,
 | |
|                  std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs)
 | |
|       : SimpleDefinedAtom(file), _archHandler(archHandler),
 | |
|         _commonEncodingsOffset(7 * sizeof(uint32_t)),
 | |
|         _personalityArrayOffset(_commonEncodingsOffset +
 | |
|                                 commonEncodings.size() * sizeof(uint32_t)),
 | |
|         _topLevelIndexOffset(_personalityArrayOffset +
 | |
|                              personalities.size() * sizeof(uint32_t)),
 | |
|         _lsdaIndexOffset(_topLevelIndexOffset +
 | |
|                          3 * (pages.size() + 1) * sizeof(uint32_t)),
 | |
|         _firstPageOffset(_lsdaIndexOffset + 2 * numLSDAs * sizeof(uint32_t)),
 | |
|         _isBig(isBig) {
 | |
| 
 | |
|     addHeader(commonEncodings.size(), personalities.size(), pages.size());
 | |
|     addCommonEncodings(commonEncodings);
 | |
|     addPersonalityFunctions(personalities);
 | |
|     addTopLevelIndexes(pages);
 | |
|     addLSDAIndexes(pages, numLSDAs);
 | |
|     addSecondLevelPages(pages);
 | |
|   }
 | |
| 
 | |
|   ~UnwindInfoAtom() override = default;
 | |
| 
 | |
|   ContentType contentType() const override {
 | |
|     return DefinedAtom::typeProcessedUnwindInfo;
 | |
|   }
 | |
| 
 | |
|   Alignment alignment() const override { return 4; }
 | |
| 
 | |
|   uint64_t size() const override { return _contents.size(); }
 | |
| 
 | |
|   ContentPermissions permissions() const override {
 | |
|     return DefinedAtom::permR__;
 | |
|   }
 | |
| 
 | |
|   ArrayRef<uint8_t> rawContent() const override { return _contents; }
 | |
| 
 | |
|   void addHeader(uint32_t numCommon, uint32_t numPersonalities,
 | |
|                  uint32_t numPages) {
 | |
|     using normalized::write32;
 | |
| 
 | |
|     uint32_t headerSize = 7 * sizeof(uint32_t);
 | |
|     _contents.resize(headerSize);
 | |
| 
 | |
|     uint8_t *headerEntries = _contents.data();
 | |
|     // version
 | |
|     write32(headerEntries, 1, _isBig);
 | |
|     // commonEncodingsArraySectionOffset
 | |
|     write32(headerEntries + sizeof(uint32_t), _commonEncodingsOffset, _isBig);
 | |
|     // commonEncodingsArrayCount
 | |
|     write32(headerEntries + 2 * sizeof(uint32_t), numCommon, _isBig);
 | |
|     // personalityArraySectionOffset
 | |
|     write32(headerEntries + 3 * sizeof(uint32_t), _personalityArrayOffset,
 | |
|             _isBig);
 | |
|     // personalityArrayCount
 | |
|     write32(headerEntries + 4 * sizeof(uint32_t), numPersonalities, _isBig);
 | |
|     // indexSectionOffset
 | |
|     write32(headerEntries + 5 * sizeof(uint32_t), _topLevelIndexOffset, _isBig);
 | |
|     // indexCount
 | |
|     write32(headerEntries + 6 * sizeof(uint32_t), numPages + 1, _isBig);
 | |
|   }
 | |
| 
 | |
|   /// Add the list of common encodings to the section; this is simply an array
 | |
|   /// of uint32_t compact values. Size has already been specified in the header.
 | |
|   void addCommonEncodings(std::vector<uint32_t> &commonEncodings) {
 | |
|     using normalized::write32;
 | |
| 
 | |
|     _contents.resize(_commonEncodingsOffset +
 | |
|                      commonEncodings.size() * sizeof(uint32_t));
 | |
|     uint8_t *commonEncodingsArea =
 | |
|         reinterpret_cast<uint8_t *>(_contents.data() + _commonEncodingsOffset);
 | |
| 
 | |
|     for (uint32_t encoding : commonEncodings) {
 | |
|       write32(commonEncodingsArea, encoding, _isBig);
 | |
|       commonEncodingsArea += sizeof(uint32_t);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void addPersonalityFunctions(std::vector<const Atom *> personalities) {
 | |
|     _contents.resize(_personalityArrayOffset +
 | |
|                      personalities.size() * sizeof(uint32_t));
 | |
| 
 | |
|     for (unsigned i = 0; i < personalities.size(); ++i)
 | |
|       addImageReferenceIndirect(_personalityArrayOffset + i * sizeof(uint32_t),
 | |
|                                 personalities[i]);
 | |
|   }
 | |
| 
 | |
|   void addTopLevelIndexes(std::vector<UnwindInfoPage> &pages) {
 | |
|     using normalized::write32;
 | |
| 
 | |
|     uint32_t numIndexes = pages.size() + 1;
 | |
|     _contents.resize(_topLevelIndexOffset + numIndexes * 3 * sizeof(uint32_t));
 | |
| 
 | |
|     uint32_t pageLoc = _firstPageOffset;
 | |
| 
 | |
|     // The most difficult job here is calculating the LSDAs; everything else
 | |
|     // follows fairly naturally, but we can't state where the first
 | |
|     uint8_t *indexData = &_contents[_topLevelIndexOffset];
 | |
|     uint32_t numLSDAs = 0;
 | |
|     for (unsigned i = 0; i < pages.size(); ++i) {
 | |
|       // functionOffset
 | |
|       addImageReference(_topLevelIndexOffset + 3 * i * sizeof(uint32_t),
 | |
|                         pages[i].entries[0].rangeStart);
 | |
|       // secondLevelPagesSectionOffset
 | |
|       write32(indexData + (3 * i + 1) * sizeof(uint32_t), pageLoc, _isBig);
 | |
|       write32(indexData + (3 * i + 2) * sizeof(uint32_t),
 | |
|               _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
 | |
| 
 | |
|       for (auto &entry : pages[i].entries)
 | |
|         if (entry.lsdaLocation)
 | |
|           ++numLSDAs;
 | |
|     }
 | |
| 
 | |
|     // Finally, write out the final sentinel index
 | |
|     auto &finalEntry = pages[pages.size() - 1].entries.back();
 | |
|     addImageReference(_topLevelIndexOffset +
 | |
|                           3 * pages.size() * sizeof(uint32_t),
 | |
|                       finalEntry.rangeStart, finalEntry.rangeLength);
 | |
|     // secondLevelPagesSectionOffset => 0
 | |
|     write32(indexData + (3 * pages.size() + 2) * sizeof(uint32_t),
 | |
|             _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
 | |
|   }
 | |
| 
 | |
|   void addLSDAIndexes(std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs) {
 | |
|     _contents.resize(_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t));
 | |
| 
 | |
|     uint32_t curOffset = _lsdaIndexOffset;
 | |
|     for (auto &page : pages) {
 | |
|       for (auto &entry : page.entries) {
 | |
|         if (!entry.lsdaLocation)
 | |
|           continue;
 | |
| 
 | |
|         addImageReference(curOffset, entry.rangeStart);
 | |
|         addImageReference(curOffset + sizeof(uint32_t), entry.lsdaLocation);
 | |
|         curOffset += 2 * sizeof(uint32_t);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void addSecondLevelPages(std::vector<UnwindInfoPage> &pages) {
 | |
|     for (auto &page : pages) {
 | |
|       addRegularSecondLevelPage(page);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void addRegularSecondLevelPage(const UnwindInfoPage &page) {
 | |
|     uint32_t curPageOffset = _contents.size();
 | |
|     const int16_t headerSize = sizeof(uint32_t) + 2 * sizeof(uint16_t);
 | |
|     uint32_t curPageSize =
 | |
|         headerSize + 2 * page.entries.size() * sizeof(uint32_t);
 | |
|     _contents.resize(curPageOffset + curPageSize);
 | |
| 
 | |
|     using normalized::write32;
 | |
|     using normalized::write16;
 | |
|     // 2 => regular page
 | |
|     write32(&_contents[curPageOffset], 2, _isBig);
 | |
|     // offset of 1st entry
 | |
|     write16(&_contents[curPageOffset + 4], headerSize, _isBig);
 | |
|     write16(&_contents[curPageOffset + 6], page.entries.size(), _isBig);
 | |
| 
 | |
|     uint32_t pagePos = curPageOffset + headerSize;
 | |
|     for (auto &entry : page.entries) {
 | |
|       addImageReference(pagePos, entry.rangeStart);
 | |
| 
 | |
|       write32(_contents.data() + pagePos + sizeof(uint32_t), entry.encoding,
 | |
|               _isBig);
 | |
|       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(),
 | |
|                  _archHandler.imageOffsetKind(), offset, dest, addend);
 | |
|   }
 | |
| 
 | |
|   void addImageReferenceIndirect(uint32_t offset, const Atom *dest) {
 | |
|     addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
 | |
|                  _archHandler.imageOffsetKindIndirect(), offset, dest, 0);
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   mach_o::ArchHandler &_archHandler;
 | |
|   std::vector<uint8_t> _contents;
 | |
|   uint32_t _commonEncodingsOffset;
 | |
|   uint32_t _personalityArrayOffset;
 | |
|   uint32_t _topLevelIndexOffset;
 | |
|   uint32_t _lsdaIndexOffset;
 | |
|   uint32_t _firstPageOffset;
 | |
|   bool _isBig;
 | |
| };
 | |
| 
 | |
| /// Pass for instantiating and optimizing GOT slots.
 | |
| ///
 | |
| class CompactUnwindPass : public Pass {
 | |
| public:
 | |
|   CompactUnwindPass(const MachOLinkingContext &context)
 | |
|       : _ctx(context), _archHandler(_ctx.archHandler()),
 | |
|         _file(*_ctx.make_file<MachOFile>("<mach-o Compact Unwind Pass>")),
 | |
|         _isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) {
 | |
|     _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   llvm::Error perform(SimpleFile &mergedFile) override {
 | |
|     DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n");
 | |
| 
 | |
|     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);
 | |
| 
 | |
|     // Skip rest of pass if no unwind info.
 | |
|     if (unwindLocs.empty() && dwarfFrames.empty())
 | |
|       return llvm::Error::success();
 | |
| 
 | |
|     // 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.
 | |
|     assert(personalities.size() <= 4);
 | |
| 
 | |
|     // TODO: Find commmon encodings for use by compressed pages.
 | |
|     std::vector<uint32_t> commonEncodings;
 | |
| 
 | |
|     // 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, dwarfFrames);
 | |
| 
 | |
|     // Remove any unused eh-frame atoms.
 | |
|     pruneUnusedEHFrames(mergedFile, unwindInfos, unwindLocs, dwarfFrames);
 | |
| 
 | |
|     // Finally, we can start creating pages based on these entries.
 | |
| 
 | |
|     DEBUG(llvm::dbgs() << "  Splitting entries into pages\n");
 | |
|     // FIXME: we split the entries into pages naively: lots of 4k pages followed
 | |
|     // by a small one. ld64 tried to minimize space and align them to real 4k
 | |
|     // boundaries. That might be worth doing, or perhaps we could perform some
 | |
|     // minor balancing for expected number of lookups.
 | |
|     std::vector<UnwindInfoPage> pages;
 | |
|     auto remainingInfos = llvm::makeArrayRef(unwindInfos);
 | |
|     do {
 | |
|       pages.push_back(UnwindInfoPage());
 | |
| 
 | |
|       // FIXME: we only create regular pages at the moment. These can hold up to
 | |
|       // 1021 entries according to the documentation.
 | |
|       unsigned entriesInPage = std::min(1021U, (unsigned)remainingInfos.size());
 | |
| 
 | |
|       pages.back().entries = remainingInfos.slice(0, entriesInPage);
 | |
|       remainingInfos = remainingInfos.slice(entriesInPage);
 | |
| 
 | |
|       DEBUG(llvm::dbgs()
 | |
|             << "    Page from " << pages.back().entries[0].rangeStart->name()
 | |
|             << " to " << pages.back().entries.back().rangeStart->name() << " + "
 | |
|             << llvm::format("0x%x", pages.back().entries.back().rangeLength)
 | |
|             << " has " << entriesInPage << " entries\n");
 | |
|     } while (!remainingInfos.empty());
 | |
| 
 | |
|     auto *unwind = new (_file.allocator())
 | |
|         UnwindInfoAtom(_archHandler, _file, _isBig, personalities,
 | |
|                        commonEncodings, pages, numLSDAs);
 | |
|     mergedFile.addAtom(*unwind);
 | |
| 
 | |
|     // Finally, remove all __compact_unwind atoms now that we've processed them.
 | |
|     mergedFile.removeDefinedAtomsIf([](const DefinedAtom *atom) {
 | |
|       return atom->contentType() == DefinedAtom::typeCompactUnwindInfo;
 | |
|     });
 | |
| 
 | |
|     return llvm::Error::success();
 | |
|   }
 | |
| 
 | |
|   void collectCompactUnwindEntries(
 | |
|       const SimpleFile &mergedFile,
 | |
|       std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
 | |
|       std::vector<const Atom *> &personalities, uint32_t &numLSDAs) {
 | |
|     DEBUG(llvm::dbgs() << "  Collecting __compact_unwind entries\n");
 | |
| 
 | |
|     for (const DefinedAtom *atom : mergedFile.defined()) {
 | |
|       if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo)
 | |
|         continue;
 | |
| 
 | |
|       auto unwindEntry = extractCompactUnwindEntry(atom);
 | |
|       unwindLocs.insert(std::make_pair(unwindEntry.rangeStart, unwindEntry));
 | |
| 
 | |
|       DEBUG(llvm::dbgs() << "    Entry for " << unwindEntry.rangeStart->name()
 | |
|                          << ", encoding="
 | |
|                          << llvm::format("0x%08x", unwindEntry.encoding));
 | |
|       if (unwindEntry.personalityFunction)
 | |
|         DEBUG(llvm::dbgs() << ", personality="
 | |
|                            << unwindEntry.personalityFunction->name()
 | |
|                            << ", lsdaLoc=" << unwindEntry.lsdaLocation->name());
 | |
|       DEBUG(llvm::dbgs() << '\n');
 | |
| 
 | |
|       // Count number of LSDAs we see, since we need to know how big the index
 | |
|       // will be while laying out the section.
 | |
|       if (unwindEntry.lsdaLocation)
 | |
|         ++numLSDAs;
 | |
| 
 | |
|       // Gather the personality functions now, so that they're in deterministic
 | |
|       // order (derived from the DefinedAtom order).
 | |
|       if (unwindEntry.personalityFunction) {
 | |
|         auto pFunc = std::find(personalities.begin(), personalities.end(),
 | |
|                                unwindEntry.personalityFunction);
 | |
|         if (pFunc == personalities.end())
 | |
|           personalities.push_back(unwindEntry.personalityFunction);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   CompactUnwindEntry extractCompactUnwindEntry(const DefinedAtom *atom) {
 | |
|     CompactUnwindEntry entry;
 | |
| 
 | |
|     for (const Reference *ref : *atom) {
 | |
|       switch (ref->offsetInAtom()) {
 | |
|       case 0:
 | |
|         // FIXME: there could legitimately be functions with multiple encoding
 | |
|         // entries. However, nothing produces them at the moment.
 | |
|         assert(ref->addend() == 0 && "unexpected offset into function");
 | |
|         entry.rangeStart = ref->target();
 | |
|         break;
 | |
|       case 0x10:
 | |
|         assert(ref->addend() == 0 && "unexpected offset into personality fn");
 | |
|         entry.personalityFunction = ref->target();
 | |
|         break;
 | |
|       case 0x18:
 | |
|         assert(ref->addend() == 0 && "unexpected offset into LSDA atom");
 | |
|         entry.lsdaLocation = ref->target();
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (atom->rawContent().size() < 4 * sizeof(uint32_t))
 | |
|       return entry;
 | |
| 
 | |
|     using normalized::read32;
 | |
|     entry.rangeLength =
 | |
|         read32(atom->rawContent().data() + 2 * sizeof(uint32_t), _isBig);
 | |
|     entry.encoding =
 | |
|         read32(atom->rawContent().data() + 3 * sizeof(uint32_t), _isBig);
 | |
|     return entry;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   collectDwarfFrameEntries(const SimpleFile &mergedFile,
 | |
|                            std::map<const Atom *, const Atom *> &dwarfFrames) {
 | |
|     for (const DefinedAtom *ehFrameAtom : mergedFile.defined()) {
 | |
|       if (ehFrameAtom->contentType() != DefinedAtom::typeCFI)
 | |
|         continue;
 | |
|       if (ArchHandler::isDwarfCIE(_isBig, ehFrameAtom))
 | |
|         continue;
 | |
| 
 | |
|       if (const Atom *function = _archHandler.fdeTargetFunction(ehFrameAtom))
 | |
|         dwarfFrames[function] = 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
 | |
|   ///      personality function offset which is only known now).
 | |
|   ///   + A synthesised reference to __eh_frame if there's no __compact_unwind
 | |
|   ///     or too many personality functions to be accommodated.
 | |
|   std::vector<CompactUnwindEntry> createUnwindInfoEntries(
 | |
|       const SimpleFile &mergedFile,
 | |
|       const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
 | |
|       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");
 | |
|     // The final order in the __unwind_info section must be derived from the
 | |
|     // order of typeCode atoms, since that's how they'll be put into the object
 | |
|     // file eventually (yuck!).
 | |
|     for (const DefinedAtom *atom : mergedFile.defined()) {
 | |
|       if (atom->contentType() != DefinedAtom::typeCode)
 | |
|         continue;
 | |
| 
 | |
|       unwindInfos.push_back(finalizeUnwindInfoEntryForAtom(
 | |
|           atom, unwindLocs, personalities, dwarfFrames));
 | |
| 
 | |
|       DEBUG(llvm::dbgs() << "    Entry for " << atom->name()
 | |
|                          << ", final encoding="
 | |
|                          << llvm::format("0x%08x", unwindInfos.back().encoding)
 | |
|                          << '\n');
 | |
|     }
 | |
| 
 | |
|     return unwindInfos;
 | |
|   }
 | |
| 
 | |
|   /// Remove unused EH frames.
 | |
|   ///
 | |
|   /// An EH frame is considered unused if there is a corresponding compact
 | |
|   /// unwind atom that doesn't require the EH frame.
 | |
|   void pruneUnusedEHFrames(
 | |
|                    SimpleFile &mergedFile,
 | |
|                    const std::vector<CompactUnwindEntry> &unwindInfos,
 | |
|                    const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
 | |
|                    const std::map<const Atom *, const Atom *> &dwarfFrames) {
 | |
| 
 | |
|     // Worklist of all 'used' FDEs.
 | |
|     std::vector<const DefinedAtom *> usedDwarfWorklist;
 | |
| 
 | |
|     // We have to check two conditions when building the worklist:
 | |
|     // (1) EH frames used by compact unwind entries.
 | |
|     for (auto &entry : unwindInfos)
 | |
|       if (entry.ehFrame)
 | |
|         usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.ehFrame));
 | |
| 
 | |
|     // (2) EH frames that reference functions with no corresponding compact
 | |
|     //     unwind info.
 | |
|     for (auto &entry : dwarfFrames)
 | |
|       if (!unwindLocs.count(entry.first))
 | |
|         usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.second));
 | |
| 
 | |
|     // Add all transitively referenced CFI atoms by processing the worklist.
 | |
|     std::set<const Atom *> usedDwarfFrames;
 | |
|     while (!usedDwarfWorklist.empty()) {
 | |
|       const DefinedAtom *cfiAtom = usedDwarfWorklist.back();
 | |
|       usedDwarfWorklist.pop_back();
 | |
|       usedDwarfFrames.insert(cfiAtom);
 | |
|       for (const auto *ref : *cfiAtom) {
 | |
|         const DefinedAtom *cfiTarget = dyn_cast<DefinedAtom>(ref->target());
 | |
|         if (cfiTarget->contentType() == DefinedAtom::typeCFI)
 | |
|           usedDwarfWorklist.push_back(cfiTarget);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Finally, delete all unreferenced CFI atoms.
 | |
|     mergedFile.removeDefinedAtomsIf([&](const DefinedAtom *atom) {
 | |
|       if ((atom->contentType() == DefinedAtom::typeCFI) &&
 | |
|           !usedDwarfFrames.count(atom))
 | |
|         return true;
 | |
|       return false;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   CompactUnwindEntry finalizeUnwindInfoEntryForAtom(
 | |
|       const DefinedAtom *function,
 | |
|       const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
 | |
|       const std::vector<const Atom *> &personalities,
 | |
|       const std::map<const Atom *, const Atom *> &dwarfFrames) {
 | |
|     auto unwindLoc = unwindLocs.find(function);
 | |
| 
 | |
|     CompactUnwindEntry entry;
 | |
|     if (unwindLoc == unwindLocs.end()) {
 | |
|       // Default entry has correct encoding (0 => no unwind), but we need to
 | |
|       // synthesise the function.
 | |
|       entry.rangeStart = function;
 | |
|       entry.rangeLength = function->size();
 | |
|     } 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;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     auto personality = std::find(personalities.begin(), personalities.end(),
 | |
|                                  entry.personalityFunction);
 | |
|     uint32_t personalityIdx = personality == personalities.end()
 | |
|                                   ? 0
 | |
|                                   : personality - personalities.begin() + 1;
 | |
| 
 | |
|     // FIXME: We should also use DWARF when there isn't enough room for the
 | |
|     // personality function in the compact encoding.
 | |
|     assert(personalityIdx < 4 && "too many personality functions");
 | |
| 
 | |
|     entry.encoding |= personalityIdx << 28;
 | |
| 
 | |
|     if (entry.lsdaLocation)
 | |
|       entry.encoding |= 1U << 30;
 | |
| 
 | |
|     return entry;
 | |
|   }
 | |
| 
 | |
|   const MachOLinkingContext &_ctx;
 | |
|   mach_o::ArchHandler &_archHandler;
 | |
|   MachOFile &_file;
 | |
|   bool _isBig;
 | |
| };
 | |
| 
 | |
| void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
 | |
|   assert(ctx.needsCompactUnwindPass());
 | |
|   pm.add(llvm::make_unique<CompactUnwindPass>(ctx));
 | |
| }
 | |
| 
 | |
| } // end namesapce mach_o
 | |
| } // end namesapce lld
 |