1//===- Symbols.h ------------------------------------------------*- C++ -*-===//
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#ifndef LLD_WASM_SYMBOLS_H
10#define LLD_WASM_SYMBOLS_H
11
12#include "Config.h"
13#include "lld/Common/LLVM.h"
14#include "llvm/Object/Archive.h"
15#include "llvm/Object/Wasm.h"
16#include <optional>
17
18namespace lld {
19namespace wasm {
20
21// Shared string constants
22
23// The default module name to use for symbol imports.
24extern const char *defaultModule;
25
26// The name under which to import or export the wasm table.
27extern const char *functionTableName;
28
29// The name under which to import or export the wasm memory.
30extern const char *memoryName;
31
32using llvm::wasm::WasmSymbolType;
33
34class InputFile;
35class InputChunk;
36class InputSegment;
37class InputFunction;
38class InputGlobal;
39class InputTag;
40class InputSection;
41class InputTable;
42class OutputSection;
43
44#define INVALID_INDEX UINT32_MAX
45
46// The base class for real symbol classes.
47class Symbol {
48public:
49 enum Kind : uint8_t {
50 DefinedFunctionKind,
51 DefinedDataKind,
52 DefinedGlobalKind,
53 DefinedTagKind,
54 DefinedTableKind,
55 SectionKind,
56 OutputSectionKind,
57 UndefinedFunctionKind,
58 UndefinedDataKind,
59 UndefinedGlobalKind,
60 UndefinedTableKind,
61 UndefinedTagKind,
62 CommonKind,
63 LazyKind,
64 SharedFunctionKind,
65 SharedDataKind,
66 SharedTagKind,
67 };
68
69 Kind kind() const { return symbolKind; }
70
71 bool isDefined() const { return !isLazy() && !isUndefined(); }
72
73 bool isUndefined() const {
74 return symbolKind == UndefinedFunctionKind ||
75 symbolKind == UndefinedDataKind ||
76 symbolKind == UndefinedGlobalKind ||
77 symbolKind == UndefinedTableKind || symbolKind == UndefinedTagKind;
78 }
79
80 bool isLazy() const { return symbolKind == LazyKind; }
81 bool isCommon() const { return symbolKind == CommonKind; }
82 bool isShared() const {
83 return symbolKind == SharedFunctionKind || symbolKind == SharedDataKind ||
84 symbolKind == SharedTagKind;
85 }
86
87 bool isLocal() const;
88 bool isWeak() const;
89 bool isHidden() const;
90 bool isTLS() const;
91
92 // Returns true if this symbol exists in a discarded (due to COMDAT) section
93 bool isDiscarded() const;
94
95 // True if this is an undefined weak symbol. This only works once
96 // all input files have been added.
97 bool isUndefWeak() const {
98 // See comment on lazy symbols for details.
99 return isWeak() && (isUndefined() || isLazy());
100 }
101
102 // Returns the symbol name.
103 StringRef getName() const { return name; }
104
105 // Returns the file from which this symbol was created.
106 InputFile *getFile() const { return file; }
107
108 InputChunk *getChunk() const;
109
110 // Indicates that the section or import for this symbol will be included in
111 // the final image.
112 bool isLive() const;
113
114 // Marks the symbol's InputChunk as Live, so that it will be included in the
115 // final image.
116 void markLive();
117
118 void setHidden(bool isHidden);
119
120 // Get/set the index in the output symbol table. This is only used for
121 // relocatable output.
122 uint32_t getOutputSymbolIndex() const;
123 void setOutputSymbolIndex(uint32_t index);
124
125 WasmSymbolType getWasmType() const;
126 bool isImported() const;
127 bool isExported() const;
128 bool isExportedExplicit() const;
129
130 // Indicates that the symbol is used in an __attribute__((used)) directive
131 // or similar.
132 bool isNoStrip() const;
133
134 const WasmSignature *getSignature() const;
135
136 uint32_t getGOTIndex() const {
137 assert(gotIndex != INVALID_INDEX);
138 return gotIndex;
139 }
140
141 void setGOTIndex(uint32_t index);
142 bool hasGOTIndex() const { return gotIndex != INVALID_INDEX; }
143
144protected:
145 Symbol(StringRef name, Kind k, uint32_t flags, InputFile *f)
146 : name(name), file(f), symbolKind(k), referenced(!ctx.arg.gcSections),
147 requiresGOT(false), isUsedInRegularObj(false), forceExport(false),
148 forceImport(false), canInline(false), traced(false), isStub(false),
149 flags(flags) {}
150
151 StringRef name;
152 InputFile *file;
153 uint32_t outputSymbolIndex = INVALID_INDEX;
154 uint32_t gotIndex = INVALID_INDEX;
155 Kind symbolKind;
156
157public:
158 bool referenced : 1;
159
160 // True for data symbols that needs a dummy GOT entry. Used for static
161 // linking of GOT accesses.
162 bool requiresGOT : 1;
163
164 // True if the symbol was used for linking and thus need to be added to the
165 // output file's symbol table. This is true for all symbols except for
166 // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
167 // are unreferenced except by other bitcode objects.
168 bool isUsedInRegularObj : 1;
169
170 // True if this symbol is explicitly marked for export (i.e. via the
171 // -e/--export command line flag)
172 bool forceExport : 1;
173
174 bool forceImport : 1;
175
176 // False if LTO shouldn't inline whatever this symbol points to. If a symbol
177 // is overwritten after LTO, LTO shouldn't inline the symbol because it
178 // doesn't know the final contents of the symbol.
179 bool canInline : 1;
180
181 // True if this symbol is specified by --trace-symbol option.
182 bool traced : 1;
183
184 // True if this symbol is a linker-synthesized stub function (traps when
185 // called) and should otherwise be treated as missing/undefined. See
186 // SymbolTable::replaceWithUndefined.
187 // These stubs never appear in the table and any table index relocations
188 // against them will produce address 0 (The table index representing
189 // the null function pointer).
190 bool isStub : 1;
191
192 uint32_t flags;
193
194 std::optional<StringRef> importName;
195 std::optional<StringRef> importModule;
196};
197
198class FunctionSymbol : public Symbol {
199public:
200 static bool classof(const Symbol *s) {
201 return s->kind() == DefinedFunctionKind ||
202 s->kind() == SharedFunctionKind ||
203 s->kind() == UndefinedFunctionKind;
204 }
205
206 // Get/set the table index
207 void setTableIndex(uint32_t index);
208 uint32_t getTableIndex() const;
209 bool hasTableIndex() const;
210
211 // Get/set the function index
212 uint32_t getFunctionIndex() const;
213 void setFunctionIndex(uint32_t index);
214 bool hasFunctionIndex() const;
215
216 const WasmSignature *signature;
217
218protected:
219 FunctionSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f,
220 const WasmSignature *sig)
221 : Symbol(name, k, flags, f), signature(sig) {}
222
223 uint32_t tableIndex = INVALID_INDEX;
224 uint32_t functionIndex = INVALID_INDEX;
225};
226
227class DefinedFunction : public FunctionSymbol {
228public:
229 DefinedFunction(StringRef name, uint32_t flags, InputFile *f,
230 InputFunction *function);
231
232 static bool classof(const Symbol *s) {
233 return s->kind() == DefinedFunctionKind;
234 }
235
236 // Get the function index to be used when exporting. This only applies to
237 // defined functions and can be differ from the regular function index for
238 // weakly defined functions (that are imported and used via one index but
239 // defined and exported via another).
240 uint32_t getExportedFunctionIndex() const;
241
242 InputFunction *function;
243};
244
245class UndefinedFunction : public FunctionSymbol {
246public:
247 UndefinedFunction(StringRef name, std::optional<StringRef> importName,
248 std::optional<StringRef> importModule, uint32_t flags,
249 InputFile *file = nullptr,
250 const WasmSignature *type = nullptr,
251 bool isCalledDirectly = true)
252 : FunctionSymbol(name, UndefinedFunctionKind, flags, file, type),
253 isCalledDirectly(isCalledDirectly) {
254 this->importName = importName;
255 this->importModule = importModule;
256 }
257
258 static bool classof(const Symbol *s) {
259 return s->kind() == UndefinedFunctionKind;
260 }
261
262 DefinedFunction *stubFunction = nullptr;
263 bool isCalledDirectly;
264};
265
266// Section symbols for output sections are different from those for input
267// section. These are generated by the linker and point the OutputSection
268// rather than an InputSection.
269class OutputSectionSymbol : public Symbol {
270public:
271 OutputSectionSymbol(const OutputSection *s)
272 : Symbol("", OutputSectionKind, llvm::wasm::WASM_SYMBOL_BINDING_LOCAL,
273 nullptr),
274 section(s) {}
275
276 static bool classof(const Symbol *s) {
277 return s->kind() == OutputSectionKind;
278 }
279
280 const OutputSection *section;
281};
282
283class SectionSymbol : public Symbol {
284public:
285 SectionSymbol(uint32_t flags, const InputChunk *s, InputFile *f = nullptr)
286 : Symbol("", SectionKind, flags, f), section(s) {}
287
288 static bool classof(const Symbol *s) { return s->kind() == SectionKind; }
289
290 const OutputSectionSymbol *getOutputSectionSymbol() const;
291
292 const InputChunk *section;
293};
294
295class DataSymbol : public Symbol {
296public:
297 static bool classof(const Symbol *s) {
298 return s->kind() == DefinedDataKind || s->kind() == UndefinedDataKind ||
299 s->kind() == SharedDataKind || s->kind() == CommonKind;
300 }
301
302protected:
303 DataSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f)
304 : Symbol(name, k, flags, f) {}
305};
306
307class DefinedData : public DataSymbol {
308public:
309 // Constructor for regular data symbols originating from input files.
310 DefinedData(StringRef name, uint32_t flags, InputFile *f, InputChunk *segment,
311 uint64_t value, uint64_t size)
312 : DataSymbol(name, DefinedDataKind, flags, f), segment(segment),
313 value(value), size(size) {}
314
315 // Constructor for linker synthetic data symbols.
316 DefinedData(StringRef name, uint32_t flags)
317 : DataSymbol(name, DefinedDataKind, flags, nullptr) {}
318
319 static bool classof(const Symbol *s) { return s->kind() == DefinedDataKind; }
320
321 // Returns the output virtual address of a defined data symbol.
322 // For TLS symbols, by default (unless absolute is set), this returns an
323 // address relative the `__tls_base`.
324 uint64_t getVA(bool absolute = false) const;
325 void setVA(uint64_t va);
326
327 // Returns the offset of a defined data symbol within its OutputSegment.
328 uint64_t getOutputSegmentOffset() const;
329 uint64_t getOutputSegmentIndex() const;
330 uint64_t getSize() const { return size; }
331
332 InputChunk *segment = nullptr;
333 uint64_t value = 0;
334
335protected:
336 uint64_t size = 0;
337};
338
339class SharedData : public DataSymbol {
340public:
341 SharedData(StringRef name, uint32_t flags, InputFile *f)
342 : DataSymbol(name, SharedDataKind, flags, f) {}
343};
344
345class UndefinedData : public DataSymbol {
346public:
347 UndefinedData(StringRef name, uint32_t flags, InputFile *file = nullptr)
348 : DataSymbol(name, UndefinedDataKind, flags, file) {}
349 static bool classof(const Symbol *s) {
350 return s->kind() == UndefinedDataKind;
351 }
352};
353
354class CommonSymbol : public DataSymbol {
355public:
356 CommonSymbol(StringRef name, uint32_t flags, InputFile *file, uint64_t size,
357 uint32_t alignment)
358 : DataSymbol(name, CommonKind, flags, file), size(size),
359 alignment(alignment) {}
360
361 static bool classof(const Symbol *s) { return s->kind() == CommonKind; }
362
363 uint64_t getSize() const { return size; }
364 uint32_t getAlignment() const { return alignment; }
365
366 void setCommon(uint64_t s, uint32_t a) {
367 size = s;
368 alignment = a;
369 }
370
371private:
372 uint64_t size;
373 uint32_t alignment;
374};
375
376class GlobalSymbol : public Symbol {
377public:
378 static bool classof(const Symbol *s) {
379 return s->kind() == DefinedGlobalKind || s->kind() == UndefinedGlobalKind;
380 }
381
382 const WasmGlobalType *getGlobalType() const { return globalType; }
383
384 // Get/set the global index
385 uint32_t getGlobalIndex() const;
386 void setGlobalIndex(uint32_t index);
387 bool hasGlobalIndex() const;
388
389protected:
390 GlobalSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f,
391 const WasmGlobalType *globalType)
392 : Symbol(name, k, flags, f), globalType(globalType) {}
393
394 const WasmGlobalType *globalType;
395 uint32_t globalIndex = INVALID_INDEX;
396};
397
398class DefinedGlobal : public GlobalSymbol {
399public:
400 DefinedGlobal(StringRef name, uint32_t flags, InputFile *file,
401 InputGlobal *global);
402
403 static bool classof(const Symbol *s) {
404 return s->kind() == DefinedGlobalKind;
405 }
406
407 InputGlobal *global;
408};
409
410class UndefinedGlobal : public GlobalSymbol {
411public:
412 UndefinedGlobal(StringRef name, std::optional<StringRef> importName,
413 std::optional<StringRef> importModule, uint32_t flags,
414 InputFile *file = nullptr,
415 const WasmGlobalType *type = nullptr)
416 : GlobalSymbol(name, UndefinedGlobalKind, flags, file, type) {
417 this->importName = importName;
418 this->importModule = importModule;
419 }
420
421 static bool classof(const Symbol *s) {
422 return s->kind() == UndefinedGlobalKind;
423 }
424};
425
426class TableSymbol : public Symbol {
427public:
428 static bool classof(const Symbol *s) {
429 return s->kind() == DefinedTableKind || s->kind() == UndefinedTableKind;
430 }
431
432 const WasmTableType *getTableType() const { return tableType; }
433 void setLimits(const WasmLimits &limits);
434
435 // Get/set the table number
436 uint32_t getTableNumber() const;
437 void setTableNumber(uint32_t number);
438 bool hasTableNumber() const;
439
440protected:
441 TableSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f,
442 const WasmTableType *type)
443 : Symbol(name, k, flags, f), tableType(type) {}
444
445 const WasmTableType *tableType;
446 uint32_t tableNumber = INVALID_INDEX;
447};
448
449class DefinedTable : public TableSymbol {
450public:
451 DefinedTable(StringRef name, uint32_t flags, InputFile *file,
452 InputTable *table);
453
454 static bool classof(const Symbol *s) { return s->kind() == DefinedTableKind; }
455
456 InputTable *table;
457};
458
459class UndefinedTable : public TableSymbol {
460public:
461 UndefinedTable(StringRef name, std::optional<StringRef> importName,
462 std::optional<StringRef> importModule, uint32_t flags,
463 InputFile *file, const WasmTableType *type)
464 : TableSymbol(name, UndefinedTableKind, flags, file, type) {
465 this->importName = importName;
466 this->importModule = importModule;
467 }
468
469 static bool classof(const Symbol *s) {
470 return s->kind() == UndefinedTableKind;
471 }
472};
473
474// A tag is a general format to distinguish typed entities. Each tag has an
475// attribute and a type. Currently the attribute can only specify that the tag
476// is for an exception tag.
477//
478// In exception handling, tags are used to distinguish different kinds of
479// exceptions. For example, they can be used to distinguish different language's
480// exceptions, e.g., all C++ exceptions have the same tag and Java exceptions
481// would have a distinct tag. Wasm can filter the exceptions it catches based on
482// their tag.
483//
484// A single TagSymbol object represents a single tag. The C++ exception symbol
485// is a weak symbol generated in every object file in which exceptions are used,
486// and is named '__cpp_exception' for linking.
487class TagSymbol : public Symbol {
488public:
489 static bool classof(const Symbol *s) {
490 return s->kind() == DefinedTagKind || s->kind() == UndefinedTagKind ||
491 s->kind() == SharedTagKind;
492 }
493
494 // Get/set the tag index
495 uint32_t getTagIndex() const;
496 void setTagIndex(uint32_t index);
497 bool hasTagIndex() const;
498
499 const WasmSignature *signature;
500
501protected:
502 TagSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f,
503 const WasmSignature *sig)
504 : Symbol(name, k, flags, f), signature(sig) {}
505
506 uint32_t tagIndex = INVALID_INDEX;
507};
508
509class DefinedTag : public TagSymbol {
510public:
511 DefinedTag(StringRef name, uint32_t flags, InputFile *file, InputTag *tag);
512
513 static bool classof(const Symbol *s) { return s->kind() == DefinedTagKind; }
514
515 InputTag *tag;
516};
517
518class UndefinedTag : public TagSymbol {
519public:
520 UndefinedTag(StringRef name, std::optional<StringRef> importName,
521 std::optional<StringRef> importModule, uint32_t flags,
522 InputFile *file = nullptr, const WasmSignature *sig = nullptr)
523 : TagSymbol(name, UndefinedTagKind, flags, file, sig) {
524 this->importName = importName;
525 this->importModule = importModule;
526 }
527
528 static bool classof(const Symbol *s) { return s->kind() == UndefinedTagKind; }
529};
530
531class SharedTagSymbol : public TagSymbol {
532public:
533 SharedTagSymbol(StringRef name, uint32_t flags, InputFile *f,
534 const WasmSignature *sig)
535 : TagSymbol(name, SharedTagKind, flags, f, sig) {}
536
537 static bool classof(const Symbol *s) { return s->kind() == SharedTagKind; }
538};
539
540class SharedFunctionSymbol : public FunctionSymbol {
541public:
542 SharedFunctionSymbol(StringRef name, uint32_t flags, InputFile *file,
543 const WasmSignature *sig)
544 : FunctionSymbol(name, SharedFunctionKind, flags, file, sig) {}
545 static bool classof(const Symbol *s) {
546 return s->kind() == SharedFunctionKind;
547 }
548};
549
550// LazySymbol symbols represent symbols in object files between --start-lib and
551// --end-lib options. LLD also handles traditional archives as if all the files
552// in the archive are surrounded by --start-lib and --end-lib.
553//
554// A special complication is the handling of weak undefined symbols. They should
555// not load a file, but we have to remember we have seen both the weak undefined
556// and the lazy. We represent that with a lazy symbol with a weak binding. This
557// means that code looking for undefined symbols normally also has to take lazy
558// symbols into consideration.
559class LazySymbol : public Symbol {
560public:
561 LazySymbol(StringRef name, uint32_t flags, InputFile *file)
562 : Symbol(name, LazyKind, flags, file) {}
563
564 static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
565 void extract();
566 void setWeak();
567
568 // Lazy symbols can have a signature because they can replace an
569 // UndefinedFunction in which case we need to be able to preserve the
570 // signature.
571 // TODO(sbc): This repetition of the signature field is inelegant. Revisit
572 // the use of class hierarchy to represent symbol taxonomy.
573 const WasmSignature *signature = nullptr;
574};
575
576// A buffer class that is large enough to hold any Symbol-derived
577// object. We allocate memory using this class and instantiate a symbol
578// using the placement new.
579union SymbolUnion {
580 alignas(DefinedFunction) char a[sizeof(DefinedFunction)];
581 alignas(DefinedData) char b[sizeof(DefinedData)];
582 alignas(DefinedGlobal) char c[sizeof(DefinedGlobal)];
583 alignas(DefinedTag) char d[sizeof(DefinedTag)];
584 alignas(DefinedTable) char e[sizeof(DefinedTable)];
585 alignas(LazySymbol) char f[sizeof(LazySymbol)];
586 alignas(UndefinedFunction) char g[sizeof(UndefinedFunction)];
587 alignas(UndefinedData) char h[sizeof(UndefinedData)];
588 alignas(UndefinedGlobal) char i[sizeof(UndefinedGlobal)];
589 alignas(UndefinedTable) char j[sizeof(UndefinedTable)];
590 alignas(SectionSymbol) char k[sizeof(SectionSymbol)];
591 alignas(SharedFunctionSymbol) char l[sizeof(SharedFunctionSymbol)];
592 alignas(SharedTagSymbol) char m[sizeof(SharedTagSymbol)];
593 alignas(CommonSymbol) char n[sizeof(CommonSymbol)];
594};
595
596// It is important to keep the size of SymbolUnion small for performance and
597// memory usage reasons. 96 bytes is a soft limit based on the size of
598// UndefinedFunction on a 64-bit system.
599static_assert(sizeof(SymbolUnion) <= 120, "SymbolUnion too large");
600
601void printTraceSymbol(Symbol *sym);
602void printTraceSymbolUndefined(StringRef name, const InputFile *file);
603
604template <typename T, typename... ArgT>
605T *replaceSymbol(Symbol *s, ArgT &&...arg) {
606 static_assert(std::is_trivially_destructible<T>(),
607 "Symbol types must be trivially destructible");
608 static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
609 static_assert(alignof(T) <= alignof(SymbolUnion),
610 "SymbolUnion not aligned enough");
611 assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
612 "Not a Symbol");
613
614 Symbol symCopy = *s;
615
616 T *s2 = new (s) T(std::forward<ArgT>(arg)...);
617 s2->isUsedInRegularObj = symCopy.isUsedInRegularObj;
618 s2->forceExport = symCopy.forceExport;
619 s2->forceImport = symCopy.forceImport;
620 s2->canInline = symCopy.canInline;
621 s2->traced = symCopy.traced;
622 s2->referenced = symCopy.referenced;
623
624 // Print out a log message if --trace-symbol was specified.
625 // This is for debugging.
626 if (s2->traced)
627 printTraceSymbol(s2);
628
629 return s2;
630}
631
632} // namespace wasm
633
634// Returns a symbol name for an error message.
635std::string toString(const wasm::Symbol &sym);
636std::string toString(wasm::Symbol::Kind kind);
637std::string maybeDemangleSymbol(StringRef name);
638
639} // namespace lld
640
641#endif
642