| //===- MarkLive.cpp -------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements --gc-sections, which is a feature to remove unused | |
| // sections from output. Unused sections are sections that are not reachable | |
| // from known GC-root symbols or sections. Naturally the feature is | |
| // implemented as a mark-sweep garbage collector. | |
| // | |
| // Here's how it works. Each InputSectionBase has a "Live" bit. The bit is off | |
| // by default. Starting with GC-root symbols or sections, markLive function | |
| // defined in this file visits all reachable sections to set their Live | |
| // bits. Writer will then ignore sections whose Live bits are off, so that | |
| // such sections are not included into output. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MarkLive.h" | |
| #include "InputFiles.h" | |
| #include "InputSection.h" | |
| #include "LinkerScript.h" | |
| #include "SymbolTable.h" | |
| #include "Symbols.h" | |
| #include "SyntheticSections.h" | |
| #include "Target.h" | |
| #include "lld/Common/Strings.h" | |
| #include "llvm/ADT/DenseMapInfoVariant.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/Support/Parallel.h" | |
| #include "llvm/Support/TimeProfiler.h" | |
| #include <variant> | |
| #include <vector> | |
| using namespace llvm; | |
| using namespace llvm::ELF; | |
| using namespace llvm::object; | |
| using namespace llvm::support::endian; | |
| using namespace lld; | |
| using namespace lld::elf; | |
| namespace { | |
| using SecOffset = std::pair<InputSectionBase *, unsigned>; | |
| // Something that can have an independent reason for being live. | |
| using LiveItem = std::variant<InputSectionBase *, Symbol *, SecOffset>; | |
| // The most proximate reason that something is live. | |
| struct LiveReason { | |
| std::optional<LiveItem> item; | |
| StringRef desc; | |
| }; | |
| template <class ELFT, bool TrackWhyLive> class MarkLive { | |
| public: | |
| MarkLive(Ctx &ctx, unsigned partition) : ctx(ctx), partition(partition) {} | |
| void run(); | |
| void moveToMain(); | |
| void printWhyLive(Symbol *s) const; | |
| private: | |
| void enqueue(InputSectionBase *sec, uint64_t offset, Symbol *sym, | |
| LiveReason reason); | |
| void markSymbol(Symbol *sym, StringRef reason); | |
| void mark(); | |
| void markParallel(); | |
| template <class RelTy> | |
| void resolveReloc(InputSectionBase &sec, const RelTy &rel, bool fromFDE); | |
| void scanEhFrameSection(EhInputSection &eh); | |
| Ctx &ctx; | |
| // The index of the partition that we are currently processing. | |
| unsigned partition; | |
| // A list of sections to visit. | |
| SmallVector<InputSection *, 0> queue; | |
| // There are normally few input sections whose names are valid C | |
| // identifiers, so we just store a SmallVector instead of a multimap. | |
| DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections; | |
| // The most proximate reason that something is live. This forms a DAG between | |
| // LiveItems. Acyclicality is maintained by only admitting the first | |
| // discovered reason for each LiveItem; this captures the acyclic region of | |
| // the liveness graph around the GC roots. | |
| DenseMap<LiveItem, LiveReason> whyLive; | |
| }; | |
| } // namespace | |
| template <class ELFT> | |
| static uint64_t getAddend(Ctx &ctx, InputSectionBase &sec, | |
| const typename ELFT::Rel &rel) { | |
| return ctx.target->getImplicitAddend(sec.content().begin() + rel.r_offset, | |
| rel.getType(ctx.arg.isMips64EL)); | |
| } | |
| template <class ELFT> | |
| static uint64_t getAddend(Ctx &, InputSectionBase &sec, | |
| const typename ELFT::Rela &rel) { | |
| return rel.r_addend; | |
| } | |
| // Currently, we assume all input CREL relocations have an explicit addend. | |
| template <class ELFT> | |
| static uint64_t getAddend(Ctx &, InputSectionBase &sec, | |
| const typename ELFT::Crel &rel) { | |
| return rel.r_addend; | |
| } | |
| template <class ELFT, bool TrackWhyLive> | |
| template <class RelTy> | |
| void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec, | |
| const RelTy &rel, | |
| bool fromFDE) { | |
| // If a symbol is referenced in a live section, it is used. | |
| Symbol *sym; | |
| if constexpr (std::is_same_v<RelTy, Relocation>) { | |
| assert(isa<EhInputSection>(sec)); | |
| sym = rel.sym; | |
| } else { | |
| sym = &sec.file->getRelocTargetSym(rel); | |
| } | |
| sym->setFlags(USED); | |
| LiveReason reason; | |
| if (TrackWhyLive) { | |
| if constexpr (std::is_same_v<RelTy, Relocation>) | |
| reason = {SecOffset(&sec, rel.offset), "referenced by"}; | |
| else | |
| reason = {SecOffset(&sec, rel.r_offset), "referenced by"}; | |
| } | |
| if (auto *d = dyn_cast<Defined>(sym)) { | |
| auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section); | |
| if (!relSec) | |
| return; | |
| uint64_t offset = d->value; | |
| if (d->isSection()) { | |
| if constexpr (std::is_same_v<RelTy, Relocation>) | |
| offset += rel.addend; | |
| else | |
| offset += getAddend<ELFT>(ctx, sec, rel); | |
| // Skip out-of-bounds offsets to avoid an assertion failure in | |
| // getSectionPiece. | |
| if (auto *ms = dyn_cast<MergeInputSection>(relSec); | |
| ms && offset >= ms->content().size()) | |
| return; | |
| } | |
| // fromFDE being true means this is referenced by a FDE in a .eh_frame | |
| // piece. The relocation points to the described function or to a LSDA. We | |
| // only need to keep the LSDA live, so ignore anything that points to | |
| // executable sections. If the LSDA is in a section group or has the | |
| // SHF_LINK_ORDER flag, we ignore the relocation as well because (a) if the | |
| // associated text section is live, the LSDA will be retained due to section | |
| // group/SHF_LINK_ORDER rules (b) if the associated text section should be | |
| // discarded, marking the LSDA will unnecessarily retain the text section. | |
| if (!(fromFDE && std::is_same_v<RelTy, Relocation> && | |
| ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) || | |
| relSec->nextInSectionGroup))) { | |
| Symbol *canonicalSym = d; | |
| if (TrackWhyLive && d->isSection()) { | |
| // This is expensive, so ideally this would be deferred until it's known | |
| // whether this reference contributes to a printed whyLive chain, but | |
| // that determination cannot be made without knowing the enclosing | |
| // symbol. | |
| if (Symbol *s = relSec->getEnclosingSymbol(offset)) | |
| canonicalSym = s; | |
| else | |
| canonicalSym = nullptr; | |
| } | |
| enqueue(relSec, offset, canonicalSym, reason); | |
| } | |
| return; | |
| } | |
| if (auto *ss = dyn_cast<SharedSymbol>(sym)) | |
| if (!ss->isWeak() && TrackWhyLive) | |
| whyLive.try_emplace(sym, reason); | |
| for (InputSectionBase *sec : cNamedSections.lookup(sym->getName())) | |
| enqueue(sec, /*offset=*/0, /*sym=*/nullptr, reason); | |
| } | |
| // The .eh_frame section is an unfortunate special case. | |
| // The section is divided in CIEs and FDEs and the relocations it can have are | |
| // * CIEs can refer to a personality function. | |
| // * FDEs can refer to a LSDA | |
| // * FDEs refer to the function they contain information about | |
| // The last kind of relocation cannot keep the referred section alive, or they | |
| // would keep everything alive in a common object file. In fact, each FDE is | |
| // alive if the section it refers to is alive. | |
| // To keep things simple, in here we just ignore the last relocation kind. The | |
| // other two keep the referred section alive. | |
| // | |
| // A possible improvement would be to fully process .eh_frame in the middle of | |
| // the gc pass. With that we would be able to also gc some sections holding | |
| // LSDAs and personality functions if we found that they were unused. | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::scanEhFrameSection(EhInputSection &eh) { | |
| if (TrackWhyLive) | |
| whyLive.try_emplace(&eh, | |
| LiveReason{std::nullopt, "exception handling frame"}); | |
| ArrayRef<Relocation> rels = eh.rels; | |
| for (const EhSectionPiece &cie : eh.cies) | |
| if (cie.firstRelocation != unsigned(-1)) | |
| resolveReloc(eh, rels[cie.firstRelocation], false); | |
| for (const EhSectionPiece &fde : eh.fdes) { | |
| size_t firstRelI = fde.firstRelocation; | |
| if (firstRelI == (unsigned)-1) | |
| continue; | |
| uint64_t pieceEnd = fde.inputOff + fde.size; | |
| for (size_t j = firstRelI, end2 = rels.size(); | |
| j < end2 && rels[j].offset < pieceEnd; ++j) | |
| resolveReloc(eh, rels[j], true); | |
| } | |
| } | |
| // Some sections are used directly by the loader, so they should never be | |
| // garbage-collected. This function returns true if a given section is such | |
| // section. | |
| static bool isReserved(InputSectionBase *sec) { | |
| switch (sec->type) { | |
| case SHT_FINI_ARRAY: | |
| case SHT_INIT_ARRAY: | |
| case SHT_PREINIT_ARRAY: | |
| return true; | |
| case SHT_NOTE: | |
| // SHT_NOTE sections in a group are subject to garbage collection. | |
| return !sec->nextInSectionGroup; | |
| default: | |
| // Support SHT_PROGBITS .init_array (https://golang.org/issue/50295) and | |
| // .init_array.N (https://github.com/rust-lang/rust/issues/92181) for a | |
| // while. | |
| StringRef s = sec->name; | |
| return s == ".init" || s == ".fini" || s.starts_with(".init_array") || | |
| s == ".jcr" || s.starts_with(".ctors") || s.starts_with(".dtors"); | |
| } | |
| } | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::enqueue(InputSectionBase *sec, | |
| uint64_t offset, Symbol *sym, | |
| LiveReason reason) { | |
| // Usually, a whole section is marked as live or dead, but in mergeable | |
| // (splittable) sections, each piece of data has independent liveness bit. | |
| // So we explicitly tell it which offset is in use. | |
| if (auto *ms = dyn_cast<MergeInputSection>(sec)) | |
| ms->getSectionPiece(offset).live = true; | |
| // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and | |
| // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition | |
| // doesn't change, we don't need to do anything. | |
| if (sec->partition == 1 || sec->partition == partition) | |
| return; | |
| sec->partition = sec->partition ? 1 : partition; | |
| if (TrackWhyLive) { | |
| if (sym) { | |
| // If a specific symbol is referenced, that keeps it live. The symbol then | |
| // keeps its section live. | |
| whyLive.try_emplace(sym, reason); | |
| whyLive.try_emplace(sec, LiveReason{sym, "contained live symbol"}); | |
| } else { | |
| // Otherwise, the reference generically keeps the section live. | |
| whyLive.try_emplace(sec, reason); | |
| } | |
| } | |
| // Add input section to the queue. | |
| if (InputSection *s = dyn_cast<InputSection>(sec)) | |
| queue.push_back(s); | |
| } | |
| // Print the stack of reasons that the given symbol is live. | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::printWhyLive(Symbol *s) const { | |
| // Skip dead symbols. A symbol is dead if it belongs to a dead section. | |
| if (auto *d = dyn_cast<Defined>(s)) { | |
| auto *sec = dyn_cast_or_null<InputSectionBase>(d->section); | |
| if (sec && !sec->isLive()) | |
| return; | |
| } | |
| auto msg = Msg(ctx); | |
| const auto printSymbol = [&](Symbol *s) { | |
| msg << s->file << ":(" << s << ')'; | |
| }; | |
| msg << "live symbol: "; | |
| printSymbol(s); | |
| LiveItem cur = s; | |
| while (true) { | |
| auto it = whyLive.find(cur); | |
| LiveReason reason; | |
| // If there is a specific reason this item is live... | |
| if (it != whyLive.end()) { | |
| reason = it->second; | |
| } else { | |
| // This item is live, but it has no tracked reason. It must be an | |
| // unreferenced symbol in a live section or a symbol with no section. | |
| InputSectionBase *sec = nullptr; | |
| if (auto *d = dyn_cast<Defined>(std::get<Symbol *>(cur))) | |
| sec = dyn_cast_or_null<InputSectionBase>(d->section); | |
| reason = sec ? LiveReason{sec, "in live section"} | |
| : LiveReason{std::nullopt, "no section"}; | |
| } | |
| if (!reason.item) { | |
| msg << " (" << reason.desc << ')'; | |
| break; | |
| } | |
| msg << "\n>>> " << reason.desc << ": "; | |
| // The reason may not yet have been resolved to a symbol; do so now. | |
| if (std::holds_alternative<SecOffset>(*reason.item)) { | |
| const auto &so = std::get<SecOffset>(*reason.item); | |
| InputSectionBase *sec = so.first; | |
| Defined *sym = sec->getEnclosingSymbol(so.second); | |
| cur = sym ? LiveItem(sym) : LiveItem(sec); | |
| } else { | |
| cur = *reason.item; | |
| } | |
| if (std::holds_alternative<Symbol *>(cur)) | |
| printSymbol(std::get<Symbol *>(cur)); | |
| else | |
| msg << std::get<InputSectionBase *>(cur); | |
| } | |
| } | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym, StringRef reason) { | |
| if (auto *d = dyn_cast_or_null<Defined>(sym)) | |
| if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section)) | |
| enqueue(isec, d->value, sym, {std::nullopt, reason}); | |
| } | |
| // This is the main function of the garbage collector. | |
| // Starting from GC-root sections, this function visits all reachable | |
| // sections to set their "Live" bits. | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::run() { | |
| // Add GC root symbols. | |
| // Preserve externally-visible symbols if the symbols defined by this | |
| // file can interpose other ELF file's symbols at runtime. | |
| for (Symbol *sym : ctx.symtab->getSymbols()) | |
| if (sym->isExported && sym->partition == partition) | |
| markSymbol(sym, "externally visible symbol; may interpose"); | |
| // If this isn't the main partition, that's all that we need to preserve. | |
| if (partition != 1) { | |
| mark(); | |
| return; | |
| } | |
| markSymbol(ctx.symtab->find(ctx.arg.entry), "entry point"); | |
| markSymbol(ctx.symtab->find(ctx.arg.init), "initializer function"); | |
| markSymbol(ctx.symtab->find(ctx.arg.fini), "finalizer function"); | |
| for (StringRef s : ctx.arg.undefined) | |
| markSymbol(ctx.symtab->find(s), "undefined command line flag"); | |
| for (StringRef s : ctx.script->referencedSymbols) | |
| markSymbol(ctx.symtab->find(s), "referenced by linker script"); | |
| for (auto [symName, _] : ctx.symtab->cmseSymMap) { | |
| markSymbol(ctx.symtab->cmseSymMap[symName].sym, "ARM CMSE symbol"); | |
| markSymbol(ctx.symtab->cmseSymMap[symName].acleSeSym, "ARM CMSE symbol"); | |
| } | |
| // Mark .eh_frame sections as live because there are usually no relocations | |
| // that point to .eh_frames. Otherwise, the garbage collector would drop | |
| // all of them. We also want to preserve personality routines and LSDA | |
| // referenced by .eh_frame sections, so we scan them for that here. | |
| for (EhInputSection *eh : ctx.ehInputSections) | |
| scanEhFrameSection(*eh); | |
| for (InputSectionBase *sec : ctx.inputSections) { | |
| if (sec->flags & SHF_GNU_RETAIN) { | |
| enqueue(sec, /*offset=*/0, /*sym=*/nullptr, {std::nullopt, "retained"}); | |
| continue; | |
| } | |
| if (sec->flags & SHF_LINK_ORDER) | |
| continue; | |
| // Usually, non-SHF_ALLOC sections are not removed even if they are | |
| // unreachable through relocations because reachability is not a good signal | |
| // whether they are garbage or not (e.g. there is usually no section | |
| // referring to a .comment section, but we want to keep it.) When a | |
| // non-SHF_ALLOC section is retained, we also retain sections dependent on | |
| // it. | |
| // | |
| // Note on SHF_LINK_ORDER: Such sections contain metadata and they | |
| // have a reverse dependency on the InputSection they are linked with. | |
| // We are able to garbage collect them. | |
| // | |
| // Note on SHF_REL{,A}: Such sections reach here only when -r | |
| // or --emit-reloc were given. And they are subject of garbage | |
| // collection because, if we remove a text section, we also | |
| // remove its relocation section. | |
| // | |
| // Note on nextInSectionGroup: The ELF spec says that group sections are | |
| // included or omitted as a unit. We take the interpretation that: | |
| // | |
| // - Group members (nextInSectionGroup != nullptr) are subject to garbage | |
| // collection. | |
| // - Groups members are retained or discarded as a unit. | |
| if (!(sec->flags & SHF_ALLOC)) { | |
| if (!isStaticRelSecType(sec->type) && !sec->nextInSectionGroup) { | |
| sec->markLive(); | |
| for (InputSection *isec : sec->dependentSections) | |
| isec->markLive(); | |
| } | |
| } | |
| // Preserve special sections and those which are specified in linker | |
| // script KEEP command. | |
| if (isReserved(sec)) { | |
| enqueue(sec, /*offset=*/0, /*sym=*/nullptr, {std::nullopt, "reserved"}); | |
| } else if (ctx.script->shouldKeep(sec)) { | |
| enqueue(sec, /*offset=*/0, /*sym=*/nullptr, | |
| {std::nullopt, "KEEP in linker script"}); | |
| } else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) && | |
| isValidCIdentifier(sec->name)) { | |
| // As a workaround for glibc libc.a before 2.34 | |
| // (https://sourceware.org/PR27492), retain __libc_atexit and similar | |
| // sections regardless of zStartStopGC. | |
| cNamedSections[ctx.saver.save("__start_" + sec->name)].push_back(sec); | |
| cNamedSections[ctx.saver.save("__stop_" + sec->name)].push_back(sec); | |
| } | |
| } | |
| mark(); | |
| if (TrackWhyLive) { | |
| const auto handleSym = [&](Symbol *sym) { | |
| if (llvm::any_of(ctx.arg.whyLive, [sym](const llvm::GlobPattern &pat) { | |
| return pat.match(sym->getName()); | |
| })) | |
| printWhyLive(sym); | |
| }; | |
| for (Symbol *sym : ctx.symtab->getSymbols()) | |
| handleSym(sym); | |
| // Handle local symbols, skipping the symbol at index 0 and section | |
| // symbols, which usually have empty names and technically not live. Note: | |
| // a live section may lack an associated section symbol, making them | |
| // unreliable liveness indicators. | |
| for (ELFFileBase *file : ctx.objectFiles) | |
| for (Symbol *sym : file->getSymbols()) | |
| if (sym->isLocal() && sym->isDefined() && !sym->isSection()) | |
| handleSym(sym); | |
| } | |
| } | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::mark() { | |
| if constexpr (!TrackWhyLive) { | |
| if (ctx.partitions.size() == 1) { | |
| markParallel(); | |
| return; | |
| } | |
| } | |
| while (!queue.empty()) { | |
| InputSectionBase &sec = *queue.pop_back_val(); | |
| const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>(); | |
| for (const typename ELFT::Rel &rel : rels.rels) | |
| resolveReloc(sec, rel, false); | |
| for (const typename ELFT::Rela &rel : rels.relas) | |
| resolveReloc(sec, rel, false); | |
| for (const typename ELFT::Crel &rel : rels.crels) | |
| resolveReloc(sec, rel, false); | |
| for (InputSectionBase *isec : sec.dependentSections) | |
| enqueue(isec, /*offset=*/0, /*sym=*/nullptr, | |
| {&sec, "depended on by section"}); | |
| // Mark the next group member. | |
| if (sec.nextInSectionGroup) | |
| enqueue(sec.nextInSectionGroup, /*offset=*/0, /*sym=*/nullptr, | |
| {&sec, "in section group with"}); | |
| } | |
| } | |
| // Helper function for markParallel. Walk all GC edges from sec, marking | |
| // everything that needs to be live. Call fn(target section, offset) for each | |
| // edge, which will mark the section live and handle further processing of edges | |
| // from that section. | |
| template <class ELFT, class Fn> | |
| static void processSectionEdges( | |
| Ctx &ctx, InputSectionBase &sec, | |
| const DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> | |
| &cNamedSections, | |
| Fn fn) { | |
| auto resolveEdge = [&](const auto &rel) { | |
| Symbol &sym = sec.file->getRelocTargetSym(rel); | |
| if (!sym.hasFlag(USED)) | |
| sym.setFlags(USED); | |
| if (auto *d = dyn_cast<Defined>(&sym)) { | |
| if (auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section)) { | |
| uint64_t offset = d->value; | |
| if (d->isSection()) { | |
| offset += getAddend<ELFT>(ctx, sec, rel); | |
| if (auto *ms = dyn_cast<MergeInputSection>(relSec); | |
| ms && offset >= ms->content().size()) | |
| return; | |
| } | |
| if (auto *ms = dyn_cast<MergeInputSection>(relSec)) { | |
| auto &piece = ms->getSectionPiece(offset); | |
| auto *word = | |
| reinterpret_cast<std::atomic<uint32_t> *>(&piece.inputOff + 1); | |
| constexpr uint32_t liveBit = sys::IsBigEndianHost ? (1U << 31) : 1U; | |
| word->fetch_or(liveBit, std::memory_order_relaxed); | |
| } | |
| fn(relSec, offset); | |
| } | |
| return; | |
| } | |
| for (InputSectionBase *csec : cNamedSections.lookup(sym.getName())) | |
| fn(csec, 0); | |
| }; | |
| const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>(); | |
| for (const typename ELFT::Rel &rel : rels.rels) | |
| resolveEdge(rel); | |
| for (const typename ELFT::Rela &rel : rels.relas) | |
| resolveEdge(rel); | |
| for (const typename ELFT::Crel &rel : rels.crels) | |
| resolveEdge(rel); | |
| for (InputSectionBase *isec : sec.dependentSections) | |
| fn(isec, 0); | |
| if (sec.nextInSectionGroup) | |
| fn(sec.nextInSectionGroup, 0); | |
| } | |
| // Parallel mark using level-synchronized BFS with depth-limited inline | |
| // recursion. Each parallelFor iteration processes a subtree up to depth 3 | |
| // (DFS for cache locality), then queues deeper discoveries for the next level. | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::markParallel() { | |
| const size_t numThreads = parallel::getThreadCount(); | |
| auto visit = [&](InputSection *sec, int depth, | |
| SmallVector<InputSection *, 0> &localQueue, | |
| auto &self) -> void { | |
| processSectionEdges<ELFT>( | |
| ctx, *sec, cNamedSections, | |
| [&](InputSectionBase *target, uint64_t offset) { | |
| auto &part = | |
| reinterpret_cast<std::atomic<uint8_t> &>(target->partition); | |
| // Optimistic load-then-exchange avoids expensive atomic | |
| // RMW on already-visited sections. | |
| if (part.load(std::memory_order_relaxed) != 0 || | |
| part.exchange(1, std::memory_order_relaxed) != 0) | |
| return; | |
| if (auto *s = dyn_cast<InputSection>(target)) { | |
| if (depth < 3) | |
| self(s, depth + 1, localQueue, self); | |
| else | |
| localQueue.push_back(s); | |
| } | |
| }); | |
| }; | |
| while (!queue.empty()) { | |
| auto queues = | |
| std::make_unique<SmallVector<InputSection *, 0>[]>(numThreads); | |
| parallelFor(0, queue.size(), [&](size_t i) { | |
| const unsigned tid = parallel::getThreadIndex(); | |
| visit(queue[i], 0, queues[tid], visit); | |
| }); | |
| queue.clear(); | |
| for (size_t t = 0; t < numThreads; ++t) | |
| queue.append(std::move(queues[t])); | |
| } | |
| } | |
| // Move the sections for some symbols to the main partition, specifically ifuncs | |
| // (because they can result in an IRELATIVE being added to the main partition's | |
| // GOT, which means that the ifunc must be available when the main partition is | |
| // loaded) and TLS symbols (because we only know how to correctly process TLS | |
| // relocations for the main partition). | |
| // | |
| // We also need to move sections whose names are C identifiers that are referred | |
| // to from __start_/__stop_ symbols because there will only be one set of | |
| // symbols for the whole program. | |
| template <class ELFT, bool TrackWhyLive> | |
| void MarkLive<ELFT, TrackWhyLive>::moveToMain() { | |
| for (ELFFileBase *file : ctx.objectFiles) | |
| for (Symbol *s : file->getSymbols()) | |
| if (auto *d = dyn_cast<Defined>(s)) | |
| if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section && | |
| d->section->isLive()) | |
| markSymbol(s, /*reason=*/{}); | |
| for (InputSectionBase *sec : ctx.inputSections) { | |
| if (!sec->isLive() || !isValidCIdentifier(sec->name)) | |
| continue; | |
| if (ctx.symtab->find(("__start_" + sec->name).str()) || | |
| ctx.symtab->find(("__stop_" + sec->name).str())) | |
| enqueue(sec, /*offset=*/0, /*sym=*/nullptr, /*reason=*/{}); | |
| } | |
| mark(); | |
| } | |
| // Before calling this function, Live bits are off for all | |
| // input sections. This function make some or all of them on | |
| // so that they are emitted to the output file. | |
| template <class ELFT> void elf::markLive(Ctx &ctx) { | |
| llvm::TimeTraceScope timeScope("markLive"); | |
| // If --gc-sections is not given, retain all input sections. | |
| if (!ctx.arg.gcSections) { | |
| // If a DSO defines a symbol referenced in a regular object, it is needed. | |
| for (Symbol *sym : ctx.symtab->getSymbols()) | |
| if (auto *s = dyn_cast<SharedSymbol>(sym)) | |
| if (s->isUsedInRegularObj && !s->isWeak()) | |
| cast<SharedFile>(s->file)->isNeeded = true; | |
| return; | |
| } | |
| parallelForEach(ctx.inputSections, | |
| [](InputSectionBase *sec) { sec->markDead(); }); | |
| // Follow the graph to mark all live sections. | |
| for (unsigned i = 1, e = ctx.partitions.size(); i <= e; ++i) | |
| if (ctx.arg.whyLive.empty()) | |
| MarkLive<ELFT, false>(ctx, i).run(); | |
| else | |
| MarkLive<ELFT, true>(ctx, i).run(); | |
| // If we have multiple partitions, some sections need to live in the main | |
| // partition even if they were allocated to a loadable partition. Move them | |
| // there now. | |
| if (ctx.partitions.size() != 1) | |
| MarkLive<ELFT, false>(ctx, 1).moveToMain(); | |
| // Determine which DSOs are needed. A DSO is needed if a non-weak SharedSymbol | |
| // is used from a live section. | |
| parallelForEach(ctx.symtab->getSymbols(), [](Symbol *sym) { | |
| if (auto *ss = dyn_cast<SharedSymbol>(sym)) | |
| if (ss->hasFlag(USED) && !ss->isWeak()) | |
| cast<SharedFile>(ss->file)->isNeeded = true; | |
| }); | |
| // Report garbage-collected sections. | |
| if (ctx.arg.printGcSections.empty()) | |
| return; | |
| std::error_code ec; | |
| raw_fd_ostream os = ctx.openAuxiliaryFile(ctx.arg.printGcSections, ec); | |
| if (ec) { | |
| Err(ctx) << "cannot open --print-gc-sections= file " | |
| << ctx.arg.printGcSections << ": " << ec.message(); | |
| return; | |
| } | |
| for (InputSectionBase *sec : ctx.inputSections) | |
| if (!sec->isLive()) | |
| os << "removing unused section " << toStr(ctx, sec) << '\n'; | |
| } | |
| template void elf::markLive<ELF32LE>(Ctx &); | |
| template void elf::markLive<ELF32BE>(Ctx &); | |
| template void elf::markLive<ELF64LE>(Ctx &); | |
| template void elf::markLive<ELF64BE>(Ctx &); |