1
1
mirror of https://github.com/rui314/mold.git synced 2024-12-28 19:04:27 +03:00
mold/gc_sections.cc

192 lines
6.0 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>
2021-03-29 14:29:57 +03:00
template <typename E>
using Feeder = tbb::parallel_do_feeder<InputSection<E> *>;
2021-03-13 14:36:23 +03:00
2021-03-29 14:29:57 +03:00
template <typename E>
static bool is_init_fini(const InputSection<E> &isec) {
2021-03-11 13:59:01 +03:00
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,
Feeder<E> &feeder, i64 depth) {
2021-01-24 10:12:34 +03:00
assert(isec->is_visited);
2021-01-24 16:14:51 +03:00
2021-01-25 13:34:22 +03:00
// A relocation can refer either a section fragment (i.e. a piece of
// string in a mergeable string section) or a symbol. Mark all
// section fragments as alive.
2021-04-12 14:09:37 +03:00
if (isec->rel_fragments)
for (i64 i = 0; isec->rel_fragments[i].idx >= 0; i++)
isec->rel_fragments[i].frag->is_alive = true;
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-03-29 14:29:57 +03:00
for (FdeRecord<E> &fde : isec->fdes)
for (EhReloc<E> &rel : std::span<EhReloc<E>>(fde.rels).subspan(1))
if (InputSection<E> *isec = rel.sym.input_section)
2021-01-25 12:48:48 +03:00
if (mark_section(isec))
feeder.add(isec);
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.
2021-04-01 17:57:18 +03:00
if (SectionFragment<E> *frag = sym.get_frag()) {
frag->is_alive = true;
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-03-29 14:29:57 +03:00
tbb::concurrent_vector<InputSection<E> *> roots;
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))
roots.push_back(isec);
};
2021-03-29 14:29:57 +03:00
auto enqueue_symbol = [&](Symbol<E> *sym) {
2021-03-17 15:52:58 +03:00
if (sym) {
2021-04-01 17:57:18 +03:00
if (SectionFragment<E> *frag = sym->get_frag())
frag->is_alive = true;
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.
2021-03-11 13:59:01 +03:00
if (!(isec->shdr.sh_flags & SHF_ALLOC))
2021-01-24 14:50:33 +03:00
isec->is_visited = true;
2021-03-11 13:59:01 +03:00
if (is_init_fini(*isec) || 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-03-29 17:50:19 +03:00
enqueue_symbol(Symbol<E>::intern(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-03-29 17:50:19 +03:00
enqueue_symbol(Symbol<E>::intern(ctx, name));
2021-03-08 18:23:06 +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)
for (EhReloc<E> &rel : cie.rels)
2021-03-17 15:52:58 +03:00
enqueue_section(rel.sym.input_section);
2021-01-24 10:12:34 +03:00
});
2021-03-08 13:31:04 +03:00
2021-01-30 02:02:26 +03:00
return roots;
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,
tbb::concurrent_vector<InputSection<E> *> &roots) {
2021-04-07 09:38:31 +03:00
Timer t(ctx, "mark");
2021-03-13 14:36:23 +03:00
2021-03-29 14:29:57 +03:00
tbb::parallel_do(roots, [&](InputSection<E> *isec, Feeder<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-01-24 10:12:34 +03:00
for (i64 i = 0; i < file->sections.size(); i++) {
2021-04-06 08:36:31 +03:00
std::unique_ptr<InputSection<E>> &isec = file->sections[i];
2021-01-24 10:12:34 +03:00
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) {
2021-04-07 09:38:31 +03:00
Timer t(ctx, "mark_nonalloc_fragments");
2021-03-29 14:29:57 +03:00
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
for (SectionFragment<E> *frag : file->fragments)
if (!(frag->output_section.shdr.sh_flags & SHF_ALLOC))
frag->is_alive = true;
});
}
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
2021-03-29 10:28:39 +03:00
mark_nonalloc_fragments(ctx);
2021-03-29 14:29:57 +03:00
tbb::concurrent_vector<InputSection<E> *> roots = collect_root_set(ctx);
2021-04-01 19:35:10 +03:00
mark(ctx, roots);
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
2021-03-30 08:57:38 +03:00
template void gc_sections(Context<X86_64> &ctx);
2021-03-30 13:27:00 +03:00
template void gc_sections(Context<I386> &ctx);