Path: blob/main/contrib/llvm-project/lld/MachO/MarkLive.cpp
34878 views
//===- MarkLive.cpp -------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "MarkLive.h"9#include "Config.h"10#include "OutputSegment.h"11#include "SymbolTable.h"12#include "Symbols.h"13#include "UnwindInfoSection.h"1415#include "lld/Common/ErrorHandler.h"16#include "llvm/Support/TimeProfiler.h"1718#include "mach-o/compact_unwind_encoding.h"1920namespace lld::macho {2122using namespace llvm;23using namespace llvm::MachO;2425struct WhyLiveEntry {26InputSection *isec;27// Keep track of the entry that caused us to mark `isec` as live.28const WhyLiveEntry *prev;2930WhyLiveEntry(InputSection *isec, const WhyLiveEntry *prev)31: isec(isec), prev(prev) {}32};3334// Type-erased interface to MarkLiveImpl. Used for adding roots to the liveness35// graph.36class MarkLive {37public:38virtual void enqueue(InputSection *isec, uint64_t off) = 0;39virtual void addSym(Symbol *s) = 0;40virtual void markTransitively() = 0;41virtual ~MarkLive() = default;42};4344template <bool RecordWhyLive> class MarkLiveImpl : public MarkLive {45public:46// -why_live is a rarely used option, so we don't want support for that flag47// to slow down the main -dead_strip code path. As such, we employ templates48// to avoid the usage of WhyLiveEntry in the main code path. This saves us49// from needless allocations and pointer indirections.50using WorklistEntry =51std::conditional_t<RecordWhyLive, WhyLiveEntry, InputSection>;5253void enqueue(InputSection *isec, uint64_t off) override {54enqueue(isec, off, nullptr);55}56void addSym(Symbol *s) override { addSym(s, nullptr); }57void markTransitively() override;5859private:60void enqueue(InputSection *isec, uint64_t off, const WorklistEntry *prev);61void addSym(Symbol *s, const WorklistEntry *prev);62const InputSection *getInputSection(const WorklistEntry *) const;63WorklistEntry *makeEntry(InputSection *, const WorklistEntry *prev) const;6465// We build up a worklist of sections which have been marked as live. We66// only push into the worklist when we discover an unmarked section, and we67// mark as we push, so sections never appear twice in the list. Literal68// sections cannot contain references to other sections, so we only store69// ConcatInputSections in our worklist.70SmallVector<WorklistEntry *, 256> worklist;71};7273template <bool RecordWhyLive>74void MarkLiveImpl<RecordWhyLive>::enqueue(75InputSection *isec, uint64_t off,76const typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) {77if (isec->isLive(off))78return;79isec->markLive(off);80if (auto s = dyn_cast<ConcatInputSection>(isec)) {81assert(!s->isCoalescedWeak());82worklist.push_back(makeEntry(s, prev));83}84}8586static void printWhyLive(const Symbol *s, const WhyLiveEntry *prev) {87std::string out = toString(*s) + " from " + toString(s->getFile());88int indent = 2;89for (const WhyLiveEntry *entry = prev; entry;90entry = entry->prev, indent += 2) {91const TinyPtrVector<Defined *> &symbols = entry->isec->symbols;92// With .subsections_with_symbols set, most isecs will have exactly one93// entry in their symbols vector, so we just print the first one.94if (!symbols.empty())95out += "\n" + std::string(indent, ' ') + toString(*symbols.front()) +96" from " + toString(symbols.front()->getFile());97}98message(out);99}100101template <bool RecordWhyLive>102void MarkLiveImpl<RecordWhyLive>::addSym(103Symbol *s,104const typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) {105if (s->used)106return;107s->used = true;108if constexpr (RecordWhyLive)109if (!config->whyLive.empty() && config->whyLive.match(s->getName()))110printWhyLive(s, prev);111if (auto *d = dyn_cast<Defined>(s)) {112if (d->isec())113enqueue(d->isec(), d->value, prev);114if (d->unwindEntry())115enqueue(d->unwindEntry(), 0, prev);116}117}118119template <bool RecordWhyLive>120const InputSection *MarkLiveImpl<RecordWhyLive>::getInputSection(121const MarkLiveImpl<RecordWhyLive>::WorklistEntry *entry) const {122if constexpr (RecordWhyLive)123return entry->isec;124else125return entry;126}127128template <bool RecordWhyLive>129typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *130MarkLiveImpl<RecordWhyLive>::makeEntry(131InputSection *isec,132const MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) const {133if constexpr (RecordWhyLive) {134if (!isec) {135assert(!prev);136return nullptr;137}138return make<WhyLiveEntry>(isec, prev);139} else {140return isec;141}142}143144template <bool RecordWhyLive>145void MarkLiveImpl<RecordWhyLive>::markTransitively() {146do {147// Mark things reachable from GC roots as live.148while (!worklist.empty()) {149WorklistEntry *entry = worklist.pop_back_val();150// Entries that get placed onto the worklist always contain151// ConcatInputSections. `WhyLiveEntry::prev` may point to entries that152// contain other types of InputSections (due to S_ATTR_LIVE_SUPPORT), but153// those entries should never be pushed onto the worklist.154auto *isec = cast<ConcatInputSection>(getInputSection(entry));155assert(isec->live && "We mark as live when pushing onto the worklist!");156157// Mark all symbols listed in the relocation table for this section.158for (const Reloc &r : isec->relocs) {159if (auto *s = r.referent.dyn_cast<Symbol *>())160addSym(s, entry);161else162enqueue(r.referent.get<InputSection *>(), r.addend, entry);163}164for (Defined *d : getInputSection(entry)->symbols)165addSym(d, entry);166}167168// S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live169// section. Process them in a second pass.170for (ConcatInputSection *isec : inputSections) {171// FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a172// separate vector and only walking that here is faster.173if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live)174continue;175176for (const Reloc &r : isec->relocs) {177if (auto *s = r.referent.dyn_cast<Symbol *>()) {178if (s->isLive()) {179InputSection *referentIsec = nullptr;180if (auto *d = dyn_cast<Defined>(s))181referentIsec = d->isec();182enqueue(isec, 0, makeEntry(referentIsec, nullptr));183}184} else {185auto *referentIsec = r.referent.get<InputSection *>();186if (referentIsec->isLive(r.addend))187enqueue(isec, 0, makeEntry(referentIsec, nullptr));188}189}190}191192// S_ATTR_LIVE_SUPPORT could have marked additional sections live,193// which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live.194// Iterate. In practice, the second iteration won't mark additional195// S_ATTR_LIVE_SUPPORT sections live.196} while (!worklist.empty());197}198199// Set live bit on for each reachable chunk. Unmarked (unreachable)200// InputSections will be ignored by Writer, so they will be excluded201// from the final output.202void markLive() {203TimeTraceScope timeScope("markLive");204MarkLive *marker;205if (config->whyLive.empty())206marker = make<MarkLiveImpl<false>>();207else208marker = make<MarkLiveImpl<true>>();209// Add GC roots.210if (config->entry)211marker->addSym(config->entry);212for (Symbol *sym : symtab->getSymbols()) {213if (auto *defined = dyn_cast<Defined>(sym)) {214// -exported_symbol(s_list)215if (!config->exportedSymbols.empty() &&216config->exportedSymbols.match(defined->getName())) {217// NOTE: Even though exporting private externs is an ill-defined218// operation, we are purposely not checking for privateExtern in219// order to follow ld64's behavior of treating all exported private220// extern symbols as live, irrespective of whether they are autohide.221marker->addSym(defined);222continue;223}224225// public symbols explicitly marked .no_dead_strip226if (defined->referencedDynamically || defined->noDeadStrip) {227marker->addSym(defined);228continue;229}230231// FIXME: When we implement these flags, make symbols from them GC232// roots:233// * -reexported_symbol(s_list)234// * -alias_list235// * -init236237// In dylibs and bundles and in executables with -export_dynamic,238// all external functions are GC roots.239bool externsAreRoots =240config->outputType != MH_EXECUTE || config->exportDynamic;241if (externsAreRoots && !defined->privateExtern) {242marker->addSym(defined);243continue;244}245}246}247// -u symbols248for (Symbol *sym : config->explicitUndefineds)249marker->addSym(sym);250// local symbols explicitly marked .no_dead_strip251for (const InputFile *file : inputFiles)252if (auto *objFile = dyn_cast<ObjFile>(file))253for (Symbol *sym : objFile->symbols)254if (auto *defined = dyn_cast_or_null<Defined>(sym))255if (!defined->isExternal() && defined->noDeadStrip)256marker->addSym(defined);257if (auto *stubBinder =258dyn_cast_or_null<DylibSymbol>(symtab->find("dyld_stub_binder")))259marker->addSym(stubBinder);260for (ConcatInputSection *isec : inputSections) {261// Sections marked no_dead_strip262if (isec->getFlags() & S_ATTR_NO_DEAD_STRIP) {263marker->enqueue(isec, 0);264continue;265}266267// mod_init_funcs, mod_term_funcs sections268if (sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS ||269sectionType(isec->getFlags()) == S_MOD_TERM_FUNC_POINTERS) {270assert(!config->emitInitOffsets ||271sectionType(isec->getFlags()) != S_MOD_INIT_FUNC_POINTERS);272marker->enqueue(isec, 0);273continue;274}275}276277for (ConcatInputSection *isec : in.initOffsets->inputs())278marker->enqueue(isec, 0);279280marker->markTransitively();281}282283} // namespace lld::macho284285286