[WebAssembly] Refactor synthetic sections and relocation processing. NFC.

Major refactor to better match the structure of the ELF linker.

- Split out relocation processing into scanRelocations
- Split out synthetic sections into their own classes.

Differential Revision: https://reviews.llvm.org/D61811

llvm-svn: 361233
This commit is contained in:
Sam Clegg 2019-05-21 09:13:09 +00:00
parent f33f181678
commit 8fcf012693
13 changed files with 1185 additions and 930 deletions

View file

@ -13,8 +13,10 @@ add_lld_library(lldWasm
LTO.cpp
MarkLive.cpp
OutputSections.cpp
Relocations.cpp
SymbolTable.cpp
Symbols.cpp
SyntheticSections.cpp
Writer.cpp
WriterUtils.cpp
@ -35,4 +37,4 @@ add_lld_library(lldWasm
DEPENDS
WasmOptionsTableGen
${tablegen_deps}
)
)

View file

@ -31,6 +31,7 @@ namespace wasm {
class ObjFile;
class OutputSegment;
class OutputSection;
class InputChunk {
public:
@ -207,6 +208,8 @@ public:
StringRef getDebugName() const override { return StringRef(); }
uint32_t getComdat() const override { return UINT32_MAX; }
OutputSection *OutputSec = nullptr;
protected:
ArrayRef<uint8_t> data() const override { return Section.Content; }

View file

@ -75,7 +75,10 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &Reloc) const {
assert(TypeIsUsed[Reloc.Index]);
return TypeMap[Reloc.Index];
}
return Symbols[Reloc.Index]->getOutputSymbolIndex();
const Symbol *Sym = Symbols[Reloc.Index];
if (auto *SS = dyn_cast<SectionSymbol>(Sym))
Sym = SS->getOutputSectionSymbol();
return Sym->getOutputSymbolIndex();
}
// Relocations can contain addend for combined sections. This function takes a
@ -395,7 +398,7 @@ Symbol *ObjFile::createDefined(const WasmSymbol &Sym) {
case WASM_SYMBOL_TYPE_SECTION: {
InputSection *Section = CustomSectionsByIndex[Sym.Info.ElementIndex];
assert(Sym.isBindingLocal());
return make<SectionSymbol>(Name, Flags, Section, this);
return make<SectionSymbol>(Flags, Section, this);
}
case WASM_SYMBOL_TYPE_EVENT: {
InputEvent *Event =

View file

@ -206,6 +206,7 @@ void CustomSection::finalizeContents() {
for (InputSection *Section : InputSections) {
Section->OutputOffset = PayloadSize;
Section->OutputSec = this;
PayloadSize += Section->getSize();
}
@ -241,9 +242,3 @@ void CustomSection::writeRelocations(raw_ostream &OS) const {
for (const InputSection *S : InputSections)
S->writeRelocations(OS);
}
void RelocSection::writeBody() {
writeUleb128(BodyOutputStream, SectionIndex, "reloc section");
writeUleb128(BodyOutputStream, Sec->numRelocations(), "reloc count");
Sec->writeRelocations(BodyOutputStream);
}

View file

@ -38,6 +38,7 @@ public:
Offset = NewOffset;
}
void createHeader(size_t BodySize);
virtual bool isNeeded() const { return true; }
virtual size_t getSize() const = 0;
virtual void writeTo(uint8_t *Buf) = 0;
virtual void finalizeContents() = 0;
@ -46,54 +47,24 @@ public:
std::string Header;
uint32_t Type;
uint32_t SectionIndex = UINT32_MAX;
std::string Name;
OutputSectionSymbol *SectionSym = nullptr;
protected:
size_t Offset = 0;
};
class SyntheticSection : public OutputSection {
public:
SyntheticSection(uint32_t Type, std::string Name = "")
: OutputSection(Type, Name), BodyOutputStream(Body) {
if (!Name.empty())
writeStr(BodyOutputStream, Name, "section name");
}
void writeTo(uint8_t *Buf) override {
assert(Offset);
log("writing " + toString(*this));
memcpy(Buf + Offset, Header.data(), Header.size());
memcpy(Buf + Offset + Header.size(), Body.data(), Body.size());
}
size_t getSize() const override { return Header.size() + Body.size(); }
virtual void writeBody() {}
void finalizeContents() override {
writeBody();
BodyOutputStream.flush();
createHeader(Body.size());
}
raw_ostream &getStream() { return BodyOutputStream; }
std::string Body;
protected:
llvm::raw_string_ostream BodyOutputStream;
};
class CodeSection : public OutputSection {
public:
explicit CodeSection(ArrayRef<InputFunction *> Functions)
: OutputSection(llvm::wasm::WASM_SEC_CODE), Functions(Functions) {}
size_t getSize() const override { assert(BodySize); return Header.size() + BodySize; }
size_t getSize() const override { return Header.size() + BodySize; }
void writeTo(uint8_t *Buf) override;
uint32_t numRelocations() const override;
void writeRelocations(raw_ostream &OS) const override;
bool isNeeded() const override { return Functions.size() > 0; }
void finalizeContents() override;
protected:
@ -111,6 +82,7 @@ public:
void writeTo(uint8_t *Buf) override;
uint32_t numRelocations() const override;
void writeRelocations(raw_ostream &OS) const override;
bool isNeeded() const override { return Segments.size() > 0; }
void finalizeContents() override;
protected:
@ -145,19 +117,6 @@ protected:
std::string NameData;
};
class RelocSection : public SyntheticSection {
public:
RelocSection(StringRef Name, OutputSection *Sec, uint32_t SectionIndex)
: SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, Name), Sec(Sec),
SectionIndex(SectionIndex) {}
void writeBody() override;
protected:
OutputSection* Sec;
uint32_t SectionIndex;
};
} // namespace wasm
} // namespace lld

86
lld/wasm/Relocations.cpp Normal file
View file

@ -0,0 +1,86 @@
//===- Relocations.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
//
//===----------------------------------------------------------------------===//
#include "Relocations.h"
#include "InputChunks.h"
#include "SyntheticSections.h"
using namespace llvm;
using namespace llvm::wasm;
using namespace lld;
using namespace lld::wasm;
static bool requiresGOTAccess(const Symbol *Sym) {
return Config->Pic && !Sym->isHidden() && !Sym->isLocal();
}
void lld::wasm::scanRelocations(InputChunk *Chunk) {
if (!Chunk->Live)
return;
ObjFile *File = Chunk->File;
ArrayRef<WasmSignature> Types = File->getWasmObj()->types();
for (const WasmRelocation &Reloc : Chunk->getRelocations()) {
if (Reloc.Type == R_WASM_TYPE_INDEX_LEB) {
// Mark target type as live
File->TypeMap[Reloc.Index] =
Out.TypeSec->registerType(Types[Reloc.Index]);
File->TypeIsUsed[Reloc.Index] = true;
continue;
}
// Other relocation types all have a corresponding symbol
Symbol *Sym = File->getSymbols()[Reloc.Index];
switch (Reloc.Type) {
case R_WASM_TABLE_INDEX_I32:
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
if (requiresGOTAccess(Sym))
break;
Out.ElemSec->addEntry(cast<FunctionSymbol>(Sym));
break;
case R_WASM_GLOBAL_INDEX_LEB:
if (!isa<GlobalSymbol>(Sym))
Out.ImportSec->addGOTEntry(Sym);
break;
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
if (!Config->Relocatable && Sym->isUndefined() && !Sym->isWeak()) {
error(toString(File) + ": cannot resolve relocation of type " +
relocTypeToString(Reloc.Type) +
" against undefined (non-weak) data symbol: " + toString(*Sym));
}
break;
}
if (Config->Pic) {
switch (Reloc.Type) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_LEB:
// Certain relocation types can't be used when building PIC output,
// since they would require absolute symbol addresses at link time.
error(toString(File) + ": relocation " + relocTypeToString(Reloc.Type) +
" cannot be used againt symbol " + toString(*Sym) +
"; recompile with -fPIC");
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
// These relocation types are only present in the data section and
// will be converted into code by `generateRelocationCode`. This code
// requires the symbols to have GOT entires.
if (requiresGOTAccess(Sym))
Out.ImportSec->addGOTEntry(Sym);
break;
}
}
}
}

22
lld/wasm/Relocations.h Normal file
View file

@ -0,0 +1,22 @@
//===- Relocations.h -------------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLD_WASM_RELOCATIONS_H
#define LLD_WASM_RELOCATIONS_H
namespace lld {
namespace wasm {
class InputChunk;
void scanRelocations(InputChunk *Chunk);
} // namespace wasm
} // namespace lld
#endif

View file

@ -12,6 +12,7 @@
#include "InputEvent.h"
#include "InputFiles.h"
#include "InputGlobal.h"
#include "OutputSections.h"
#include "OutputSegment.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
@ -41,7 +42,7 @@ WasmSymbolType Symbol::getWasmType() const {
return WASM_SYMBOL_TYPE_GLOBAL;
if (isa<EventSymbol>(this))
return WASM_SYMBOL_TYPE_EVENT;
if (isa<SectionSymbol>(this))
if (isa<SectionSymbol>(this) || isa<OutputSectionSymbol>(this))
return WASM_SYMBOL_TYPE_SECTION;
llvm_unreachable("invalid symbol kind");
}
@ -262,17 +263,9 @@ DefinedEvent::DefinedEvent(StringRef Name, uint32_t Flags, InputFile *File,
Event ? &Event->Signature : nullptr),
Event(Event) {}
uint32_t SectionSymbol::getOutputSectionIndex() const {
LLVM_DEBUG(dbgs() << "getOutputSectionIndex: " << getName() << "\n");
assert(OutputSectionIndex != INVALID_INDEX);
return OutputSectionIndex;
}
void SectionSymbol::setOutputSectionIndex(uint32_t Index) {
LLVM_DEBUG(dbgs() << "setOutputSectionIndex: " << getName() << " -> " << Index
<< "\n");
assert(Index != INVALID_INDEX);
OutputSectionIndex = Index;
const OutputSectionSymbol *SectionSymbol::getOutputSectionSymbol() const {
assert(Section->OutputSec && Section->OutputSec->SectionSym);
return Section->OutputSec->SectionSym;
}
void LazySymbol::fetch() { cast<ArchiveFile>(File)->addMember(&ArchiveSymbol); }
@ -308,6 +301,8 @@ std::string lld::toString(wasm::Symbol::Kind Kind) {
return "LazyKind";
case wasm::Symbol::SectionKind:
return "SectionKind";
case wasm::Symbol::OutputSectionKind:
return "OutputSectionKind";
}
llvm_unreachable("invalid symbol kind");
}
@ -324,3 +319,6 @@ void lld::wasm::printTraceSymbol(Symbol *Sym) {
message(toString(Sym->getFile()) + S + Sym->getName());
}
const char *lld::wasm::DefaultModule = "env";
const char *lld::wasm::FunctionTableName = "__indirect_function_table";

View file

@ -17,6 +17,14 @@
namespace lld {
namespace wasm {
// Shared string constants
// The default module name to use for symbol imports.
extern const char *DefaultModule;
// The name under which to import or export the wasm table.
extern const char *FunctionTableName;
using llvm::wasm::WasmSymbolType;
class InputFile;
@ -26,6 +34,7 @@ class InputFunction;
class InputGlobal;
class InputEvent;
class InputSection;
class OutputSection;
#define INVALID_INDEX UINT32_MAX
@ -38,6 +47,7 @@ public:
DefinedGlobalKind,
DefinedEventKind,
SectionKind,
OutputSectionKind,
UndefinedFunctionKind,
UndefinedDataKind,
UndefinedGlobalKind,
@ -46,11 +56,7 @@ public:
Kind kind() const { return SymbolKind; }
bool isDefined() const {
return SymbolKind == DefinedFunctionKind || SymbolKind == DefinedDataKind ||
SymbolKind == DefinedGlobalKind || SymbolKind == DefinedEventKind ||
SymbolKind == SectionKind;
}
bool isDefined() const { return !isLazy() && !isUndefined(); }
bool isUndefined() const {
return SymbolKind == UndefinedFunctionKind ||
@ -195,21 +201,33 @@ public:
StringRef ImportModule;
};
// Section symbols for output sections are different from those for input
// section. These are generated by the linker and point the OutputSection
// rather than an InputSection.
class OutputSectionSymbol : public Symbol {
public:
OutputSectionSymbol(const OutputSection *S)
: Symbol("", OutputSectionKind, llvm::wasm::WASM_SYMBOL_BINDING_LOCAL,
nullptr),
Section(S) {}
static bool classof(const Symbol *S) {
return S->kind() == OutputSectionKind;
}
const OutputSection *Section;
};
class SectionSymbol : public Symbol {
public:
SectionSymbol(uint32_t Flags, const InputSection *S, InputFile *F = nullptr)
: Symbol("", SectionKind, Flags, F), Section(S) {}
static bool classof(const Symbol *S) { return S->kind() == SectionKind; }
SectionSymbol(StringRef Name, uint32_t Flags, const InputSection *S,
InputFile *F = nullptr)
: Symbol(Name, SectionKind, Flags, F), Section(S) {}
const OutputSectionSymbol *getOutputSectionSymbol() const;
const InputSection *Section;
uint32_t getOutputSectionIndex() const;
void setOutputSectionIndex(uint32_t Index);
protected:
uint32_t OutputSectionIndex = INVALID_INDEX;
};
class DataSymbol : public Symbol {

View file

@ -0,0 +1,543 @@
//===- 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;
using namespace lld;
using namespace lld::wasm;
OutStruct lld::wasm::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::numImports() const {
uint32_t NumImports = ImportedSymbols.size() + GOTSymbols.size();
if (Config->ImportMemory)
++NumImports;
if (Config->ImportTable)
++NumImports;
return NumImports;
}
void ImportSection::addGOTEntry(Symbol *Sym) {
if (Sym->hasGOTIndex())
return;
Sym->setGOTIndex(NumImportedGlobals++);
GOTSymbols.push_back(Sym);
}
void ImportSection::addImport(Symbol *Sym) {
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, numImports(), "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 = Out.ElemSec->ElemOffset + 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;
Import.Module = F->ImportModule;
} else if (auto *G = dyn_cast<UndefinedGlobal>(Sym)) {
Import.Field = G->ImportName;
Import.Module = G->ImportModule;
} 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->NumImportedFunctions + InputFunctions.size();
InputFunctions.emplace_back(Func);
Func->setFunctionIndex(FunctionIndex);
}
void TableSection::writeBody() {
uint32_t TableSize = Out.ElemSec->ElemOffset + Out.ElemSec->numEntries();
raw_ostream &OS = BodyOutputStream;
writeUleb128(OS, 1, "table count");
WasmLimits 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 GlobalSection::writeBody() {
raw_ostream &OS = BodyOutputStream;
writeUleb128(OS, numGlobals(), "global count");
for (const InputGlobal *G : InputGlobals)
writeGlobal(OS, G->Global);
for (const DefinedData *Sym : DefinedFakeGlobals) {
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) {
if (!Global->Live)
return;
uint32_t GlobalIndex =
Out.ImportSec->NumImportedGlobals + InputGlobals.size();
LLVM_DEBUG(dbgs() << "addGlobal: " << GlobalIndex << "\n");
Global->setGlobalIndex(GlobalIndex);
Out.GlobalSec->InputGlobals.push_back(Global);
}
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->NumImportedEvents + InputEvents.size();
LLVM_DEBUG(dbgs() << "addEvent: " << EventIndex << "\n");
Event->setEventIndex(EventIndex);
InputEvents.push_back(Event);
}
void ExportSection::writeBody() {
raw_ostream &OS = BodyOutputStream;
writeUleb128(OS, Exports.size(), "export count");
for (const WasmExport &Export : Exports)
writeExport(OS, Export);
}
void ElemSection::addEntry(FunctionSymbol *Sym) {
if (Sym->hasTableIndex())
return;
Sym->setTableIndex(ElemOffset + 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->Pic) {
InitExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
InitExpr.Value.Global = WasmSym::TableBase->getGlobalIndex();
} else {
InitExpr.Opcode = WASM_OPCODE_I32_CONST;
InitExpr.Value.Int32 = ElemOffset;
}
writeInitExpr(OS, InitExpr);
writeUleb128(OS, IndirectFunctions.size(), "elem count");
uint32_t TableIndex = ElemOffset;
for (const FunctionSymbol *Sym : IndirectFunctions) {
assert(Sym->getTableIndex() == TableIndex);
writeUleb128(OS, Sym->getFunctionIndex(), "function index");
++TableIndex;
}
}
void DataCountSection::writeBody() {
writeUleb128(BodyOutputStream, NumSegments, "data count");
}
bool DataCountSection::isNeeded() const {
return NumSegments && Out.TargetFeaturesSec->Features.count("bulk-memory");
}
static uint32_t getWasmFlags(const Symbol *Sym) {
uint32_t Flags = 0;
if (Sym->isLocal())
Flags |= WASM_SYMBOL_BINDING_LOCAL;
if (Sym->isWeak())
Flags |= WASM_SYMBOL_BINDING_WEAK;
if (Sym->isHidden())
Flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
if (Sym->isUndefined())
Flags |= WASM_SYMBOL_UNDEFINED;
if (auto *F = dyn_cast<UndefinedFunction>(Sym)) {
if (F->getName() != F->ImportName)
Flags |= WASM_SYMBOL_EXPLICIT_NAME;
} else if (auto *G = dyn_cast<UndefinedGlobal>(Sym)) {
if (G->getName() != G->ImportName)
Flags |= WASM_SYMBOL_EXPLICIT_NAME;
}
return Flags;
}
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 = getWasmFlags(Sym);
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 *IS : InputSegments)
assert(IS->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->NumImportedFunctions;
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->numRelocations();
assert(Sec->SectionIndex != UINT32_MAX);
writeUleb128(BodyOutputStream, Sec->SectionIndex, "reloc section");
writeUleb128(BodyOutputStream, Count, "reloc count");
Sec->writeRelocations(BodyOutputStream);
}

View file

@ -0,0 +1,323 @@
//===- SyntheticSection.h ---------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Synthetic sections represent chunks of linker-created data. If you
// need to create a chunk of data that to be included in some section
// in the result, you probably want to create that as a synthetic section.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_WASM_SYNTHETIC_SECTIONS_H
#define LLD_WASM_SYNTHETIC_SECTIONS_H
#include "OutputSections.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Object/WasmTraits.h"
#define DEBUG_TYPE "lld"
namespace lld {
namespace wasm {
// An init entry to be written to either the synthetic init func or the
// linking metadata.
struct WasmInitEntry {
const FunctionSymbol *Sym;
uint32_t Priority;
};
class SyntheticSection : public OutputSection {
public:
SyntheticSection(uint32_t Type, std::string Name = "")
: OutputSection(Type, Name), BodyOutputStream(Body) {
if (!Name.empty())
writeStr(BodyOutputStream, Name, "section name");
}
void writeTo(uint8_t *Buf) override {
assert(Offset);
log("writing " + toString(*this));
memcpy(Buf + Offset, Header.data(), Header.size());
memcpy(Buf + Offset + Header.size(), Body.data(), Body.size());
}
size_t getSize() const override { return Header.size() + Body.size(); }
virtual void writeBody() {}
void finalizeContents() override {
writeBody();
BodyOutputStream.flush();
createHeader(Body.size());
}
raw_ostream &getStream() { return BodyOutputStream; }
std::string Body;
protected:
llvm::raw_string_ostream BodyOutputStream;
};
// Create the custom "dylink" section containing information for the dynamic
// linker.
// See
// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
class DylinkSection : public SyntheticSection {
public:
DylinkSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "dylink") {}
bool isNeeded() const override { return Config->Pic; }
void writeBody() override;
uint32_t MemAlign = 0;
uint32_t MemSize = 0;
};
class TypeSection : public SyntheticSection {
public:
TypeSection() : SyntheticSection(llvm::wasm::WASM_SEC_TYPE) {}
bool isNeeded() const override { return Types.size() > 0; };
void writeBody() override;
uint32_t registerType(const WasmSignature &Sig);
uint32_t lookupType(const WasmSignature &Sig);
protected:
std::vector<const WasmSignature *> Types;
llvm::DenseMap<WasmSignature, int32_t> TypeIndices;
};
class ImportSection : public SyntheticSection {
public:
ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {}
bool isNeeded() const override { return numImports() > 0; }
void writeBody() override;
void addImport(Symbol *Sym);
void addGOTEntry(Symbol *Sym);
uint32_t numImports() const;
unsigned NumImportedGlobals = 0;
unsigned NumImportedFunctions = 0;
unsigned NumImportedEvents = 0;
std::vector<const Symbol *> ImportedSymbols;
protected:
std::vector<const Symbol *> GOTSymbols;
};
class FunctionSection : public SyntheticSection {
public:
FunctionSection() : SyntheticSection(llvm::wasm::WASM_SEC_FUNCTION) {}
bool isNeeded() const override { return InputFunctions.size() > 0; };
void writeBody() override;
void addFunction(InputFunction *Func);
std::vector<InputFunction *> InputFunctions;
protected:
};
class MemorySection : public SyntheticSection {
public:
MemorySection() : SyntheticSection(llvm::wasm::WASM_SEC_MEMORY) {}
bool isNeeded() const override { return !Config->ImportMemory; }
void writeBody() override;
uint32_t NumMemoryPages = 0;
uint32_t MaxMemoryPages = 0;
};
class TableSection : public SyntheticSection {
public:
TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {}
bool isNeeded() const override {
// Always output a table section (or table import), even if there are no
// indirect calls. There are two reasons for this:
// 1. For executables it is useful to have an empty table slot at 0
// which can be filled with a null function call handler.
// 2. If we don't do this, any program that contains a call_indirect but
// no address-taken function will fail at validation time since it is
// a validation error to include a call_indirect instruction if there
// is not table.
return !Config->ImportTable;
}
void writeBody() override;
};
class GlobalSection : public SyntheticSection {
public:
GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {}
uint32_t numGlobals() const {
return InputGlobals.size() + DefinedFakeGlobals.size();
}
bool isNeeded() const override { return numGlobals() > 0; }
void writeBody() override;
void addGlobal(InputGlobal *Global);
std::vector<const DefinedData *> DefinedFakeGlobals;
std::vector<InputGlobal *> InputGlobals;
};
// The event section contains a list of declared wasm events associated with the
// module. Currently the only supported event kind is exceptions. A single event
// entry represents a single event with an event tag. All C++ exceptions are
// represented by a single event. An event entry in this section contains
// information on what kind of event it is (e.g. exception) and the type of
// values contained in a single event object. (In wasm, an event can contain
// multiple values of primitive types. But for C++ exceptions, we just throw a
// pointer which is an i32 value (for wasm32 architecture), so the signature of
// C++ exception is (i32)->(void), because all event types are assumed to have
// void return type to share WasmSignature with functions.)
class EventSection : public SyntheticSection {
public:
EventSection() : SyntheticSection(llvm::wasm::WASM_SEC_EVENT) {}
void writeBody() override;
bool isNeeded() const override { return InputEvents.size() > 0; }
void addEvent(InputEvent *Event);
std::vector<InputEvent *> InputEvents;
};
class ExportSection : public SyntheticSection {
public:
ExportSection() : SyntheticSection(llvm::wasm::WASM_SEC_EXPORT) {}
bool isNeeded() const override { return Exports.size() > 0; }
void writeBody() override;
std::vector<llvm::wasm::WasmExport> Exports;
};
class ElemSection : public SyntheticSection {
public:
ElemSection(uint32_t Offset)
: SyntheticSection(llvm::wasm::WASM_SEC_ELEM), ElemOffset(Offset) {}
bool isNeeded() const override { return IndirectFunctions.size() > 0; };
void writeBody() override;
void addEntry(FunctionSymbol *Sym);
uint32_t numEntries() const { return IndirectFunctions.size(); }
uint32_t ElemOffset;
protected:
std::vector<const FunctionSymbol *> IndirectFunctions;
};
class DataCountSection : public SyntheticSection {
public:
DataCountSection(uint32_t NumSegments)
: SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT),
NumSegments(NumSegments) {}
bool isNeeded() const override;
void writeBody() override;
protected:
uint32_t NumSegments;
};
// Create the custom "linking" section containing linker metadata.
// This is only created when relocatable output is requested.
class LinkingSection : public SyntheticSection {
public:
LinkingSection(const std::vector<WasmInitEntry> &InitFunctions,
const std::vector<OutputSegment *> &DataSegments)
: SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "linking"),
InitFunctions(InitFunctions), DataSegments(DataSegments) {}
bool isNeeded() const override { return Config->Relocatable; }
void writeBody() override;
void addToSymtab(Symbol *Sym);
protected:
std::vector<const Symbol *> SymtabEntries;
llvm::StringMap<uint32_t> SectionSymbolIndices;
const std::vector<WasmInitEntry> &InitFunctions;
const std::vector<OutputSegment *> &DataSegments;
};
// Create the custom "name" section containing debug symbol names.
class NameSection : public SyntheticSection {
public:
NameSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "name") {}
bool isNeeded() const override {
return !Config->StripDebug && !Config->StripAll && numNames() > 0;
}
void writeBody() override;
unsigned numNames() const;
};
class ProducersSection : public SyntheticSection {
public:
ProducersSection()
: SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "producers") {}
bool isNeeded() const override {
return !Config->StripAll && fieldCount() > 0;
}
void writeBody() override;
void addInfo(const llvm::wasm::WasmProducerInfo &Info);
protected:
int fieldCount() const {
return int(!Languages.empty()) + int(!Tools.empty()) + int(!SDKs.empty());
}
SmallVector<std::pair<std::string, std::string>, 8> Languages;
SmallVector<std::pair<std::string, std::string>, 8> Tools;
SmallVector<std::pair<std::string, std::string>, 8> SDKs;
};
class TargetFeaturesSection : public SyntheticSection {
public:
TargetFeaturesSection()
: SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "target_features") {}
bool isNeeded() const override {
return !Config->StripAll && Features.size() > 0;
}
void writeBody() override;
llvm::SmallSet<std::string, 8> Features;
};
class RelocSection : public SyntheticSection {
public:
RelocSection(StringRef Name, OutputSection *Sec)
: SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, Name), Sec(Sec) {}
void writeBody() override;
bool isNeeded() const override { return Sec->numRelocations() > 0; };
protected:
OutputSection *Sec;
};
// Linker generated output sections
struct OutStruct {
DylinkSection *DylinkSec;
TypeSection *TypeSec;
FunctionSection *FunctionSec;
ImportSection *ImportSec;
TableSection *TableSec;
MemorySection *MemorySec;
GlobalSection *GlobalSec;
EventSection *EventSec;
ExportSection *ExportSec;
ElemSection *ElemSec;
DataCountSection *DataCountSec;
LinkingSection *LinkingSec;
NameSection *NameSec;
ProducersSection *ProducersSec;
TargetFeaturesSection *TargetFeaturesSec;
};
extern OutStruct Out;
} // namespace wasm
} // namespace lld
#endif

File diff suppressed because it is too large Load diff

View file

@ -14,8 +14,6 @@ namespace wasm {
void writeResult();
extern const char *DefaultModule;
} // namespace wasm
} // namespace lld