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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lld/wasm/Symbols.cpp