Index: wasm/InputChunks.h =================================================================== --- wasm/InputChunks.h +++ wasm/InputChunks.h @@ -25,6 +25,7 @@ #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/Wasm.h" +#include "llvm/Support/LEB128.h" using llvm::object::WasmSection; using llvm::object::WasmSegment; @@ -44,7 +45,7 @@ class InputChunk { public: - enum Kind { DataSegment, Function, SyntheticFunction }; + enum Kind { DataSegment, Function, SyntheticFunction, Section }; Kind kind() const { return SectionKind; } @@ -173,6 +174,37 @@ ArrayRef Body; }; +// Represents a single Wasm Section within an input file. +class InputSection : public InputChunk { +public: + InputSection(const WasmSection &S, ObjFile *F) + : InputChunk(F, InputChunk::Section), Section(S) { + if (Section.Type == llvm::wasm::WASM_SEC_CUSTOM) { + // TODO check LEB errors + unsigned Count; + uint64_t NameSize = llvm::decodeULEB128(Section.Content.data(), &Count); + PayloadOffset = Count + NameSize; + } else + PayloadOffset = 0; + } + + const WasmSection &getSection() { return Section; } + + StringRef getName() const override { return Section.Name; } + uint32_t getComdat() const override { return UINT32_MAX; } + + size_t getPayloadSize() const { return Section.Content.size() - PayloadOffset; } + +protected: + ArrayRef data() const override { return Section.Content.slice(PayloadOffset); } + uint32_t getInputSectionOffset() const override { + return 0; // TODO if we decide to make absolute offset: Section.Offset + } + + const WasmSection &Section; + size_t PayloadOffset; +}; + } // namespace wasm std::string toString(const wasm::InputChunk *); Index: wasm/InputChunks.cpp =================================================================== --- wasm/InputChunks.cpp +++ wasm/InputChunks.cpp @@ -89,6 +89,8 @@ break; case R_WEBASSEMBLY_TABLE_INDEX_I32: case R_WEBASSEMBLY_MEMORY_ADDR_I32: + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: + case R_WEBASSEMBLY_SECTION_OFFSET_I32: ExistingValue = static_cast(read32le(Loc)); write32le(Loc, Value); break; @@ -124,6 +126,8 @@ case R_WEBASSEMBLY_MEMORY_ADDR_LEB: case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case R_WEBASSEMBLY_MEMORY_ADDR_I32: + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: + case R_WEBASSEMBLY_SECTION_OFFSET_I32: writeUleb128(OS, Rel.Addend, "reloc addend"); break; } Index: wasm/InputFiles.h =================================================================== --- wasm/InputFiles.h +++ wasm/InputFiles.h @@ -14,6 +14,7 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/MemoryBuffer.h" @@ -35,6 +36,7 @@ class InputFunction; class InputSegment; class InputGlobal; +class InputSection; class InputFile { public: @@ -108,12 +110,14 @@ std::vector Segments; std::vector Functions; std::vector Globals; + llvm::StringMap CustomSections; ArrayRef getSymbols() const { return Symbols; } Symbol *getSymbol(uint32_t Index) const { return Symbols[Index]; } FunctionSymbol *getFunctionSymbol(uint32_t Index) const; DataSymbol *getDataSymbol(uint32_t Index) const; GlobalSymbol *getGlobalSymbol(uint32_t Index) const; + SectionSymbol *getSectionSymbol(uint32_t Index) const; private: Symbol *createDefined(const WasmSymbol &Sym); Index: wasm/InputFiles.cpp =================================================================== --- wasm/InputFiles.cpp +++ wasm/InputFiles.cpp @@ -80,6 +80,17 @@ return Segment.Data.Offset.Value.Int32 + Sym.Info.DataRef.Offset + Reloc.Addend; } + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: { + const WasmSymbol& Sym = WasmObj->syms()[Reloc.Index]; + if (Sym.isUndefined()) + return 0; + const WasmFunction& Function = + WasmObj->functions()[Sym.Info.ElementIndex - WasmObj->getNumImportedFunctions()]; + const uint8_t FunctionSizeSize = Function.Size > 126 ? 2 : 1; // FIXME + return Function.CodeSectionOffset + FunctionSizeSize + Reloc.Addend; + } + case R_WEBASSEMBLY_SECTION_OFFSET_I32: + return Reloc.Addend; // TODO absolute to wasm file start? case R_WEBASSEMBLY_TYPE_INDEX_LEB: return Reloc.Index; case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: @@ -110,6 +121,20 @@ return getFunctionSymbol(Reloc.Index)->getFunctionIndex(); case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: return getGlobalSymbol(Reloc.Index)->getGlobalIndex(); + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: + if (auto *Sym = dyn_cast(getFunctionSymbol(Reloc.Index))) { + const uint8_t FunctionSizeSize = Sym->Function->getSize() > 128 ? 2 : 1; // FIXME + return Sym->Function->OutputOffset + FunctionSizeSize + Reloc.Addend; + } + return 0; + case R_WEBASSEMBLY_SECTION_OFFSET_I32: + if (auto *Sym = dyn_cast(getSectionSymbol(Reloc.Index))) { + const auto &SyntheticSection = Symtab->SyntheticSections[Sym->SectionIndex]; + const auto &InputSection = SyntheticSection.find(this); + if (InputSection != SyntheticSection.end()) + return InputSection->second->OutputOffset + Reloc.Addend; + } + return 0; default: llvm_unreachable("unknown relocation type"); } @@ -153,6 +178,13 @@ CodeSection = &Section; else if (Section.Type == WASM_SEC_DATA) DataSection = &Section; + else if (Section.Type == WASM_SEC_CUSTOM && + Section.Name.startswith(".debug_")) { + // Recording debug sections (starts) as symbols. + InputSection* InSect = make(Section, this); + InSect->copyRelocations(Section); + CustomSections.insert(std::pair(Section.Name, InSect)); + } } TypeMap.resize(getWasmObj()->types().size()); @@ -213,6 +245,10 @@ return cast(Symbols[Index]); } +SectionSymbol *ObjFile::getSectionSymbol(uint32_t Index) const { + return cast(Symbols[Index]); +} + DataSymbol *ObjFile::getDataSymbol(uint32_t Index) const { return cast(Symbols[Index]); } @@ -251,14 +287,22 @@ return make(Name, Flags, this, Seg, Offset, Size); return Symtab->addDefinedData(Name, Flags, this, Seg, Offset, Size); } - case WASM_SYMBOL_TYPE_GLOBAL: + case WASM_SYMBOL_TYPE_GLOBAL: { InputGlobal *Global = Globals[Sym.Info.ElementIndex - WasmObj->getNumImportedGlobals()]; if (Sym.isBindingLocal()) return make(Name, Flags, this, Global); - return Symtab->addDefinedGlobal(Name, Flags, this, Global); + return Symtab->addDefinedGlobal(Name, Flags, this, Global); + } + case WASM_SYMBOL_TYPE_SECTION: { + if (Sym.Info.SectionCode != WASM_SEC_CUSTOM) + llvm_unreachable("invalid section symbol"); + InputSection *Section = + CustomSections[Sym.Info.SectionName]; + return Symtab->addDefinedSection(Name, Flags, this, Section); + } } - llvm_unreachable("unkown symbol kind"); + llvm_unreachable("unknown symbol kind"); } Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) { @@ -272,8 +316,10 @@ return Symtab->addUndefinedData(Name, Flags, this); case WASM_SYMBOL_TYPE_GLOBAL: return Symtab->addUndefinedGlobal(Name, Flags, this, Sym.GlobalType); + case WASM_SYMBOL_TYPE_SECTION: + return Symtab->addUndefinedSection(Name, Flags, this); } - llvm_unreachable("unkown symbol kind"); + llvm_unreachable("unknown symbol kind"); } void ArchiveFile::parse() { Index: wasm/OutputSections.h =================================================================== --- wasm/OutputSections.h +++ wasm/OutputSections.h @@ -113,6 +113,20 @@ size_t BodySize = 0; }; +class CustomSection : public OutputSection { +public: + explicit CustomSection(std::string Name, size_t Size, ArrayRef Fragments); + size_t getSize() const override { return Header.size() + NameData.size() + FragmentsSize; } + void writeTo(uint8_t *Buf) override; + uint32_t numRelocations() const override; + void writeRelocations(raw_ostream &OS) const override; + +protected: + size_t FragmentsSize = 0; + ArrayRef Fragments; + std::string NameData; +}; + } // namespace wasm } // namespace lld Index: wasm/OutputSections.cpp =================================================================== --- wasm/OutputSections.cpp +++ wasm/OutputSections.cpp @@ -188,3 +188,45 @@ for (const InputChunk *C : Seg->InputSegments) C->writeRelocations(OS); } + +CustomSection::CustomSection(std::string Name, size_t Size, ArrayRef Fragments) + : OutputSection(WASM_SEC_CUSTOM, Name), FragmentsSize(Size), Fragments(Fragments) { + raw_string_ostream OS(NameData); + encodeULEB128(Name.size(), OS); + OS << Name; + OS.flush(); + + createHeader(Size + NameData.size()); +} + +void CustomSection::writeTo(uint8_t *Buf) { + log("writing " + toString(*this) + " size=" + Twine(getSize()) + + " fragments_count=" + Twine(Fragments.size())); + + assert(Offset); + Buf += Offset; + + // Write section header + memcpy(Buf, Header.data(), Header.size()); + Buf += Header.size(); + memcpy(Buf, NameData.data(), NameData.size()); + Buf += NameData.size(); + + // Write custom sections payload + + parallelForEach(Fragments, [&](const InputSection *Section) { + Section->writeTo(Buf); + }); +} + +uint32_t CustomSection::numRelocations() const { + uint32_t Count = 0; + for (const InputSection *InputSect : Fragments) + Count += InputSect->NumRelocations(); + return Count; +} + +void CustomSection::writeRelocations(raw_ostream &OS) const { + for (const InputSection *S : Fragments) + S->writeRelocations(OS); +} Index: wasm/SymbolTable.h =================================================================== --- wasm/SymbolTable.h +++ wasm/SymbolTable.h @@ -13,6 +13,7 @@ #include "InputFiles.h" #include "Symbols.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/raw_ostream.h" @@ -38,11 +39,14 @@ // There is one add* function per symbol type. class SymbolTable { public: + using SyntheticSection = llvm::DenseMap; + void addFile(InputFile *File); std::vector ObjectFiles; std::vector SyntheticFunctions; std::vector SyntheticGlobals; + std::vector SyntheticSections; void reportRemainingUndefines(); @@ -56,12 +60,15 @@ uint32_t Size); Symbol *addDefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File, InputGlobal *G); + Symbol *addDefinedSection(StringRef Name, uint32_t Flags, InputFile *File, + InputSection *S); Symbol *addUndefinedFunction(StringRef Name, uint32_t Flags, InputFile *File, const WasmSignature *Signature); Symbol *addUndefinedData(StringRef Name, uint32_t Flags, InputFile *File); Symbol *addUndefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File, const WasmGlobalType *Type); + Symbol *addUndefinedSection(StringRef Name, uint32_t Flags, InputFile *File); void addLazy(ArchiveFile *F, const Archive::Symbol *Sym); Index: wasm/SymbolTable.cpp =================================================================== --- wasm/SymbolTable.cpp +++ wasm/SymbolTable.cpp @@ -231,6 +231,26 @@ return S; } +Symbol *SymbolTable::addDefinedSection( + StringRef Name, uint32_t Flags, InputFile *File, InputSection* Section) { + DEBUG(dbgs() << "addDefinedSection:" << Name << "\n"); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + + if (WasInserted || S->isLazy() || isa(S)) { + replaceSymbol(S, Name, Flags, SyntheticSections.size()); + SyntheticSections.emplace_back(); + SyntheticSections.back()[File] = Section; + return S; + } + + assert(isa(S)); + auto &SectionItems = SyntheticSections[dyn_cast(S)->SectionIndex]; + SectionItems[File] = Section; + return S; +} + Symbol *SymbolTable::addUndefinedFunction(StringRef Name, uint32_t Flags, InputFile *File, const WasmSignature *Sig) { @@ -284,6 +304,20 @@ return S; } +Symbol *SymbolTable::addUndefinedSection( + StringRef Name, uint32_t Flags, InputFile *File) { + DEBUG(dbgs() << "addUndefinedSection:" << Name << "\n"); + + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + + if (WasInserted) + replaceSymbol(S, Name, Flags); + assert(isa(S)); + return S; +} + void SymbolTable::addLazy(ArchiveFile *File, const Archive::Symbol *Sym) { DEBUG(dbgs() << "addLazy: " << Sym->getName() << "\n"); StringRef Name = Sym->getName(); Index: wasm/Symbols.h =================================================================== --- wasm/Symbols.h +++ wasm/Symbols.h @@ -29,6 +29,7 @@ class InputSegment; class InputFunction; class InputGlobal; +class InputSection; #define INVALID_INDEX UINT32_MAX @@ -39,9 +40,11 @@ DefinedFunctionKind, DefinedDataKind, DefinedGlobalKind, + DefinedSectionKind, UndefinedFunctionKind, UndefinedDataKind, UndefinedGlobalKind, + UndefinedSectionKind, LazyKind, }; @@ -49,12 +52,14 @@ bool isDefined() const { return SymbolKind == DefinedFunctionKind || SymbolKind == DefinedDataKind || - SymbolKind == DefinedGlobalKind; + SymbolKind == DefinedGlobalKind || SymbolKind == DefinedSectionKind; } bool isUndefined() const { return SymbolKind == UndefinedFunctionKind || - SymbolKind == UndefinedDataKind || SymbolKind == UndefinedGlobalKind; + SymbolKind == UndefinedDataKind || + SymbolKind == UndefinedGlobalKind || + SymbolKind == UndefinedSectionKind; } bool isLazy() const { return SymbolKind == LazyKind; } @@ -147,6 +152,45 @@ } }; +class SectionSymbol : public Symbol { +public: + static bool classof(const Symbol *S) { + return S->kind() == DefinedSectionKind || + S->kind() == UndefinedSectionKind; + } + + SectionSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F = nullptr) + : Symbol(Name, K, Flags, F) {} + + uint32_t getOutputSymbolIndex() const; + void setOutputSymbolIndex(uint32_t Index); + +protected: + uint32_t OutputSymbolIndex = INVALID_INDEX; +}; + +class DefinedSection : public SectionSymbol { +public: + DefinedSection(StringRef Name, uint32_t Flags, size_t Index) + : SectionSymbol(Name, DefinedSectionKind, Flags), SectionIndex(Index) {} + + static bool classof(const Symbol *S) { + return S->kind() == DefinedSectionKind; + } + + size_t SectionIndex; +}; + +class UndefinedSection : public SectionSymbol { +public: + UndefinedSection(StringRef Name, uint32_t Flags) + : SectionSymbol(Name, UndefinedSectionKind, Flags) {} + + static bool classof(const Symbol *S) { + return S->kind() == UndefinedSectionKind; + } +}; + class DataSymbol : public Symbol { public: static bool classof(const Symbol *S) { @@ -289,10 +333,12 @@ alignas(DefinedFunction) char A[sizeof(DefinedFunction)]; alignas(DefinedData) char B[sizeof(DefinedData)]; alignas(DefinedGlobal) char C[sizeof(DefinedGlobal)]; + alignas(DefinedSection) char I[sizeof(DefinedSection)]; alignas(LazySymbol) char D[sizeof(LazySymbol)]; alignas(UndefinedFunction) char E[sizeof(UndefinedFunction)]; alignas(UndefinedData) char F[sizeof(UndefinedData)]; alignas(UndefinedGlobal) char G[sizeof(UndefinedGlobal)]; + alignas(UndefinedSection) char J[sizeof(UndefinedSection)]; }; template Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -36,6 +36,8 @@ return llvm::wasm::WASM_SYMBOL_TYPE_DATA; if (isa(this)) return llvm::wasm::WASM_SYMBOL_TYPE_GLOBAL; + if (isa(this)) + return llvm::wasm::WASM_SYMBOL_TYPE_SECTION; llvm_unreachable("invalid symbol kind"); } @@ -187,6 +189,18 @@ Global ? &Global->getType() : nullptr), Global(Global) {} +uint32_t SectionSymbol::getOutputSymbolIndex() const { + DEBUG(dbgs() << "getOutputSymbolIndex: " << getName() << "\n"); + assert(OutputSymbolIndex != INVALID_INDEX); + return OutputSymbolIndex; +} + +void SectionSymbol::setOutputSymbolIndex(uint32_t Index) { + DEBUG(dbgs() << "setOutputSymbolIndex: " << getName() << " -> " << Index << "\n"); + assert(Index != INVALID_INDEX); + OutputSymbolIndex = Index; +} + void LazySymbol::fetch() { cast(File)->addMember(&ArchiveSymbol); } std::string lld::toString(const wasm::Symbol &Sym) { @@ -204,12 +218,16 @@ return "DefinedData"; case wasm::Symbol::DefinedGlobalKind: return "DefinedGlobal"; + case wasm::Symbol::DefinedSectionKind: + return "DefinedSection"; case wasm::Symbol::UndefinedFunctionKind: return "UndefinedFunction"; case wasm::Symbol::UndefinedDataKind: return "UndefinedData"; case wasm::Symbol::UndefinedGlobalKind: return "UndefinedGlobal"; + case wasm::Symbol::UndefinedSectionKind: + return "UndefinedSection"; case wasm::Symbol::LazyKind: return "LazyKind"; } @@ -224,6 +242,8 @@ return "Data"; case llvm::wasm::WASM_SYMBOL_TYPE_GLOBAL: return "Global"; + case llvm::wasm::WASM_SYMBOL_TYPE_SECTION: + return "Section"; } llvm_unreachable("invalid symbol type"); } Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -20,6 +20,7 @@ #include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Object/WasmTraits.h" #include "llvm/Support/FileOutputBuffer.h" @@ -49,6 +50,16 @@ uint32_t Priority; }; +struct ConcatCustomSection { + StringRef Name; + uint32_t Size; + std::vector Fragments; + + ConcatCustomSection(StringRef Name, uint32_t Size, + std::vector &&Fragments) + : Name(Name), Size(Size), Fragments(Fragments) { } +}; + // The writer writes a SymbolTable result to a file. class Writer { public: @@ -65,6 +76,7 @@ void assignIndexes(); void calculateImports(); void calculateExports(); + void calculateCustomSections(); void assignSymtab(); void calculateTypes(); void createOutputSegments(); @@ -84,6 +96,7 @@ void createElemSection(); void createCodeSection(); void createDataSection(); + void createCustomSections(); // Custom sections void createRelocSections(); @@ -103,6 +116,7 @@ unsigned NumImportedFunctions = 0; unsigned NumImportedGlobals = 0; std::vector ExportedSymbols; + std::vector ConcatCustomSections; std::vector DefinedFakeGlobals; std::vector InputGlobals; std::vector InputFunctions; @@ -118,6 +132,7 @@ std::vector Segments; llvm::SmallDenseMap SegmentMap; + llvm::StringSet<> InternedNames; }; } // anonymous namespace @@ -276,6 +291,15 @@ } } +void Writer::createCustomSections() { + log("createCustomSections"); + for (auto &CS : ConcatCustomSections) { + DEBUG(dbgs() << "create custom section: " << CS.Name << "\n"); + auto Section = make(CS.Name, CS.Size, CS.Fragments); + OutputSections.push_back(Section); + } +} + void Writer::createElemSection() { if (IndirectFunctions.empty()) return; @@ -335,12 +359,17 @@ Name = "reloc.DATA"; else if (OSec->Type == WASM_SEC_CODE) Name = "reloc.CODE"; + else if (OSec->Type == WASM_SEC_CUSTOM && + StringRef(OSec->Name).startswith(".debug")) + Name = InternedNames.insert("reloc." + OSec->Name).first->getKey(); else - llvm_unreachable("relocations only supported for code and data"); + llvm_unreachable("relocations only supported for code, data and debug"); SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, Name); raw_ostream &OS = Section->getStream(); writeUleb128(OS, OSec->Type, "reloc section"); + if (OSec->Type == WASM_SEC_CUSTOM) + writeStr(OS, OSec->Name, "reloc custom section name"); writeUleb128(OS, Count, "reloc count"); OSec->writeRelocations(OS); } @@ -413,8 +442,7 @@ writeUleb128(Sub.OS, G->getGlobalIndex(), "index"); if (Sym->isDefined()) writeStr(Sub.OS, Sym->getName(), "sym name"); - } else { - assert(isa(Sym)); + } else if (auto *D = dyn_cast(Sym)) { writeStr(Sub.OS, Sym->getName(), "sym name"); if (auto *DataSym = dyn_cast(Sym)) { writeUleb128(Sub.OS, DataSym->getOutputSegmentIndex(), "index"); @@ -422,7 +450,15 @@ "data offset"); writeUleb128(Sub.OS, DataSym->getSize(), "data size"); } - } + } else if (auto *S = dyn_cast(Sym)) { + const auto &SyntheticSection = Symtab->SyntheticSections[S->SectionIndex]; + const auto &InputSection = SyntheticSection.begin()->second; + const WasmSection& Section = InputSection->getSection(); + writeUleb128(Sub.OS, Section.Type, "sym section type"); + if (Section.Type == WASM_SEC_CUSTOM) + writeStr(Sub.OS, Section.Name, "sym section name"); + } else + assert(false); } Sub.writeTo(OS); @@ -629,6 +665,9 @@ createCodeSection(); createDataSection(); + if (!Config->StripDebug && !Config->StripAll) + createCustomSections(); + // Custom sections if (Config->Relocatable) { createLinkingSection(); @@ -636,7 +675,7 @@ } if (!Config->StripDebug && !Config->StripAll) createNameSection(); - + for (OutputSection *S : OutputSections) { S->setOffset(FileSize); S->finalizeContents(); @@ -682,6 +721,24 @@ } } +void Writer::calculateCustomSections() { + for (auto &SectionsSet : Symtab->SyntheticSections) { + InputSection *SampleSection = SectionsSet.begin()->second; + DEBUG(dbgs() << "custom section: " << SampleSection->getSection().Name << "\n"); + uint32_t Offset = 0; + std::vector Fragments; + for (auto &P : SectionsSet) { + InputSection *Section = P.second; + Section->OutputOffset = Offset; + Fragments.push_back(Section); + Offset += Section->getPayloadSize(); + } + + ConcatCustomSections.emplace_back(SampleSection->getSection().Name, + Offset, std::move(Fragments)); + } +} + void Writer::assignSymtab() { if (!Config->Relocatable) return; @@ -690,7 +747,7 @@ for (ObjFile *File : Symtab->ObjectFiles) { DEBUG(dbgs() << "Symtab entries: " << File->getName() << "\n"); for (Symbol *Sym : File->getSymbols()) { - if (Sym->getFile() != File) + if (Sym->getFile() != File && !isa(Sym)) continue; // (Since this is relocatable output, GC is not performed so symbols must // be live.) @@ -797,6 +854,8 @@ HandleRelocs(Chunk); for (InputChunk *Chunk : File->Segments) HandleRelocs(Chunk); + for (auto &P : File->CustomSections) + HandleRelocs(P.second); } uint32_t GlobalIndex = NumImportedGlobals + InputGlobals.size(); @@ -918,6 +977,8 @@ layoutMemory(); log("-- calculateExports"); calculateExports(); + log("-- calculateCustomSections"); + calculateCustomSections(); log("-- assignSymtab"); assignSymtab();