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