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 40 Kind kind() const { return sectionKind; } 41 42 virtual uint32_t getSize() const { return data().size(); } 43 virtual uint32_t getInputSize() const { return getSize(); }; 44 45 virtual void writeTo(uint8_t *sectionStart) const; 46 47 ArrayRef<WasmRelocation> getRelocations() const { return relocations; } 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 56 size_t getNumRelocations() const { return relocations.size(); } 57 void writeRelocations(llvm::raw_ostream &os) const; 58 59 ObjFile *file; 60 OutputSection *outputSec = nullptr; 61 // Offset withing the output section 62 int32_t outputOffset = 0; 63 64 // Signals that the section is part of the output. The garbage collector, 65 // and COMDAT handling can set a sections' Live bit. 66 // If GC is disabled, all sections start out as live by default. 67 unsigned live : 1; 68 69 // Signals the chunk was discarded by COMDAT handling. 70 unsigned discarded : 1; 71 72 protected: 73 InputChunk(ObjFile *f, Kind k) 74 : file(f), live(!config->gcSections), discarded(false), sectionKind(k) {} 75 virtual ~InputChunk() = default; 76 virtual ArrayRef<uint8_t> data() const = 0; 77 virtual uint64_t getTombstone() const { return 0; } 78 79 // Verifies the existing data at relocation targets matches our expectations. 80 // This is performed only debug builds as an extra sanity check. 81 void verifyRelocTargets() const; 82 83 ArrayRef<WasmRelocation> relocations; 84 Kind sectionKind; 85 }; 86 87 // Represents a WebAssembly data segment which can be included as part of 88 // an output data segments. Note that in WebAssembly, unlike ELF and other 89 // formats, used the term "data segment" to refer to the continuous regions of 90 // memory that make on the data section. See: 91 // https://webassembly.github.io/spec/syntax/modules.html#syntax-data 92 // 93 // For example, by default, clang will produce a separate data section for 94 // each global variable. 95 class InputSegment : public InputChunk { 96 public: 97 InputSegment(const WasmSegment &seg, ObjFile *f) 98 : InputChunk(f, InputChunk::DataSegment), segment(seg) {} 99 100 static bool classof(const InputChunk *c) { return c->kind() == DataSegment; } 101 102 void generateRelocationCode(raw_ostream &os) const; 103 104 uint32_t getAlignment() const { return segment.Data.Alignment; } 105 StringRef getName() const override { return segment.Data.Name; } 106 StringRef getDebugName() const override { return StringRef(); } 107 uint32_t getComdat() const override { return segment.Data.Comdat; } 108 uint32_t getInputSectionOffset() const override { 109 return segment.SectionOffset; 110 } 111 uint64_t getVA() const; 112 113 const OutputSegment *outputSeg = nullptr; 114 int32_t outputSegmentOffset = 0; 115 116 protected: 117 ArrayRef<uint8_t> data() const override { return segment.Data.Content; } 118 119 const WasmSegment &segment; 120 }; 121 122 // Represents a single wasm function within and input file. These are 123 // combined to create the final output CODE section. 124 class InputFunction : public InputChunk { 125 public: 126 InputFunction(const WasmSignature &s, const WasmFunction *func, ObjFile *f) 127 : InputChunk(f, InputChunk::Function), signature(s), function(func), 128 exportName(func && func->ExportName.hasValue() 129 ? (*func->ExportName).str() 130 : llvm::Optional<std::string>()) {} 131 132 static bool classof(const InputChunk *c) { 133 return c->kind() == InputChunk::Function || 134 c->kind() == InputChunk::SyntheticFunction; 135 } 136 137 void writeTo(uint8_t *sectionStart) const override; 138 StringRef getName() const override { return function->SymbolName; } 139 StringRef getDebugName() const override { return function->DebugName; } 140 llvm::Optional<StringRef> getExportName() const { 141 return exportName.hasValue() ? llvm::Optional<StringRef>(*exportName) 142 : llvm::Optional<StringRef>(); 143 } 144 void setExportName(std::string exportName) { this->exportName = exportName; } 145 uint32_t getComdat() const override { return function->Comdat; } 146 uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); } 147 uint32_t getFunctionCodeOffset() const { return function->CodeOffset; } 148 uint32_t getSize() const override { 149 if (config->compressRelocations && file) { 150 assert(compressedSize); 151 return compressedSize; 152 } 153 return data().size(); 154 } 155 uint32_t getInputSize() const override { return function->Size; } 156 uint32_t getFunctionIndex() const { return functionIndex.getValue(); } 157 bool hasFunctionIndex() const { return functionIndex.hasValue(); } 158 void setFunctionIndex(uint32_t index); 159 uint32_t getInputSectionOffset() const override { 160 return function->CodeSectionOffset; 161 } 162 uint32_t getTableIndex() const { return tableIndex.getValue(); } 163 bool hasTableIndex() const { return tableIndex.hasValue(); } 164 void setTableIndex(uint32_t index); 165 166 // The size of a given input function can depend on the values of the 167 // LEB relocations within it. This finalizeContents method is called after 168 // all the symbol values have be calculated but before getSize() is ever 169 // called. 170 void calculateSize(); 171 172 const WasmSignature &signature; 173 174 protected: 175 ArrayRef<uint8_t> data() const override { 176 assert(!config->compressRelocations); 177 return file->codeSection->Content.slice(getInputSectionOffset(), 178 function->Size); 179 } 180 181 const WasmFunction *function; 182 llvm::Optional<std::string> exportName; 183 llvm::Optional<uint32_t> functionIndex; 184 llvm::Optional<uint32_t> tableIndex; 185 uint32_t compressedFuncSize = 0; 186 uint32_t compressedSize = 0; 187 }; 188 189 class SyntheticFunction : public InputFunction { 190 public: 191 SyntheticFunction(const WasmSignature &s, StringRef name, 192 StringRef debugName = {}) 193 : InputFunction(s, nullptr, nullptr), name(name), debugName(debugName) { 194 sectionKind = InputChunk::SyntheticFunction; 195 } 196 197 static bool classof(const InputChunk *c) { 198 return c->kind() == InputChunk::SyntheticFunction; 199 } 200 201 StringRef getName() const override { return name; } 202 StringRef getDebugName() const override { return debugName; } 203 uint32_t getComdat() const override { return UINT32_MAX; } 204 205 void setBody(ArrayRef<uint8_t> body_) { body = body_; } 206 207 protected: 208 ArrayRef<uint8_t> data() const override { return body; } 209 210 StringRef name; 211 StringRef debugName; 212 ArrayRef<uint8_t> body; 213 }; 214 215 // Represents a single Wasm Section within an input file. 216 class InputSection : public InputChunk { 217 public: 218 InputSection(const WasmSection &s, ObjFile *f) 219 : InputChunk(f, InputChunk::Section), section(s), tombstoneValue(getTombstoneForSection(s.Name)) { 220 assert(section.Type == llvm::wasm::WASM_SEC_CUSTOM); 221 } 222 223 StringRef getName() const override { return section.Name; } 224 StringRef getDebugName() const override { return StringRef(); } 225 uint32_t getComdat() const override { return section.Comdat; } 226 227 protected: 228 ArrayRef<uint8_t> data() const override { return section.Content; } 229 230 // Offset within the input section. This is only zero since this chunk 231 // type represents an entire input section, not part of one. 232 uint32_t getInputSectionOffset() const override { return 0; } 233 uint64_t getTombstone() const override { return tombstoneValue; } 234 static uint64_t getTombstoneForSection(StringRef name); 235 236 const WasmSection §ion; 237 const uint64_t tombstoneValue; 238 }; 239 240 } // namespace wasm 241 242 std::string toString(const wasm::InputChunk *); 243 StringRef relocTypeToString(uint8_t relocType); 244 245 } // namespace lld 246 247 #endif // LLD_WASM_INPUT_CHUNKS_H 248