1
1
mirror of https://github.com/rui314/mold.git synced 2024-10-05 17:17:40 +03:00
mold/elf/gc-sections.cc

203 lines
6.4 KiB
C++
Raw Normal View History

2021-01-25 13:34:22 +03:00
// This file implements a mark-sweep garbage collector for -gc-sections.
// In this algorithm, vertices are sections and edges are relocations.
// Any section that is reachable from a root section is considered alive.
2021-01-24 10:12:34 +03:00
#include "mold.h"
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
2021-03-29 14:29:57 +03:00
template <typename E>
static bool is_init_fini(const InputSection<E> &isec) {
return isec.shdr().sh_type == SHT_INIT_ARRAY ||
isec.shdr().sh_type == SHT_FINI_ARRAY ||
isec.shdr().sh_type == SHT_PREINIT_ARRAY ||
2021-04-12 14:18:43 +03:00
isec.name().starts_with(".ctors") ||
isec.name().starts_with(".dtors") ||
isec.name().starts_with(".init") ||
isec.name().starts_with(".fini");
2021-01-24 10:12:34 +03:00
}
2021-03-29 14:29:57 +03:00
template <typename E>
static bool mark_section(InputSection<E> *isec) {
2021-01-24 10:12:34 +03:00
return isec && isec->is_alive && !isec->is_visited.exchange(true);
}
2021-03-29 14:29:57 +03:00
template <typename E>
2021-04-01 19:35:10 +03:00
static void visit(Context<E> &ctx, InputSection<E> *isec,
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
2021-09-08 13:51:02 +03:00
assert(isec->is_visited);
2021-01-24 16:14:51 +03:00
// A relocation can refer either a section fragment (i.e. a piece of
2021-01-25 13:34:22 +03:00
// string in a mergeable string section) or a symbol. Mark all
// section fragments as alive.
if (SectionFragmentRef<E> *refs = isec->rel_fragments.get())
for (i64 i = 0; refs[i].idx >= 0; i++)
refs[i].frag->is_alive.store(true, std::memory_order_relaxed);
2021-01-24 16:14:51 +03:00
2021-01-25 13:34:22 +03:00
// If this is a text section, .eh_frame may contain records
// describing how to handle exceptions for that function.
// We want to keep associated .eh_frame records.
2021-04-15 15:48:30 +03:00
for (FdeRecord<E> &fde : isec->get_fdes())
2021-04-16 16:39:53 +03:00
for (ElfRel<E> &rel : fde.get_rels().subspan(1))
2021-04-15 15:48:30 +03:00
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
if (mark_section(sym->input_section))
feeder.add(sym->input_section);
2021-01-25 07:16:26 +03:00
2021-04-01 19:35:10 +03:00
for (ElfRel<E> &rel : isec->get_rels(ctx)) {
2021-03-29 14:29:57 +03:00
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
2021-01-25 13:34:22 +03:00
// Symbol can refer either a section fragment or an input section.
// Mark a fragment as alive.
if (SectionFragment<E> *frag = sym.get_frag()) {
frag->is_alive.store(true, std::memory_order_relaxed);
2021-01-25 12:48:48 +03:00
continue;
}
if (!mark_section(sym.input_section))
continue;
2021-01-25 13:34:22 +03:00
// Mark a section alive. For better performacne, we don't call
// `feeder.add` too often.
2021-01-25 12:48:48 +03:00
if (depth < 3)
2021-04-01 19:35:10 +03:00
visit(ctx, sym.input_section, feeder, depth + 1);
2021-01-25 07:47:48 +03:00
else
2021-01-25 12:48:48 +03:00
feeder.add(sym.input_section);
2021-01-25 07:47:48 +03:00
}
2021-01-24 10:12:34 +03:00
}
2021-03-29 14:29:57 +03:00
template <typename E>
static tbb::concurrent_vector<InputSection<E> *>
collect_root_set(Context<E> &ctx) {
2021-04-07 09:38:31 +03:00
Timer t(ctx, "collect_root_set");
2021-11-12 13:29:12 +03:00
tbb::concurrent_vector<InputSection<E> *> rootset;
2021-01-24 10:12:34 +03:00
2021-03-29 14:29:57 +03:00
auto enqueue_section = [&](InputSection<E> *isec) {
2021-01-24 10:12:34 +03:00
if (mark_section(isec))
2021-11-12 13:29:12 +03:00
rootset.push_back(isec);
2021-01-24 10:12:34 +03:00
};
2021-03-29 14:29:57 +03:00
auto enqueue_symbol = [&](Symbol<E> *sym) {
2021-03-17 15:52:58 +03:00
if (sym) {
if (SectionFragment<E> *frag = sym->get_frag())
frag->is_alive.store(true, std::memory_order_relaxed);
2021-03-17 15:52:58 +03:00
else
enqueue_section(sym->input_section);
}
};
2021-01-25 12:34:39 +03:00
// Add sections that are not subject to garbage collection.
2021-03-29 14:29:57 +03:00
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
2021-04-06 08:36:31 +03:00
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (!isec || !isec->is_alive)
2021-01-24 14:50:33 +03:00
continue;
// -gc-sections discards only SHF_ALLOC sections. If you want to
// reduce the amount of non-memory-mapped segments, you should
// use `strip` command, compile without debug info or use
// -strip-all linker option.
if (!(isec->shdr().sh_flags & SHF_ALLOC))
2021-01-24 14:50:33 +03:00
isec->is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr().sh_type == SHT_NOTE)
2021-04-06 08:36:31 +03:00
enqueue_section(isec.get());
2021-01-24 14:50:33 +03:00
}
2021-01-24 10:12:34 +03:00
});
2021-03-08 13:31:04 +03:00
// Add sections containing exported symbols
2021-03-29 14:29:57 +03:00
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols)
2021-03-17 15:52:58 +03:00
if (sym->file == file && sym->is_exported)
enqueue_symbol(sym);
2021-03-08 13:31:04 +03:00
});
2021-01-24 10:12:34 +03:00
// Add sections referenced by root symbols.
2021-12-31 15:27:00 +03:00
enqueue_symbol(get_symbol(ctx, ctx.arg.entry));
2021-01-24 10:12:34 +03:00
2021-03-29 07:20:51 +03:00
for (std::string_view name : ctx.arg.undefined)
2021-12-31 15:27:00 +03:00
enqueue_symbol(get_symbol(ctx, name));
2021-03-08 18:23:06 +03:00
2021-09-12 12:34:41 +03:00
for (std::string_view name : ctx.arg.require_defined)
2021-12-31 15:27:00 +03:00
enqueue_symbol(get_symbol(ctx, name));
2021-09-12 12:34:41 +03:00
2021-01-24 10:12:34 +03:00
// .eh_frame consists of variable-length records called CIE and FDE
// records, and they are a unit of inclusion or exclusion.
// We just keep all CIEs and everything that are referenced by them.
2021-03-29 14:29:57 +03:00
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (CieRecord<E> &cie : file->cies)
2021-04-15 15:48:30 +03:00
for (ElfRel<E> &rel : cie.get_rels())
enqueue_symbol(file->symbols[rel.r_sym]);
2021-01-24 10:12:34 +03:00
});
2021-03-08 13:31:04 +03:00
2021-11-12 13:29:12 +03:00
return rootset;
2021-01-30 02:01:03 +03:00
}
2021-01-24 10:12:34 +03:00
2021-01-30 02:01:03 +03:00
// Mark all reachable sections
2021-03-29 14:29:57 +03:00
template <typename E>
2021-04-01 19:35:10 +03:00
static void mark(Context<E> &ctx,
2021-11-12 13:29:12 +03:00
tbb::concurrent_vector<InputSection<E> *> &rootset) {
2021-04-07 09:38:31 +03:00
Timer t(ctx, "mark");
2021-03-13 14:36:23 +03:00
2021-11-12 13:29:12 +03:00
tbb::parallel_for_each(rootset, [&](InputSection<E> *isec,
tbb::feeder<InputSection<E> *> &feeder) {
2021-04-01 19:35:10 +03:00
visit(ctx, isec, feeder, 0);
2021-01-30 02:01:03 +03:00
});
}
2021-01-24 10:12:34 +03:00
2021-01-30 02:01:03 +03:00
// Remove unreachable sections
2021-03-29 14:29:57 +03:00
template <typename E>
static void sweep(Context<E> &ctx) {
2021-04-07 09:38:31 +03:00
Timer t(ctx, "sweep");
2021-01-26 03:04:55 +03:00
static Counter counter("garbage_sections");
2021-03-29 14:29:57 +03:00
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
2021-09-26 06:43:51 +03:00
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
2021-01-25 08:31:40 +03:00
if (isec && isec->is_alive && !isec->is_visited) {
2021-03-29 07:20:51 +03:00
if (ctx.arg.print_gc_sections)
2021-03-29 10:48:23 +03:00
SyncOut(ctx) << "removing unused section " << *isec;
2021-01-29 05:59:02 +03:00
isec->kill();
2021-01-29 15:44:46 +03:00
counter++;
2021-01-24 10:12:34 +03:00
}
}
});
}
2021-01-30 02:01:03 +03:00
// Non-alloc section fragments are not subject of garbage collection.
// This function marks such fragments.
2021-03-29 14:29:57 +03:00
template <typename E>
static void mark_nonalloc_fragments(Context<E> &ctx) {
Timer t(ctx, "mark_nonalloc_fragments");
2021-03-29 14:29:57 +03:00
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
2022-01-13 14:38:40 +03:00
for (std::unique_ptr<MergeableSection<E>> &m : file->mergeable_sections)
if (m)
for (SectionFragment<E> *frag : m->fragments)
if (!(frag->output_section.shdr.sh_flags & SHF_ALLOC))
frag->is_alive.store(true, std::memory_order_relaxed);
});
}
2021-03-29 14:29:57 +03:00
template <typename E>
void gc_sections(Context<E> &ctx) {
2021-04-07 09:38:31 +03:00
Timer t(ctx, "gc");
2021-01-30 02:01:03 +03:00
mark_nonalloc_fragments(ctx);
2021-11-12 13:29:12 +03:00
tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
mark(ctx, rootset);
2021-03-29 10:28:39 +03:00
sweep(ctx);
2021-01-30 02:01:03 +03:00
}
2021-03-29 14:29:57 +03:00
#define INSTANTIATE(E) \
template void gc_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
2021-12-11 15:33:39 +03:00
INSTANTIATE(ARM64);
INSTANTIATE(RISCV64);
} // namespace mold::elf