Skip to content

Commit ff1f5bb

Browse files
committed
[LLD][COFF] Add support for ARM64EC entry thunks.
For x86_64 callable functions, ARM64EC requires an entry thunk generated by the compiler. The linker interprets .hybmp sections to associate function chunks with their entry points and writes an offset to thunks preceding function section contents. Additionally, ICF needs to be aware of entry thunks to not consider chunks to be equal when they have different entry thunks, and GC needs to mark entry thunks together with function chunks. I used a new SectionChunkEC class instead of storing entry thunks in SectionChunk, following the guideline to keep SectionChunk as compact as possible. This way, there is no memory usage increase on non-EC targets.
1 parent a2bdbc6 commit ff1f5bb

File tree

13 files changed

+587
-12
lines changed

13 files changed

+587
-12
lines changed

lld/COFF/Chunks.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ using llvm::support::ulittle32_t;
3131

3232
namespace lld::coff {
3333

34-
SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
35-
: Chunk(SectionKind), file(f), header(h), repl(this) {
34+
SectionChunk::SectionChunk(ObjFile *f, const coff_section *h, Kind k)
35+
: Chunk(k), file(f), header(h), repl(this) {
3636
// Initialize relocs.
3737
if (file)
3838
setRelocs(file->getCOFFObj()->getRelocations(header));
@@ -410,6 +410,10 @@ void SectionChunk::writeTo(uint8_t *buf) const {
410410

411411
applyRelocation(buf + rel.VirtualAddress, rel);
412412
}
413+
414+
// Write the offset to EC entry thunk preceding section contents.
415+
if (Defined *entryThunk = getEntryThunk())
416+
write32le(buf - sizeof(uint32_t), entryThunk->getRVA() - rva + 1);
413417
}
414418

415419
void SectionChunk::applyRelocation(uint8_t *off,

lld/COFF/Chunks.h

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ enum : unsigned { Log2MaxSectionAlignment = 13 };
5555
// doesn't even have actual data (if common or bss).
5656
class Chunk {
5757
public:
58-
enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind };
58+
enum Kind : uint8_t {
59+
SectionKind,
60+
SectionECKind,
61+
OtherKind,
62+
ImportThunkKind
63+
};
5964
Kind kind() const { return chunkKind; }
6065

6166
// Returns the size of this chunk (even if this is a common or BSS.)
@@ -120,6 +125,10 @@ class Chunk {
120125
llvm::Triple::ArchType getArch() const;
121126
std::optional<chpe_range_type> getArm64ECRangeType() const;
122127

128+
// ARM64EC entry thunk associated with the chunk.
129+
Defined *getEntryThunk() const;
130+
void setEntryThunk(Defined *entryThunk);
131+
123132
protected:
124133
Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {}
125134

@@ -176,7 +185,7 @@ class NonSectionChunk : public Chunk {
176185
// bytes, so this is used only for logging or debugging.
177186
virtual StringRef getDebugName() const { return ""; }
178187

179-
static bool classof(const Chunk *c) { return c->kind() != SectionKind; }
188+
static bool classof(const Chunk *c) { return c->kind() >= OtherKind; }
180189

181190
protected:
182191
NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}
@@ -210,7 +219,7 @@ class RuntimePseudoReloc {
210219
};
211220

212221
// A chunk corresponding a section of an input file.
213-
class SectionChunk final : public Chunk {
222+
class SectionChunk : public Chunk {
214223
// Identical COMDAT Folding feature accesses section internal data.
215224
friend class ICF;
216225

@@ -231,8 +240,8 @@ class SectionChunk final : public Chunk {
231240
Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); }
232241
};
233242

234-
SectionChunk(ObjFile *file, const coff_section *header);
235-
static bool classof(const Chunk *c) { return c->kind() == SectionKind; }
243+
SectionChunk(ObjFile *file, const coff_section *header, Kind k = SectionKind);
244+
static bool classof(const Chunk *c) { return c->kind() <= SectionECKind; }
236245
size_t getSize() const { return header->SizeOfRawData; }
237246
ArrayRef<uint8_t> getContents() const;
238247
void writeTo(uint8_t *buf) const;
@@ -393,6 +402,16 @@ class SectionChunk final : public Chunk {
393402
uint32_t sectionNameSize = 0;
394403
};
395404

405+
// A section chunk corresponding a section of an EC input file.
406+
class SectionChunkEC final : public SectionChunk {
407+
public:
408+
static bool classof(const Chunk *c) { return c->kind() == SectionECKind; }
409+
410+
SectionChunkEC(ObjFile *file, const coff_section *header)
411+
: SectionChunk(file, header, SectionECKind) {}
412+
Defined *entryThunk = nullptr;
413+
};
414+
396415
// Inline methods to implement faux-virtual dispatch for SectionChunk.
397416

398417
inline size_t Chunk::getSize() const {
@@ -775,6 +794,17 @@ inline bool Chunk::isHotPatchable() const {
775794
return false;
776795
}
777796

797+
inline Defined *Chunk::getEntryThunk() const {
798+
if (auto *c = dyn_cast<const SectionChunkEC>(this))
799+
return c->entryThunk;
800+
return nullptr;
801+
}
802+
803+
inline void Chunk::setEntryThunk(Defined *entryThunk) {
804+
if (auto c = dyn_cast<SectionChunkEC>(this))
805+
c->entryThunk = entryThunk;
806+
}
807+
778808
void applyMOV32T(uint8_t *off, uint32_t v);
779809
void applyBranch24T(uint8_t *off, int32_t v);
780810

lld/COFF/Driver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
26042604
if (auto *arg = args.getLastArg(OPT_print_symbol_order))
26052605
config->printSymbolOrder = arg->getValue();
26062606

2607+
ctx.symtab.initializeEntryThunks();
2608+
26072609
// Identify unreferenced COMDAT sections.
26082610
if (config->doGC) {
26092611
if (config->mingw) {

lld/COFF/ICF.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,25 @@ bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) {
184184
// Compare "moving" part of two sections, namely relocation targets.
185185
bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) {
186186
// Compare relocations.
187-
auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
188-
Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
189-
Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
187+
auto eqSym = [&](Symbol *b1, Symbol *b2) {
190188
if (b1 == b2)
191189
return true;
192190
if (auto *d1 = dyn_cast<DefinedRegular>(b1))
193191
if (auto *d2 = dyn_cast<DefinedRegular>(b2))
194192
return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
195193
return false;
196194
};
195+
auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
196+
Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
197+
Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
198+
return eqSym(b1, b2);
199+
};
200+
201+
Symbol *e1 = a->getEntryThunk();
202+
Symbol *e2 = b->getEntryThunk();
203+
if ((e1 || e2) && (!e1 || !e2 || !eqSym(e1, e2)))
204+
return false;
205+
197206
return std::equal(a->getRelocs().begin(), a->getRelocs().end(),
198207
b->getRelocs().begin(), eq) &&
199208
assocEquals(a, b);

lld/COFF/InputFiles.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,38 @@ void ObjFile::parseLazy() {
152152
}
153153
}
154154

155+
struct ECMapEntry {
156+
ulittle32_t src;
157+
ulittle32_t dst;
158+
ulittle32_t type;
159+
};
160+
161+
void ObjFile::initializeECThunks() {
162+
for (SectionChunk *chunk : hybmpChunks) {
163+
if (chunk->getContents().size() % sizeof(ECMapEntry)) {
164+
error("Invalid .hybmp chunk size " + Twine(chunk->getContents().size()));
165+
return;
166+
}
167+
168+
const uint8_t *end =
169+
chunk->getContents().data() + chunk->getContents().size();
170+
for (const uint8_t *iter = chunk->getContents().data(); iter != end;
171+
iter += sizeof(ECMapEntry)) {
172+
auto entry = reinterpret_cast<const ECMapEntry *>(iter);
173+
switch (entry->type) {
174+
case Arm64ECThunkType::Entry:
175+
ctx.symtab.addEntryThunk(getSymbol(entry->src), getSymbol(entry->dst));
176+
break;
177+
case Arm64ECThunkType::Exit:
178+
case Arm64ECThunkType::GuestExit:
179+
break;
180+
default:
181+
warn("Ignoring unknown EC thunk type " + Twine(entry->type));
182+
}
183+
}
184+
}
185+
}
186+
155187
void ObjFile::parse() {
156188
// Parse a memory buffer as a COFF file.
157189
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
@@ -168,6 +200,7 @@ void ObjFile::parse() {
168200
initializeSymbols();
169201
initializeFlags();
170202
initializeDependencies();
203+
initializeECThunks();
171204
}
172205

173206
const coff_section *ObjFile::getSection(uint32_t i) {
@@ -242,7 +275,11 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
242275

243276
if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
244277
return nullptr;
245-
auto *c = make<SectionChunk>(this, sec);
278+
SectionChunk *c;
279+
if (isArm64EC(getMachineType()))
280+
c = make<SectionChunkEC>(this, sec);
281+
else
282+
c = make<SectionChunk>(this, sec);
246283
if (def)
247284
c->checksum = def->CheckSum;
248285

@@ -260,6 +297,8 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
260297
guardEHContChunks.push_back(c);
261298
else if (name == ".sxdata")
262299
sxDataChunks.push_back(c);
300+
else if (isArm64EC(getMachineType()) && name == ".hybmp$x")
301+
hybmpChunks.push_back(c);
263302
else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 &&
264303
name == ".rdata" && leaderName.starts_with("??_C@"))
265304
// COFF sections that look like string literal sections (i.e. no

lld/COFF/InputFiles.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ class ObjFile : public InputFile {
227227
void initializeSymbols();
228228
void initializeFlags();
229229
void initializeDependencies();
230+
void initializeECThunks();
230231

231232
SectionChunk *
232233
readSection(uint32_t sectionNumber,
@@ -292,6 +293,8 @@ class ObjFile : public InputFile {
292293
std::vector<SectionChunk *> guardLJmpChunks;
293294
std::vector<SectionChunk *> guardEHContChunks;
294295

296+
std::vector<SectionChunk *> hybmpChunks;
297+
295298
// This vector contains a list of all symbols defined or referenced by this
296299
// file. They are indexed such that you can get a Symbol by symbol
297300
// index. Nonexistent indices (which are occupied by auxiliary

lld/COFF/MarkLive.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ void markLive(COFFLinkerContext &ctx) {
6868
// Mark associative sections if any.
6969
for (SectionChunk &c : sc->children())
7070
enqueue(&c);
71+
72+
// Mark EC entry thunks.
73+
if (Defined *entryThunk = sc->getEntryThunk())
74+
addSym(entryThunk);
7175
}
7276
}
7377
}

lld/COFF/SymbolTable.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,25 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
561561
return result;
562562
}
563563

564+
void SymbolTable::addEntryThunk(Symbol *from, Symbol *to) {
565+
entryThunks.push_back({from, to});
566+
}
567+
568+
void SymbolTable::initializeEntryThunks() {
569+
for (auto it : entryThunks) {
570+
auto *to = dyn_cast<Defined>(it.second);
571+
if (!to)
572+
continue;
573+
auto *from = dyn_cast<DefinedRegular>(it.first);
574+
if (!from || !from->getChunk()->isCOMDAT() ||
575+
cast<DefinedRegular>(from)->getValue()) {
576+
error("non COMDAT symbol '" + from->getName() + "' in hybrid map");
577+
continue;
578+
}
579+
from->getChunk()->setEntryThunk(to);
580+
}
581+
}
582+
564583
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
565584
bool isWeakAlias) {
566585
auto [s, wasInserted] = insert(name, f);

lld/COFF/SymbolTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ class SymbolTable {
106106
Symbol *addImportThunk(StringRef name, DefinedImportData *s,
107107
uint16_t machine);
108108
void addLibcall(StringRef name);
109+
void addEntryThunk(Symbol *from, Symbol *to);
110+
void initializeEntryThunks();
109111

110112
void reportDuplicate(Symbol *existing, InputFile *newFile,
111113
SectionChunk *newSc = nullptr,
@@ -134,6 +136,7 @@ class SymbolTable {
134136
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
135137
std::unique_ptr<BitcodeCompiler> lto;
136138
bool ltoCompilationDone = false;
139+
std::vector<std::pair<Symbol *, Symbol *>> entryThunks;
137140

138141
COFFLinkerContext &ctx;
139142
};

lld/COFF/Writer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,10 @@ void Writer::assignAddresses() {
15321532
}
15331533
if (padding && c->isHotPatchable())
15341534
virtualSize += padding;
1535+
// If chunk has EC entry thunk, reserve a space for an offset to the
1536+
// thunk.
1537+
if (c->getEntryThunk())
1538+
virtualSize += sizeof(uint32_t);
15351539
virtualSize = alignTo(virtualSize, c->getAlignment());
15361540
c->setRVA(rva + virtualSize);
15371541
virtualSize += c->getSize();

0 commit comments

Comments
 (0)