diff options
Diffstat (limited to 'wasm/Writer.cpp')
-rw-r--r-- | wasm/Writer.cpp | 190 |
1 files changed, 155 insertions, 35 deletions
diff --git a/wasm/Writer.cpp b/wasm/Writer.cpp index 37ad32452a91..819d4298fef2 100644 --- a/wasm/Writer.cpp +++ b/wasm/Writer.cpp @@ -10,6 +10,7 @@ #include "Writer.h" #include "Config.h" #include "InputChunks.h" +#include "InputEvent.h" #include "InputGlobal.h" #include "OutputSections.h" #include "OutputSegment.h" @@ -39,7 +40,6 @@ using namespace lld; using namespace lld::wasm; static constexpr int kStackAlignment = 16; -static constexpr int kInitialTableOffset = 1; static constexpr const char *kFunctionTableName = "__indirect_function_table"; namespace { @@ -81,6 +81,7 @@ private: void createFunctionSection(); void createTableSection(); void createGlobalSection(); + void createEventSection(); void createExportSection(); void createImportSection(); void createMemorySection(); @@ -90,6 +91,7 @@ private: void createCustomSections(); // Custom sections + void createDylinkSection(); void createRelocSections(); void createLinkingSection(); void createNameSection(); @@ -98,18 +100,25 @@ private: void writeSections(); uint64_t FileSize = 0; + uint32_t TableBase = 0; uint32_t NumMemoryPages = 0; uint32_t MaxMemoryPages = 0; + // Memory size and aligment. Written to the "dylink" section + // when build with -shared or -pie. + uint32_t MemAlign = 0; + uint32_t MemSize = 0; std::vector<const WasmSignature *> Types; DenseMap<WasmSignature, int32_t> TypeIndices; std::vector<const Symbol *> ImportedSymbols; unsigned NumImportedFunctions = 0; unsigned NumImportedGlobals = 0; + unsigned NumImportedEvents = 0; std::vector<WasmExport> Exports; std::vector<const DefinedData *> DefinedFakeGlobals; std::vector<InputGlobal *> InputGlobals; std::vector<InputFunction *> InputFunctions; + std::vector<InputEvent *> InputEvents; std::vector<const FunctionSymbol *> IndirectFunctions; std::vector<const Symbol *> SymtabEntries; std::vector<WasmInitEntry> InitFunctions; @@ -155,17 +164,19 @@ void Writer::createImportSection() { Import.Memory.Flags |= WASM_LIMITS_FLAG_HAS_MAX; Import.Memory.Maximum = MaxMemoryPages; } + if (Config->SharedMemory) + Import.Memory.Flags |= WASM_LIMITS_FLAG_IS_SHARED; writeImport(OS, Import); } if (Config->ImportTable) { - uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size(); + uint32_t TableSize = TableBase + IndirectFunctions.size(); WasmImport Import; Import.Module = "env"; Import.Field = kFunctionTableName; Import.Kind = WASM_EXTERNAL_TABLE; - Import.Table.ElemType = WASM_TYPE_ANYFUNC; - Import.Table.Limits = {WASM_LIMITS_FLAG_HAS_MAX, TableSize, TableSize}; + Import.Table.ElemType = WASM_TYPE_FUNCREF; + Import.Table.Limits = {0, TableSize, 0}; writeImport(OS, Import); } @@ -175,11 +186,15 @@ void Writer::createImportSection() { Import.Field = Sym->getName(); if (auto *FunctionSym = dyn_cast<FunctionSymbol>(Sym)) { Import.Kind = WASM_EXTERNAL_FUNCTION; - Import.SigIndex = lookupType(*FunctionSym->FunctionType); - } else { - auto *GlobalSym = cast<GlobalSymbol>(Sym); + Import.SigIndex = 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 = lookupType(*EventSym->Signature); } writeImport(OS, Import); } @@ -214,8 +229,12 @@ void Writer::createMemorySection() { bool HasMax = MaxMemoryPages != 0; writeUleb128(OS, 1, "memory count"); - writeUleb128(OS, HasMax ? static_cast<unsigned>(WASM_LIMITS_FLAG_HAS_MAX) : 0, - "memory limits flags"); + 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"); @@ -241,6 +260,31 @@ void Writer::createGlobalSection() { } } +// 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.) +void Writer::createEventSection() { + unsigned NumEvents = InputEvents.size(); + if (NumEvents == 0) + return; + + SyntheticSection *Section = createSyntheticSection(WASM_SEC_EVENT); + raw_ostream &OS = Section->getStream(); + + writeUleb128(OS, NumEvents, "event count"); + for (InputEvent *E : InputEvents) { + E->Event.Type.SigIndex = lookupType(E->Signature); + writeEvent(OS, E->Event); + } +} + void Writer::createTableSection() { if (Config->ImportTable) return; @@ -253,14 +297,14 @@ void Writer::createTableSection() { // 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. - uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size(); + uint32_t TableSize = TableBase + IndirectFunctions.size(); SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE); raw_ostream &OS = Section->getStream(); writeUleb128(OS, 1, "table count"); WasmLimits Limits = {WASM_LIMITS_FLAG_HAS_MAX, TableSize, TableSize}; - writeTableType(OS, WasmTable{WASM_TYPE_ANYFUNC, Limits}); + writeTableType(OS, WasmTable{WASM_TYPE_FUNCREF, Limits}); } void Writer::createExportSection() { @@ -319,12 +363,17 @@ void Writer::createElemSection() { writeUleb128(OS, 1, "segment count"); writeUleb128(OS, 0, "table index"); WasmInitExpr InitExpr; - InitExpr.Opcode = WASM_OPCODE_I32_CONST; - InitExpr.Value.Int32 = kInitialTableOffset; + 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 = TableBase; + } writeInitExpr(OS, InitExpr); writeUleb128(OS, IndirectFunctions.size(), "elem count"); - uint32_t TableIndex = kInitialTableOffset; + uint32_t TableIndex = TableBase; for (const FunctionSymbol *Sym : IndirectFunctions) { assert(Sym->getTableIndex() == TableIndex); writeUleb128(OS, Sym->getFunctionIndex(), "function index"); @@ -419,6 +468,21 @@ public: raw_string_ostream OS{Body}; }; +// Create the custom "dylink" section containing information for the dynamic +// linker. +// See +// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md +void Writer::createDylinkSection() { + SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, "dylink"); + raw_ostream &OS = Section->getStream(); + + writeUleb128(OS, MemSize, "MemSize"); + writeUleb128(OS, MemAlign, "MemAlign"); + writeUleb128(OS, IndirectFunctions.size(), "TableSize"); + writeUleb128(OS, 0, "TableAlign"); + writeUleb128(OS, 0, "Needed"); // TODO: Support "needed" shared libraries +} + // Create the custom "linking" section containing linker metadata. // This is only created when relocatable output is requested. void Writer::createLinkingSection() { @@ -448,6 +512,10 @@ void Writer::createLinkingSection() { writeUleb128(Sub.OS, G->getGlobalIndex(), "index"); if (Sym->isDefined()) writeStr(Sub.OS, Sym->getName(), "sym name"); + } else if (auto *E = dyn_cast<EventSymbol>(Sym)) { + writeUleb128(Sub.OS, E->getEventIndex(), "index"); + if (Sym->isDefined()) + 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)) { @@ -548,8 +616,7 @@ void Writer::createNameSection() { for (const Symbol *S : ImportedSymbols) { if (auto *F = dyn_cast<FunctionSymbol>(S)) { writeUleb128(Sub.OS, F->getFunctionIndex(), "func index"); - Optional<std::string> Name = demangleItanium(F->getName()); - writeStr(Sub.OS, Name ? StringRef(*Name) : F->getName(), "symbol name"); + writeStr(Sub.OS, toString(*S), "symbol name"); } } for (const InputFunction *F : InputFunctions) { @@ -558,8 +625,7 @@ void Writer::createNameSection() { if (!F->getDebugName().empty()) { writeStr(Sub.OS, F->getDebugName(), "symbol name"); } else { - Optional<std::string> Name = demangleItanium(F->getName()); - writeStr(Sub.OS, Name ? StringRef(*Name) : F->getName(), "symbol name"); + writeStr(Sub.OS, maybeDemangleSymbol(F->getName()), "symbol name"); } } } @@ -586,16 +652,16 @@ void Writer::writeSections() { // - heap start / unallocated // // The --stack-first option means that stack is placed before any static data. -// This can be useful since it means that stack overflow traps immediately rather -// than overwriting global data, but also increases code size since all static -// data loads and stores requires larger offsets. +// This can be useful since it means that stack overflow traps immediately +// rather than overwriting global data, but also increases code size since all +// static data loads and stores requires larger offsets. void Writer::layoutMemory() { createOutputSegments(); uint32_t MemoryPtr = 0; auto PlaceStack = [&]() { - if (Config->Relocatable) + if (Config->Relocatable || Config->Shared) return; MemoryPtr = alignTo(MemoryPtr, kStackAlignment); if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment)) @@ -603,7 +669,8 @@ void Writer::layoutMemory() { log("mem: stack size = " + Twine(Config->ZStackSize)); log("mem: stack base = " + Twine(MemoryPtr)); MemoryPtr += Config->ZStackSize; - WasmSym::StackPointer->Global->Global.InitExpr.Value.Int32 = MemoryPtr; + auto *SP = cast<DefinedGlobal>(WasmSym::StackPointer); + SP->Global->Global.InitExpr.Value.Int32 = MemoryPtr; log("mem: stack top = " + Twine(MemoryPtr)); }; @@ -621,8 +688,10 @@ void Writer::layoutMemory() { if (WasmSym::DsoHandle) WasmSym::DsoHandle->setVirtualAddress(DataStart); + MemAlign = 0; for (OutputSegment *Seg : Segments) { - MemoryPtr = alignTo(MemoryPtr, Seg->Alignment); + MemAlign = std::max(MemAlign, Seg->Alignment); + MemoryPtr = alignTo(MemoryPtr, 1 << Seg->Alignment); Seg->StartVA = MemoryPtr; log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", Seg->Name, MemoryPtr, Seg->Size, Seg->Alignment)); @@ -635,6 +704,11 @@ void Writer::layoutMemory() { log("mem: static data = " + Twine(MemoryPtr - DataStart)); + if (Config->Shared) { + MemSize = MemoryPtr; + return; + } + if (!Config->StackFirst) PlaceStack(); @@ -654,8 +728,8 @@ void Writer::layoutMemory() { else MemoryPtr = Config->InitialMemory; } - uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize); - NumMemoryPages = MemSize / WasmPageSize; + MemSize = MemoryPtr; + NumMemoryPages = alignTo(MemoryPtr, WasmPageSize) / WasmPageSize; log("mem: total pages = " + Twine(NumMemoryPages)); if (Config->MaxMemory != 0) { @@ -678,12 +752,15 @@ SyntheticSection *Writer::createSyntheticSection(uint32_t Type, void Writer::createSections() { // Known sections + if (Config->Pic) + createDylinkSection(); createTypeSection(); createImportSection(); createFunctionSection(); createTableSection(); createMemorySection(); createGlobalSection(); + createEventSection(); createExportSection(); createElemSection(); createCodeSection(); @@ -722,8 +799,10 @@ void Writer::calculateImports() { 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<GlobalSymbol>(Sym)->setGlobalIndex(NumImportedGlobals++); + cast<EventSymbol>(Sym)->setEventIndex(NumImportedEvents++); } } @@ -759,6 +838,8 @@ void Writer::calculateExports() { continue; } Export = {Name, WASM_EXTERNAL_GLOBAL, G->getGlobalIndex()}; + } else if (auto *E = dyn_cast<DefinedEvent>(Sym)) { + Export = {Name, WASM_EXTERNAL_EVENT, E->getEventIndex()}; } else { auto *D = cast<DefinedData>(Sym); DefinedFakeGlobals.emplace_back(D); @@ -836,6 +917,8 @@ void Writer::calculateTypes() { // 1. Any signature used in the TYPE relocation // 2. The signatures of all imported functions // 3. The signatures of all defined functions + // 4. The signatures of all imported events + // 5. The signatures of all defined events for (ObjFile *File : Symtab->ObjectFiles) { ArrayRef<WasmSignature> Types = File->getWasmObj()->types(); @@ -844,16 +927,23 @@ void Writer::calculateTypes() { File->TypeMap[I] = registerType(Types[I]); } - for (const Symbol *Sym : ImportedSymbols) + for (const Symbol *Sym : ImportedSymbols) { if (auto *F = dyn_cast<FunctionSymbol>(Sym)) - registerType(*F->FunctionType); + registerType(*F->Signature); + else if (auto *E = dyn_cast<EventSymbol>(Sym)) + registerType(*E->Signature); + } for (const InputFunction *F : InputFunctions) registerType(F->Signature); + + for (const InputEvent *E : InputEvents) + registerType(E->Signature); } void Writer::assignIndexes() { - uint32_t FunctionIndex = NumImportedFunctions + InputFunctions.size(); + assert(InputFunctions.empty()); + uint32_t FunctionIndex = NumImportedFunctions; auto AddDefinedFunction = [&](InputFunction *Func) { if (!Func->Live) return; @@ -870,7 +960,7 @@ void Writer::assignIndexes() { AddDefinedFunction(Func); } - uint32_t TableIndex = kInitialTableOffset; + uint32_t TableIndex = TableBase; auto HandleRelocs = [&](InputChunk *Chunk) { if (!Chunk->Live) return; @@ -902,7 +992,8 @@ void Writer::assignIndexes() { HandleRelocs(P); } - uint32_t GlobalIndex = NumImportedGlobals + InputGlobals.size(); + assert(InputGlobals.empty()); + uint32_t GlobalIndex = NumImportedGlobals; auto AddDefinedGlobal = [&](InputGlobal *Global) { if (Global->Live) { LLVM_DEBUG(dbgs() << "AddDefinedGlobal: " << GlobalIndex << "\n"); @@ -919,9 +1010,29 @@ void Writer::assignIndexes() { for (InputGlobal *Global : File->Globals) AddDefinedGlobal(Global); } + + assert(InputEvents.empty()); + uint32_t EventIndex = NumImportedEvents; + auto AddDefinedEvent = [&](InputEvent *Event) { + if (Event->Live) { + LLVM_DEBUG(dbgs() << "AddDefinedEvent: " << EventIndex << "\n"); + Event->setEventIndex(EventIndex++); + InputEvents.push_back(Event); + } + }; + + for (ObjFile *File : Symtab->ObjectFiles) { + LLVM_DEBUG(dbgs() << "Events: " << File->getName() << "\n"); + for (InputEvent *Event : File->Events) + AddDefinedEvent(Event); + } } static StringRef getOutputDataSegmentName(StringRef Name) { + // With PIC code we currently only support a single data segment since + // we only have a single __memory_base to use as our base address. + if (Config->Pic) + return "data"; if (!Config->MergeDataSegments) return Name; if (Name.startswith(".text.")) @@ -930,6 +1041,8 @@ static StringRef getOutputDataSegmentName(StringRef Name) { return ".data"; if (Name.startswith(".bss.")) return ".bss"; + if (Name.startswith(".rodata.")) + return ".rodata"; return Name; } @@ -977,7 +1090,7 @@ void Writer::createCtorFunction() { OS << BodyContent; } - ArrayRef<uint8_t> Body = toArrayRef(Saver.save(FunctionBody)); + ArrayRef<uint8_t> Body = arrayRefFromStringRef(Saver.save(FunctionBody)); cast<SyntheticFunction>(WasmSym::CallCtors->Function)->setBody(Body); } @@ -989,7 +1102,7 @@ void Writer::calculateInitFunctions() { const WasmLinkingData &L = File->getWasmObj()->linkingData(); for (const WasmInitFunc &F : L.InitFunctions) { FunctionSymbol *Sym = File->getFunctionSymbol(F.Symbol); - if (*Sym->FunctionType != WasmSignature{{}, WASM_TYPE_NORESULT}) + if (*Sym->Signature != WasmSignature{{}, {}}) error("invalid signature for init func: " + toString(*Sym)); InitFunctions.emplace_back(WasmInitEntry{Sym, F.Priority}); } @@ -1004,9 +1117,14 @@ void Writer::calculateInitFunctions() { } void Writer::run() { - if (Config->Relocatable) + if (Config->Relocatable || Config->Pic) Config->GlobalBase = 0; + // For PIC code the table base is assigned dynamically by the loader. + // For non-PIC, we start at 1 so that accessing table index 0 always traps. + if (!Config->Pic) + TableBase = 1; + log("-- calculateImports"); calculateImports(); log("-- assignIndexes"); @@ -1029,8 +1147,10 @@ void Writer::run() { if (errorHandler().Verbose) { log("Defined Functions: " + Twine(InputFunctions.size())); log("Defined Globals : " + Twine(InputGlobals.size())); + log("Defined Events : " + Twine(InputEvents.size())); log("Function Imports : " + Twine(NumImportedFunctions)); log("Global Imports : " + Twine(NumImportedGlobals)); + log("Event Imports : " + Twine(NumImportedEvents)); for (ObjFile *File : Symtab->ObjectFiles) File->dumpInfo(); } |