//===- SyntheticSection.h ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Synthetic sections represent chunks of linker-created data. If you // need to create a chunk of data that to be included in some section // in the result, you probably want to create that as a synthetic section. // //===----------------------------------------------------------------------===// #ifndef LLD_WASM_SYNTHETIC_SECTIONS_H #define LLD_WASM_SYNTHETIC_SECTIONS_H #include "OutputSections.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/BinaryFormat/WasmTraits.h" #include #define DEBUG_TYPE "lld" namespace lld::wasm { // An init entry to be written to either the synthetic init func or the // linking metadata. struct WasmInitEntry { const FunctionSymbol *sym; uint32_t priority; }; class SyntheticSection : public OutputSection { public: SyntheticSection(uint32_t type, std::string name = "") : OutputSection(type, name), bodyOutputStream(body) { if (!name.empty()) writeStr(bodyOutputStream, name, "section name"); } void writeTo(uint8_t *buf) override { assert(offset); log("writing " + toString(*this)); memcpy(buf + offset, header.data(), header.size()); memcpy(buf + offset + header.size(), body.data(), body.size()); } size_t getSize() const override { return header.size() + body.size(); } virtual void writeBody() {} virtual void assignIndexes() {} void finalizeContents() override { writeBody(); bodyOutputStream.flush(); createHeader(body.size()); } raw_ostream &getStream() { return bodyOutputStream; } std::string body; protected: llvm::raw_string_ostream bodyOutputStream; }; // Create the custom "dylink" section containing information for the dynamic // linker. // See // https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md class DylinkSection : public SyntheticSection { public: DylinkSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "dylink.0") {} bool isNeeded() const override; void writeBody() override; uint32_t memAlign = 0; uint32_t memSize = 0; }; class TypeSection : public SyntheticSection { public: TypeSection() : SyntheticSection(llvm::wasm::WASM_SEC_TYPE) {} bool isNeeded() const override { return types.size() > 0; }; void writeBody() override; uint32_t registerType(const WasmSignature &sig); uint32_t lookupType(const WasmSignature &sig); protected: std::vector types; llvm::DenseMap typeIndices; }; /** * A key for some kind of imported entity of type `T`. * * Used when de-duplicating imports. */ template struct ImportKey { public: enum class State { Plain, Empty, Tombstone }; public: T type; std::optional importModule; std::optional importName; State state; public: ImportKey(T type) : type(type), state(State::Plain) {} ImportKey(T type, State state) : type(type), state(state) {} ImportKey(T type, std::optional importModule, std::optional importName) : type(type), importModule(importModule), importName(importName), state(State::Plain) {} }; template inline bool operator==(const ImportKey &lhs, const ImportKey &rhs) { return lhs.state == rhs.state && lhs.importModule == rhs.importModule && lhs.importName == rhs.importName && lhs.type == rhs.type; } } // namespace wasm::lld // `ImportKey` can be used as a key in a `DenseMap` if `T` can be used as a // key in a `DenseMap`. namespace llvm { template struct DenseMapInfo> { static lld::wasm::ImportKey getEmptyKey() { typename lld::wasm::ImportKey key(llvm::DenseMapInfo::getEmptyKey()); key.state = lld::wasm::ImportKey::State::Empty; return key; } static lld::wasm::ImportKey getTombstoneKey() { typename lld::wasm::ImportKey key(llvm::DenseMapInfo::getEmptyKey()); key.state = lld::wasm::ImportKey::State::Tombstone; return key; } static unsigned getHashValue(const lld::wasm::ImportKey &key) { uintptr_t hash = hash_value(key.importModule); hash = hash_combine(hash, key.importName); hash = hash_combine(hash, llvm::DenseMapInfo::getHashValue(key.type)); hash = hash_combine(hash, key.state); return hash; } static bool isEqual(const lld::wasm::ImportKey &lhs, const lld::wasm::ImportKey &rhs) { return lhs == rhs; } }; } // end namespace llvm namespace lld { namespace wasm { class ImportSection : public SyntheticSection { public: ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {} bool isNeeded() const override { return getNumImports() > 0; } void writeBody() override; void addImport(Symbol *sym); void addGOTEntry(Symbol *sym); void seal() { isSealed = true; } uint32_t getNumImports() const; uint32_t getNumImportedGlobals() const { assert(isSealed); return numImportedGlobals; } uint32_t getNumImportedFunctions() const { assert(isSealed); return numImportedFunctions; } uint32_t getNumImportedTags() const { assert(isSealed); return numImportedTags; } uint32_t getNumImportedTables() const { assert(isSealed); return numImportedTables; } std::vector importedSymbols; std::vector gotSymbols; protected: bool isSealed = false; unsigned numImportedGlobals = 0; unsigned numImportedFunctions = 0; unsigned numImportedTags = 0; unsigned numImportedTables = 0; llvm::DenseMap, uint32_t> importedGlobals; llvm::DenseMap, uint32_t> importedFunctions; llvm::DenseMap, uint32_t> importedTables; llvm::DenseMap, uint32_t> importedTags; }; class FunctionSection : public SyntheticSection { public: FunctionSection() : SyntheticSection(llvm::wasm::WASM_SEC_FUNCTION) {} bool isNeeded() const override { return inputFunctions.size() > 0; }; void writeBody() override; void addFunction(InputFunction *func); std::vector inputFunctions; protected: }; class TableSection : public SyntheticSection { public: TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {} bool isNeeded() const override { return inputTables.size() > 0; }; void assignIndexes() override; void writeBody() override; void addTable(InputTable *table); std::vector inputTables; }; class MemorySection : public SyntheticSection { public: MemorySection() : SyntheticSection(llvm::wasm::WASM_SEC_MEMORY) {} bool isNeeded() const override { return !config->memoryImport.has_value(); } void writeBody() override; uint64_t numMemoryPages = 0; uint64_t maxMemoryPages = 0; }; // The tag section contains a list of declared wasm tags associated with the // module. Currently the only supported tag kind is exceptions. All C++ // exceptions are represented by a single tag. A tag entry in this section // contains information on what kind of tag it is (e.g. exception) and the type // of values associated with the tag. (In Wasm, a tag can contain multiple // values of primitive types. But for C++ exceptions, we just throw a pointer // which is an i32 value (for wasm32 architecture), so the signature of C++ // exception is (i32)->(void), because all exception tag types are assumed to // have void return type to share WasmSignature with functions.) class TagSection : public SyntheticSection { public: TagSection() : SyntheticSection(llvm::wasm::WASM_SEC_TAG) {} void writeBody() override; bool isNeeded() const override { return inputTags.size() > 0; } void addTag(InputTag *tag); std::vector inputTags; }; class GlobalSection : public SyntheticSection { public: GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {} static bool classof(const OutputSection *sec) { return sec->type == llvm::wasm::WASM_SEC_GLOBAL; } uint32_t numGlobals() const { assert(isSealed); return inputGlobals.size() + dataAddressGlobals.size() + internalGotSymbols.size(); } bool isNeeded() const override { return numGlobals() > 0; } void assignIndexes() override; void writeBody() override; void addGlobal(InputGlobal *global); // Add an internal GOT entry global that corresponds to the given symbol. // Normally GOT entries are imported and assigned by the external dynamic // linker. However, when linking PIC code statically or when linking with // -Bsymbolic we can internalize GOT entries by declaring globals the hold // symbol addresses. // // For the static linking case these internal globals can be completely // eliminated by a post-link optimizer such as wasm-opt. // // TODO(sbc): Another approach to optimizing these away could be to use // specific relocation types combined with linker relaxation which could // transform a `global.get` to an `i32.const`. void addInternalGOTEntry(Symbol *sym); bool needsRelocations() { if (config->extendedConst) return false; return llvm::any_of(internalGotSymbols, [=](Symbol *sym) { return !sym->isTLS(); }); } bool needsTLSRelocations() { return llvm::any_of(internalGotSymbols, [=](Symbol *sym) { return sym->isTLS(); }); } void generateRelocationCode(raw_ostream &os, bool TLS) const; std::vector dataAddressGlobals; std::vector inputGlobals; std::vector internalGotSymbols; protected: bool isSealed = false; }; class ExportSection : public SyntheticSection { public: ExportSection() : SyntheticSection(llvm::wasm::WASM_SEC_EXPORT) {} bool isNeeded() const override { return exports.size() > 0; } void writeBody() override; std::vector exports; std::vector exportedSymbols; }; class StartSection : public SyntheticSection { public: StartSection() : SyntheticSection(llvm::wasm::WASM_SEC_START) {} bool isNeeded() const override; void writeBody() override; }; class ElemSection : public SyntheticSection { public: ElemSection() : SyntheticSection(llvm::wasm::WASM_SEC_ELEM) {} bool isNeeded() const override { return indirectFunctions.size() > 0; }; void writeBody() override; void addEntry(FunctionSymbol *sym); uint32_t numEntries() const { return indirectFunctions.size(); } protected: std::vector indirectFunctions; }; class DataCountSection : public SyntheticSection { public: DataCountSection(ArrayRef segments); bool isNeeded() const override; void writeBody() override; protected: uint32_t numSegments; }; // Create the custom "linking" section containing linker metadata. // This is only created when relocatable output is requested. class LinkingSection : public SyntheticSection { public: LinkingSection(const std::vector &initFunctions, const std::vector &dataSegments) : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "linking"), initFunctions(initFunctions), dataSegments(dataSegments) {} bool isNeeded() const override { return config->relocatable || config->emitRelocs; } void writeBody() override; void addToSymtab(Symbol *sym); protected: std::vector symtabEntries; llvm::StringMap sectionSymbolIndices; const std::vector &initFunctions; const std::vector &dataSegments; }; // Create the custom "name" section containing debug symbol names. class NameSection : public SyntheticSection { public: NameSection(ArrayRef segments) : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "name"), segments(segments) {} bool isNeeded() const override { return !config->stripAll && numNames() > 0; } void writeBody() override; unsigned numNames() const { return numNamedGlobals() + numNamedFunctions(); } unsigned numNamedGlobals() const; unsigned numNamedFunctions() const; unsigned numNamedDataSegments() const; protected: ArrayRef segments; }; class ProducersSection : public SyntheticSection { public: ProducersSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "producers") {} bool isNeeded() const override { return !config->stripAll && fieldCount() > 0; } void writeBody() override; void addInfo(const llvm::wasm::WasmProducerInfo &info); protected: int fieldCount() const { return int(!languages.empty()) + int(!tools.empty()) + int(!sDKs.empty()); } SmallVector, 8> languages; SmallVector, 8> tools; SmallVector, 8> sDKs; }; class TargetFeaturesSection : public SyntheticSection { public: TargetFeaturesSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "target_features") {} bool isNeeded() const override { return !config->stripAll && features.size() > 0; } void writeBody() override; llvm::SmallSet features; }; class RelocSection : public SyntheticSection { public: RelocSection(StringRef name, OutputSection *sec) : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, std::string(name)), sec(sec) {} void writeBody() override; bool isNeeded() const override { return sec->getNumRelocations() > 0; }; protected: OutputSection *sec; }; class BuildIdSection : public SyntheticSection { public: BuildIdSection(); void writeBody() override; bool isNeeded() const override { return config->buildId != BuildIdKind::None; } void writeBuildId(llvm::ArrayRef buf); void writeTo(uint8_t *buf) override { LLVM_DEBUG(llvm::dbgs() << "BuildId writeto buf " << buf << " offset " << offset << " headersize " << header.size() << '\n'); // The actual build ID is derived from a hash of all of the output // sections, so it can't be calculated until they are written. Here // we write the section leaving zeros in place of the hash. SyntheticSection::writeTo(buf); // Calculate and store the location where the hash will be written. hashPlaceholderPtr = buf + offset + header.size() + +sizeof(buildIdSectionName) /*name string*/ + 1 /* hash size */; } const uint32_t hashSize; private: static constexpr char buildIdSectionName[] = "build_id"; uint8_t *hashPlaceholderPtr = nullptr; }; // Linker generated output sections struct OutStruct { DylinkSection *dylinkSec; TypeSection *typeSec; FunctionSection *functionSec; ImportSection *importSec; TableSection *tableSec; MemorySection *memorySec; GlobalSection *globalSec; TagSection *tagSec; ExportSection *exportSec; StartSection *startSec; ElemSection *elemSec; DataCountSection *dataCountSec; LinkingSection *linkingSec; NameSection *nameSec; ProducersSection *producersSec; TargetFeaturesSection *targetFeaturesSec; BuildIdSection *buildIdSec; }; extern OutStruct out; } // namespace wasm } // namespace lld #endif