1//===- Symbols.cpp --------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Symbols.h"
10#include "Config.h"
11#include "InputChunks.h"
12#include "InputElement.h"
13#include "InputFiles.h"
14#include "OutputSections.h"
15#include "OutputSegment.h"
16#include "SymbolTable.h"
17#include "lld/Common/ErrorHandler.h"
18#include "lld/Common/Memory.h"
19#include "llvm/Demangle/Demangle.h"
20
21#define DEBUG_TYPE "lld"
22
23using namespace llvm;
24using namespace llvm::object;
25using namespace llvm::wasm;
26using namespace lld::wasm;
27
28namespace lld {
29std::string toString(const wasm::Symbol &sym) {
30 return maybeDemangleSymbol(name: sym.getName());
31}
32
33std::string maybeDemangleSymbol(StringRef name) {
34 // WebAssembly requires caller and callee signatures to match, so we mangle
35 // `main` in the case where we need to pass it arguments.
36 if (name == "__main_argc_argv")
37 return "main";
38 if (wasm::ctx.arg.demangle)
39 return demangle(MangledName: name);
40 return name.str();
41}
42
43std::string toString(wasm::Symbol::Kind kind) {
44 switch (kind) {
45 case wasm::Symbol::DefinedFunctionKind:
46 return "DefinedFunction";
47 case wasm::Symbol::DefinedDataKind:
48 return "DefinedData";
49 case wasm::Symbol::DefinedGlobalKind:
50 return "DefinedGlobal";
51 case wasm::Symbol::DefinedTableKind:
52 return "DefinedTable";
53 case wasm::Symbol::DefinedTagKind:
54 return "DefinedTag";
55 case wasm::Symbol::UndefinedFunctionKind:
56 return "UndefinedFunction";
57 case wasm::Symbol::UndefinedDataKind:
58 return "UndefinedData";
59 case wasm::Symbol::UndefinedGlobalKind:
60 return "UndefinedGlobal";
61 case wasm::Symbol::UndefinedTableKind:
62 return "UndefinedTable";
63 case wasm::Symbol::UndefinedTagKind:
64 return "UndefinedTag";
65 case wasm::Symbol::CommonKind:
66 return "CommonKind";
67 case wasm::Symbol::LazyKind:
68 return "LazyKind";
69 case wasm::Symbol::SectionKind:
70 return "SectionKind";
71 case wasm::Symbol::OutputSectionKind:
72 return "OutputSectionKind";
73 case wasm::Symbol::SharedFunctionKind:
74 return "SharedFunctionKind";
75 case wasm::Symbol::SharedDataKind:
76 return "SharedDataKind";
77 case wasm::Symbol::SharedTagKind:
78 return "SharedTagKind";
79 }
80 llvm_unreachable("invalid symbol kind");
81}
82
83namespace wasm {
84
85WasmSymbolType Symbol::getWasmType() const {
86 if (isa<FunctionSymbol>(Val: this))
87 return WASM_SYMBOL_TYPE_FUNCTION;
88 if (isa<DataSymbol>(Val: this))
89 return WASM_SYMBOL_TYPE_DATA;
90 if (isa<GlobalSymbol>(Val: this))
91 return WASM_SYMBOL_TYPE_GLOBAL;
92 if (isa<TagSymbol>(Val: this))
93 return WASM_SYMBOL_TYPE_TAG;
94 if (isa<TableSymbol>(Val: this))
95 return WASM_SYMBOL_TYPE_TABLE;
96 if (isa<SectionSymbol>(Val: this) || isa<OutputSectionSymbol>(Val: this))
97 return WASM_SYMBOL_TYPE_SECTION;
98 llvm_unreachable("invalid symbol kind");
99}
100
101const WasmSignature *Symbol::getSignature() const {
102 if (auto *f = dyn_cast<FunctionSymbol>(Val: this))
103 return f->signature;
104 if (auto *t = dyn_cast<TagSymbol>(Val: this))
105 return t->signature;
106 if (auto *l = dyn_cast<LazySymbol>(Val: this))
107 return l->signature;
108 return nullptr;
109}
110
111InputChunk *Symbol::getChunk() const {
112 if (auto *f = dyn_cast<DefinedFunction>(Val: this))
113 return f->function;
114 if (auto *f = dyn_cast<UndefinedFunction>(Val: this))
115 if (f->stubFunction)
116 return f->stubFunction->function;
117 if (auto *d = dyn_cast<DefinedData>(Val: this))
118 return d->segment;
119 return nullptr;
120}
121
122bool Symbol::isDiscarded() const {
123 if (InputChunk *c = getChunk())
124 return c->discarded;
125 return false;
126}
127
128bool Symbol::isLive() const {
129 if (auto *g = dyn_cast<DefinedGlobal>(Val: this))
130 return g->global->live;
131 if (auto *t = dyn_cast<DefinedTag>(Val: this))
132 return t->tag->live;
133 if (auto *t = dyn_cast<DefinedTable>(Val: this))
134 return t->table->live;
135 if (InputChunk *c = getChunk())
136 return c->live;
137 return referenced;
138}
139
140void Symbol::markLive() {
141 assert(!isDiscarded());
142 referenced = true;
143 if (file != nullptr && isDefined())
144 file->markLive();
145 if (auto *g = dyn_cast<DefinedGlobal>(Val: this))
146 g->global->live = true;
147 if (auto *t = dyn_cast<DefinedTag>(Val: this))
148 t->tag->live = true;
149 if (auto *t = dyn_cast<DefinedTable>(Val: this))
150 t->table->live = true;
151 if (InputChunk *c = getChunk()) {
152 // Usually, a whole chunk is marked as live or dead, but in mergeable
153 // (splittable) sections, each piece of data has independent liveness bit.
154 // So we explicitly tell it which offset is in use.
155 if (auto *d = dyn_cast<DefinedData>(Val: this)) {
156 if (auto *ms = dyn_cast<MergeInputChunk>(Val: c)) {
157 ms->getSectionPiece(offset: d->value)->live = true;
158 }
159 }
160 c->live = true;
161 }
162}
163
164uint32_t Symbol::getOutputSymbolIndex() const {
165 assert(outputSymbolIndex != INVALID_INDEX || !isLive());
166 return outputSymbolIndex;
167}
168
169void Symbol::setOutputSymbolIndex(uint32_t index) {
170 LLVM_DEBUG(dbgs() << "setOutputSymbolIndex " << name << " -> " << index
171 << "\n");
172 assert(outputSymbolIndex == INVALID_INDEX);
173 outputSymbolIndex = index;
174}
175
176void Symbol::setGOTIndex(uint32_t index) {
177 LLVM_DEBUG(dbgs() << "setGOTIndex " << name << " -> " << index << "\n");
178 assert(gotIndex == INVALID_INDEX);
179 gotIndex = index;
180}
181
182bool Symbol::isWeak() const {
183 return (flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK;
184}
185
186bool Symbol::isLocal() const {
187 return (flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_LOCAL;
188}
189
190bool Symbol::isHidden() const {
191 return (flags & WASM_SYMBOL_VISIBILITY_MASK) == WASM_SYMBOL_VISIBILITY_HIDDEN;
192}
193
194bool Symbol::isTLS() const { return flags & WASM_SYMBOL_TLS; }
195
196void Symbol::setHidden(bool isHidden) {
197 LLVM_DEBUG(dbgs() << "setHidden: " << name << " -> " << isHidden << "\n");
198 flags &= ~WASM_SYMBOL_VISIBILITY_MASK;
199 if (isHidden)
200 flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
201 else
202 flags |= WASM_SYMBOL_VISIBILITY_DEFAULT;
203}
204
205bool Symbol::isImported() const {
206 return isShared() ||
207 (isUndefined() && (importName.has_value() || forceImport));
208}
209
210bool Symbol::isExported() const {
211 if (!isDefined() || isShared() || isLocal())
212 return false;
213
214 // Shared libraries must export all weakly defined symbols
215 // in case they contain the version that will be chosen by
216 // the dynamic linker.
217 if (ctx.arg.shared && isLive() && isWeak() && !isHidden())
218 return true;
219
220 if (ctx.arg.exportAll || (ctx.arg.exportDynamic && !isHidden()))
221 return true;
222
223 return isExportedExplicit();
224}
225
226bool Symbol::isExportedExplicit() const {
227 return forceExport || flags & WASM_SYMBOL_EXPORTED;
228}
229
230bool Symbol::isNoStrip() const { return flags & WASM_SYMBOL_NO_STRIP; }
231
232uint32_t FunctionSymbol::getFunctionIndex() const {
233 if (const auto *u = dyn_cast<UndefinedFunction>(Val: this))
234 if (u->stubFunction)
235 return u->stubFunction->getFunctionIndex();
236 if (functionIndex != INVALID_INDEX)
237 return functionIndex;
238 auto *f = cast<DefinedFunction>(Val: this);
239 return f->function->getFunctionIndex();
240}
241
242void FunctionSymbol::setFunctionIndex(uint32_t index) {
243 LLVM_DEBUG(dbgs() << "setFunctionIndex " << name << " -> " << index << "\n");
244 assert(functionIndex == INVALID_INDEX);
245 functionIndex = index;
246}
247
248bool FunctionSymbol::hasFunctionIndex() const {
249 if (auto *f = dyn_cast<DefinedFunction>(Val: this))
250 return f->function->hasFunctionIndex();
251 return functionIndex != INVALID_INDEX;
252}
253
254uint32_t FunctionSymbol::getTableIndex() const {
255 if (auto *f = dyn_cast<DefinedFunction>(Val: this))
256 return f->function->getTableIndex();
257 assert(tableIndex != INVALID_INDEX);
258 return tableIndex;
259}
260
261bool FunctionSymbol::hasTableIndex() const {
262 if (auto *f = dyn_cast<DefinedFunction>(Val: this))
263 return f->function->hasTableIndex();
264 return tableIndex != INVALID_INDEX;
265}
266
267void FunctionSymbol::setTableIndex(uint32_t index) {
268 // For imports, we set the table index here on the Symbol; for defined
269 // functions we set the index on the InputFunction so that we don't export
270 // the same thing twice (keeps the table size down).
271 if (auto *f = dyn_cast<DefinedFunction>(Val: this)) {
272 f->function->setTableIndex(index);
273 return;
274 }
275 LLVM_DEBUG(dbgs() << "setTableIndex " << name << " -> " << index << "\n");
276 assert(tableIndex == INVALID_INDEX);
277 tableIndex = index;
278}
279
280DefinedFunction::DefinedFunction(StringRef name, uint32_t flags, InputFile *f,
281 InputFunction *function)
282 : FunctionSymbol(name, DefinedFunctionKind, flags, f,
283 function ? &function->signature : nullptr),
284 function(function) {}
285
286uint32_t DefinedFunction::getExportedFunctionIndex() const {
287 return function->getFunctionIndex();
288}
289
290uint64_t DefinedData::getVA(bool absolute) const {
291 LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
292 // TLS symbols (by default) are relative to the start of the TLS output
293 // segment (__tls_base).
294 if (isTLS() && !absolute)
295 return getOutputSegmentOffset();
296 if (segment)
297 return segment->getVA(offset: value);
298 return value;
299}
300
301void DefinedData::setVA(uint64_t value_) {
302 LLVM_DEBUG(dbgs() << "setVA " << name << " -> " << value_ << "\n");
303 assert(!segment);
304 value = value_;
305}
306
307uint64_t DefinedData::getOutputSegmentOffset() const {
308 LLVM_DEBUG(dbgs() << "getOutputSegmentOffset: " << getName() << "\n");
309 return segment->getChunkOffset(offset: value);
310}
311
312uint64_t DefinedData::getOutputSegmentIndex() const {
313 LLVM_DEBUG(dbgs() << "getOutputSegmentIndex: " << getName() << "\n");
314 return segment->outputSeg->index;
315}
316
317uint32_t GlobalSymbol::getGlobalIndex() const {
318 if (auto *f = dyn_cast<DefinedGlobal>(Val: this))
319 return f->global->getAssignedIndex();
320 assert(globalIndex != INVALID_INDEX);
321 return globalIndex;
322}
323
324void GlobalSymbol::setGlobalIndex(uint32_t index) {
325 LLVM_DEBUG(dbgs() << "setGlobalIndex " << name << " -> " << index << "\n");
326 assert(globalIndex == INVALID_INDEX);
327 globalIndex = index;
328}
329
330bool GlobalSymbol::hasGlobalIndex() const {
331 if (auto *f = dyn_cast<DefinedGlobal>(Val: this))
332 return f->global->hasAssignedIndex();
333 return globalIndex != INVALID_INDEX;
334}
335
336DefinedGlobal::DefinedGlobal(StringRef name, uint32_t flags, InputFile *file,
337 InputGlobal *global)
338 : GlobalSymbol(name, DefinedGlobalKind, flags, file,
339 global ? &global->getType() : nullptr),
340 global(global) {}
341
342uint32_t TagSymbol::getTagIndex() const {
343 if (auto *f = dyn_cast<DefinedTag>(Val: this))
344 return f->tag->getAssignedIndex();
345 assert(tagIndex != INVALID_INDEX);
346 return tagIndex;
347}
348
349void TagSymbol::setTagIndex(uint32_t index) {
350 LLVM_DEBUG(dbgs() << "setTagIndex " << name << " -> " << index << "\n");
351 assert(tagIndex == INVALID_INDEX);
352 tagIndex = index;
353}
354
355bool TagSymbol::hasTagIndex() const {
356 if (auto *f = dyn_cast<DefinedTag>(Val: this))
357 return f->tag->hasAssignedIndex();
358 return tagIndex != INVALID_INDEX;
359}
360
361DefinedTag::DefinedTag(StringRef name, uint32_t flags, InputFile *file,
362 InputTag *tag)
363 : TagSymbol(name, DefinedTagKind, flags, file,
364 tag ? &tag->signature : nullptr),
365 tag(tag) {}
366
367void TableSymbol::setLimits(const WasmLimits &limits) {
368 if (auto *t = dyn_cast<DefinedTable>(Val: this))
369 t->table->setLimits(limits);
370 auto *newType = make<WasmTableType>(args: *tableType);
371 newType->Limits = limits;
372 tableType = newType;
373}
374
375uint32_t TableSymbol::getTableNumber() const {
376 if (const auto *t = dyn_cast<DefinedTable>(Val: this))
377 return t->table->getAssignedIndex();
378 assert(tableNumber != INVALID_INDEX);
379 return tableNumber;
380}
381
382void TableSymbol::setTableNumber(uint32_t number) {
383 if (const auto *t = dyn_cast<DefinedTable>(Val: this))
384 return t->table->assignIndex(index: number);
385 LLVM_DEBUG(dbgs() << "setTableNumber " << name << " -> " << number << "\n");
386 assert(tableNumber == INVALID_INDEX);
387 tableNumber = number;
388}
389
390bool TableSymbol::hasTableNumber() const {
391 if (const auto *t = dyn_cast<DefinedTable>(Val: this))
392 return t->table->hasAssignedIndex();
393 return tableNumber != INVALID_INDEX;
394}
395
396DefinedTable::DefinedTable(StringRef name, uint32_t flags, InputFile *file,
397 InputTable *table)
398 : TableSymbol(name, DefinedTableKind, flags, file,
399 table ? &table->getType() : nullptr),
400 table(table) {}
401
402const OutputSectionSymbol *SectionSymbol::getOutputSectionSymbol() const {
403 assert(section->outputSec && section->outputSec->sectionSym);
404 return section->outputSec->sectionSym;
405}
406
407void LazySymbol::extract() {
408 if (file->lazy) {
409 file->lazy = false;
410 symtab->addFile(file, symName: name);
411 }
412}
413
414void LazySymbol::setWeak() {
415 flags |= (flags & ~WASM_SYMBOL_BINDING_MASK) | WASM_SYMBOL_BINDING_WEAK;
416}
417
418void printTraceSymbolUndefined(StringRef name, const InputFile *file) {
419 message(msg: toString(file) + ": reference to " + name);
420}
421
422// Print out a log message for --trace-symbol.
423void printTraceSymbol(Symbol *sym) {
424 // Undefined symbols are traced via printTraceSymbolUndefined
425 if (sym->isUndefined())
426 return;
427
428 std::string s;
429 if (sym->isLazy())
430 s = ": lazy definition of ";
431 else
432 s = ": definition of ";
433
434 message(msg: toString(file: sym->getFile()) + s + sym->getName());
435}
436
437const char *defaultModule = "env";
438const char *functionTableName = "__indirect_function_table";
439const char *memoryName = "memory";
440
441} // namespace wasm
442} // namespace lld
443