1 //===- InputChunks.h --------------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // An InputChunks represents an indivisible opaque region of a input wasm file. 10 // i.e. a single wasm data segment or a single wasm function. 11 // 12 // They are written directly to the mmap'd output file after which relocations 13 // are applied. Because each Chunk is independent they can be written in 14 // parallel. 15 // 16 // Chunks are also unit on which garbage collection (--gc-sections) operates. 17 // 18 //===----------------------------------------------------------------------===// 19 20 #ifndef LLD_WASM_INPUT_CHUNKS_H 21 #define LLD_WASM_INPUT_CHUNKS_H 22 23 #include "Config.h" 24 #include "InputFiles.h" 25 #include "lld/Common/ErrorHandler.h" 26 #include "lld/Common/LLVM.h" 27 #include "llvm/Object/Wasm.h" 28 29 namespace lld { 30 namespace wasm { 31 32 class ObjFile; 33 class OutputSegment; 34 class OutputSection; 35 36 class InputChunk { 37 public: 38 enum Kind { DataSegment, Function, SyntheticFunction, Section }; 39 kind()40 Kind kind() const { return sectionKind; } 41 getSize()42 virtual uint32_t getSize() const { return data().size(); } getInputSize()43 virtual uint32_t getInputSize() const { return getSize(); }; 44 45 virtual void writeTo(uint8_t *sectionStart) const; 46 getRelocations()47 ArrayRef<WasmRelocation> getRelocations() const { return relocations; } setRelocations(ArrayRef<WasmRelocation> rs)48 void setRelocations(ArrayRef<WasmRelocation> rs) { relocations = rs; } 49 50 virtual StringRef getName() const = 0; 51 virtual StringRef getDebugName() const = 0; 52 virtual uint32_t getComdat() const = 0; 53 StringRef getComdatName() const; 54 virtual uint32_t getInputSectionOffset() const = 0; 55 getNumRelocations()56 size_t getNumRelocations() const { return relocations.size(); } 57 void writeRelocations(llvm::raw_ostream &os) const; 58 59 ObjFile *file; 60 int32_t outputOffset = 0; 61 62 // Signals that the section is part of the output. The garbage collector, 63 // and COMDAT handling can set a sections' Live bit. 64 // If GC is disabled, all sections start out as live by default. 65 unsigned live : 1; 66 67 // Signals the chunk was discarded by COMDAT handling. 68 unsigned discarded : 1; 69 70 protected: InputChunk(ObjFile * f,Kind k)71 InputChunk(ObjFile *f, Kind k) 72 : file(f), live(!config->gcSections), discarded(false), sectionKind(k) {} 73 virtual ~InputChunk() = default; 74 virtual ArrayRef<uint8_t> data() const = 0; 75 76 // Verifies the existing data at relocation targets matches our expectations. 77 // This is performed only debug builds as an extra sanity check. 78 void verifyRelocTargets() const; 79 80 ArrayRef<WasmRelocation> relocations; 81 Kind sectionKind; 82 }; 83 84 // Represents a WebAssembly data segment which can be included as part of 85 // an output data segments. Note that in WebAssembly, unlike ELF and other 86 // formats, used the term "data segment" to refer to the continous regions of 87 // memory that make on the data section. See: 88 // https://webassembly.github.io/spec/syntax/modules.html#syntax-data 89 // 90 // For example, by default, clang will produce a separate data section for 91 // each global variable. 92 class InputSegment : public InputChunk { 93 public: InputSegment(const WasmSegment & seg,ObjFile * f)94 InputSegment(const WasmSegment &seg, ObjFile *f) 95 : InputChunk(f, InputChunk::DataSegment), segment(seg) {} 96 classof(const InputChunk * c)97 static bool classof(const InputChunk *c) { return c->kind() == DataSegment; } 98 99 void generateRelocationCode(raw_ostream &os) const; 100 getAlignment()101 uint32_t getAlignment() const { return segment.Data.Alignment; } getName()102 StringRef getName() const override { return segment.Data.Name; } getDebugName()103 StringRef getDebugName() const override { return StringRef(); } getComdat()104 uint32_t getComdat() const override { return segment.Data.Comdat; } getInputSectionOffset()105 uint32_t getInputSectionOffset() const override { 106 return segment.SectionOffset; 107 } 108 109 const OutputSegment *outputSeg = nullptr; 110 int32_t outputSegmentOffset = 0; 111 112 protected: data()113 ArrayRef<uint8_t> data() const override { return segment.Data.Content; } 114 115 const WasmSegment &segment; 116 }; 117 118 // Represents a single wasm function within and input file. These are 119 // combined to create the final output CODE section. 120 class InputFunction : public InputChunk { 121 public: InputFunction(const WasmSignature & s,const WasmFunction * func,ObjFile * f)122 InputFunction(const WasmSignature &s, const WasmFunction *func, ObjFile *f) 123 : InputChunk(f, InputChunk::Function), signature(s), function(func) {} 124 classof(const InputChunk * c)125 static bool classof(const InputChunk *c) { 126 return c->kind() == InputChunk::Function || 127 c->kind() == InputChunk::SyntheticFunction; 128 } 129 130 void writeTo(uint8_t *sectionStart) const override; getName()131 StringRef getName() const override { return function->SymbolName; } getDebugName()132 StringRef getDebugName() const override { return function->DebugName; } getComdat()133 uint32_t getComdat() const override { return function->Comdat; } getFunctionInputOffset()134 uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); } getFunctionCodeOffset()135 uint32_t getFunctionCodeOffset() const { return function->CodeOffset; } getSize()136 uint32_t getSize() const override { 137 if (config->compressRelocations && file) { 138 assert(compressedSize); 139 return compressedSize; 140 } 141 return data().size(); 142 } getInputSize()143 uint32_t getInputSize() const override { return function->Size; } getFunctionIndex()144 uint32_t getFunctionIndex() const { return functionIndex.getValue(); } hasFunctionIndex()145 bool hasFunctionIndex() const { return functionIndex.hasValue(); } 146 void setFunctionIndex(uint32_t index); getInputSectionOffset()147 uint32_t getInputSectionOffset() const override { 148 return function->CodeSectionOffset; 149 } getTableIndex()150 uint32_t getTableIndex() const { return tableIndex.getValue(); } hasTableIndex()151 bool hasTableIndex() const { return tableIndex.hasValue(); } 152 void setTableIndex(uint32_t index); 153 154 // The size of a given input function can depend on the values of the 155 // LEB relocations within it. This finalizeContents method is called after 156 // all the symbol values have be calcualted but before getSize() is ever 157 // called. 158 void calculateSize(); 159 160 const WasmSignature &signature; 161 162 protected: data()163 ArrayRef<uint8_t> data() const override { 164 assert(!config->compressRelocations); 165 return file->codeSection->Content.slice(getInputSectionOffset(), 166 function->Size); 167 } 168 169 const WasmFunction *function; 170 llvm::Optional<uint32_t> functionIndex; 171 llvm::Optional<uint32_t> tableIndex; 172 uint32_t compressedFuncSize = 0; 173 uint32_t compressedSize = 0; 174 }; 175 176 class SyntheticFunction : public InputFunction { 177 public: 178 SyntheticFunction(const WasmSignature &s, StringRef name, 179 StringRef debugName = {}) InputFunction(s,nullptr,nullptr)180 : InputFunction(s, nullptr, nullptr), name(name), debugName(debugName) { 181 sectionKind = InputChunk::SyntheticFunction; 182 } 183 classof(const InputChunk * c)184 static bool classof(const InputChunk *c) { 185 return c->kind() == InputChunk::SyntheticFunction; 186 } 187 getName()188 StringRef getName() const override { return name; } getDebugName()189 StringRef getDebugName() const override { return debugName; } getComdat()190 uint32_t getComdat() const override { return UINT32_MAX; } 191 setBody(ArrayRef<uint8_t> body_)192 void setBody(ArrayRef<uint8_t> body_) { body = body_; } 193 194 protected: data()195 ArrayRef<uint8_t> data() const override { return body; } 196 197 StringRef name; 198 StringRef debugName; 199 ArrayRef<uint8_t> body; 200 }; 201 202 // Represents a single Wasm Section within an input file. 203 class InputSection : public InputChunk { 204 public: InputSection(const WasmSection & s,ObjFile * f)205 InputSection(const WasmSection &s, ObjFile *f) 206 : InputChunk(f, InputChunk::Section), section(s) { 207 assert(section.Type == llvm::wasm::WASM_SEC_CUSTOM); 208 } 209 getName()210 StringRef getName() const override { return section.Name; } getDebugName()211 StringRef getDebugName() const override { return StringRef(); } getComdat()212 uint32_t getComdat() const override { return UINT32_MAX; } 213 214 OutputSection *outputSec = nullptr; 215 216 protected: data()217 ArrayRef<uint8_t> data() const override { return section.Content; } 218 219 // Offset within the input section. This is only zero since this chunk 220 // type represents an entire input section, not part of one. getInputSectionOffset()221 uint32_t getInputSectionOffset() const override { return 0; } 222 223 const WasmSection §ion; 224 }; 225 226 } // namespace wasm 227 228 std::string toString(const wasm::InputChunk *); 229 StringRef relocTypeToString(uint8_t relocType); 230 231 } // namespace lld 232 233 #endif // LLD_WASM_INPUT_CHUNKS_H 234