Index: include/lld/Core/DefinedAtom.h =================================================================== --- include/lld/Core/DefinedAtom.h +++ include/lld/Core/DefinedAtom.h @@ -137,6 +137,7 @@ typeDTraceDOF, // runtime data for Dtrace [Darwin] typeTempLTO, // temporary atom for bitcode reader typeCompactUnwindInfo, // runtime data for unwinder [Darwin] + typeProcessedUnwindInfo,// compressed compact unwind info [Darwin] typeThunkTLV, // thunk used to access a TLV [Darwin] typeTLVInitialData, // initial data for a TLV [Darwin] typeTLVInitialZeroFill, // TLV initial zero fill data [Darwin] Index: include/lld/ReaderWriter/MachOLinkingContext.h =================================================================== --- include/lld/ReaderWriter/MachOLinkingContext.h +++ include/lld/ReaderWriter/MachOLinkingContext.h @@ -210,6 +210,9 @@ // GOT creation Pass should be run. bool needsGOTPass() const; + /// Pass to transform __compact_unwind into __unwind_info should be run. + bool needsCompactUnwindPass() const; + /// Magic symbol name stubs will need to help lazy bind. StringRef binderSymbolName() const; Index: lib/Core/DefinedAtom.cpp =================================================================== --- lib/Core/DefinedAtom.cpp +++ lib/Core/DefinedAtom.cpp @@ -41,6 +41,7 @@ case typeLiteral16: case typeDTraceDOF: case typeCompactUnwindInfo: + case typeProcessedUnwindInfo: case typeRONote: case typeNoAlloc: return permR__; Index: lib/ReaderWriter/MachO/ArchHandler.h =================================================================== --- lib/ReaderWriter/MachO/ArchHandler.h +++ lib/ReaderWriter/MachO/ArchHandler.h @@ -53,6 +53,20 @@ /// Used by GOTPass to update GOT References virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {} + /// Does this architecture make use of __unwind_info sections for exception + /// handling? If so, it will need a separate pass to create them. + virtual bool needsCompactUnwind() = 0; + + /// Returns the kind of reference to use to synthesize a 32-bit image-offset + /// value, used in the __unwind_info section. + virtual Reference::KindValue imageOffsetKind() = 0; + + /// Returns the kind of reference to use to synthesize a 32-bit image-offset + /// indirect value. Used for personality functions in the __unwind_info + /// section. + virtual Reference::KindValue imageOffsetKindIndirect() = 0; + + /// Used by normalizedFromAtoms() to know where to generated rebasing and /// binding info in final executables. virtual bool isPointer(const Reference &) = 0; @@ -126,6 +140,7 @@ /// Copy raw content then apply all fixup References on an Atom. virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + uint64_t imageBaseAddress, uint8_t *atomContentBuffer) = 0; /// Used in -r mode to convert a Reference to a mach-o relocation. Index: lib/ReaderWriter/MachO/ArchHandler_arm.cpp =================================================================== --- lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -36,6 +36,17 @@ bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; + + bool needsCompactUnwind() override { + return false; + } + Reference::KindValue imageOffsetKind() override { + return invalid; + } + Reference::KindValue imageOffsetKindIndirect() override { + return invalid; + } + std::error_code getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, @@ -59,6 +70,7 @@ void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + uint64_t imageBaseAddress, uint8_t *atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, @@ -901,9 +913,10 @@ } void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, - bool relocatable, - FindAddressForAtom findAddress, - uint8_t *atomContentBuffer) { + bool relocatable, + FindAddressForAtom findAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) { // Copy raw bytes. memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); // Apply fix-ups. Index: lib/ReaderWriter/MachO/ArchHandler_x86.cpp =================================================================== --- lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -36,6 +36,17 @@ bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; + + bool needsCompactUnwind() override { + return false; + } + Reference::KindValue imageOffsetKind() override { + return invalid; + } + Reference::KindValue imageOffsetKindIndirect() override { + return invalid; + } + std::error_code getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, @@ -59,6 +70,7 @@ void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + uint64_t imageBaseAddress, uint8_t *atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, @@ -361,9 +373,10 @@ } void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom, - bool relocatable, - FindAddressForAtom findAddress, - uint8_t *atomContentBuffer) { + bool relocatable, + FindAddressForAtom findAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) { // Copy raw bytes. memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); // Apply fix-ups. Index: lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp =================================================================== --- lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -46,6 +46,9 @@ case ripRel32Got: canBypassGOT = false; return true; + case imageOffsetGot: + canBypassGOT = false; + return true; default: return false; } @@ -55,8 +58,30 @@ void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::x86_64); - const_cast(ref) + + switch (ref->kindValue()) { + case ripRel32Got: + assert(targetNowGOT && "target must be GOT"); + case ripRel32GotLoad: + const_cast(ref) ->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea); + break; + case imageOffsetGot: + const_cast(ref)->setKindValue(imageOffset); + break; + default: + llvm_unreachable("unknown GOT reference kind"); + } + } + + bool needsCompactUnwind() override { + return true; + } + Reference::KindValue imageOffsetKind() override { + return imageOffset; + } + Reference::KindValue imageOffsetKindIndirect() override { + return imageOffsetGot; } const StubInfo &stubInfo() override { return _sStubInfo; } @@ -64,6 +89,7 @@ bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; + std::error_code getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, @@ -91,6 +117,7 @@ void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + uint64_t imageBase, uint8_t *atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, @@ -130,6 +157,11 @@ /// to "leaq _foo(%rip), %rax lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. + + imageOffset, /// Location contains offset of atom in final image + imageOffsetGot, /// Location contains offset of GOT entry for atom in + /// final image (typically personality function). + }; Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); @@ -138,7 +170,7 @@ void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, - uint64_t inAtomAddress); + uint64_t inAtomAddress, uint64_t imageBaseAddress); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, @@ -165,6 +197,7 @@ LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon), + LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_END }; @@ -379,6 +412,7 @@ void ArchHandler_x86_64::generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + uint64_t imageBaseAddress, uint8_t *atomContentBuffer) { // Copy raw bytes. memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); @@ -397,8 +431,8 @@ atomAddress); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], - fixupAddress, targetAddress, - atomAddress); + fixupAddress, targetAddress, + atomAddress, imageBaseAddress); } } } @@ -407,7 +441,8 @@ uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, - uint64_t inAtomAddress) { + uint64_t inAtomAddress, + uint64_t imageBaseAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86_64); @@ -452,6 +487,10 @@ case lazyImmediateLocation: // do nothing return; + case imageOffset: + case imageOffsetGot: + write32(*loc32, _swap, (targetAddress - imageBaseAddress) + ref.addend()); + return; case invalid: // Fall into llvm_unreachable(). break; @@ -511,6 +550,10 @@ case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); return; + case imageOffset: + case imageOffsetGot: + llvm_unreachable("image offset implies __unwind_info"); + return; case invalid: // Fall into llvm_unreachable(). break; @@ -602,6 +645,10 @@ case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); return; + case imageOffset: + case imageOffsetGot: + llvm_unreachable("__unwind_info references should have been resolved"); + return; case invalid: // Fall into llvm_unreachable(). break; Index: lib/ReaderWriter/MachO/CMakeLists.txt =================================================================== --- lib/ReaderWriter/MachO/CMakeLists.txt +++ lib/ReaderWriter/MachO/CMakeLists.txt @@ -3,6 +3,7 @@ ArchHandler_arm.cpp ArchHandler_x86.cpp ArchHandler_x86_64.cpp + CompactUnwindPass.cpp GOTPass.cpp MachOLinkingContext.cpp MachONormalizedFileBinaryReader.cpp Index: lib/ReaderWriter/MachO/CompactUnwindPass.hpp =================================================================== --- /dev/null +++ lib/ReaderWriter/MachO/CompactUnwindPass.hpp @@ -0,0 +1,31 @@ +//===- lib/ReaderWriter/MachO/CompactUnwindPass.hpp -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_MACHO_COMPACT_UNWIND_PASS_H +#define LLD_READER_WRITER_MACHO_COMPACT_UNWIND_PASS_H + +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/SharedLibraryAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Pass.h" + +#include "MachOPasses.h" +#include "ReferenceKinds.h" +#include "StubAtoms.hpp" + +namespace lld { +namespace mach_o { + + +} // namespace mach_o +} // namespace lld + + +#endif // LLD_READER_WRITER_MACHO_GOT_PASS_H Index: lib/ReaderWriter/MachO/CompactUnwindPass.cpp =================================================================== --- /dev/null +++ lib/ReaderWriter/MachO/CompactUnwindPass.cpp @@ -0,0 +1,452 @@ +//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.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/Endian.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; + + uint32_t rangeLength; + uint32_t encoding; + }; + + struct UnwindInfoPage { + std::vector entries; + }; +} + +class UnwindInfoAtom : public SimpleDefinedAtom { +public: + UnwindInfoAtom(ArchHandler &archHandler, const File &file, + std::vector commonEncodings, + std::vector personalities, + std::vector 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)) { + + addHeader(commonEncodings.size(), personalities.size(), pages.size()); + addCommonEncodings(commonEncodings); + addPersonalityFunctions(personalities); + addTopLevelIndexes(pages); + addLSDAIndexes(pages, numLSDAs); + addSecondLevelPages(pages); + } + + ContentType contentType() const override { + return DefinedAtom::typeProcessedUnwindInfo; + } + + Alignment alignment() const override { + return Alignment(2); + } + + uint64_t size() const override { + return contents.size(); + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR__; + } + + ArrayRef rawContent() const override { + return contents; + } + + void addHeader(uint32_t numCommon, uint32_t numPersonalities, + uint32_t numPages) { + using llvm::support::ulittle32_t; + + uint32_t headerSize = 7 * sizeof(uint32_t); + contents.resize(headerSize); + + ulittle32_t *headerEntries = (ulittle32_t *)contents.data(); + // version + headerEntries[0] = 1; + // commonEncodingsArraySectionOffset + headerEntries[1] = commonEncodingsOffset; + // commonEncodingsArrayCount + headerEntries[2] = numCommon; + // personalityArraySectionOffset + headerEntries[3] = personalityArrayOffset; + // personalityArrayCount + headerEntries[4] = numPersonalities; + // indexSectionOffset + headerEntries[5] = topLevelIndexOffset; + // indexCount + headerEntries[6] = numPages + 1; + } + + /// 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 &commonEncodings) { + using llvm::support::ulittle32_t; + + contents.resize(commonEncodingsOffset + + commonEncodings.size() * sizeof(uint32_t)); + ulittle32_t *commonEncodingsArea = + (ulittle32_t *)&contents[commonEncodingsOffset]; + + std::copy(commonEncodings.begin(), commonEncodings.end(), + commonEncodingsArea); + } + + void addPersonalityFunctions(std::vector 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 &pages) { + using llvm::support::ulittle32_t; + + 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 + ulittle32_t *indexData = (ulittle32_t *)&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 + indexData[3 * i + 1] = pageLoc; + indexData[3 * i + 2] = lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t); + + for (auto &entry : pages[i].entries) + if (entry.lsdaLocation) + ++numLSDAs; + } + + // Finally, write out the final sentinel index + CompactUnwindEntry &finalEntry = pages[pages.size() - 1].entries.back(); + addImageReference(topLevelIndexOffset + 3 * pages.size() * sizeof(uint32_t), + finalEntry.rangeStart, finalEntry.rangeLength); + // secondLevelPagesSectionOffset => 0 + indexData[3 * pages.size() + 2] = + lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t); + } + + void addLSDAIndexes(std::vector &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 &pages) { + for (auto &page : pages) { + addRegularSecondLevelPage(page); + } + } + + void addRegularSecondLevelPage(const UnwindInfoPage &page) { + uint32_t curPageOffset = contents.size(); + uint32_t curPageSize = sizeof(uint32_t) + 2 * sizeof(uint16_t) + + 2 * page.entries.size() * sizeof(uint32_t); + contents.resize(curPageOffset + curPageSize); + + using llvm::support::ulittle32_t; + using llvm::support::ulittle16_t; + *(ulittle32_t *)&contents[curPageOffset] = 2; // 2 => regular page + *(ulittle16_t *)&contents[curPageOffset + 4] = 8; // offset of 1st entry + *(ulittle16_t *)&contents[curPageOffset + 6] = page.entries.size(); + + uint32_t pagePos = curPageOffset + 8; + for (auto &entry : page.entries) { + addImageReference(pagePos, entry.rangeStart); + *(ulittle32_t *)&contents[pagePos + 4] = entry.encoding; + pagePos += 2 * sizeof(uint32_t); + } + } + + 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 contents; + uint32_t commonEncodingsOffset; + uint32_t personalityArrayOffset; + uint32_t topLevelIndexOffset; + uint32_t lsdaIndexOffset; + uint32_t firstPageOffset; +}; + + +/// Pass for instantiating and optimizing GOT slots. +/// +class CompactUnwindPass : public Pass { +public: + CompactUnwindPass(const MachOLinkingContext &context) + : _context(context), _archHandler(_context.archHandler()), + _file("") { } + +private: + void perform(std::unique_ptr &mergedFile) override { + if (!_archHandler.needsCompactUnwind()) + return; + + DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n"); + + // First collect all __compact_unwind entries, addressable by the function + // it's referring to. + std::map unwindLocs; + std::vector personalities; + uint32_t numLSDAs = 0; + + collectCompactUnwindEntries(mergedFile, unwindLocs, personalities, + numLSDAs); + + // 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); + + // 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 unwindInfos = + createUnwindInfoEntries(mergedFile, unwindLocs, personalities); + + // 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 pages; + unsigned pageStart = 0; + 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)unwindInfos.size() - pageStart); + + std::copy(unwindInfos.begin() + pageStart, + unwindInfos.begin() + pageStart + entriesInPage, + std::back_inserter(pages.back().entries)); + pageStart += 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 (pageStart < unwindInfos.size()); + + // FIXME: we should also erase all compact-unwind atoms; their job is done. + UnwindInfoAtom *unwind = new (_file.allocator()) + UnwindInfoAtom(_archHandler, _file, std::vector(), + personalities, pages, numLSDAs); + mergedFile->addAtom(*unwind); + } + + void collectCompactUnwindEntries( + std::unique_ptr &mergedFile, + std::map &unwindLocs, + std::vector &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 = { nullptr, nullptr, nullptr, 0, 0}; + + 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; + } + } + + using llvm::support::ulittle32_t; + entry.rangeLength = *(ulittle32_t *)(atom->rawContent().data() + 8); + entry.encoding = *(ulittle32_t *)(atom->rawContent().data() + 12); + return entry; + } + + /// 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 createUnwindInfoEntries( + const std::unique_ptr &mergedFile, + const std::map &unwindLocs, + const std::vector &personalities) { + std::vector 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)); + + DEBUG(llvm::dbgs() << " Entry for " << atom->name() + << ", final encoding=" + << llvm::format("0x%08x", unwindInfos.back().encoding) + << '\n'); + } + + return unwindInfos; + } + + CompactUnwindEntry finalizeUnwindInfoEntryForAtom( + const DefinedAtom *function, + const std::map &unwindLocs, + const std::vector &personalities) { + 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. + if (unwindLoc == unwindLocs.end()) { + CompactUnwindEntry entry; + memset(&entry, 0, sizeof(CompactUnwindEntry)); + entry.rangeStart = function; + entry.rangeLength = function->size(); + return entry; + } + + CompactUnwindEntry entry = unwindLoc->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 &_context; + mach_o::ArchHandler &_archHandler; + MachOFile _file; +}; + +void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) { + assert(ctx.needsCompactUnwindPass()); + pm.add(std::unique_ptr(new CompactUnwindPass(ctx))); +} + +} // end namesapce mach_o +} // end namesapce lld Index: lib/ReaderWriter/MachO/MachOLinkingContext.cpp =================================================================== --- lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -270,6 +270,14 @@ return (_outputMachOType != MH_OBJECT); } +bool MachOLinkingContext::needsCompactUnwindPass() const { + switch (_outputMachOType) { + case MH_EXECUTE: + return true; + default: + return false; + } +} StringRef MachOLinkingContext::binderSymbolName() const { return archHandler().stubInfo().binderSymbolName; @@ -500,6 +508,8 @@ pm.add(std::unique_ptr(new LayoutPass(registry()))); if (needsStubsPass()) mach_o::addStubsPass(pm, *this); + if (needsCompactUnwindPass()) + mach_o::addCompactUnwindPass(pm, *this); if (needsGOTPass()) mach_o::addGOTPass(pm, *this); } Index: lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp =================================================================== --- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -95,8 +95,9 @@ class Util { public: - Util(const MachOLinkingContext &ctxt) : _context(ctxt), - _archHandler(ctxt.archHandler()), _entryAtom(nullptr) {} + Util(const MachOLinkingContext &ctxt) + : _context(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr), + _imageBase(0) {} void assignAtomsToSections(const lld::File &atomFile); void organizeSections(); @@ -106,6 +107,7 @@ void copySectionInfo(NormalizedFile &file); void updateSectionInfo(NormalizedFile &file); void buildAtomToAddressMap(); + void layoutUnwindInfoSection(NormalizedFile &file); std::error_code addSymbols(const lld::File &atomFile, NormalizedFile &file); void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file); void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file); @@ -169,6 +171,7 @@ DylibPathToInfo _dylibInfo; const DefinedAtom *_entryAtom; AtomToIndex _atomToSymbolIndex; + uint64_t _imageBase; }; @@ -220,6 +223,7 @@ ENTRY("__TEXT", "__stub_helper", S_REGULAR, typeStubHelper), ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA), ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI), + ENTRY("__TEXT", "__unwind_info", S_REGULAR, typeProcessedUnwindInfo), ENTRY("__DATA", "__data", S_REGULAR, typeData), ENTRY("__DATA", "__const", S_REGULAR, typeConstData), ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString), @@ -445,7 +449,7 @@ // __TEXT segment lays out backwards so padding is at front after load commands. void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, uint64_t &addr) { - seg->address = addr; + seg->address = _imageBase = addr; // Walks sections starting at end to calculate padding for start. int64_t taddr = 0; for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) { @@ -562,7 +566,8 @@ for (AtomInfo &ai : si->atomsAndOffsets) { uint8_t *atomContent = reinterpret_cast (§ionContent[ai.offsetInSection]); - _archHandler.generateAtomContent(*ai.atom, r, addrForAtom, atomContent); + _archHandler.generateAtomContent(*ai.atom, r, addrForAtom, _imageBase, + atomContent); } } } Index: lib/ReaderWriter/MachO/MachOPasses.h =================================================================== --- lib/ReaderWriter/MachO/MachOPasses.h +++ lib/ReaderWriter/MachO/MachOPasses.h @@ -18,6 +18,7 @@ void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx); void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx); +void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx); } // namespace mach_o } // namespace lld Index: lib/ReaderWriter/YAML/ReaderWriterYAML.cpp =================================================================== --- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -439,6 +439,7 @@ io.enumCase(value, "dtraceDOF", DefinedAtom::typeDTraceDOF); io.enumCase(value, "lto-temp", DefinedAtom::typeTempLTO); io.enumCase(value, "compact-unwind", DefinedAtom::typeCompactUnwindInfo); + io.enumCase(value, "unwind-info", DefinedAtom::typeProcessedUnwindInfo); io.enumCase(value, "tlv-thunk", DefinedAtom::typeThunkTLV); io.enumCase(value, "tlv-data", DefinedAtom::typeTLVInitialData); io.enumCase(value, "tlv-zero-fill", DefinedAtom::typeTLVInitialZeroFill); Index: test/mach-o/exe-offsets.yaml =================================================================== --- test/mach-o/exe-offsets.yaml +++ test/mach-o/exe-offsets.yaml @@ -54,6 +54,9 @@ # CHECK: Offset: 0 # CHECK-LABEL: Section { +# CHECK: Name: __unwind_info + +# CHECK-LABEL: Section { # CHECK: Name: __data # CHECK: Segment: __DATA # CHECK: Size: 0x5 Index: test/mach-o/exe-segment-overlap.yaml =================================================================== --- test/mach-o/exe-segment-overlap.yaml +++ test/mach-o/exe-segment-overlap.yaml @@ -43,7 +43,10 @@ # CHECK: Name: __text # CHECK: Segment: __TEXT # CHECK: Size: 0x1 -# CHECK: Offset: 4095 +# CHECK: Offset: 4027 + +# CHECK-LABEL: Section { +# CHECK: Name: __unwind_info # CHECK-LABEL: Section { # CHECK: Name: __data Index: test/mach-o/unwind-info-simple.yaml =================================================================== --- /dev/null +++ test/mach-o/unwind-info-simple.yaml @@ -0,0 +1,75 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e _main +# RUN: llvm-objdump -unwind-info %t | FileCheck %s + +# CHECK: Contents of __unwind_info section: +# CHECK: Version: 0x1 +# CHECK: Common encodings array section offset: 0x1c +# CHECK: Number of common encodings in array: 0x0 +# CHECK: Personality function array section offset: 0x1c +# CHECK: Number of personality functions in array: 0x1 +# CHECK: Index array section offset: 0x20 +# CHECK: Number of indices in array: 0x2 +# CHECK: Common encodings: (count = 0) +# CHECK: Personality functions: (count = 1) +# CHECK: personality[1]: 0x00001008 +# 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: LSDA descriptors: +# CHECK: [0]: function offset=0x00000f7e, LSDA offset=0x00000f80 +# 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 + +--- !native +path: '' +defined-atoms: + - name: GCC_except_table1 + type: unwind-lsda + content: [ FF, 9B, A2, 80, 80, 00, 03, 1A, 08, 00, 00, 00, + 05, 00, 00, 00, 1A, 00, 00, 00, 01, 0D, 00, 00, + 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, + 04, 00, 00, 00 ] + - type: compact-unwind + content: [ 40, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, + 00, 00, 00, 41, 00, 00, 00, 00, 00, 00, 00, 00, + E0, 00, 00, 00, 00, 00, 00, 00 ] + references: + - kind: pointer64Anon + offset: 0 + target: __Z3barv + - kind: pointer64 + offset: 16 + target: ___gxx_personality_v0 + - kind: pointer64Anon + offset: 24 + target: GCC_except_table1 + - type: compact-unwind + content: [ C0, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, + 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00 ] + references: + - kind: pointer64Anon + offset: 0 + target: _main + + - name: __Z3barv + scope: global + content: [ C3 ] + - name: _main + scope: global + content: [ C3 ] + references: + - kind: branch32 + offset: 9 + target: __Z3barv + + - name: dyld_stub_binder + scope: global + type: data + content: [ 00 ] +shared-library-atoms: + - name: ___gxx_personality_v0 + load-name: '/usr/lib/libc++abi.dylib' + type: unknown