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 @@ -25,6 +25,7 @@ #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/Wasm.h" +#include "llvm/Support/LEB128.h" using llvm::object::WasmSection; using llvm::object::WasmSegment; @@ -44,7 +45,7 @@ class InputChunk { public: - enum Kind { DataSegment, Function, SyntheticFunction }; + enum Kind { DataSegment, Function, SyntheticFunction, Section }; Kind kind() const { return SectionKind; } @@ -173,6 +174,35 @@ ArrayRef Body; }; +// Represents a single Wasm Section within an input file. +class InputSection : public InputChunk { +public: + 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); + PayloadOffset = Count + NameSize; + } + + const WasmSection &getSection() { return Section; } + + StringRef getName() const override { return Section.Name; } + uint32_t getComdat() const override { return UINT32_MAX; } + +protected: + ArrayRef data() const override { + return Section.Content.slice(PayloadOffset); + } + uint32_t getInputSectionOffset() const override { + return 0; // TODO if we decide to make absolute offset: Section.Offset + } + + const WasmSection &Section; + size_t PayloadOffset; +}; + } // namespace wasm std::string toString(const wasm::InputChunk *); Index: wasm/InputChunks.cpp =================================================================== --- wasm/InputChunks.cpp +++ wasm/InputChunks.cpp @@ -56,6 +56,9 @@ // Copy this input chunk to an mmap'ed output file and apply relocations. void InputChunk::writeTo(uint8_t *Buf) const { // Copy contents + DEBUG(dbgs() << "writing chunk offset = " << OutputOffset << "\n"); + DEBUG(dbgs() << "writing chunk " << this << "\n"); + DEBUG(dbgs() << "writing chunk " << getInputSectionOffset() << "\n"); memcpy(Buf + OutputOffset, data().data(), data().size()); // Apply relocations 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,10 @@ CodeSection = &Section; else if (Section.Type == WASM_SEC_DATA) DataSection = &Section; + else if (Section.Type == WASM_SEC_CUSTOM) { + DEBUG(dbgs() << " --------- custom section " << Section.Name << "\n"); + CustomSections.emplace_back(make(Section, this)); + } } TypeMap.resize(getWasmObj()->types().size()); Index: wasm/OutputSections.h =================================================================== --- wasm/OutputSections.h +++ wasm/OutputSections.h @@ -113,6 +113,21 @@ size_t BodySize = 0; }; +class CustomSection : public OutputSection { +public: + explicit CustomSection(std::string Name, size_t Size, + ArrayRef InputSections); + size_t getSize() const override { + return Header.size() + NameData.size() + Size; + } + void writeTo(uint8_t *Buf) override; + +protected: + size_t Size; + ArrayRef InputSections; + std::string NameData; +}; + } // namespace wasm } // namespace lld Index: wasm/OutputSections.cpp =================================================================== --- wasm/OutputSections.cpp +++ wasm/OutputSections.cpp @@ -188,3 +188,34 @@ for (const InputChunk *C : Seg->InputSegments) C->writeRelocations(OS); } + +CustomSection::CustomSection(std::string Name, size_t Size, + ArrayRef InputSections) + : OutputSection(WASM_SEC_CUSTOM, Name), Size(Size), + InputSections(InputSections) { + raw_string_ostream OS(NameData); + encodeULEB128(Name.size(), OS); + OS << Name; + OS.flush(); + + createHeader(Size + 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(); @@ -104,6 +106,7 @@ unsigned NumImportedFunctions = 0; unsigned NumImportedGlobals = 0; std::vector ExportedSymbols; + std::vector CustomSections; std::vector DefinedFakeGlobals; std::vector InputGlobals; std::vector InputFunctions; @@ -111,6 +114,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 +300,39 @@ } } +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; + ArrayRef Sections = Pair.second; + DEBUG(dbgs() << "custom section: " << Name << "\n"); + uint32_t Offset = 0; + for (InputSection *Section : Sections) { + Section->OutputOffset = Offset; + DEBUG(dbgs() << " input section: " << Section->getName() << "\n"); + DEBUG(dbgs() << " input section: " << Section << "\n"); + DEBUG(dbgs() << " input offset: " << Offset << "\n"); + DEBUG(dbgs() << " input size: " << Section->getSize() << "\n"); + Offset += Section->getSize(); + } + + DEBUG(dbgs() << "createCustomSection: " << Name << " size=" << Offset + << "\n"); + auto Section = make(Name, Offset, Sections); + OutputSections.push_back(Section); + } +} + void Writer::createElemSection() { if (IndirectFunctions.empty()) return; @@ -647,6 +685,7 @@ createElemSection(); createCodeSection(); createDataSection(); + createCustomSections(); // Custom sections if (Config->Relocatable) {