llvm::Error perform(SimpleFile &mergedFile) override { // Scan all references in all atoms. for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { // Look at instructions accessing the GOT. bool canBypassGOT; if (!_archHandler.isGOTAccess(*ref, canBypassGOT)) continue; const Atom *target = ref->target(); assert(target != nullptr); if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) { // Update reference kind to reflect that target is a direct accesss. _archHandler.updateReferenceToGOT(ref, false); } else { // Replace the target with a reference to a GOT entry. const DefinedAtom *gotEntry = makeGOTEntry(target); const_cast<Reference *>(ref)->setTarget(gotEntry); // Update reference kind to reflect that target is now a GOT entry. _archHandler.updateReferenceToGOT(ref, true); } } } // Sort and add all created GOT Atoms to master file std::vector<const GOTEntryAtom *> entries; entries.reserve(_targetToGOT.size()); for (auto &it : _targetToGOT) entries.push_back(it.second); std::sort(entries.begin(), entries.end(), [](const GOTEntryAtom *left, const GOTEntryAtom *right) { return (left->slotName().compare(right->slotName()) < 0); }); for (const GOTEntryAtom *slot : entries) mergedFile.addAtom(*slot); return llvm::Error::success(); }
std::error_code perform(SimpleFile &mergedFile) override { // Skip this pass if output format uses text relocations instead of stubs. if (!this->noTextRelocs()) return std::error_code(); // Scan all references in all atoms. for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { // Look at call-sites. if (!this->isCallSite(*ref)) continue; const Atom *target = ref->target(); assert(target != nullptr); if (isa<SharedLibraryAtom>(target)) { // Calls to shared libraries go through stubs. _targetToUses[target].push_back(ref); continue; } const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){ // Calls to interposable functions in same linkage unit must also go // through a stub. assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit); _targetToUses[target].push_back(ref); } } } // Exit early if no stubs needed. if (_targetToUses.empty()) return std::error_code(); // First add help-common and GOT slots used by lazy binding. SimpleDefinedAtom *helperCommonAtom = new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo); SimpleDefinedAtom *helperCacheNLPAtom = new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit()); SimpleDefinedAtom *helperBinderNLPAtom = new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit()); addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, helperCacheNLPAtom); addOptReference( helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, _stubInfo.optStubHelperCommonReferenceToCache, helperCacheNLPAtom); addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, helperBinderNLPAtom); addOptReference( helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, _stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom); mergedFile.addAtom(*helperCommonAtom); mergedFile.addAtom(*helperBinderNLPAtom); mergedFile.addAtom(*helperCacheNLPAtom); // Add reference to dyld_stub_binder in libSystem.dylib auto I = std::find_if( mergedFile.sharedLibrary().begin(), mergedFile.sharedLibrary().end(), [&](const SharedLibraryAtom *atom) { return atom->name().equals(_stubInfo.binderSymbolName); }); assert(I != mergedFile.sharedLibrary().end() && "dyld_stub_binder not found"); addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I); // Sort targets by name, so stubs and lazy pointers are consistent std::vector<const Atom *> targetsNeedingStubs; for (auto it : _targetToUses) targetsNeedingStubs.push_back(it.first); std::sort(targetsNeedingStubs.begin(), targetsNeedingStubs.end(), [](const Atom * left, const Atom * right) { return (left->name().compare(right->name()) < 0); }); // Make and append stubs, lazy pointers, and helpers in alphabetical order. unsigned lazyOffset = 0; for (const Atom *target : targetsNeedingStubs) { auto *stub = new (_file.allocator()) StubAtom(_file, _stubInfo); auto *lp = new (_file.allocator()) LazyPointerAtom(_file, _ctx.is64Bit()); auto *helper = new (_file.allocator()) StubHelperAtom(_file, _stubInfo); addReference(stub, _stubInfo.stubReferenceToLP, lp); addOptReference(stub, _stubInfo.stubReferenceToLP, _stubInfo.optStubReferenceToLP, lp); addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper); addReference(lp, _stubInfo.lazyPointerReferenceToFinal, target); addReference(helper, _stubInfo.stubHelperReferenceToImm, helper); addReferenceAddend(helper, _stubInfo.stubHelperReferenceToImm, helper, lazyOffset); addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon, helperCommonAtom); mergedFile.addAtom(*stub); mergedFile.addAtom(*lp); mergedFile.addAtom(*helper); // Update each reference to use stub. for (const Reference *ref : _targetToUses[target]) { assert(ref->target() == target); // Switch call site to reference stub atom instead. const_cast<Reference *>(ref)->setTarget(stub); } // Calculate new offset lazyOffset += target->name().size() + 12; } return std::error_code(); }
std::error_code perform(SimpleFile &mergedFile) override { // Add the image info. mergedFile.addAtom(*getImageInfo()); return std::error_code(); }
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(); }