441 lines · cpp
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-exception6//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(sym.getName());31}32 33std::string maybeDemangleSymbol(StringRef name) {34 // WebAssembly requires caller and callee signatures to match, so we mangle35 // `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(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>(this))83 return WASM_SYMBOL_TYPE_FUNCTION;84 if (isa<DataSymbol>(this))85 return WASM_SYMBOL_TYPE_DATA;86 if (isa<GlobalSymbol>(this))87 return WASM_SYMBOL_TYPE_GLOBAL;88 if (isa<TagSymbol>(this))89 return WASM_SYMBOL_TYPE_TAG;90 if (isa<TableSymbol>(this))91 return WASM_SYMBOL_TYPE_TABLE;92 if (isa<SectionSymbol>(this) || isa<OutputSectionSymbol>(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>(this))99 return f->signature;100 if (auto *t = dyn_cast<TagSymbol>(this))101 return t->signature;102 if (auto *l = dyn_cast<LazySymbol>(this))103 return l->signature;104 return nullptr;105}106 107InputChunk *Symbol::getChunk() const {108 if (auto *f = dyn_cast<DefinedFunction>(this))109 return f->function;110 if (auto *f = dyn_cast<UndefinedFunction>(this))111 if (f->stubFunction)112 return f->stubFunction->function;113 if (auto *d = dyn_cast<DefinedData>(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>(this))126 return g->global->live;127 if (auto *t = dyn_cast<DefinedTag>(this))128 return t->tag->live;129 if (auto *t = dyn_cast<DefinedTable>(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>(this))142 g->global->live = true;143 if (auto *t = dyn_cast<DefinedTag>(this))144 t->tag->live = true;145 if (auto *t = dyn_cast<DefinedTable>(this))146 t->table->live = true;147 if (InputChunk *c = getChunk()) {148 // Usually, a whole chunk is marked as live or dead, but in mergeable149 // (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>(this)) {152 if (auto *ms = dyn_cast<MergeInputChunk>(c)) {153 ms->getSectionPiece(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 << " -> " << index167 << "\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 else198 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 symbols211 // in case they contain the version that will be chosen by212 // 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>(this))232 if (u->stubFunction)233 return u->stubFunction->getFunctionIndex();234 if (functionIndex != INVALID_INDEX)235 return functionIndex;236 auto *f = cast<DefinedFunction>(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>(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>(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>(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 defined267 // functions we set the index on the InputFunction so that we don't export268 // the same thing twice (keeps the table size down).269 if (auto *f = dyn_cast<DefinedFunction>(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 output291 // segment (__tls_base).292 if (isTLS() && !absolute)293 return getOutputSegmentOffset();294 if (segment)295 return segment->getVA(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(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>(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>(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>(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>(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>(this))367 t->table->setLimits(limits);368 auto *newType = make<WasmTableType>(*tableType);369 newType->Limits = limits;370 tableType = newType;371}372 373uint32_t TableSymbol::getTableNumber() const {374 if (const auto *t = dyn_cast<DefinedTable>(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>(this))382 return t->table->assignIndex(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>(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, 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(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 printTraceSymbolUndefined423 if (sym->isUndefined())424 return;425 426 std::string s;427 if (sym->isLazy())428 s = ": lazy definition of ";429 else430 s = ": definition of ";431 432 message(toString(sym->getFile()) + s + sym->getName());433}434 435const char *defaultModule = "env";436const char *functionTableName = "__indirect_function_table";437const char *memoryName = "memory";438 439} // namespace wasm440} // namespace lld441