llvm-project/lld/wasm/SyntheticSections.cpp
Sam Clegg 22b7b84860
[lld][WebAssembly] Report undefined symbols in -shared/-pie builds (#75242)
Previously we would ignore all undefined symbols when using
`-shared` or `-pie`. All undefined symbols would be treated as imports
regardless of whether those symbols we defined in any shared library.
With this change we now track symbol in shared libraries and report
undefined symbols in the main program by default.
The old behavior is still available via the
`--unresolved-symbols=import-dynamic` command line flag.

This rationale for allowing this type of breaking change is that `-pie`
and `-shared` are both still experimental will warn as such, unless
`--experimental-pic` is passed.

As part of this change the linker now models shared library symbols
via new SharedFunctionSymbol and SharedDataSymbol types.

I've also added a new `--no-shlib-sigcheck` option that bypassed the
checking of functions signature in shared libraries. This is
specifically required by emscripten the case where the imports/exports
of shared libraries have been modified by via JS type legalization (this
is only needed when targeting old JS engines where bigint is not yet
available                                         

See https://github.com/emscripten-core/emscripten/issues/18198
2024-07-12 13:26:52 -07:00

953 lines
31 KiB
C++

//===- 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::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<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() << "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<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 (ctx.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) || isa<SharedData>(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<DefinedData>(sym)) {
if (!sym->isTLS()) {
globalIdx = WasmSym::memoryBase->getGlobalIndex();
offset = d->getVA();
useExtendedConst = true;
}
} else if (auto *f = dyn_cast<FunctionSymbol>(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<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) || isa<SharedData>(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<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)) {
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<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() {
{
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<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);
}
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<char>(hashSize, ' ').data(), hashSize,
"placeholder");
}
void BuildIdSection::writeBuildId(llvm::ArrayRef<uint8_t> buf) {
assert(buf.size() == hashSize);
LLVM_DEBUG(dbgs() << "buildid write " << buf.size() << " "
<< hashPlaceholderPtr << '\n');
memcpy(hashPlaceholderPtr, buf.data(), hashSize);
}
} // namespace wasm::lld