//===- SyntheticSections.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains linker-synthesized sections. // //===----------------------------------------------------------------------===// #include "SyntheticSections.h" #include "InputChunks.h" #include "InputElement.h" #include "OutputSegment.h" #include "SymbolTable.h" #include "llvm/Support/Path.h" #include using namespace llvm; using namespace llvm::wasm; namespace lld::wasm { OutStruct out; namespace { // Some synthetic sections (e.g. "name" and "linking") have subsections. // Just like the synthetic sections themselves these need to be created before // they can be written out (since they are preceded by their length). This // class is used to create subsections and then write them into the stream // of the parent section. class SubSection { public: explicit SubSection(uint32_t type) : type(type) {} void writeTo(raw_ostream &to) { os.flush(); writeUleb128(to, type, "subsection type"); writeUleb128(to, body.size(), "subsection size"); to.write(body.data(), body.size()); } private: uint32_t type; std::string body; public: raw_string_ostream os{body}; }; } // namespace bool DylinkSection::isNeeded() const { return ctx.isPic || config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic || !ctx.sharedFiles.empty(); } void DylinkSection::writeBody() { raw_ostream &os = bodyOutputStream; { SubSection sub(WASM_DYLINK_MEM_INFO); writeUleb128(sub.os, memSize, "MemSize"); writeUleb128(sub.os, memAlign, "MemAlign"); writeUleb128(sub.os, out.elemSec->numEntries(), "TableSize"); writeUleb128(sub.os, 0, "TableAlign"); sub.writeTo(os); } if (ctx.sharedFiles.size()) { SubSection sub(WASM_DYLINK_NEEDED); writeUleb128(sub.os, ctx.sharedFiles.size(), "Needed"); for (auto *so : ctx.sharedFiles) writeStr(sub.os, llvm::sys::path::filename(so->getName()), "so name"); sub.writeTo(os); } // Under certain circumstances we need to include extra information about our // exports and/or imports to the dynamic linker. // For exports we need to notify the linker when an export is TLS since the // exported value is relative to __tls_base rather than __memory_base. // For imports we need to notify the dynamic linker when an import is weak // so that knows not to report an error for such symbols. std::vector importInfo; std::vector exportInfo; for (const Symbol *sym : symtab->symbols()) { if (sym->isLive()) { if (sym->isExported() && sym->isTLS() && isa(sym)) { exportInfo.push_back(sym); } if (sym->isUndefWeak()) { importInfo.push_back(sym); } } } if (!exportInfo.empty()) { SubSection sub(WASM_DYLINK_EXPORT_INFO); writeUleb128(sub.os, exportInfo.size(), "num exports"); for (const Symbol *sym : exportInfo) { LLVM_DEBUG(llvm::dbgs() << "export info: " << toString(*sym) << "\n"); StringRef name = sym->getName(); if (auto *f = dyn_cast(sym)) { if (std::optional exportName = f->function->getExportName()) { name = *exportName; } } writeStr(sub.os, name, "sym name"); writeUleb128(sub.os, sym->flags, "sym flags"); } sub.writeTo(os); } if (!importInfo.empty()) { SubSection sub(WASM_DYLINK_IMPORT_INFO); writeUleb128(sub.os, importInfo.size(), "num imports"); for (const Symbol *sym : importInfo) { LLVM_DEBUG(llvm::dbgs() << "imports info: " << toString(*sym) << "\n"); StringRef module = sym->importModule.value_or(defaultModule); StringRef name = sym->importName.value_or(sym->getName()); writeStr(sub.os, module, "import module"); writeStr(sub.os, name, "import name"); writeUleb128(sub.os, sym->flags, "sym flags"); } sub.writeTo(os); } } uint32_t TypeSection::registerType(const WasmSignature &sig) { auto pair = typeIndices.insert(std::make_pair(sig, types.size())); if (pair.second) { LLVM_DEBUG(llvm::dbgs() << "registerType " << toString(sig) << "\n"); types.push_back(&sig); } return pair.first->second; } uint32_t TypeSection::lookupType(const WasmSignature &sig) { auto it = typeIndices.find(sig); if (it == typeIndices.end()) { error("type not found: " + toString(sig)); return 0; } return it->second; } void TypeSection::writeBody() { writeUleb128(bodyOutputStream, types.size(), "type count"); for (const WasmSignature *sig : types) writeSig(bodyOutputStream, *sig); } uint32_t ImportSection::getNumImports() const { assert(isSealed); uint32_t numImports = importedSymbols.size() + gotSymbols.size(); if (config->memoryImport.has_value()) ++numImports; return numImports; } void ImportSection::addGOTEntry(Symbol *sym) { assert(!isSealed); if (sym->hasGOTIndex()) return; LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n"); sym->setGOTIndex(numImportedGlobals++); if (ctx.isPic) { // Any symbol that is assigned an normal GOT entry must be exported // otherwise the dynamic linker won't be able create the entry that contains // it. sym->forceExport = true; } gotSymbols.push_back(sym); } void ImportSection::addImport(Symbol *sym) { assert(!isSealed); StringRef module = sym->importModule.value_or(defaultModule); StringRef name = sym->importName.value_or(sym->getName()); if (auto *f = dyn_cast(sym)) { ImportKey key(*(f->getSignature()), module, name); auto entry = importedFunctions.try_emplace(key, numImportedFunctions); if (entry.second) { importedSymbols.emplace_back(sym); f->setFunctionIndex(numImportedFunctions++); } else { f->setFunctionIndex(entry.first->second); } } else if (auto *g = dyn_cast(sym)) { ImportKey key(*(g->getGlobalType()), module, name); auto entry = importedGlobals.try_emplace(key, numImportedGlobals); if (entry.second) { importedSymbols.emplace_back(sym); g->setGlobalIndex(numImportedGlobals++); } else { g->setGlobalIndex(entry.first->second); } } else if (auto *t = dyn_cast(sym)) { ImportKey key(*(t->getSignature()), module, name); auto entry = importedTags.try_emplace(key, numImportedTags); if (entry.second) { importedSymbols.emplace_back(sym); t->setTagIndex(numImportedTags++); } else { t->setTagIndex(entry.first->second); } } else { assert(TableSymbol::classof(sym)); auto *table = cast(sym); ImportKey key(*(table->getTableType()), module, name); auto entry = importedTables.try_emplace(key, numImportedTables); if (entry.second) { importedSymbols.emplace_back(sym); table->setTableNumber(numImportedTables++); } else { table->setTableNumber(entry.first->second); } } } void ImportSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, getNumImports(), "import count"); bool is64 = config->is64.value_or(false); if (config->memoryImport) { WasmImport import; import.Module = config->memoryImport->first; import.Field = config->memoryImport->second; import.Kind = WASM_EXTERNAL_MEMORY; import.Memory.Flags = 0; import.Memory.Minimum = out.memorySec->numMemoryPages; if (out.memorySec->maxMemoryPages != 0 || config->sharedMemory) { import.Memory.Flags |= WASM_LIMITS_FLAG_HAS_MAX; import.Memory.Maximum = out.memorySec->maxMemoryPages; } if (config->sharedMemory) import.Memory.Flags |= WASM_LIMITS_FLAG_IS_SHARED; if (is64) import.Memory.Flags |= WASM_LIMITS_FLAG_IS_64; writeImport(os, import); } for (const Symbol *sym : importedSymbols) { WasmImport import; import.Field = sym->importName.value_or(sym->getName()); import.Module = sym->importModule.value_or(defaultModule); if (auto *functionSym = dyn_cast(sym)) { import.Kind = WASM_EXTERNAL_FUNCTION; import.SigIndex = out.typeSec->lookupType(*functionSym->signature); } else if (auto *globalSym = dyn_cast(sym)) { import.Kind = WASM_EXTERNAL_GLOBAL; import.Global = *globalSym->getGlobalType(); } else if (auto *tagSym = dyn_cast(sym)) { import.Kind = WASM_EXTERNAL_TAG; import.SigIndex = out.typeSec->lookupType(*tagSym->signature); } else { auto *tableSym = cast(sym); import.Kind = WASM_EXTERNAL_TABLE; import.Table = *tableSym->getTableType(); } writeImport(os, import); } for (const Symbol *sym : gotSymbols) { WasmImport import; import.Kind = WASM_EXTERNAL_GLOBAL; auto ptrType = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32; import.Global = {static_cast(ptrType), true}; if (isa(sym)) import.Module = "GOT.mem"; else import.Module = "GOT.func"; import.Field = sym->getName(); writeImport(os, import); } } void FunctionSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, inputFunctions.size(), "function count"); for (const InputFunction *func : inputFunctions) writeUleb128(os, out.typeSec->lookupType(func->signature), "sig index"); } void FunctionSection::addFunction(InputFunction *func) { if (!func->live) return; uint32_t functionIndex = out.importSec->getNumImportedFunctions() + inputFunctions.size(); inputFunctions.emplace_back(func); func->setFunctionIndex(functionIndex); } void TableSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, inputTables.size(), "table count"); for (const InputTable *table : inputTables) writeTableType(os, table->getType()); } void TableSection::addTable(InputTable *table) { if (!table->live) return; // Some inputs require that the indirect function table be assigned to table // number 0. if (ctx.legacyFunctionTable && isa(WasmSym::indirectFunctionTable) && cast(WasmSym::indirectFunctionTable)->table == table) { if (out.importSec->getNumImportedTables()) { // Alack! Some other input imported a table, meaning that we are unable // to assign table number 0 to the indirect function table. for (const auto *culprit : out.importSec->importedSymbols) { if (isa(culprit)) { error("object file not built with 'reference-types' feature " "conflicts with import of table " + culprit->getName() + " by file " + toString(culprit->getFile())); return; } } llvm_unreachable("failed to find conflicting table import"); } inputTables.insert(inputTables.begin(), table); return; } inputTables.push_back(table); } void TableSection::assignIndexes() { uint32_t tableNumber = out.importSec->getNumImportedTables(); for (InputTable *t : inputTables) t->assignIndex(tableNumber++); } void MemorySection::writeBody() { raw_ostream &os = bodyOutputStream; bool hasMax = maxMemoryPages != 0 || config->sharedMemory; writeUleb128(os, 1, "memory count"); unsigned flags = 0; if (hasMax) flags |= WASM_LIMITS_FLAG_HAS_MAX; if (config->sharedMemory) flags |= WASM_LIMITS_FLAG_IS_SHARED; if (config->is64.value_or(false)) flags |= WASM_LIMITS_FLAG_IS_64; writeUleb128(os, flags, "memory limits flags"); writeUleb128(os, numMemoryPages, "initial pages"); if (hasMax) writeUleb128(os, maxMemoryPages, "max pages"); } void TagSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, inputTags.size(), "tag count"); for (InputTag *t : inputTags) { writeUleb128(os, 0, "tag attribute"); // Reserved "attribute" field writeUleb128(os, out.typeSec->lookupType(t->signature), "sig index"); } } void TagSection::addTag(InputTag *tag) { if (!tag->live) return; uint32_t tagIndex = out.importSec->getNumImportedTags() + inputTags.size(); LLVM_DEBUG(dbgs() << "addTag: " << tagIndex << "\n"); tag->assignIndex(tagIndex); inputTags.push_back(tag); } void GlobalSection::assignIndexes() { uint32_t globalIndex = out.importSec->getNumImportedGlobals(); for (InputGlobal *g : inputGlobals) g->assignIndex(globalIndex++); for (Symbol *sym : internalGotSymbols) sym->setGOTIndex(globalIndex++); isSealed = true; } static void ensureIndirectFunctionTable() { if (!WasmSym::indirectFunctionTable) WasmSym::indirectFunctionTable = symtab->resolveIndirectFunctionTable(/*required =*/true); } void GlobalSection::addInternalGOTEntry(Symbol *sym) { assert(!isSealed); if (sym->requiresGOT) return; LLVM_DEBUG(dbgs() << "addInternalGOTEntry: " << sym->getName() << " " << toString(sym->kind()) << "\n"); sym->requiresGOT = true; if (auto *F = dyn_cast(sym)) { ensureIndirectFunctionTable(); out.elemSec->addEntry(F); } internalGotSymbols.push_back(sym); } void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const { assert(!config->extendedConst); bool is64 = config->is64.value_or(false); unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD; for (const Symbol *sym : internalGotSymbols) { if (TLS != sym->isTLS()) continue; if (auto *d = dyn_cast(sym)) { // Get __memory_base writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); if (sym->isTLS()) writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base"); else writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base"); // Add the virtual address of the data symbol writeU8(os, opcode_ptr_const, "CONST"); writeSleb128(os, d->getVA(), "offset"); } else if (auto *f = dyn_cast(sym)) { if (f->isStub) continue; // Get __table_base writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, WasmSym::tableBase->getGlobalIndex(), "__table_base"); // Add the table index to __table_base writeU8(os, opcode_ptr_const, "CONST"); writeSleb128(os, f->getTableIndex(), "offset"); } else { assert(isa(sym) || isa(sym)); continue; } writeU8(os, opcode_ptr_add, "ADD"); writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET"); writeUleb128(os, sym->getGOTIndex(), "got_entry"); } } void GlobalSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, numGlobals(), "global count"); for (InputGlobal *g : inputGlobals) { writeGlobalType(os, g->getType()); writeInitExpr(os, g->getInitExpr()); } bool is64 = config->is64.value_or(false); uint8_t itype = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32; for (const Symbol *sym : internalGotSymbols) { bool mutable_ = false; if (!sym->isStub) { // In the case of dynamic linking, unless we have 'extended-const' // available, these global must to be mutable since they get updated to // the correct runtime value during `__wasm_apply_global_relocs`. if (!config->extendedConst && ctx.isPic && !sym->isTLS()) mutable_ = true; // With multi-theadeding any TLS globals must be mutable since they get // set during `__wasm_apply_global_tls_relocs` if (config->sharedMemory && sym->isTLS()) mutable_ = true; } WasmGlobalType type{itype, mutable_}; writeGlobalType(os, type); bool useExtendedConst = false; uint32_t globalIdx; int64_t offset; if (config->extendedConst && ctx.isPic) { if (auto *d = dyn_cast(sym)) { if (!sym->isTLS()) { globalIdx = WasmSym::memoryBase->getGlobalIndex(); offset = d->getVA(); useExtendedConst = true; } } else if (auto *f = dyn_cast(sym)) { if (!sym->isStub) { globalIdx = WasmSym::tableBase->getGlobalIndex(); offset = f->getTableIndex(); useExtendedConst = true; } } } if (useExtendedConst) { // We can use an extended init expression to add a constant // offset of __memory_base/__table_base. writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get"); writeUleb128(os, globalIdx, "literal (global index)"); if (offset) { writePtrConst(os, offset, is64, "offset"); writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "add"); } writeU8(os, WASM_OPCODE_END, "opcode:end"); } else { WasmInitExpr initExpr; if (auto *d = dyn_cast(sym)) initExpr = intConst(d->getVA(), is64); else if (auto *f = dyn_cast(sym)) initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64); else { assert(isa(sym) || isa(sym)); initExpr = intConst(0, is64); } writeInitExpr(os, initExpr); } } for (const DefinedData *sym : dataAddressGlobals) { WasmGlobalType type{itype, false}; writeGlobalType(os, type); writeInitExpr(os, intConst(sym->getVA(), is64)); } } void GlobalSection::addGlobal(InputGlobal *global) { assert(!isSealed); if (!global->live) return; inputGlobals.push_back(global); } void ExportSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, exports.size(), "export count"); for (const WasmExport &export_ : exports) writeExport(os, export_); } bool StartSection::isNeeded() const { return WasmSym::startFunction != nullptr; } void StartSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, WasmSym::startFunction->getFunctionIndex(), "function index"); } void ElemSection::addEntry(FunctionSymbol *sym) { // Don't add stub functions to the wasm table. The address of all stub // functions should be zero and they should they don't appear in the table. // They only exist so that the calls to missing functions can validate. if (sym->hasTableIndex() || sym->isStub) return; sym->setTableIndex(config->tableBase + indirectFunctions.size()); indirectFunctions.emplace_back(sym); } void ElemSection::writeBody() { raw_ostream &os = bodyOutputStream; assert(WasmSym::indirectFunctionTable); writeUleb128(os, 1, "segment count"); uint32_t tableNumber = WasmSym::indirectFunctionTable->getTableNumber(); uint32_t flags = 0; if (tableNumber) flags |= WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER; writeUleb128(os, flags, "elem segment flags"); if (flags & WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER) writeUleb128(os, tableNumber, "table number"); WasmInitExpr initExpr; initExpr.Extended = false; if (ctx.isPic) { initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET; initExpr.Inst.Value.Global = WasmSym::tableBase->getGlobalIndex(); } else { bool is64 = config->is64.value_or(false); initExpr.Inst.Opcode = is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; initExpr.Inst.Value.Int32 = config->tableBase; } writeInitExpr(os, initExpr); if (flags & WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) { // We only write active function table initializers, for which the elem kind // is specified to be written as 0x00 and interpreted to mean "funcref". const uint8_t elemKind = 0; writeU8(os, elemKind, "elem kind"); } writeUleb128(os, indirectFunctions.size(), "elem count"); uint32_t tableIndex = config->tableBase; for (const FunctionSymbol *sym : indirectFunctions) { assert(sym->getTableIndex() == tableIndex); (void) tableIndex; writeUleb128(os, sym->getFunctionIndex(), "function index"); ++tableIndex; } } DataCountSection::DataCountSection(ArrayRef segments) : SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT), numSegments(llvm::count_if(segments, [](OutputSegment *const segment) { return segment->requiredInBinary(); })) {} void DataCountSection::writeBody() { writeUleb128(bodyOutputStream, numSegments, "data count"); } bool DataCountSection::isNeeded() const { return numSegments && config->sharedMemory; } void LinkingSection::writeBody() { raw_ostream &os = bodyOutputStream; writeUleb128(os, WasmMetadataVersion, "Version"); if (!symtabEntries.empty()) { SubSection sub(WASM_SYMBOL_TABLE); writeUleb128(sub.os, symtabEntries.size(), "num symbols"); for (const Symbol *sym : symtabEntries) { assert(sym->isDefined() || sym->isUndefined()); WasmSymbolType kind = sym->getWasmType(); uint32_t flags = sym->flags; writeU8(sub.os, kind, "sym kind"); writeUleb128(sub.os, flags, "sym flags"); if (auto *f = dyn_cast(sym)) { if (auto *d = dyn_cast(sym)) { writeUleb128(sub.os, d->getExportedFunctionIndex(), "index"); } else { writeUleb128(sub.os, f->getFunctionIndex(), "index"); } if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) writeStr(sub.os, sym->getName(), "sym name"); } else if (auto *g = dyn_cast(sym)) { writeUleb128(sub.os, g->getGlobalIndex(), "index"); if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) writeStr(sub.os, sym->getName(), "sym name"); } else if (auto *t = dyn_cast(sym)) { writeUleb128(sub.os, t->getTagIndex(), "index"); if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) writeStr(sub.os, sym->getName(), "sym name"); } else if (auto *t = dyn_cast(sym)) { writeUleb128(sub.os, t->getTableNumber(), "table number"); if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) writeStr(sub.os, sym->getName(), "sym name"); } else if (isa(sym)) { writeStr(sub.os, sym->getName(), "sym name"); if (auto *dataSym = dyn_cast(sym)) { if (dataSym->segment) { writeUleb128(sub.os, dataSym->getOutputSegmentIndex(), "index"); writeUleb128(sub.os, dataSym->getOutputSegmentOffset(), "data offset"); } else { writeUleb128(sub.os, 0, "index"); writeUleb128(sub.os, dataSym->getVA(), "data offset"); } writeUleb128(sub.os, dataSym->getSize(), "data size"); } } else { auto *s = cast(sym); writeUleb128(sub.os, s->section->sectionIndex, "sym section index"); } } sub.writeTo(os); } if (dataSegments.size()) { SubSection sub(WASM_SEGMENT_INFO); writeUleb128(sub.os, dataSegments.size(), "num data segments"); for (const OutputSegment *s : dataSegments) { writeStr(sub.os, s->name, "segment name"); writeUleb128(sub.os, s->alignment, "alignment"); writeUleb128(sub.os, s->linkingFlags, "flags"); } sub.writeTo(os); } if (!initFunctions.empty()) { SubSection sub(WASM_INIT_FUNCS); writeUleb128(sub.os, initFunctions.size(), "num init functions"); for (const WasmInitEntry &f : initFunctions) { writeUleb128(sub.os, f.priority, "priority"); writeUleb128(sub.os, f.sym->getOutputSymbolIndex(), "function index"); } sub.writeTo(os); } struct ComdatEntry { unsigned kind; uint32_t index; }; std::map> comdats; for (const InputFunction *f : out.functionSec->inputFunctions) { StringRef comdat = f->getComdatName(); if (!comdat.empty()) comdats[comdat].emplace_back( ComdatEntry{WASM_COMDAT_FUNCTION, f->getFunctionIndex()}); } for (uint32_t i = 0; i < dataSegments.size(); ++i) { const auto &inputSegments = dataSegments[i]->inputSegments; if (inputSegments.empty()) continue; StringRef comdat = inputSegments[0]->getComdatName(); #ifndef NDEBUG for (const InputChunk *isec : inputSegments) assert(isec->getComdatName() == comdat); #endif if (!comdat.empty()) comdats[comdat].emplace_back(ComdatEntry{WASM_COMDAT_DATA, i}); } if (!comdats.empty()) { SubSection sub(WASM_COMDAT_INFO); writeUleb128(sub.os, comdats.size(), "num comdats"); for (const auto &c : comdats) { writeStr(sub.os, c.first, "comdat name"); writeUleb128(sub.os, 0, "comdat flags"); // flags for future use writeUleb128(sub.os, c.second.size(), "num entries"); for (const ComdatEntry &entry : c.second) { writeU8(sub.os, entry.kind, "entry kind"); writeUleb128(sub.os, entry.index, "entry index"); } } sub.writeTo(os); } } void LinkingSection::addToSymtab(Symbol *sym) { sym->setOutputSymbolIndex(symtabEntries.size()); symtabEntries.emplace_back(sym); } unsigned NameSection::numNamedFunctions() const { unsigned numNames = out.importSec->getNumImportedFunctions(); for (const InputFunction *f : out.functionSec->inputFunctions) if (!f->name.empty() || !f->debugName.empty()) ++numNames; return numNames; } unsigned NameSection::numNamedGlobals() const { unsigned numNames = out.importSec->getNumImportedGlobals(); for (const InputGlobal *g : out.globalSec->inputGlobals) if (!g->getName().empty()) ++numNames; numNames += out.globalSec->internalGotSymbols.size(); return numNames; } unsigned NameSection::numNamedDataSegments() const { unsigned numNames = 0; for (const OutputSegment *s : segments) if (!s->name.empty() && s->requiredInBinary()) ++numNames; return numNames; } // Create the custom "name" section containing debug symbol names. void NameSection::writeBody() { { SubSection sub(WASM_NAMES_MODULE); StringRef moduleName = config->soName; if (config->soName.empty()) moduleName = llvm::sys::path::filename(config->outputFile); writeStr(sub.os, moduleName, "module name"); sub.writeTo(bodyOutputStream); } unsigned count = numNamedFunctions(); if (count) { SubSection sub(WASM_NAMES_FUNCTION); writeUleb128(sub.os, count, "name count"); // Function names appear in function index order. As it happens // importedSymbols and inputFunctions are numbered in order with imported // functions coming first. for (const Symbol *s : out.importSec->importedSymbols) { if (auto *f = dyn_cast(s)) { writeUleb128(sub.os, f->getFunctionIndex(), "func index"); writeStr(sub.os, toString(*s), "symbol name"); } } for (const InputFunction *f : out.functionSec->inputFunctions) { if (!f->name.empty()) { writeUleb128(sub.os, f->getFunctionIndex(), "func index"); if (!f->debugName.empty()) { writeStr(sub.os, f->debugName, "symbol name"); } else { writeStr(sub.os, maybeDemangleSymbol(f->name), "symbol name"); } } } sub.writeTo(bodyOutputStream); } count = numNamedGlobals(); if (count) { SubSection sub(WASM_NAMES_GLOBAL); writeUleb128(sub.os, count, "name count"); for (const Symbol *s : out.importSec->importedSymbols) { if (auto *g = dyn_cast(s)) { writeUleb128(sub.os, g->getGlobalIndex(), "global index"); writeStr(sub.os, toString(*s), "symbol name"); } } for (const Symbol *s : out.importSec->gotSymbols) { writeUleb128(sub.os, s->getGOTIndex(), "global index"); writeStr(sub.os, toString(*s), "symbol name"); } for (const InputGlobal *g : out.globalSec->inputGlobals) { if (!g->getName().empty()) { writeUleb128(sub.os, g->getAssignedIndex(), "global index"); writeStr(sub.os, maybeDemangleSymbol(g->getName()), "symbol name"); } } for (Symbol *s : out.globalSec->internalGotSymbols) { writeUleb128(sub.os, s->getGOTIndex(), "global index"); if (isa(s)) writeStr(sub.os, "GOT.func.internal." + toString(*s), "symbol name"); else writeStr(sub.os, "GOT.data.internal." + toString(*s), "symbol name"); } sub.writeTo(bodyOutputStream); } count = numNamedDataSegments(); if (count) { SubSection sub(WASM_NAMES_DATA_SEGMENT); writeUleb128(sub.os, count, "name count"); for (OutputSegment *s : segments) { if (!s->name.empty() && s->requiredInBinary()) { writeUleb128(sub.os, s->index, "global index"); writeStr(sub.os, s->name, "segment name"); } } sub.writeTo(bodyOutputStream); } } void ProducersSection::addInfo(const WasmProducerInfo &info) { for (auto &producers : {std::make_pair(&info.Languages, &languages), std::make_pair(&info.Tools, &tools), std::make_pair(&info.SDKs, &sDKs)}) for (auto &producer : *producers.first) if (llvm::none_of(*producers.second, [&](std::pair seen) { return seen.first == producer.first; })) producers.second->push_back(producer); } void ProducersSection::writeBody() { auto &os = bodyOutputStream; writeUleb128(os, fieldCount(), "field count"); for (auto &field : {std::make_pair("language", languages), std::make_pair("processed-by", tools), std::make_pair("sdk", sDKs)}) { if (field.second.empty()) continue; writeStr(os, field.first, "field name"); writeUleb128(os, field.second.size(), "number of entries"); for (auto &entry : field.second) { writeStr(os, entry.first, "producer name"); writeStr(os, entry.second, "producer version"); } } } void TargetFeaturesSection::writeBody() { SmallVector emitted(features.begin(), features.end()); llvm::sort(emitted); auto &os = bodyOutputStream; writeUleb128(os, emitted.size(), "feature count"); for (auto &feature : emitted) { writeU8(os, WASM_FEATURE_PREFIX_USED, "feature used prefix"); writeStr(os, feature, "feature name"); } } void RelocSection::writeBody() { uint32_t count = sec->getNumRelocations(); assert(sec->sectionIndex != UINT32_MAX); writeUleb128(bodyOutputStream, sec->sectionIndex, "reloc section"); writeUleb128(bodyOutputStream, count, "reloc count"); sec->writeRelocations(bodyOutputStream); } static size_t getHashSize() { switch (config->buildId) { case BuildIdKind::Fast: case BuildIdKind::Uuid: return 16; case BuildIdKind::Sha1: return 20; case BuildIdKind::Hexstring: return config->buildIdVector.size(); case BuildIdKind::None: return 0; } llvm_unreachable("build id kind not implemented"); } BuildIdSection::BuildIdSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, buildIdSectionName), hashSize(getHashSize()) {} void BuildIdSection::writeBody() { LLVM_DEBUG(llvm::dbgs() << "BuildId writebody\n"); // Write hash size auto &os = bodyOutputStream; writeUleb128(os, hashSize, "build id size"); writeBytes(os, std::vector(hashSize, ' ').data(), hashSize, "placeholder"); } void BuildIdSection::writeBuildId(llvm::ArrayRef buf) { assert(buf.size() == hashSize); LLVM_DEBUG(dbgs() << "buildid write " << buf.size() << " " << hashPlaceholderPtr << '\n'); memcpy(hashPlaceholderPtr, buf.data(), hashSize); } } // namespace wasm::lld