Files
clang-p2996/lld/wasm/SyntheticSections.cpp
Thomas Lively d851fce4cb [lld][WebAssembly] Do not emit initialization for .bss segments
Summary:
This patch fixes a bug where initialization code for .bss segments was
emitted in the memory initialization function even though the .bss
segments were discounted in the datacount section and omitted in the
data section. This was producing invalid binaries due to out-of-bounds
segment indices on the memory.init and data.drop instructions that
were trying to operate on the nonexistent .bss segments.

Reviewers: sbc100

Subscribers: dschuff, jgravelle-google, aheejin, sunfish, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D80354
2020-05-21 11:33:25 -07:00

583 lines
18 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 "InputEvent.h"
#include "InputGlobal.h"
#include "OutputSegment.h"
#include "SymbolTable.h"
#include "llvm/Support/Path.h"
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
void DylinkSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, memSize, "MemSize");
writeUleb128(os, memAlign, "MemAlign");
writeUleb128(os, out.elemSec->numEntries(), "TableSize");
writeUleb128(os, 0, "TableAlign");
writeUleb128(os, symtab->sharedFiles.size(), "Needed");
for (auto *so : symtab->sharedFiles)
writeStr(os, llvm::sys::path::filename(so->getName()), "so name");
}
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->importMemory)
++numImports;
if (config->importTable)
++numImports;
return numImports;
}
void ImportSection::addGOTEntry(Symbol *sym) {
assert(!isSealed);
if (sym->hasGOTIndex())
return;
LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n");
sym->setGOTIndex(numImportedGlobals++);
gotSymbols.push_back(sym);
}
void ImportSection::addImport(Symbol *sym) {
assert(!isSealed);
importedSymbols.emplace_back(sym);
if (auto *f = dyn_cast<FunctionSymbol>(sym))
f->setFunctionIndex(numImportedFunctions++);
else if (auto *g = dyn_cast<GlobalSymbol>(sym))
g->setGlobalIndex(numImportedGlobals++);
else
cast<EventSymbol>(sym)->setEventIndex(numImportedEvents++);
}
void ImportSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, getNumImports(), "import count");
if (config->importMemory) {
WasmImport import;
import.Module = defaultModule;
import.Field = "memory";
import.Kind = WASM_EXTERNAL_MEMORY;
import.Memory.Flags = 0;
import.Memory.Initial = 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;
writeImport(os, import);
}
if (config->importTable) {
uint32_t tableSize = config->tableBase + out.elemSec->numEntries();
WasmImport import;
import.Module = defaultModule;
import.Field = functionTableName;
import.Kind = WASM_EXTERNAL_TABLE;
import.Table.ElemType = WASM_TYPE_FUNCREF;
import.Table.Limits = {0, tableSize, 0};
writeImport(os, import);
}
for (const Symbol *sym : importedSymbols) {
WasmImport import;
if (auto *f = dyn_cast<UndefinedFunction>(sym)) {
import.Field = f->importName ? *f->importName : sym->getName();
import.Module = f->importModule ? *f->importModule : defaultModule;
} else if (auto *g = dyn_cast<UndefinedGlobal>(sym)) {
import.Field = g->importName ? *g->importName : sym->getName();
import.Module = g->importModule ? *g->importModule : defaultModule;
} else {
import.Field = sym->getName();
import.Module = 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 {
auto *eventSym = cast<EventSymbol>(sym);
import.Kind = WASM_EXTERNAL_EVENT;
import.Event.Attribute = eventSym->getEventType()->Attribute;
import.Event.SigIndex = out.typeSec->lookupType(*eventSym->signature);
}
writeImport(os, import);
}
for (const Symbol *sym : gotSymbols) {
WasmImport import;
import.Kind = WASM_EXTERNAL_GLOBAL;
import.Global = {WASM_TYPE_I32, 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() {
uint32_t tableSize = config->tableBase + out.elemSec->numEntries();
raw_ostream &os = bodyOutputStream;
writeUleb128(os, 1, "table count");
WasmLimits limits;
if (config->growableTable)
limits = {0, tableSize, 0};
else
limits = {WASM_LIMITS_FLAG_HAS_MAX, tableSize, tableSize};
writeTableType(os, WasmTable{WASM_TYPE_FUNCREF, limits});
}
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;
writeUleb128(os, flags, "memory limits flags");
writeUleb128(os, numMemoryPages, "initial pages");
if (hasMax)
writeUleb128(os, maxMemoryPages, "max pages");
}
void EventSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, inputEvents.size(), "event count");
for (InputEvent *e : inputEvents) {
e->event.Type.SigIndex = out.typeSec->lookupType(e->signature);
writeEvent(os, e->event);
}
}
void EventSection::addEvent(InputEvent *event) {
if (!event->live)
return;
uint32_t eventIndex =
out.importSec->getNumImportedEvents() + inputEvents.size();
LLVM_DEBUG(dbgs() << "addEvent: " << eventIndex << "\n");
event->setEventIndex(eventIndex);
inputEvents.push_back(event);
}
void GlobalSection::assignIndexes() {
uint32_t globalIndex = out.importSec->getNumImportedGlobals();
for (InputGlobal *g : inputGlobals)
g->setGlobalIndex(globalIndex++);
for (Symbol *sym : staticGotSymbols)
sym->setGOTIndex(globalIndex++);
isSealed = true;
}
void GlobalSection::addStaticGOTEntry(Symbol *sym) {
assert(!isSealed);
if (sym->requiresGOT)
return;
LLVM_DEBUG(dbgs() << "addStaticGOTEntry: " << sym->getName() << " "
<< toString(sym->kind()) << "\n");
sym->requiresGOT = true;
if (auto *F = dyn_cast<FunctionSymbol>(sym))
out.elemSec->addEntry(F);
staticGotSymbols.push_back(sym);
}
void GlobalSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, numGlobals(), "global count");
for (InputGlobal *g : inputGlobals)
writeGlobal(os, g->global);
for (const Symbol *sym : staticGotSymbols) {
WasmGlobal global;
global.Type = {WASM_TYPE_I32, false};
global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
if (auto *d = dyn_cast<DefinedData>(sym))
global.InitExpr.Value.Int32 = d->getVirtualAddress();
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
global.InitExpr.Value.Int32 = f->getTableIndex();
else {
assert(isa<UndefinedData>(sym));
global.InitExpr.Value.Int32 = 0;
}
writeGlobal(os, global);
}
for (const DefinedData *sym : dataAddressGlobals) {
WasmGlobal global;
global.Type = {WASM_TYPE_I32, false};
global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
global.InitExpr.Value.Int32 = sym->getVirtualAddress();
writeGlobal(os, global);
}
}
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 !config->relocatable && hasInitializedSegments && config->sharedMemory;
}
void StartSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, WasmSym::initMemory->getFunctionIndex(), "function index");
}
void ElemSection::addEntry(FunctionSymbol *sym) {
if (sym->hasTableIndex())
return;
sym->setTableIndex(config->tableBase + indirectFunctions.size());
indirectFunctions.emplace_back(sym);
}
void ElemSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, 1, "segment count");
writeUleb128(os, 0, "table index");
WasmInitExpr initExpr;
if (config->isPic) {
initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Value.Global = WasmSym::tableBase->getGlobalIndex();
} else {
initExpr.Opcode = WASM_OPCODE_I32_CONST;
initExpr.Value.Int32 = config->tableBase;
}
writeInitExpr(os, initExpr);
writeUleb128(os, indirectFunctions.size(), "elem count");
uint32_t tableIndex = config->tableBase;
for (const FunctionSymbol *sym : indirectFunctions) {
assert(sym->getTableIndex() == tableIndex);
writeUleb128(os, sym->getFunctionIndex(), "function index");
++tableIndex;
}
}
DataCountSection::DataCountSection(ArrayRef<OutputSegment *> segments)
: SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT),
numSegments(std::count_if(
segments.begin(), segments.end(),
[](OutputSegment *const segment) { return !segment->isBss; })) {}
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)) {
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 *e = dyn_cast<EventSymbol>(sym)) {
writeUleb128(sub.os, e->getEventIndex(), "index");
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, 0, "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 InputSegment *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::numNames() const {
unsigned numNames = out.importSec->getNumImportedFunctions();
for (const InputFunction *f : out.functionSec->inputFunctions)
if (!f->getName().empty() || !f->getDebugName().empty())
++numNames;
return numNames;
}
// Create the custom "name" section containing debug symbol names.
void NameSection::writeBody() {
SubSection sub(WASM_NAMES_FUNCTION);
writeUleb128(sub.os, numNames(), "name count");
// Names must 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->getName().empty()) {
writeUleb128(sub.os, f->getFunctionIndex(), "func index");
if (!f->getDebugName().empty()) {
writeStr(sub.os, f->getDebugName(), "symbol name");
} else {
writeStr(sub.os, maybeDemangleSymbol(f->getName()), "symbol 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 (producers.second->end() ==
llvm::find_if(*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