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/ADT/CachedHashString.h"
28 #include "llvm/MC/StringTableBuilder.h"
29 #include "llvm/Object/Wasm.h"
30 
31 namespace lld {
32 namespace wasm {
33 
34 class ObjFile;
35 class OutputSegment;
36 class OutputSection;
37 
38 class InputChunk {
39 public:
40   enum Kind {
41     DataSegment,
42     Merge,
43     MergedChunk,
44     Function,
45     SyntheticFunction,
46     Section,
47   };
48 
49   StringRef name;
50   StringRef debugName;
51 
kind()52   Kind kind() const { return (Kind)sectionKind; }
53 
54   uint32_t getSize() const;
55   uint32_t getInputSize() const;
56 
57   void writeTo(uint8_t *buf) const;
58   void relocate(uint8_t *buf) const;
59 
getRelocations()60   ArrayRef<WasmRelocation> getRelocations() const { return relocations; }
setRelocations(ArrayRef<WasmRelocation> rs)61   void setRelocations(ArrayRef<WasmRelocation> rs) { relocations = rs; }
62 
63   // Translate an offset into the input chunk to an offset in the output
64   // section.
65   uint64_t getOffset(uint64_t offset) const;
66   // Translate an offset into the input chunk into an offset into the output
67   // chunk.  For data segments (InputSegment) this will return and offset into
68   // the output segment.  For MergeInputChunk, this will return an offset into
69   // the parent merged chunk.  For other chunk types this is no-op and we just
70   // return unmodified offset.
71   uint64_t getChunkOffset(uint64_t offset) const;
72   uint64_t getVA(uint64_t offset = 0) const;
73 
getComdat()74   uint32_t getComdat() const { return comdat; }
75   StringRef getComdatName() const;
getInputSectionOffset()76   uint32_t getInputSectionOffset() const { return inputSectionOffset; }
77 
getNumRelocations()78   size_t getNumRelocations() const { return relocations.size(); }
79   void writeRelocations(llvm::raw_ostream &os) const;
80   void generateRelocationCode(raw_ostream &os) const;
81 
isTLS()82   bool isTLS() const { return flags & llvm::wasm::WASM_SEG_FLAG_TLS; }
83 
84   ObjFile *file;
85   OutputSection *outputSec = nullptr;
86   uint32_t comdat = UINT32_MAX;
87   uint32_t inputSectionOffset = 0;
88   uint32_t alignment;
89   uint32_t flags;
90 
91   // Only applies to data segments.
92   uint32_t outputSegmentOffset = 0;
93   const OutputSegment *outputSeg = nullptr;
94 
95   // After assignAddresses is called, this represents the offset from
96   // the beginning of the output section this chunk was assigned to.
97   int32_t outSecOff = 0;
98 
99   uint8_t sectionKind : 3;
100 
101   // Signals that the section is part of the output.  The garbage collector,
102   // and COMDAT handling can set a sections' Live bit.
103   // If GC is disabled, all sections start out as live by default.
104   unsigned live : 1;
105 
106   // Signals the chunk was discarded by COMDAT handling.
107   unsigned discarded : 1;
108 
109 protected:
110   InputChunk(ObjFile *f, Kind k, StringRef name, uint32_t alignment = 0,
111              uint32_t flags = 0)
name(name)112       : name(name), file(f), alignment(alignment), flags(flags), sectionKind(k),
113         live(!config->gcSections), discarded(false) {}
data()114   ArrayRef<uint8_t> data() const { return rawData; }
115   uint64_t getTombstone() const;
116 
117   ArrayRef<WasmRelocation> relocations;
118   ArrayRef<uint8_t> rawData;
119 };
120 
121 // Represents a WebAssembly data segment which can be included as part of
122 // an output data segments.  Note that in WebAssembly, unlike ELF and other
123 // formats, used the term "data segment" to refer to the continuous regions of
124 // memory that make on the data section. See:
125 // https://webassembly.github.io/spec/syntax/modules.html#syntax-data
126 //
127 // For example, by default, clang will produce a separate data section for
128 // each global variable.
129 class InputSegment : public InputChunk {
130 public:
InputSegment(const WasmSegment & seg,ObjFile * f)131   InputSegment(const WasmSegment &seg, ObjFile *f)
132       : InputChunk(f, InputChunk::DataSegment, seg.Data.Name,
133                    seg.Data.Alignment, seg.Data.LinkingFlags),
134         segment(seg) {
135     rawData = segment.Data.Content;
136     comdat = segment.Data.Comdat;
137     inputSectionOffset = segment.SectionOffset;
138   }
139 
classof(const InputChunk * c)140   static bool classof(const InputChunk *c) { return c->kind() == DataSegment; }
141 
142 protected:
143   const WasmSegment &segment;
144 };
145 
146 class SyntheticMergedChunk;
147 
148 // Merge segment handling copied from lld/ELF/InputSection.h.  Keep in sync
149 // where possible.
150 
151 // SectionPiece represents a piece of splittable segment contents.
152 // We allocate a lot of these and binary search on them. This means that they
153 // have to be as compact as possible, which is why we don't store the size (can
154 // be found by looking at the next one).
155 struct SectionPiece {
SectionPieceSectionPiece156   SectionPiece(size_t off, uint32_t hash, bool live)
157       : inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {}
158 
159   uint32_t inputOff;
160   uint32_t live : 1;
161   uint32_t hash : 31;
162   uint64_t outputOff = 0;
163 };
164 
165 static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
166 
167 // This corresponds segments marked as WASM_SEG_FLAG_STRINGS.
168 class MergeInputChunk : public InputChunk {
169 public:
MergeInputChunk(const WasmSegment & seg,ObjFile * f)170   MergeInputChunk(const WasmSegment &seg, ObjFile *f)
171       : InputChunk(f, Merge, seg.Data.Name, seg.Data.Alignment,
172                    seg.Data.LinkingFlags) {
173     rawData = seg.Data.Content;
174     comdat = seg.Data.Comdat;
175     inputSectionOffset = seg.SectionOffset;
176   }
177 
MergeInputChunk(const WasmSection & s,ObjFile * f)178   MergeInputChunk(const WasmSection &s, ObjFile *f)
179       : InputChunk(f, Merge, s.Name, 0, llvm::wasm::WASM_SEG_FLAG_STRINGS) {
180     assert(s.Type == llvm::wasm::WASM_SEC_CUSTOM);
181     comdat = s.Comdat;
182     rawData = s.Content;
183   }
184 
classof(const InputChunk * s)185   static bool classof(const InputChunk *s) { return s->kind() == Merge; }
186   void splitIntoPieces();
187 
188   // Translate an offset in the input section to an offset in the parent
189   // MergeSyntheticSection.
190   uint64_t getParentOffset(uint64_t offset) const;
191 
192   // Splittable sections are handled as a sequence of data
193   // rather than a single large blob of data.
194   std::vector<SectionPiece> pieces;
195 
196   // Returns I'th piece's data. This function is very hot when
197   // string merging is enabled, so we want to inline.
198   LLVM_ATTRIBUTE_ALWAYS_INLINE
getData(size_t i)199   llvm::CachedHashStringRef getData(size_t i) const {
200     size_t begin = pieces[i].inputOff;
201     size_t end =
202         (pieces.size() - 1 == i) ? data().size() : pieces[i + 1].inputOff;
203     return {toStringRef(data().slice(begin, end - begin)), pieces[i].hash};
204   }
205 
206   // Returns the SectionPiece at a given input section offset.
207   SectionPiece *getSectionPiece(uint64_t offset);
getSectionPiece(uint64_t offset)208   const SectionPiece *getSectionPiece(uint64_t offset) const {
209     return const_cast<MergeInputChunk *>(this)->getSectionPiece(offset);
210   }
211 
212   SyntheticMergedChunk *parent = nullptr;
213 
214 private:
215   void splitStrings(ArrayRef<uint8_t> a);
216 };
217 
218 // SyntheticMergedChunk is a class that allows us to put mergeable
219 // sections with different attributes in a single output sections. To do that we
220 // put them into SyntheticMergedChunk synthetic input sections which are
221 // attached to regular output sections.
222 class SyntheticMergedChunk : public InputChunk {
223 public:
SyntheticMergedChunk(StringRef name,uint32_t alignment,uint32_t flags)224   SyntheticMergedChunk(StringRef name, uint32_t alignment, uint32_t flags)
225       : InputChunk(nullptr, InputChunk::MergedChunk, name, alignment, flags),
226         builder(llvm::StringTableBuilder::RAW, 1ULL << alignment) {}
227 
classof(const InputChunk * c)228   static bool classof(const InputChunk *c) {
229     return c->kind() == InputChunk::MergedChunk;
230   }
231 
addMergeChunk(MergeInputChunk * ms)232   void addMergeChunk(MergeInputChunk *ms) {
233     comdat = ms->getComdat();
234     ms->parent = this;
235     chunks.push_back(ms);
236   }
237 
238   void finalizeContents();
239 
240   llvm::StringTableBuilder builder;
241 
242 protected:
243   std::vector<MergeInputChunk *> chunks;
244 };
245 
246 // Represents a single wasm function within and input file.  These are
247 // combined to create the final output CODE section.
248 class InputFunction : public InputChunk {
249 public:
InputFunction(const WasmSignature & s,const WasmFunction * func,ObjFile * f)250   InputFunction(const WasmSignature &s, const WasmFunction *func, ObjFile *f)
251       : InputChunk(f, InputChunk::Function, func->SymbolName), signature(s),
252         function(func),
253         exportName(func && func->ExportName ? (*func->ExportName).str()
254                                             : llvm::Optional<std::string>()) {
255     inputSectionOffset = function->CodeSectionOffset;
256     rawData =
257         file->codeSection->Content.slice(inputSectionOffset, function->Size);
258     debugName = function->DebugName;
259     comdat = function->Comdat;
260   }
261 
InputFunction(StringRef name,const WasmSignature & s)262   InputFunction(StringRef name, const WasmSignature &s)
263       : InputChunk(nullptr, InputChunk::Function, name), signature(s) {}
264 
classof(const InputChunk * c)265   static bool classof(const InputChunk *c) {
266     return c->kind() == InputChunk::Function ||
267            c->kind() == InputChunk::SyntheticFunction;
268   }
269 
getExportName()270   llvm::Optional<StringRef> getExportName() const {
271     return exportName ? llvm::Optional<StringRef>(*exportName)
272                       : llvm::Optional<StringRef>();
273   }
setExportName(std::string exportName)274   void setExportName(std::string exportName) { this->exportName = exportName; }
getFunctionInputOffset()275   uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
getFunctionCodeOffset()276   uint32_t getFunctionCodeOffset() const { return function->CodeOffset; }
getFunctionIndex()277   uint32_t getFunctionIndex() const { return functionIndex.value(); }
hasFunctionIndex()278   bool hasFunctionIndex() const { return functionIndex.has_value(); }
279   void setFunctionIndex(uint32_t index);
getTableIndex()280   uint32_t getTableIndex() const { return tableIndex.value(); }
hasTableIndex()281   bool hasTableIndex() const { return tableIndex.has_value(); }
282   void setTableIndex(uint32_t index);
283   void writeCompressed(uint8_t *buf) const;
284 
285   // The size of a given input function can depend on the values of the
286   // LEB relocations within it.  This finalizeContents method is called after
287   // all the symbol values have be calculated but before getSize() is ever
288   // called.
289   void calculateSize();
290 
291   const WasmSignature &signature;
292 
getCompressedSize()293   uint32_t getCompressedSize() const {
294     assert(compressedSize);
295     return compressedSize;
296   }
297 
298   const WasmFunction *function;
299 
300 protected:
301   llvm::Optional<std::string> exportName;
302   llvm::Optional<uint32_t> functionIndex;
303   llvm::Optional<uint32_t> tableIndex;
304   uint32_t compressedFuncSize = 0;
305   uint32_t compressedSize = 0;
306 };
307 
308 class SyntheticFunction : public InputFunction {
309 public:
310   SyntheticFunction(const WasmSignature &s, StringRef name,
311                     StringRef debugName = {})
InputFunction(name,s)312       : InputFunction(name, s) {
313     sectionKind = InputChunk::SyntheticFunction;
314     this->debugName = debugName;
315   }
316 
classof(const InputChunk * c)317   static bool classof(const InputChunk *c) {
318     return c->kind() == InputChunk::SyntheticFunction;
319   }
320 
setBody(ArrayRef<uint8_t> body)321   void setBody(ArrayRef<uint8_t> body) { rawData = body; }
322 };
323 
324 // Represents a single Wasm Section within an input file.
325 class InputSection : public InputChunk {
326 public:
InputSection(const WasmSection & s,ObjFile * f)327   InputSection(const WasmSection &s, ObjFile *f)
328       : InputChunk(f, InputChunk::Section, s.Name),
329         tombstoneValue(getTombstoneForSection(s.Name)), section(s) {
330     assert(section.Type == llvm::wasm::WASM_SEC_CUSTOM);
331     comdat = section.Comdat;
332     rawData = section.Content;
333   }
334 
classof(const InputChunk * c)335   static bool classof(const InputChunk *c) {
336     return c->kind() == InputChunk::Section;
337   }
338 
339   const uint64_t tombstoneValue;
340 
341 protected:
342   static uint64_t getTombstoneForSection(StringRef name);
343   const WasmSection &section;
344 };
345 
346 } // namespace wasm
347 
348 std::string toString(const wasm::InputChunk *);
349 StringRef relocTypeToString(uint8_t relocType);
350 
351 } // namespace lld
352 
353 #endif // LLD_WASM_INPUT_CHUNKS_H
354