[WebAssembly] Add support for custom sections

Copy user-defined custom sections into the output, concatenating
sections with the same name.

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

llvm-svn: 329717
This commit is contained in:
Sam Clegg 2018-04-10 16:12:49 +00:00
parent e4b90d82a0
commit 80ba43872e
9 changed files with 140 additions and 1 deletions

View File

@ -0,0 +1,6 @@
target triple = "wasm32-unknown-unknown-wasm"
!0 = !{ !"red", !"foo" }
!1 = !{ !"green", !"bar" }
!2 = !{ !"green", !"qux" }
!wasm.custom_sections = !{ !0, !1, !2 }

View File

@ -0,0 +1,22 @@
; RUN: llc -filetype=obj %s -o %t1.o
; RUN: llc -filetype=obj %S/Inputs/custom.ll -o %t2.o
; RUN: wasm-ld --check-signatures --relocatable -o %t.wasm %t1.o %t2.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", !"extra" }
!wasm.custom_sections = !{ !0 }
; CHECK: - Type: CUSTOM
; CHECK-NEXT: Name: green
; CHECK-NEXT: Payload: '05677265656E626172717578'
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: red
; CHECK-NEXT: Payload: 037265646578747261666F6F

View File

@ -143,3 +143,13 @@ void InputFunction::setTableIndex(uint32_t Index) {
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);
}

View File

@ -44,7 +44,7 @@ class OutputSegment;
class InputChunk {
public:
enum Kind { DataSegment, Function, SyntheticFunction };
enum Kind { DataSegment, Function, SyntheticFunction, Section };
Kind kind() const { return SectionKind; }
@ -173,6 +173,25 @@ protected:
ArrayRef<uint8_t> 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<uint8_t> 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<uint8_t> Payload;
};
} // namespace wasm
std::string toString(const wasm::InputChunk *);

View File

@ -153,6 +153,8 @@ void ObjFile::parse() {
CodeSection = &Section;
else if (Section.Type == WASM_SEC_DATA)
DataSection = &Section;
else if (Section.Type == WASM_SEC_CUSTOM)
CustomSections.emplace_back(make<InputSection>(Section, this));
}
TypeMap.resize(getWasmObj()->types().size());

View File

@ -35,6 +35,7 @@ class InputChunk;
class InputFunction;
class InputSegment;
class InputGlobal;
class InputSection;
class InputFile {
public:
@ -108,6 +109,7 @@ public:
std::vector<InputSegment *> Segments;
std::vector<InputFunction *> Functions;
std::vector<InputGlobal *> Globals;
std::vector<InputSection *> CustomSections;
ArrayRef<Symbol *> getSymbols() const { return Symbols; }
Symbol *getSymbol(uint32_t Index) const { return Symbols[Index]; }

View File

@ -189,3 +189,38 @@ void DataSection::writeRelocations(raw_ostream &OS) const {
for (const InputChunk *C : Seg->InputSegments)
C->writeRelocations(OS);
}
CustomSection::CustomSection(std::string Name,
ArrayRef<InputSection *> 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); });
}

View File

@ -113,6 +113,27 @@ protected:
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<InputSection *> InputSections);
size_t getSize() const override {
return Header.size() + NameData.size() + PayloadSize;
}
void writeTo(uint8_t *Buf) override;
protected:
size_t PayloadSize;
ArrayRef<InputSection *> InputSections;
std::string NameData;
};
} // namespace wasm
} // namespace lld

View File

@ -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 @@ private:
void createElemSection();
void createCodeSection();
void createDataSection();
void createCustomSections();
// Custom sections
void createRelocSections();
@ -111,6 +113,8 @@ private:
std::vector<const Symbol *> SymtabEntries;
std::vector<WasmInitEntry> InitFunctions;
llvm::StringMap<std::vector<InputSection *>> CustomSectionMapping;
// Elements that are used to construct the final output
std::string Header;
std::vector<OutputSection *> OutputSections;
@ -295,6 +299,23 @@ void Writer::createExportSection() {
}
}
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<CustomSection>(Name, Pair.second));
}
}
void Writer::createElemSection() {
if (IndirectFunctions.empty())
return;
@ -647,6 +668,7 @@ void Writer::createSections() {
createElemSection();
createCodeSection();
createDataSection();
createCustomSections();
// Custom sections
if (Config->Relocatable) {