forked from rui314/mold
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gc_sections.cc
155 lines (128 loc) · 4.79 KB
/
gc_sections.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// 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.
#include "mold.h"
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>
static bool is_init_fini(const InputSection &isec) {
return isec.shdr.sh_type == SHT_INIT_ARRAY ||
isec.shdr.sh_type == SHT_FINI_ARRAY ||
isec.shdr.sh_type == SHT_PREINIT_ARRAY ||
isec.name.starts_with(".ctors") ||
isec.name.starts_with(".dtors") ||
isec.name.starts_with(".init") ||
isec.name.starts_with(".fini");
}
static bool mark_section(InputSection *isec) {
return isec && isec->is_alive && !isec->is_visited.exchange(true);
}
static void visit(InputSection *isec,
tbb::parallel_do_feeder<InputSection *> &feeder,
i64 depth) {
assert(isec->is_visited);
// 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.
for (SectionFragmentRef &ref : isec->rel_fragments)
ref.frag->is_alive = true;
// 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.
for (FdeRecord &fde : isec->fdes)
for (EhReloc &rel : std::span(fde.rels).subspan(1))
if (InputSection *isec = rel.sym.input_section)
if (mark_section(isec))
feeder.add(isec);
for (ElfRela &rel : isec->rels) {
Symbol &sym = *isec->file.symbols[rel.r_sym];
// Symbol can refer either a section fragment or an input section.
// Mark a fragment as alive.
if (sym.frag) {
sym.frag->is_alive = true;
continue;
}
if (!mark_section(sym.input_section))
continue;
// Mark a section alive. For better performacne, we don't call
// `feeder.add` too often.
if (depth < 3)
visit(sym.input_section, feeder, depth + 1);
else
feeder.add(sym.input_section);
}
}
static tbb::concurrent_vector<InputSection *> collect_root_set() {
Timer t("collect_root_set");
tbb::concurrent_vector<InputSection *> roots;
auto enqueue = [&](InputSection *isec) {
if (mark_section(isec))
roots.push_back(isec);
};
// Add sections that are not subject to garbage collection.
tbb::parallel_for_each(out::objs, [&](ObjectFile *file) {
for (InputSection *isec : file->sections) {
if (!isec)
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))
isec->is_visited = true;
if (is_init_fini(*isec) || isec->shdr.sh_type == SHT_NOTE)
enqueue(isec);
}
});
// Add sections containing exported symbols
tbb::parallel_for_each(out::objs, [&](ObjectFile *file) {
for (Symbol *sym : file->symbols)
if (sym->file == file)
if (InputSection *sec = sym->input_section)
if (sec->shdr.sh_flags & SHF_ALLOC)
enqueue(sec);
});
// Add sections referenced by root symbols.
enqueue(Symbol::intern(config.entry)->input_section);
for (std::string_view name : config.undefined)
if (InputSection *sec = Symbol::intern(config.entry)->input_section)
enqueue(sec);
// .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.
tbb::parallel_for_each(out::objs, [&](ObjectFile *file) {
for (CieRecord &cie : file->cies)
for (EhReloc &rel : cie.rels)
enqueue(rel.sym.input_section);
});
return roots;
}
// Mark all reachable sections
static void mark(tbb::concurrent_vector<InputSection *> roots) {
Timer t("mark");
tbb::parallel_do(roots, [&](InputSection *isec,
tbb::parallel_do_feeder<InputSection *> &feeder) {
visit(isec, feeder, 0);
});
}
// Remove unreachable sections
static void sweep() {
Timer t("sweep");
static Counter counter("garbage_sections");
tbb::parallel_for_each(out::objs, [&](ObjectFile *file) {
for (i64 i = 0; i < file->sections.size(); i++) {
InputSection *isec = file->sections[i];
if (isec && isec->is_alive && !isec->is_visited) {
if (config.print_gc_sections)
SyncOut() << "removing unused section " << *isec;
isec->kill();
counter++;
}
}
});
}
void gc_sections() {
Timer t("gc");
tbb::concurrent_vector<InputSection *> roots = collect_root_set();
mark(roots);
sweep();
}