Index: test/wasm/custom-sections.ll =================================================================== --- /dev/null +++ test/wasm/custom-sections.ll @@ -0,0 +1,23 @@ +; RUN: llc -filetype=obj %s -o %t.o +; RUN: wasm-ld --check-signatures --relocatable -o %t.wasm %t.o +; RUN: obj2yaml %t.wasm | FileCheck %s + +target triple = "wasm32-unknown-unknown-wasm" + +define i32 @_start() local_unnamed_addr { +entry: + %retval = alloca i32, align 4 + ret i32 0 +} + +!0 = !{ !"red", !"foo" } +!1 = !{ !"green", !"bar" } +!2 = !{ !"green", !"qux" } +!wasm.custom_sections = !{ !0, !1, !2 } + +; CHECK: - Type: CUSTOM +; CHECK_NEXT: Name: green +; CHECK_NEXT: Payload: '05677265656E626172717578' +; CHECK_NEXT: - Type: CUSTOM +; CHECK_NEXT: Name: red +; CHECK_NEXT: Payload: 03726564666F6F Index: wasm/InputChunks.h =================================================================== --- wasm/InputChunks.h +++ wasm/InputChunks.h @@ -44,7 +44,7 @@ class InputChunk { public: - enum Kind { DataSegment, Function, SyntheticFunction }; + enum Kind { DataSegment, Function, SyntheticFunction, Section }; Kind kind() const { return SectionKind; } @@ -173,6 +173,25 @@ ArrayRef Body; }; +// Represents a single Wasm Section within an input file. +class InputSection : public InputChunk { +public: + InputSection(const WasmSection &S, ObjFile *F); + + StringRef getName() const override { return Section.Name; } + uint32_t getComdat() const override { return UINT32_MAX; } + +protected: + ArrayRef data() const override { return Payload; } + + // Offset within the input section. This is only zero since this chunk + // type represents an entire input section, not part of one. + uint32_t getInputSectionOffset() const override { return 0; } + + const WasmSection &Section; + ArrayRef Payload; +}; + } // namespace wasm std::string toString(const wasm::InputChunk *); Index: wasm/InputChunks.cpp =================================================================== --- wasm/InputChunks.cpp +++ wasm/InputChunks.cpp @@ -143,3 +143,13 @@ assert(!hasTableIndex()); TableIndex = Index; } + +InputSection::InputSection(const WasmSection &S, ObjFile *F) + : InputChunk(F, InputChunk::Section), Section(S) { + assert(Section.Type == llvm::wasm::WASM_SEC_CUSTOM); + // TODO check LEB errors + unsigned Count; + uint64_t NameSize = llvm::decodeULEB128(Section.Content.data(), &Count); + uint32_t PayloadOffset = Count + NameSize; + Payload = Section.Content.slice(PayloadOffset); +} Index: wasm/InputFiles.h =================================================================== --- wasm/InputFiles.h +++ wasm/InputFiles.h @@ -35,6 +35,7 @@ class InputFunction; class InputSegment; class InputGlobal; +class InputSection; class InputFile { public: @@ -108,6 +109,7 @@ std::vector Segments; std::vector Functions; std::vector Globals; + std::vector CustomSections; ArrayRef getSymbols() const { return Symbols; } Symbol *getSymbol(uint32_t Index) const { return Symbols[Index]; } Index: wasm/InputFiles.cpp =================================================================== --- wasm/InputFiles.cpp +++ wasm/InputFiles.cpp @@ -153,6 +153,8 @@ CodeSection = &Section; else if (Section.Type == WASM_SEC_DATA) DataSection = &Section; + else if (Section.Type == WASM_SEC_CUSTOM) + CustomSections.emplace_back(make(Section, this)); } TypeMap.resize(getWasmObj()->types().size()); Index: wasm/OutputSections.h =================================================================== --- wasm/OutputSections.h +++ wasm/OutputSections.h @@ -113,6 +113,27 @@ size_t BodySize = 0; }; +// Represents a custom section in the output file. Wasm custom sections are +// used for storing user-defined metadata. Unlike the core sections types +// they are identified by their string name. +// The linker combines custom sections that have the same name by simply +// concatenating them. +// Note that some custom sections such as "name" and "linking" are handled +// separately and are instead synthesized by the linker. +class CustomSection : public OutputSection { +public: + CustomSection(std::string Name, ArrayRef InputSections); + size_t getSize() const override { + return Header.size() + NameData.size() + PayloadSize; + } + void writeTo(uint8_t *Buf) override; + +protected: + size_t PayloadSize; + ArrayRef InputSections; + std::string NameData; +}; + } // namespace wasm } // namespace lld Index: wasm/OutputSections.cpp =================================================================== --- wasm/OutputSections.cpp +++ wasm/OutputSections.cpp @@ -189,3 +189,38 @@ for (const InputChunk *C : Seg->InputSegments) C->writeRelocations(OS); } + +CustomSection::CustomSection(std::string Name, + ArrayRef InputSections) + : OutputSection(WASM_SEC_CUSTOM, Name), PayloadSize(0), + InputSections(InputSections) { + raw_string_ostream OS(NameData); + encodeULEB128(Name.size(), OS); + OS << Name; + OS.flush(); + + for (InputSection *Section : InputSections) { + Section->OutputOffset = PayloadSize; + PayloadSize += Section->getSize(); + } + + createHeader(PayloadSize + NameData.size()); +} + +void CustomSection::writeTo(uint8_t *Buf) { + log("writing " + toString(*this) + " size=" + Twine(getSize()) + + " chunks=" + Twine(InputSections.size())); + + assert(Offset); + Buf += Offset; + + // Write section header + memcpy(Buf, Header.data(), Header.size()); + Buf += Header.size(); + memcpy(Buf, NameData.data(), NameData.size()); + Buf += NameData.size(); + + // Write custom sections payload + parallelForEach(InputSections, + [&](const InputSection *Section) { Section->writeTo(Buf); }); +} Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -20,6 +20,7 @@ #include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Object/WasmTraits.h" #include "llvm/Support/FileOutputBuffer.h" @@ -85,6 +86,7 @@ void createElemSection(); void createCodeSection(); void createDataSection(); + void createCustomSections(); // Custom sections void createRelocSections(); @@ -111,6 +113,8 @@ std::vector SymtabEntries; std::vector InitFunctions; + llvm::StringMap> CustomSectionMapping; + // Elements that are used to construct the final output std::string Header; std::vector OutputSections; @@ -295,6 +299,23 @@ } } +void Writer::createCustomSections() { + log("createCustomSections"); + for (ObjFile *File : Symtab->ObjectFiles) + for (InputSection *Section : File->CustomSections) + CustomSectionMapping[Section->getName()].push_back(Section); + + for (auto &Pair : CustomSectionMapping) { + StringRef Name = Pair.first(); + // These custom sections are known the linker and synthesized rather than + // blindly copied + if (Name == "linking" || Name == "name" || Name.startswith("reloc.")) + continue; + DEBUG(dbgs() << "createCustomSection: " << Name << "\n"); + OutputSections.push_back(make(Name, Pair.second)); + } +} + void Writer::createElemSection() { if (IndirectFunctions.empty()) return; @@ -647,6 +668,7 @@ createElemSection(); createCodeSection(); createDataSection(); + createCustomSections(); // Custom sections if (Config->Relocatable) {