diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp index 004d71097387a..9ee0c29a4cd3c 100644 --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -31,8 +31,8 @@ using llvm::support::ulittle32_t; namespace lld::coff { -SectionChunk::SectionChunk(ObjFile *f, const coff_section *h) - : Chunk(SectionKind), file(f), header(h), repl(this) { +SectionChunk::SectionChunk(ObjFile *f, const coff_section *h, Kind k) + : Chunk(k), file(f), header(h), repl(this) { // Initialize relocs. if (file) setRelocs(file->getCOFFObj()->getRelocations(header)); @@ -410,6 +410,10 @@ void SectionChunk::writeTo(uint8_t *buf) const { applyRelocation(buf + rel.VirtualAddress, rel); } + + // Write the offset to EC entry thunk preceding section contents. + if (Defined *entryThunk = getEntryThunk()) + write32le(buf - sizeof(uint32_t), entryThunk->getRVA() - rva + 1); } void SectionChunk::applyRelocation(uint8_t *off, diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h index bb919037ecc26..df311524a8d18 100644 --- a/lld/COFF/Chunks.h +++ b/lld/COFF/Chunks.h @@ -55,7 +55,12 @@ enum : unsigned { Log2MaxSectionAlignment = 13 }; // doesn't even have actual data (if common or bss). class Chunk { public: - enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind }; + enum Kind : uint8_t { + SectionKind, + SectionECKind, + OtherKind, + ImportThunkKind + }; Kind kind() const { return chunkKind; } // Returns the size of this chunk (even if this is a common or BSS.) @@ -120,6 +125,10 @@ class Chunk { llvm::Triple::ArchType getArch() const; std::optional getArm64ECRangeType() const; + // ARM64EC entry thunk associated with the chunk. + Defined *getEntryThunk() const; + void setEntryThunk(Defined *entryThunk); + protected: Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {} @@ -176,7 +185,7 @@ class NonSectionChunk : public Chunk { // bytes, so this is used only for logging or debugging. virtual StringRef getDebugName() const { return ""; } - static bool classof(const Chunk *c) { return c->kind() != SectionKind; } + static bool classof(const Chunk *c) { return c->kind() >= OtherKind; } protected: NonSectionChunk(Kind k = OtherKind) : Chunk(k) {} @@ -210,7 +219,7 @@ class RuntimePseudoReloc { }; // A chunk corresponding a section of an input file. -class SectionChunk final : public Chunk { +class SectionChunk : public Chunk { // Identical COMDAT Folding feature accesses section internal data. friend class ICF; @@ -231,8 +240,8 @@ class SectionChunk final : public Chunk { Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); } }; - SectionChunk(ObjFile *file, const coff_section *header); - static bool classof(const Chunk *c) { return c->kind() == SectionKind; } + SectionChunk(ObjFile *file, const coff_section *header, Kind k = SectionKind); + static bool classof(const Chunk *c) { return c->kind() <= SectionECKind; } size_t getSize() const { return header->SizeOfRawData; } ArrayRef getContents() const; void writeTo(uint8_t *buf) const; @@ -393,6 +402,16 @@ class SectionChunk final : public Chunk { uint32_t sectionNameSize = 0; }; +// A section chunk corresponding a section of an EC input file. +class SectionChunkEC final : public SectionChunk { +public: + static bool classof(const Chunk *c) { return c->kind() == SectionECKind; } + + SectionChunkEC(ObjFile *file, const coff_section *header) + : SectionChunk(file, header, SectionECKind) {} + Defined *entryThunk = nullptr; +}; + // Inline methods to implement faux-virtual dispatch for SectionChunk. inline size_t Chunk::getSize() const { @@ -775,6 +794,17 @@ inline bool Chunk::isHotPatchable() const { return false; } +inline Defined *Chunk::getEntryThunk() const { + if (auto *c = dyn_cast(this)) + return c->entryThunk; + return nullptr; +} + +inline void Chunk::setEntryThunk(Defined *entryThunk) { + if (auto c = dyn_cast(this)) + c->entryThunk = entryThunk; +} + void applyMOV32T(uint8_t *off, uint32_t v); void applyBranch24T(uint8_t *off, int32_t v); diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index b0365b5b94417..cd143479a11e2 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -2604,6 +2604,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (auto *arg = args.getLastArg(OPT_print_symbol_order)) config->printSymbolOrder = arg->getValue(); + ctx.symtab.initializeEntryThunks(); + // Identify unreferenced COMDAT sections. if (config->doGC) { if (config->mingw) { diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp index b899a25324239..3140d094ae0f0 100644 --- a/lld/COFF/ICF.cpp +++ b/lld/COFF/ICF.cpp @@ -184,9 +184,7 @@ bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) { // Compare "moving" part of two sections, namely relocation targets. bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) { // Compare relocations. - auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { - Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); - Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + auto eqSym = [&](Symbol *b1, Symbol *b2) { if (b1 == b2) return true; if (auto *d1 = dyn_cast(b1)) @@ -194,6 +192,17 @@ bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) { return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + return eqSym(b1, b2); + }; + + Symbol *e1 = a->getEntryThunk(); + Symbol *e2 = b->getEntryThunk(); + if ((e1 || e2) && (!e1 || !e2 || !eqSym(e1, e2))) + return false; + return std::equal(a->getRelocs().begin(), a->getRelocs().end(), b->getRelocs().begin(), eq) && assocEquals(a, b); diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index 037fae45242c6..146bd53020932 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -152,6 +152,38 @@ void ObjFile::parseLazy() { } } +struct ECMapEntry { + ulittle32_t src; + ulittle32_t dst; + ulittle32_t type; +}; + +void ObjFile::initializeECThunks() { + for (SectionChunk *chunk : hybmpChunks) { + if (chunk->getContents().size() % sizeof(ECMapEntry)) { + error("Invalid .hybmp chunk size " + Twine(chunk->getContents().size())); + return; + } + + const uint8_t *end = + chunk->getContents().data() + chunk->getContents().size(); + for (const uint8_t *iter = chunk->getContents().data(); iter != end; + iter += sizeof(ECMapEntry)) { + auto entry = reinterpret_cast(iter); + switch (entry->type) { + case Arm64ECThunkType::Entry: + ctx.symtab.addEntryThunk(getSymbol(entry->src), getSymbol(entry->dst)); + break; + case Arm64ECThunkType::Exit: + case Arm64ECThunkType::GuestExit: + break; + default: + warn("Ignoring unknown EC thunk type " + Twine(entry->type)); + } + } + } +} + void ObjFile::parse() { // Parse a memory buffer as a COFF file. std::unique_ptr bin = CHECK(createBinary(mb), this); @@ -168,6 +200,7 @@ void ObjFile::parse() { initializeSymbols(); initializeFlags(); initializeDependencies(); + initializeECThunks(); } const coff_section *ObjFile::getSection(uint32_t i) { @@ -242,7 +275,11 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) return nullptr; - auto *c = make(this, sec); + SectionChunk *c; + if (isArm64EC(getMachineType())) + c = make(this, sec); + else + c = make(this, sec); if (def) c->checksum = def->CheckSum; @@ -260,6 +297,8 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, guardEHContChunks.push_back(c); else if (name == ".sxdata") sxDataChunks.push_back(c); + else if (isArm64EC(getMachineType()) && name == ".hybmp$x") + hybmpChunks.push_back(c); else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 && name == ".rdata" && leaderName.starts_with("??_C@")) // COFF sections that look like string literal sections (i.e. no diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index 3b55cd791bfda..cabd87ba673e3 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -227,6 +227,7 @@ class ObjFile : public InputFile { void initializeSymbols(); void initializeFlags(); void initializeDependencies(); + void initializeECThunks(); SectionChunk * readSection(uint32_t sectionNumber, @@ -292,6 +293,8 @@ class ObjFile : public InputFile { std::vector guardLJmpChunks; std::vector guardEHContChunks; + std::vector hybmpChunks; + // This vector contains a list of all symbols defined or referenced by this // file. They are indexed such that you can get a Symbol by symbol // index. Nonexistent indices (which are occupied by auxiliary diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp index 2cf216a6aaad5..06079a98f2d00 100644 --- a/lld/COFF/MarkLive.cpp +++ b/lld/COFF/MarkLive.cpp @@ -68,6 +68,10 @@ void markLive(COFFLinkerContext &ctx) { // Mark associative sections if any. for (SectionChunk &c : sc->children()) enqueue(&c); + + // Mark EC entry thunks. + if (Defined *entryThunk = sc->getEntryThunk()) + addSym(entryThunk); } } } diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 3accf24663c6a..b4ce79879b6b9 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -561,6 +561,25 @@ std::pair SymbolTable::insert(StringRef name, InputFile *file) { return result; } +void SymbolTable::addEntryThunk(Symbol *from, Symbol *to) { + entryThunks.push_back({from, to}); +} + +void SymbolTable::initializeEntryThunks() { + for (auto it : entryThunks) { + auto *to = dyn_cast(it.second); + if (!to) + continue; + auto *from = dyn_cast(it.first); + if (!from || !from->getChunk()->isCOMDAT() || + cast(from)->getValue()) { + error("non COMDAT symbol '" + from->getName() + "' in hybrid map"); + continue; + } + from->getChunk()->setEntryThunk(to); + } +} + Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, bool isWeakAlias) { auto [s, wasInserted] = insert(name, f); diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index fc623c2840d40..93b376b69f7ec 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -106,6 +106,8 @@ class SymbolTable { Symbol *addImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); void addLibcall(StringRef name); + void addEntryThunk(Symbol *from, Symbol *to); + void initializeEntryThunks(); void reportDuplicate(Symbol *existing, InputFile *newFile, SectionChunk *newSc = nullptr, @@ -134,6 +136,7 @@ class SymbolTable { llvm::DenseMap symMap; std::unique_ptr lto; bool ltoCompilationDone = false; + std::vector> entryThunks; COFFLinkerContext &ctx; }; diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 9c20bbb83d86d..43f71d8cc385c 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1532,6 +1532,10 @@ void Writer::assignAddresses() { } if (padding && c->isHotPatchable()) virtualSize += padding; + // If chunk has EC entry thunk, reserve a space for an offset to the + // thunk. + if (c->getEntryThunk()) + virtualSize += sizeof(uint32_t); virtualSize = alignTo(virtualSize, c->getAlignment()); c->setRVA(rva + virtualSize); virtualSize += c->getSize(); diff --git a/lld/test/COFF/arm64ec-entry-thunk.s b/lld/test/COFF/arm64ec-entry-thunk.s new file mode 100644 index 0000000000000..280c132e32e1a --- /dev/null +++ b/lld/test/COFF/arm64ec-entry-thunk.s @@ -0,0 +1,345 @@ +// REQUIRES: aarch64 +// RUN: split-file %s %t.dir && cd %t.dir + +#--- test-simple.s +// Build a simple function with an entry thunk. + + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + mov w0, #10 + ret + + .section .hybmp$x, "yi" + .symidx func + .symidx thunk + .word 1 + + .data + .rva func + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadcfg.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-simple.s -o test-simple.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-simple.dll loadcfg.obj test-simple.obj +// RUN: llvm-objdump -d out-simple.dll | FileCheck --check-prefix=DISASM %s + +// DISASM: Disassembly of section .text: +// DISASM-EMPTY: +// DISASM-NEXT: 0000000180001000 <.text>: +// DISASM-NEXT: 180001000: 00000009 udf #0x9 +// DISASM-NEXT: 180001004: 52800020 mov w0, #0x1 // =1 +// DISASM-NEXT: 180001008: d65f03c0 ret +// DISASM-NEXT: 18000100c: 52800140 mov w0, #0xa // =10 +// DISASM-NEXT: 180001010: d65f03c0 ret + +// RUN: llvm-readobj --sections out-simple.dll | FileCheck --check-prefix=HYBMP %s +// HYBMP-NOT: .hybmp + +// RUN: lld-link -machine:arm64x -dll -noentry -out:out-simplex.dll loadcfg.obj test-simple.obj +// RUN: llvm-objdump -d out-simplex.dll | FileCheck --check-prefix=DISASM %s + +#--- test-split-func.s +// Build a simple function with an entry thunk, but pass it in multiple files. + + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + ret + +#--- test-split-thunk.s + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + mov w0, #10 + ret + +#--- test-split-hybmp.s + .section .hybmp$x, "yi" + .symidx func + .symidx thunk + .word 1 + +#--- test-split-data.s + .data + .rva func + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-split-func.s -o test-split-func.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-split-thunk.s -o test-split-thunk.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-split-hybmp.s -o test-split-hybmp.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-split-data.s -o test-split-data.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-split.dll loadcfg.obj \ +// RUN: test-split-func.obj test-split-thunk.obj test-split-data.obj test-split-hybmp.obj +// RUN: llvm-objdump -d out-split.dll | FileCheck --check-prefix=DISASM %s + +#--- test-align.s +// Build multiple functions with thunks and various alignments and check that entry thunk offsets +// are correctly placed. + + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + nop + ret + + .section .text,"xr",discard,func2 + .globl func2 + .p2align 2 +func2: + mov w0, #2 + ret + + .section .text,"xr",discard,func3 + .globl func3 + .p2align 3 +func3: + mov w0, #3 + nop + ret + + .section .text,"xr",discard,func4 + .globl func4 + .p2align 3 +func4: + mov w0, #4 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + mov w0, #10 + ret + + .section .wowthk$aa,"xr",discard,thunk2 + .globl thunk2 + .p2align 2 +thunk2: + mov w0, #20 + ret + + .section .hybmp$x, "yi" + .symidx func + .symidx thunk + .word 1 + .symidx func2 + .symidx thunk2 + .word 1 + .symidx func3 + .symidx thunk + .word 1 + .symidx func4 + .symidx thunk + .word 1 + + .data + .rva func + .rva func2 + .rva func3 + .rva func4 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-align.s -o test-align.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-align.dll loadcfg.obj test-align.obj +// RUN: llvm-objdump -d out-align.dll | FileCheck --check-prefix=ALIGN %s + +// ALIGN: Disassembly of section .text: +// ALIGN-EMPTY: +// ALIGN-NEXT: 0000000180001000 <.text>: +// ALIGN-NEXT: 180001000: 00000035 udf #0x35 +// ALIGN-NEXT: 180001004: 52800020 mov w0, #0x1 // =1 +// ALIGN-NEXT: 180001008: d503201f nop +// ALIGN-NEXT: 18000100c: d65f03c0 ret +// ALIGN-NEXT: 180001010: 0000002d udf #0x2d +// ALIGN-NEXT: 180001014: 52800040 mov w0, #0x2 // =2 +// ALIGN-NEXT: 180001018: d65f03c0 ret +// ALIGN-NEXT: 18000101c: 00000019 udf #0x19 +// ALIGN-NEXT: 180001020: 52800060 mov w0, #0x3 // =3 +// ALIGN-NEXT: 180001024: d503201f nop +// ALIGN-NEXT: 180001028: d65f03c0 ret +// ALIGN-NEXT: 18000102c: 00000009 udf #0x9 +// ALIGN-NEXT: 180001030: 52800080 mov w0, #0x4 // =4 +// ALIGN-NEXT: 180001034: d65f03c0 ret +// ALIGN-NEXT: 180001038: 52800140 mov w0, #0xa // =10 +// ALIGN-NEXT: 18000103c: d65f03c0 ret +// ALIGN-NEXT: 180001040: 52800280 mov w0, #0x14 // =20 +// ALIGN-NEXT: 180001044: d65f03c0 ret + +#--- test-icf-thunk.s +// Build two functions with identical entry thunks and check that thunks are merged by ICF. + + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + ret + + .section .text,"xr",discard,func2 + .globl func2 + .p2align 2 +func2: + mov w0, #2 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + mov w0, #10 + ret + + .section .wowthk$aa,"xr",discard,thunk2 + .globl thunk2 + .p2align 2 +thunk2: + mov w0, #10 + ret + + .section .hybmp$x, "yi" + .symidx func + .symidx thunk + .word 1 + .symidx func2 + .symidx thunk2 + .word 1 + + .data + .rva func + .rva func2 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-icf-thunk.s -o test-icf-thunk.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-icf-thunk.dll loadcfg.obj test-icf-thunk.obj +// RUN: llvm-objdump -d out-icf-thunk.dll | FileCheck --check-prefix=ICF-THUNK %s + +// ICF-THUNK: Disassembly of section .text: +// ICF-THUNK-EMPTY: +// ICF-THUNK-NEXT: 0000000180001000 <.text>: +// ICF-THUNK-NEXT: 180001000: 00000015 udf #0x15 +// ICF-THUNK-NEXT: 180001004: 52800020 mov w0, #0x1 // =1 +// ICF-THUNK-NEXT: 180001008: d65f03c0 ret +// ICF-THUNK-NEXT: 18000100c: 00000009 udf #0x9 +// ICF-THUNK-NEXT: 180001010: 52800040 mov w0, #0x2 // =2 +// ICF-THUNK-NEXT: 180001014: d65f03c0 ret +// ICF-THUNK-NEXT: 180001018: 52800140 mov w0, #0xa // =10 +// ICF-THUNK-NEXT: 18000101c: d65f03c0 ret + +#--- test-icf-diff-thunk.s +// Build two identical functions with different entry thunks and check that they are not merged by ICF. + + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + ret + + .section .text,"xr",discard,func2 + .globl func2 + .p2align 2 +func2: + mov w0, #1 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + mov w0, #10 + ret + + .section .wowthk$aa,"xr",discard,thunk2 + .globl thunk2 + .p2align 2 +thunk2: + mov w0, #20 + ret + + .section .hybmp$x, "yi" + .symidx func + .symidx thunk + .word 1 + .symidx func2 + .symidx thunk2 + .word 1 + + .data + .rva func + .rva func2 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-icf-diff-thunk.s -o test-icf-diff-thunk.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-icf-diff-thunk.dll loadcfg.obj test-icf-diff-thunk.obj +// RUN: llvm-objdump -d out-icf-diff-thunk.dll | FileCheck --check-prefix=ICF-DIFF-THUNK %s + +// ICF-DIFF-THUNK: Disassembly of section .text: +// ICF-DIFF-THUNK-EMPTY: +// ICF-DIFF-THUNK-NEXT: 0000000180001000 <.text>: +// ICF-DIFF-THUNK-NEXT: 180001000: 00000015 udf #0x15 +// ICF-DIFF-THUNK-NEXT: 180001004: 52800020 mov w0, #0x1 // =1 +// ICF-DIFF-THUNK-NEXT: 180001008: d65f03c0 ret +// ICF-DIFF-THUNK-NEXT: 18000100c: 00000011 udf #0x11 +// ICF-DIFF-THUNK-NEXT: 180001010: 52800020 mov w0, #0x1 // =1 +// ICF-DIFF-THUNK-NEXT: 180001014: d65f03c0 ret +// ICF-DIFF-THUNK-NEXT: 180001018: 52800140 mov w0, #0xa // =10 +// ICF-DIFF-THUNK-NEXT: 18000101c: d65f03c0 ret +// ICF-DIFF-THUNK-NEXT: 180001020: 52800280 mov w0, #0x14 // =20 +// ICF-DIFF-THUNK-NEXT: 180001024: d65f03c0 ret + +#--- test-icf-both.s +// Build two identical functions with identical entry thunks and check that they are merged by ICF. + + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + ret + + .section .text,"xr",discard,func2 + .globl func2 + .p2align 2 +func2: + mov w0, #1 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + mov w0, #10 + ret + + .section .wowthk$aa,"xr",discard,thunk2 + .globl thunk2 + .p2align 2 +thunk2: + mov w0, #10 + ret + + .section .hybmp$x, "yi" + .symidx func + .symidx thunk + .word 1 + .symidx func2 + .symidx thunk2 + .word 1 + + .data + .rva func + .rva func2 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-icf-both.s -o test-icf-both.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-icf-both.dll loadcfg.obj test-icf-both.obj +// RUN: llvm-objdump -d out-icf-both.dll | FileCheck --check-prefix=DISASM %s + diff --git a/lld/test/COFF/arm64ec-hybmp.s b/lld/test/COFF/arm64ec-hybmp.s new file mode 100644 index 0000000000000..564109e067fae --- /dev/null +++ b/lld/test/COFF/arm64ec-hybmp.s @@ -0,0 +1,113 @@ +REQUIRES: aarch64 +RUN: split-file %s %t.dir && cd %t.dir + +#--- text-func.s + .text + .globl func + .p2align 2, 0x0 +func: + mov w0, #1 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + ret + + .section .hybmp$x,"yi" + .symidx func + .symidx thunk + .word 1 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows text-func.s -o text-func.obj +// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll text-func.obj 2>&1 | FileCheck -check-prefix=FUNC-NON-COMDAT %s +// FUNC-NON-COMDAT: error: non COMDAT symbol 'func' in hybrid map + +#--- offset-func.s + .section .text,"xr",discard,func + mov w0, #2 + .globl func + .p2align 2, 0x0 +func: + mov w0, #1 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + ret + + .section .hybmp$x,"yi" + .symidx func + .symidx thunk + .word 1 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows offset-func.s -o offset-func.obj +// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll offset-func.obj 2>&1 | FileCheck -check-prefix=FUNC-NON-COMDAT %s + +#--- undef-func.s + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + ret + + .section .hybmp$x,"yi" + .symidx func + .symidx thunk + .word 1 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows undef-func.s -o undef-func.obj +// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll undef-func.obj 2>&1 | FileCheck -check-prefix=UNDEF-FUNC %s +// UNDEF-FUNC: error: undefined symbol: func + +#--- undef-thunk.s + .section .text,"xr",discard,func + .globl func + .p2align 2, 0x0 +func: + mov w0, #1 + ret + + .section .hybmp$x,"yi" + .symidx func + .symidx thunk + .word 1 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows undef-thunk.s -o undef-thunk.obj +// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll undef-thunk.obj 2>&1 | FileCheck -check-prefix=UNDEF-THUNK %s +// UNDEF-THUNK: error: undefined symbol: thunk + +#--- invalid-type.s + .section .text,"xr",discard,func + .globl func + .p2align 2, 0x0 +func: + mov w0, #1 + ret + + .section .wowthk$aa,"xr",discard,thunk + .globl thunk + .p2align 2 +thunk: + ret + + .section .hybmp$x,"yi" + .symidx func + .symidx thunk + .word 3 + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows invalid-type.s -o invalid-type.obj +// RUN: lld-link -machine:arm64ec -dll -noentry -out:test.dll invalid-type.obj 2>&1 | FileCheck -check-prefix=INVALID-TYPE %s +// INVALID-TYPE: warning: Ignoring unknown EC thunk type 3 + +#--- invalid-size.s + .section .hybmp$x,"yi" + .symidx func + .symidx thunk + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows invalid-size.s -o invalid-size.obj +// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll invalid-size.obj 2>&1 | FileCheck -check-prefix=INVALID-SIZE %s +// INVALID-SIZE: error: Invalid .hybmp chunk size 8 diff --git a/llvm/include/llvm/BinaryFormat/COFF.h b/llvm/include/llvm/BinaryFormat/COFF.h index 4c31cd847bdfb..c8642864af63b 100644 --- a/llvm/include/llvm/BinaryFormat/COFF.h +++ b/llvm/include/llvm/BinaryFormat/COFF.h @@ -806,7 +806,7 @@ enum Feat00Flags : uint32_t { Kernel = 0x40000000, }; -enum class Arm64ECThunkType : uint8_t { +enum Arm64ECThunkType : uint8_t { GuestExit = 0, Entry = 1, Exit = 4,