blob: 5808ebb8da3d06a1fc9f6640ec540b5c4a085f47 [file] [log] [blame]
//===- 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 <optional>
using namespace llvm;
using namespace llvm::wasm;
namespace lld {
namespace 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 config->isPic ||
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic ||
!symtab->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 (symtab->sharedFiles.size()) {
SubSection sub(WASM_DYLINK_NEEDED);
writeUleb128(sub.os, symtab->sharedFiles.size(), "Needed");
for (auto *so : symtab->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<const Symbol *> importInfo;
std::vector<const Symbol *> exportInfo;
for (const Symbol *sym : symtab->symbols()) {
if (sym->isLive()) {
if (sym->isExported() && sym->isTLS() && isa<DefinedData>(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<DefinedFunction>(sym)) {
if (std::optional<StringRef> 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() << "type " << 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 (config->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<FunctionSymbol>(sym)) {
ImportKey<WasmSignature> 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<GlobalSymbol>(sym)) {
ImportKey<WasmGlobalType> 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<TagSymbol>(sym)) {
ImportKey<WasmSignature> 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<TableSymbol>(sym);
ImportKey<WasmTableType> 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<FunctionSymbol>(sym)) {
import.Kind = WASM_EXTERNAL_FUNCTION;
import.SigIndex = out.typeSec->lookupType(*functionSym->signature);
} else if (auto *globalSym = dyn_cast<GlobalSymbol>(sym)) {
import.Kind = WASM_EXTERNAL_GLOBAL;
import.Global = *globalSym->getGlobalType();
} else if (auto *tagSym = dyn_cast<TagSymbol>(sym)) {
import.Kind = WASM_EXTERNAL_TAG;
import.SigIndex = out.typeSec->lookupType(*tagSym->signature);
} else {
auto *tableSym = cast<TableSymbol>(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<uint8_t>(ptrType), true};
if (isa<DataSymbol>(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 (config->legacyFunctionTable &&
isa<DefinedTable>(WasmSym::indirectFunctionTable) &&
cast<DefinedTable>(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<UndefinedTable>(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<FunctionSymbol>(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<DefinedData>(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<FunctionSymbol>(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<UndefinedData>(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 && config->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);
if (config->extendedConst && config->isPic && !sym->isTLS() &&
isa<DefinedData>(sym)) {
// We can use an extended init expression to add a constant
// offset of __memory_base.
auto *d = cast<DefinedData>(sym);
writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get");
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
"literal (global index)");
if (d->getVA()) {
writePtrConst(os, d->getVA(), 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<DefinedData>(sym))
initExpr = intConst(d->getVA(), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
assert(isa<UndefinedData>(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 (config->isPic) {
initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Inst.Value.Global =
(config->is64.value_or(false) ? WasmSym::tableBase32
: WasmSym::tableBase)
->getGlobalIndex();
} else {
initExpr.Inst.Opcode = 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<OutputSegment *> 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<FunctionSymbol>(sym)) {
if (auto *d = dyn_cast<DefinedFunction>(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<GlobalSymbol>(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<TagSymbol>(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<TableSymbol>(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<DataSymbol>(sym)) {
writeStr(sub.os, sym->getName(), "sym name");
if (auto *dataSym = dyn_cast<DefinedData>(sym)) {
writeUleb128(sub.os, dataSym->getOutputSegmentIndex(), "index");
writeUleb128(sub.os, dataSym->getOutputSegmentOffset(),
"data offset");
writeUleb128(sub.os, dataSym->getSize(), "data size");
}
} else {
auto *s = cast<OutputSectionSymbol>(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<StringRef, std::vector<ComdatEntry>> 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() {
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<FunctionSymbol>(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<GlobalSymbol>(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<FunctionSymbol>(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<std::string, std::string> 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<std::string, 8> 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);
}
} // namespace wasm
} // namespace lld