brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.4 KiB · 1fe78d7 Raw
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 &section;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