xref: /freebsd/contrib/llvm-project/lld/COFF/DLL.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
1 //===- DLL.cpp ------------------------------------------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines various types of chunks for the DLL import or export
10 // descriptor tables. They are inherently Windows-specific.
11 // You need to read Microsoft PE/COFF spec to understand details
12 // about the data structures.
13 //
14 // If you are not particularly interested in linking against Windows
15 // DLL, you can skip this file, and you should still be able to
16 // understand the rest of the linker.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "DLL.h"
21 #include "COFFLinkerContext.h"
22 #include "Chunks.h"
23 #include "SymbolTable.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/Object/COFF.h"
26 #include "llvm/Support/Endian.h"
27 #include "llvm/Support/Path.h"
28 
29 using namespace llvm;
30 using namespace llvm::object;
31 using namespace llvm::support::endian;
32 using namespace llvm::COFF;
33 
34 namespace lld::coff {
35 namespace {
36 
37 // Import table
38 
39 // A chunk for the import descriptor table.
40 class HintNameChunk : public NonSectionChunk {
41 public:
HintNameChunk(StringRef n,uint16_t h)42   HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {}
43 
getSize() const44   size_t getSize() const override {
45     // Starts with 2 byte Hint field, followed by a null-terminated string,
46     // ends with 0 or 1 byte padding.
47     return alignTo(name.size() + 3, 2);
48   }
49 
writeTo(uint8_t * buf) const50   void writeTo(uint8_t *buf) const override {
51     memset(buf, 0, getSize());
52     write16le(buf, hint);
53     memcpy(buf + 2, name.data(), name.size());
54   }
55 
56 private:
57   StringRef name;
58   uint16_t hint;
59 };
60 
61 // A chunk for the import descriptor table.
62 class LookupChunk : public NonSectionChunk {
63 public:
LookupChunk(COFFLinkerContext & ctx,Chunk * c)64   explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c)
65       : hintName(c), ctx(ctx) {
66     setAlignment(ctx.config.wordsize);
67   }
getSize() const68   size_t getSize() const override { return ctx.config.wordsize; }
69 
writeTo(uint8_t * buf) const70   void writeTo(uint8_t *buf) const override {
71     if (ctx.config.is64())
72       write64le(buf, hintName->getRVA());
73     else
74       write32le(buf, hintName->getRVA());
75   }
76 
77   Chunk *hintName;
78 
79 private:
80   COFFLinkerContext &ctx;
81 };
82 
83 // A chunk for the import descriptor table.
84 // This chunk represent import-by-ordinal symbols.
85 // See Microsoft PE/COFF spec 7.1. Import Header for details.
86 class OrdinalOnlyChunk : public NonSectionChunk {
87 public:
OrdinalOnlyChunk(COFFLinkerContext & c,uint16_t v)88   explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v)
89       : ordinal(v), ctx(c) {
90     setAlignment(ctx.config.wordsize);
91   }
getSize() const92   size_t getSize() const override { return ctx.config.wordsize; }
93 
writeTo(uint8_t * buf) const94   void writeTo(uint8_t *buf) const override {
95     // An import-by-ordinal slot has MSB 1 to indicate that
96     // this is import-by-ordinal (and not import-by-name).
97     if (ctx.config.is64()) {
98       write64le(buf, (1ULL << 63) | ordinal);
99     } else {
100       write32le(buf, (1ULL << 31) | ordinal);
101     }
102   }
103 
104   uint16_t ordinal;
105 
106 private:
107   COFFLinkerContext &ctx;
108 };
109 
110 // A chunk for the import descriptor table.
111 class ImportDirectoryChunk : public NonSectionChunk {
112 public:
ImportDirectoryChunk(Chunk * n)113   explicit ImportDirectoryChunk(Chunk *n) : dllName(n) { setAlignment(4); }
getSize() const114   size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
115 
writeTo(uint8_t * buf) const116   void writeTo(uint8_t *buf) const override {
117     memset(buf, 0, getSize());
118 
119     auto *e = (coff_import_directory_table_entry *)(buf);
120     e->ImportLookupTableRVA = lookupTab->getRVA();
121     e->NameRVA = dllName->getRVA();
122     e->ImportAddressTableRVA = addressTab->getRVA();
123   }
124 
125   Chunk *dllName;
126   Chunk *lookupTab;
127   Chunk *addressTab;
128 };
129 
130 // A chunk representing null terminator in the import table.
131 // Contents of this chunk is always null bytes.
132 class NullChunk : public NonSectionChunk {
133 public:
NullChunk(size_t n,uint32_t align)134   explicit NullChunk(size_t n, uint32_t align) : size(n) {
135     setAlignment(align);
136   }
NullChunk(COFFLinkerContext & ctx)137   explicit NullChunk(COFFLinkerContext &ctx)
138       : NullChunk(ctx.config.wordsize, ctx.config.wordsize) {}
NullChunk(COFFLinkerContext & ctx,size_t n)139   explicit NullChunk(COFFLinkerContext &ctx, size_t n)
140       : NullChunk(n, ctx.config.wordsize) {}
getSize() const141   size_t getSize() const override { return size; }
142 
writeTo(uint8_t * buf) const143   void writeTo(uint8_t *buf) const override {
144     memset(buf, 0, size);
145   }
146 
147 private:
148   size_t size;
149 };
150 
151 // A chunk for ARM64EC auxiliary IAT.
152 class AuxImportChunk : public NonSectionChunk {
153 public:
AuxImportChunk(ImportFile * file)154   explicit AuxImportChunk(ImportFile *file) : file(file) {
155     setAlignment(sizeof(uint64_t));
156   }
getSize() const157   size_t getSize() const override { return sizeof(uint64_t); }
158 
writeTo(uint8_t * buf) const159   void writeTo(uint8_t *buf) const override {
160     uint64_t impchkVA = 0;
161     if (file->impchkThunk)
162       impchkVA =
163           file->impchkThunk->getRVA() + file->symtab.ctx.config.imageBase;
164     write64le(buf, impchkVA);
165   }
166 
getBaserels(std::vector<Baserel> * res)167   void getBaserels(std::vector<Baserel> *res) override {
168     if (file->impchkThunk)
169       res->emplace_back(rva, file->symtab.machine);
170   }
171 
172 private:
173   ImportFile *file;
174 };
175 
176 static std::vector<std::vector<DefinedImportData *>>
binImports(COFFLinkerContext & ctx,const std::vector<DefinedImportData * > & imports)177 binImports(COFFLinkerContext &ctx,
178            const std::vector<DefinedImportData *> &imports) {
179   // Group DLL-imported symbols by DLL name because that's how
180   // symbols are laid out in the import descriptor table.
181   auto less = [&ctx](const std::string &a, const std::string &b) {
182     return ctx.config.dllOrder[a] < ctx.config.dllOrder[b];
183   };
184   std::map<std::string, std::vector<DefinedImportData *>, decltype(less)> m(
185       less);
186   for (DefinedImportData *sym : imports)
187     m[sym->getDLLName().lower()].push_back(sym);
188 
189   std::vector<std::vector<DefinedImportData *>> v;
190   for (auto &kv : m) {
191     // Sort symbols by name for each group.
192     std::vector<DefinedImportData *> &syms = kv.second;
193     llvm::sort(syms, [](DefinedImportData *a, DefinedImportData *b) {
194       auto getBaseName = [](DefinedImportData *sym) {
195         StringRef name = sym->getName();
196         name.consume_front("__imp_");
197         // Skip aux_ part of ARM64EC function symbol name.
198         if (sym->file->impchkThunk)
199           name.consume_front("aux_");
200         return name;
201       };
202       return getBaseName(a) < getBaseName(b);
203     });
204     v.push_back(std::move(syms));
205   }
206   return v;
207 }
208 
209 // See Microsoft PE/COFF spec 4.3 for details.
210 
211 // A chunk for the delay import descriptor table etnry.
212 class DelayDirectoryChunk : public NonSectionChunk {
213 public:
DelayDirectoryChunk(Chunk * n)214   explicit DelayDirectoryChunk(Chunk *n) : dllName(n) { setAlignment(4); }
215 
getSize() const216   size_t getSize() const override {
217     return sizeof(delay_import_directory_table_entry);
218   }
219 
writeTo(uint8_t * buf) const220   void writeTo(uint8_t *buf) const override {
221     memset(buf, 0, getSize());
222 
223     auto *e = (delay_import_directory_table_entry *)(buf);
224     e->Attributes = 1;
225     e->Name = dllName->getRVA();
226     e->ModuleHandle = moduleHandle->getRVA();
227     e->DelayImportAddressTable = addressTab->getRVA();
228     e->DelayImportNameTable = nameTab->getRVA();
229   }
230 
231   Chunk *dllName;
232   Chunk *moduleHandle;
233   Chunk *addressTab;
234   Chunk *nameTab;
235 };
236 
237 // Initial contents for delay-loaded functions.
238 // This code calls __delayLoadHelper2 function to resolve a symbol
239 // which then overwrites its jump table slot with the result
240 // for subsequent function calls.
241 static const uint8_t thunkX64[] = {
242     0x48, 0x8D, 0x05, 0, 0, 0, 0,       // lea     rax, [__imp_<FUNCNAME>]
243     0xE9, 0, 0, 0, 0,                   // jmp     __tailMerge_<lib>
244 };
245 
246 static const uint8_t tailMergeX64[] = {
247     0x48, 0x89, 0x4C, 0x24, 0x08,          // mov    qword ptr [rsp+8], rcx
248     0x48, 0x89, 0x54, 0x24, 0x10,          // mov    qword ptr [rsp+10h], rdx
249     0x4C, 0x89, 0x44, 0x24, 0x18,          // mov    qword ptr [rsp+18h], r8
250     0x4C, 0x89, 0x4C, 0x24, 0x20,          // mov    qword ptr [rsp+20h], r9
251     0x48, 0x83, 0xEC, 0x68,                // sub    rsp, 68h
252     0x66, 0x0F, 0x7F, 0x44, 0x24, 0x20,    // movdqa xmmword ptr [rsp+20h], xmm0
253     0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x30,    // movdqa xmmword ptr [rsp+30h], xmm1
254     0x66, 0x0F, 0x7F, 0x54, 0x24, 0x40,    // movdqa xmmword ptr [rsp+40h], xmm2
255     0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x50,    // movdqa xmmword ptr [rsp+50h], xmm3
256     0x48, 0x8B, 0xD0,                      // mov    rdx, rax
257     0x48, 0x8D, 0x0D, 0, 0, 0, 0,          // lea    rcx, [___DELAY_IMPORT_...]
258     0xE8, 0, 0, 0, 0,                      // call   __delayLoadHelper2
259     0x66, 0x0F, 0x6F, 0x44, 0x24, 0x20,    // movdqa xmm0, xmmword ptr [rsp+20h]
260     0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x30,    // movdqa xmm1, xmmword ptr [rsp+30h]
261     0x66, 0x0F, 0x6F, 0x54, 0x24, 0x40,    // movdqa xmm2, xmmword ptr [rsp+40h]
262     0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x50,    // movdqa xmm3, xmmword ptr [rsp+50h]
263     0x48, 0x8B, 0x4C, 0x24, 0x70,          // mov    rcx, qword ptr [rsp+70h]
264     0x48, 0x8B, 0x54, 0x24, 0x78,          // mov    rdx, qword ptr [rsp+78h]
265     0x4C, 0x8B, 0x84, 0x24, 0x80, 0, 0, 0, // mov    r8, qword ptr [rsp+80h]
266     0x4C, 0x8B, 0x8C, 0x24, 0x88, 0, 0, 0, // mov    r9, qword ptr [rsp+88h]
267     0x48, 0x83, 0xC4, 0x68,                // add    rsp, 68h
268     0xFF, 0xE0,                            // jmp    rax
269 };
270 
271 static const uint8_t tailMergeUnwindInfoX64[] = {
272     0x01,       // Version=1, Flags=UNW_FLAG_NHANDLER
273     0x18,       // Size of prolog
274     0x01,       // Count of unwind codes
275     0x00,       // No frame register
276     0x18, 0xC2, // Offset 0x18: UWOP_ALLOC_SMALL(0x68)
277     0x00, 0x00  // Padding to align on 32-bits
278 };
279 
280 static const uint8_t thunkX86[] = {
281     0xB8, 0, 0, 0, 0,  // mov   eax, offset ___imp__<FUNCNAME>
282     0xE9, 0, 0, 0, 0,  // jmp   __tailMerge_<lib>
283 };
284 
285 static const uint8_t tailMergeX86[] = {
286     0x51,              // push  ecx
287     0x52,              // push  edx
288     0x50,              // push  eax
289     0x68, 0, 0, 0, 0,  // push  offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll
290     0xE8, 0, 0, 0, 0,  // call  ___delayLoadHelper2@8
291     0x5A,              // pop   edx
292     0x59,              // pop   ecx
293     0xFF, 0xE0,        // jmp   eax
294 };
295 
296 static const uint8_t thunkARM[] = {
297     0x40, 0xf2, 0x00, 0x0c, // mov.w   ip, #0 __imp_<FUNCNAME>
298     0xc0, 0xf2, 0x00, 0x0c, // mov.t   ip, #0 __imp_<FUNCNAME>
299     0x00, 0xf0, 0x00, 0xb8, // b.w     __tailMerge_<lib>
300 };
301 
302 static const uint8_t tailMergeARM[] = {
303     0x2d, 0xe9, 0x0f, 0x48, // push.w  {r0, r1, r2, r3, r11, lr}
304     0x0d, 0xf2, 0x10, 0x0b, // addw    r11, sp, #16
305     0x2d, 0xed, 0x10, 0x0b, // vpush   {d0, d1, d2, d3, d4, d5, d6, d7}
306     0x61, 0x46,             // mov     r1, ip
307     0x40, 0xf2, 0x00, 0x00, // mov.w   r0, #0 DELAY_IMPORT_DESCRIPTOR
308     0xc0, 0xf2, 0x00, 0x00, // mov.t   r0, #0 DELAY_IMPORT_DESCRIPTOR
309     0x00, 0xf0, 0x00, 0xd0, // bl      #0 __delayLoadHelper2
310     0x84, 0x46,             // mov     ip, r0
311     0xbd, 0xec, 0x10, 0x0b, // vpop    {d0, d1, d2, d3, d4, d5, d6, d7}
312     0xbd, 0xe8, 0x0f, 0x48, // pop.w   {r0, r1, r2, r3, r11, lr}
313     0x60, 0x47,             // bx      ip
314 };
315 
316 static const uint8_t thunkARM64[] = {
317     0x11, 0x00, 0x00, 0x90, // adrp    x17, #0      __imp_<FUNCNAME>
318     0x31, 0x02, 0x00, 0x91, // add     x17, x17, #0 :lo12:__imp_<FUNCNAME>
319     0x00, 0x00, 0x00, 0x14, // b       __tailMerge_<lib>
320 };
321 
322 static const uint8_t tailMergeARM64[] = {
323     0xfd, 0x7b, 0xb2, 0xa9, // stp     x29, x30, [sp, #-224]!
324     0xfd, 0x03, 0x00, 0x91, // mov     x29, sp
325     0xe0, 0x07, 0x01, 0xa9, // stp     x0, x1, [sp, #16]
326     0xe2, 0x0f, 0x02, 0xa9, // stp     x2, x3, [sp, #32]
327     0xe4, 0x17, 0x03, 0xa9, // stp     x4, x5, [sp, #48]
328     0xe6, 0x1f, 0x04, 0xa9, // stp     x6, x7, [sp, #64]
329     0xe8, 0x2b, 0x00, 0xf9, // str     x8,     [sp, #80]
330     0xe0, 0x07, 0x03, 0xad, // stp     q0, q1, [sp, #96]
331     0xe2, 0x0f, 0x04, 0xad, // stp     q2, q3, [sp, #128]
332     0xe4, 0x17, 0x05, 0xad, // stp     q4, q5, [sp, #160]
333     0xe6, 0x1f, 0x06, 0xad, // stp     q6, q7, [sp, #192]
334     0xe1, 0x03, 0x11, 0xaa, // mov     x1, x17
335     0x00, 0x00, 0x00, 0x90, // adrp    x0, #0     DELAY_IMPORT_DESCRIPTOR
336     0x00, 0x00, 0x00, 0x91, // add     x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR
337     0x02, 0x00, 0x00, 0x90, // adrp    x2, #0     __delayLoadHelper2
338     0x42, 0x00, 0x00, 0x91, // add     x2, x2, #0 :lo12:__delayLoadHelper2
339     0x40, 0x00, 0x3f, 0xd6, // blr     x2
340     0xf0, 0x03, 0x00, 0xaa, // mov     x16, x0
341     0xe6, 0x1f, 0x46, 0xad, // ldp     q6, q7, [sp, #192]
342     0xe4, 0x17, 0x45, 0xad, // ldp     q4, q5, [sp, #160]
343     0xe2, 0x0f, 0x44, 0xad, // ldp     q2, q3, [sp, #128]
344     0xe0, 0x07, 0x43, 0xad, // ldp     q0, q1, [sp, #96]
345     0xe8, 0x2b, 0x40, 0xf9, // ldr     x8,     [sp, #80]
346     0xe6, 0x1f, 0x44, 0xa9, // ldp     x6, x7, [sp, #64]
347     0xe4, 0x17, 0x43, 0xa9, // ldp     x4, x5, [sp, #48]
348     0xe2, 0x0f, 0x42, 0xa9, // ldp     x2, x3, [sp, #32]
349     0xe0, 0x07, 0x41, 0xa9, // ldp     x0, x1, [sp, #16]
350     0xfd, 0x7b, 0xce, 0xa8, // ldp     x29, x30, [sp], #224
351     0x00, 0x02, 0x1f, 0xd6, // br      x16
352 };
353 
354 // A chunk for the delay import thunk.
355 class ThunkChunkX64 : public NonSectionCodeChunk {
356 public:
ThunkChunkX64(Defined * i,Chunk * tm)357   ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
358 
getSize() const359   size_t getSize() const override { return sizeof(thunkX64); }
getMachine() const360   MachineTypes getMachine() const override { return AMD64; }
361 
writeTo(uint8_t * buf) const362   void writeTo(uint8_t *buf) const override {
363     memcpy(buf, thunkX64, sizeof(thunkX64));
364     write32le(buf + 3, imp->getRVA() - rva - 7);
365     write32le(buf + 8, tailMerge->getRVA() - rva - 12);
366   }
367 
368   Defined *imp = nullptr;
369   Chunk *tailMerge = nullptr;
370 };
371 
372 class TailMergeChunkX64 : public NonSectionCodeChunk {
373 public:
TailMergeChunkX64(Chunk * d,Defined * h)374   TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {}
375 
getSize() const376   size_t getSize() const override { return sizeof(tailMergeX64); }
getMachine() const377   MachineTypes getMachine() const override { return AMD64; }
378 
writeTo(uint8_t * buf) const379   void writeTo(uint8_t *buf) const override {
380     memcpy(buf, tailMergeX64, sizeof(tailMergeX64));
381     write32le(buf + 54, desc->getRVA() - rva - 58);
382     write32le(buf + 59, helper->getRVA() - rva - 63);
383   }
384 
385   Chunk *desc = nullptr;
386   Defined *helper = nullptr;
387 };
388 
389 class TailMergePDataChunkX64 : public NonSectionChunk {
390 public:
TailMergePDataChunkX64(Chunk * tm,Chunk * unwind)391   TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) {
392     // See
393     // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
394     setAlignment(4);
395   }
396 
getSize() const397   size_t getSize() const override { return 3 * sizeof(uint32_t); }
getMachine() const398   MachineTypes getMachine() const override { return AMD64; }
399 
writeTo(uint8_t * buf) const400   void writeTo(uint8_t *buf) const override {
401     write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA
402     write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA
403     write32le(buf + 8, unwind->getRVA());             // UnwindInfo RVA
404   }
405 
406   Chunk *tm = nullptr;
407   Chunk *unwind = nullptr;
408 };
409 
410 class TailMergeUnwindInfoX64 : public NonSectionChunk {
411 public:
TailMergeUnwindInfoX64()412   TailMergeUnwindInfoX64() {
413     // See
414     // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
415     setAlignment(4);
416   }
417 
getSize() const418   size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); }
getMachine() const419   MachineTypes getMachine() const override { return AMD64; }
420 
writeTo(uint8_t * buf) const421   void writeTo(uint8_t *buf) const override {
422     memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64));
423   }
424 };
425 
426 class ThunkChunkX86 : public NonSectionCodeChunk {
427 public:
ThunkChunkX86(COFFLinkerContext & ctx,Defined * i,Chunk * tm)428   ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
429       : imp(i), tailMerge(tm), ctx(ctx) {}
430 
getSize() const431   size_t getSize() const override { return sizeof(thunkX86); }
getMachine() const432   MachineTypes getMachine() const override { return I386; }
433 
writeTo(uint8_t * buf) const434   void writeTo(uint8_t *buf) const override {
435     memcpy(buf, thunkX86, sizeof(thunkX86));
436     write32le(buf + 1, imp->getRVA() + ctx.config.imageBase);
437     write32le(buf + 6, tailMerge->getRVA() - rva - 10);
438   }
439 
getBaserels(std::vector<Baserel> * res)440   void getBaserels(std::vector<Baserel> *res) override {
441     res->emplace_back(rva + 1, ctx.config.machine);
442   }
443 
444   Defined *imp = nullptr;
445   Chunk *tailMerge = nullptr;
446 
447 private:
448   const COFFLinkerContext &ctx;
449 };
450 
451 class TailMergeChunkX86 : public NonSectionCodeChunk {
452 public:
TailMergeChunkX86(COFFLinkerContext & ctx,Chunk * d,Defined * h)453   TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h)
454       : desc(d), helper(h), ctx(ctx) {}
455 
getSize() const456   size_t getSize() const override { return sizeof(tailMergeX86); }
getMachine() const457   MachineTypes getMachine() const override { return I386; }
458 
writeTo(uint8_t * buf) const459   void writeTo(uint8_t *buf) const override {
460     memcpy(buf, tailMergeX86, sizeof(tailMergeX86));
461     write32le(buf + 4, desc->getRVA() + ctx.config.imageBase);
462     write32le(buf + 9, helper->getRVA() - rva - 13);
463   }
464 
getBaserels(std::vector<Baserel> * res)465   void getBaserels(std::vector<Baserel> *res) override {
466     res->emplace_back(rva + 4, ctx.config.machine);
467   }
468 
469   Chunk *desc = nullptr;
470   Defined *helper = nullptr;
471 
472 private:
473   const COFFLinkerContext &ctx;
474 };
475 
476 class ThunkChunkARM : public NonSectionCodeChunk {
477 public:
ThunkChunkARM(COFFLinkerContext & ctx,Defined * i,Chunk * tm)478   ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
479       : imp(i), tailMerge(tm), ctx(ctx) {
480     setAlignment(2);
481   }
482 
getSize() const483   size_t getSize() const override { return sizeof(thunkARM); }
getMachine() const484   MachineTypes getMachine() const override { return ARMNT; }
485 
writeTo(uint8_t * buf) const486   void writeTo(uint8_t *buf) const override {
487     memcpy(buf, thunkARM, sizeof(thunkARM));
488     applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase);
489     applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
490   }
491 
getBaserels(std::vector<Baserel> * res)492   void getBaserels(std::vector<Baserel> *res) override {
493     res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T);
494   }
495 
496   Defined *imp = nullptr;
497   Chunk *tailMerge = nullptr;
498 
499 private:
500   const COFFLinkerContext &ctx;
501 };
502 
503 class TailMergeChunkARM : public NonSectionCodeChunk {
504 public:
TailMergeChunkARM(COFFLinkerContext & ctx,Chunk * d,Defined * h)505   TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h)
506       : desc(d), helper(h), ctx(ctx) {
507     setAlignment(2);
508   }
509 
getSize() const510   size_t getSize() const override { return sizeof(tailMergeARM); }
getMachine() const511   MachineTypes getMachine() const override { return ARMNT; }
512 
writeTo(uint8_t * buf) const513   void writeTo(uint8_t *buf) const override {
514     memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
515     applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase);
516     applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
517   }
518 
getBaserels(std::vector<Baserel> * res)519   void getBaserels(std::vector<Baserel> *res) override {
520     res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T);
521   }
522 
523   Chunk *desc = nullptr;
524   Defined *helper = nullptr;
525 
526 private:
527   const COFFLinkerContext &ctx;
528 };
529 
530 class ThunkChunkARM64 : public NonSectionCodeChunk {
531 public:
ThunkChunkARM64(Defined * i,Chunk * tm)532   ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
533     setAlignment(4);
534   }
535 
getSize() const536   size_t getSize() const override { return sizeof(thunkARM64); }
getMachine() const537   MachineTypes getMachine() const override { return ARM64; }
538 
writeTo(uint8_t * buf) const539   void writeTo(uint8_t *buf) const override {
540     memcpy(buf, thunkARM64, sizeof(thunkARM64));
541     applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12);
542     applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0);
543     applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8);
544   }
545 
546   Defined *imp = nullptr;
547   Chunk *tailMerge = nullptr;
548 };
549 
550 class TailMergeChunkARM64 : public NonSectionCodeChunk {
551 public:
TailMergeChunkARM64(Chunk * d,Defined * h)552   TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {
553     setAlignment(4);
554   }
555 
getSize() const556   size_t getSize() const override { return sizeof(tailMergeARM64); }
getMachine() const557   MachineTypes getMachine() const override { return ARM64; }
558 
writeTo(uint8_t * buf) const559   void writeTo(uint8_t *buf) const override {
560     memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64));
561     applyArm64Addr(buf + 48, desc->getRVA(), rva + 48, 12);
562     applyArm64Imm(buf + 52, desc->getRVA() & 0xfff, 0);
563     if (helper) {
564       applyArm64Addr(buf + 56, helper->getRVA(), rva + 56, 12);
565       applyArm64Imm(buf + 60, helper->getRVA() & 0xfff, 0);
566     }
567   }
568 
569   Chunk *desc = nullptr;
570   Defined *helper = nullptr;
571 };
572 
573 // A chunk for the import descriptor table.
574 class DelayAddressChunk : public NonSectionChunk {
575 public:
DelayAddressChunk(COFFLinkerContext & ctx,Chunk * c)576   explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c)
577       : thunk(c), ctx(ctx) {
578     setAlignment(ctx.config.wordsize);
579   }
getSize() const580   size_t getSize() const override { return ctx.config.wordsize; }
581 
writeTo(uint8_t * buf) const582   void writeTo(uint8_t *buf) const override {
583     if (ctx.config.is64()) {
584       write64le(buf, thunk->getRVA() + ctx.config.imageBase);
585     } else {
586       uint32_t bit = 0;
587       // Pointer to thumb code must have the LSB set, so adjust it.
588       if (ctx.config.machine == ARMNT)
589         bit = 1;
590       write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit);
591     }
592   }
593 
getBaserels(std::vector<Baserel> * res)594   void getBaserels(std::vector<Baserel> *res) override {
595     res->emplace_back(rva, ctx.config.machine);
596   }
597 
598   Chunk *thunk;
599 
600 private:
601   const COFFLinkerContext &ctx;
602 };
603 
604 // Export table
605 // Read Microsoft PE/COFF spec 5.3 for details.
606 
607 // A chunk for the export descriptor table.
608 class ExportDirectoryChunk : public NonSectionChunk {
609 public:
ExportDirectoryChunk(int baseOrdinal,int maxOrdinal,int nameTabSize,Chunk * d,Chunk * a,Chunk * n,Chunk * o)610   ExportDirectoryChunk(int baseOrdinal, int maxOrdinal, int nameTabSize,
611                        Chunk *d, Chunk *a, Chunk *n, Chunk *o)
612       : baseOrdinal(baseOrdinal), maxOrdinal(maxOrdinal),
613         nameTabSize(nameTabSize), dllName(d), addressTab(a), nameTab(n),
614         ordinalTab(o) {}
615 
getSize() const616   size_t getSize() const override {
617     return sizeof(export_directory_table_entry);
618   }
619 
writeTo(uint8_t * buf) const620   void writeTo(uint8_t *buf) const override {
621     memset(buf, 0, getSize());
622 
623     auto *e = (export_directory_table_entry *)(buf);
624     e->NameRVA = dllName->getRVA();
625     e->OrdinalBase = baseOrdinal;
626     e->AddressTableEntries = (maxOrdinal - baseOrdinal) + 1;
627     e->NumberOfNamePointers = nameTabSize;
628     e->ExportAddressTableRVA = addressTab->getRVA();
629     e->NamePointerRVA = nameTab->getRVA();
630     e->OrdinalTableRVA = ordinalTab->getRVA();
631   }
632 
633   uint16_t baseOrdinal;
634   uint16_t maxOrdinal;
635   uint16_t nameTabSize;
636   Chunk *dllName;
637   Chunk *addressTab;
638   Chunk *nameTab;
639   Chunk *ordinalTab;
640 };
641 
642 class AddressTableChunk : public NonSectionChunk {
643 public:
AddressTableChunk(SymbolTable & symtab,size_t baseOrdinal,size_t maxOrdinal)644   explicit AddressTableChunk(SymbolTable &symtab, size_t baseOrdinal,
645                              size_t maxOrdinal)
646       : baseOrdinal(baseOrdinal), size((maxOrdinal - baseOrdinal) + 1),
647         symtab(symtab) {}
getSize() const648   size_t getSize() const override { return size * 4; }
649 
writeTo(uint8_t * buf) const650   void writeTo(uint8_t *buf) const override {
651     memset(buf, 0, getSize());
652 
653     for (const Export &e : symtab.exports) {
654       assert(e.ordinal >= baseOrdinal && "Export symbol has invalid ordinal");
655       // Subtract the OrdinalBase to get the index.
656       uint8_t *p = buf + (e.ordinal - baseOrdinal) * 4;
657       uint32_t bit = 0;
658       // Pointer to thumb code must have the LSB set, so adjust it.
659       if (symtab.machine == ARMNT && !e.data)
660         bit = 1;
661       if (e.forwardChunk) {
662         write32le(p, e.forwardChunk->getRVA() | bit);
663       } else {
664         assert(cast<Defined>(e.sym)->getRVA() != 0 &&
665                "Exported symbol unmapped");
666         write32le(p, cast<Defined>(e.sym)->getRVA() | bit);
667       }
668     }
669   }
670 
671 private:
672   size_t baseOrdinal;
673   size_t size;
674   const SymbolTable &symtab;
675 };
676 
677 class NamePointersChunk : public NonSectionChunk {
678 public:
NamePointersChunk(std::vector<Chunk * > & v)679   explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {}
getSize() const680   size_t getSize() const override { return chunks.size() * 4; }
681 
writeTo(uint8_t * buf) const682   void writeTo(uint8_t *buf) const override {
683     for (Chunk *c : chunks) {
684       write32le(buf, c->getRVA());
685       buf += 4;
686     }
687   }
688 
689 private:
690   std::vector<Chunk *> chunks;
691 };
692 
693 class ExportOrdinalChunk : public NonSectionChunk {
694 public:
ExportOrdinalChunk(const SymbolTable & symtab,size_t baseOrdinal,size_t tableSize)695   explicit ExportOrdinalChunk(const SymbolTable &symtab, size_t baseOrdinal,
696                               size_t tableSize)
697       : baseOrdinal(baseOrdinal), size(tableSize), symtab(symtab) {}
getSize() const698   size_t getSize() const override { return size * 2; }
699 
writeTo(uint8_t * buf) const700   void writeTo(uint8_t *buf) const override {
701     for (const Export &e : symtab.exports) {
702       if (e.noname)
703         continue;
704       assert(e.ordinal >= baseOrdinal && "Export symbol has invalid ordinal");
705       // This table stores unbiased indices, so subtract OrdinalBase.
706       write16le(buf, e.ordinal - baseOrdinal);
707       buf += 2;
708     }
709   }
710 
711 private:
712   size_t baseOrdinal;
713   size_t size;
714   const SymbolTable &symtab;
715 };
716 
717 } // anonymous namespace
718 
create(COFFLinkerContext & ctx)719 void IdataContents::create(COFFLinkerContext &ctx) {
720   std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
721 
722   // In hybrid images, EC and native code are usually very similar,
723   // resulting in a highly similar set of imported symbols. Consequently,
724   // their import tables can be shared, with ARM64X relocations handling any
725   // differences. Identify matching import files used by EC and native code, and
726   // merge them into a single hybrid import entry.
727   if (ctx.hybridSymtab) {
728     for (std::vector<DefinedImportData *> &syms : v) {
729       std::vector<DefinedImportData *> hybridSyms;
730       ImportFile *prev = nullptr;
731       for (DefinedImportData *sym : syms) {
732         ImportFile *file = sym->file;
733         // At this stage, symbols are sorted by base name, ensuring that
734         // compatible import files, if present, are adjacent. Check if the
735         // current symbol's file imports the same symbol as the previously added
736         // one (if any and if it was not already merged). Additionally, verify
737         // that one of them is native while the other is EC. In rare cases,
738         // separate matching import entries may exist within the same namespace,
739         // which cannot be merged.
740         if (!prev || file->isEC() == prev->isEC() ||
741             !file->isSameImport(prev)) {
742           // We can't merge the import file, just add it to hybridSyms
743           // and set prev to its file so that we can try to match the next
744           // symbol.
745           hybridSyms.push_back(sym);
746           prev = file;
747           continue;
748         }
749 
750         // A matching symbol may appear in syms in any order. The native variant
751         // exposes a subset of EC symbols and chunks, so always use the EC
752         // variant as the hybrid import file. If the native file was already
753         // added, replace it with the EC symbol in hybridSyms. Otherwise, the EC
754         // variant is already pushed, so we can simply merge it.
755         if (file->isEC()) {
756           hybridSyms.pop_back();
757           hybridSyms.push_back(sym);
758         }
759 
760         // Merge import files by storing their hybrid form in the corresponding
761         // file class.
762         prev->hybridFile = file;
763         file->hybridFile = prev;
764         prev = nullptr; // A hybrid import file cannot be merged again.
765       }
766 
767       // Sort symbols by type: native-only files first, followed by merged
768       // hybrid files, and then EC-only files.
769       llvm::stable_sort(hybridSyms,
770                         [](DefinedImportData *a, DefinedImportData *b) {
771                           if (a->file->hybridFile)
772                             return !b->file->hybridFile && b->file->isEC();
773                           return !a->file->isEC() && b->file->isEC();
774                         });
775       syms = std::move(hybridSyms);
776     }
777   }
778 
779   // Create .idata contents for each DLL.
780   for (std::vector<DefinedImportData *> &syms : v) {
781     // Create lookup and address tables. If they have external names,
782     // we need to create hintName chunks to store the names.
783     // If they don't (if they are import-by-ordinals), we store only
784     // ordinal values to the table.
785     size_t base = lookups.size();
786     Chunk *lookupsTerminator = nullptr, *addressesTerminator = nullptr;
787     uint32_t nativeOnly = 0;
788     for (DefinedImportData *s : syms) {
789       uint16_t ord = s->getOrdinal();
790       HintNameChunk *hintChunk = nullptr;
791       Chunk *lookupsChunk, *addressesChunk;
792 
793       if (s->getExternalName().empty()) {
794         lookupsChunk = make<OrdinalOnlyChunk>(ctx, ord);
795         addressesChunk = make<OrdinalOnlyChunk>(ctx, ord);
796       } else {
797         hintChunk = make<HintNameChunk>(s->getExternalName(), ord);
798         lookupsChunk = make<LookupChunk>(ctx, hintChunk);
799         addressesChunk = make<LookupChunk>(ctx, hintChunk);
800         hints.push_back(hintChunk);
801       }
802 
803       // Detect the first EC-only import in the hybrid IAT. Emit null chunk
804       // as a terminator for the native view, and add an ARM64X relocation to
805       // replace it with the correct import for the EC view.
806       //
807       // Additionally, for MSVC compatibility, store the lookup and address
808       // chunks and append them at the end of EC-only imports, where a null
809       // terminator chunk would typically be placed. Since they appear after
810       // the native terminator, they will be ignored in the native view.
811       // In the EC view, they should act as terminators, so emit ZEROFILL
812       // relocations overriding them.
813       if (ctx.config.machine == ARM64X && !lookupsTerminator &&
814           s->file->isEC() && !s->file->hybridFile) {
815         lookupsTerminator = lookupsChunk;
816         addressesTerminator = addressesChunk;
817         lookupsChunk = make<NullChunk>(ctx);
818         addressesChunk = make<NullChunk>(ctx);
819 
820         Arm64XRelocVal relocVal = hintChunk;
821         if (!hintChunk)
822           relocVal = (1ULL << 63) | ord;
823         ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
824                                sizeof(uint64_t), lookupsChunk, relocVal);
825         ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
826                                sizeof(uint64_t), addressesChunk, relocVal);
827         ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
828                                sizeof(uint64_t), lookupsTerminator);
829         ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
830                                sizeof(uint64_t), addressesTerminator);
831       }
832 
833       lookups.push_back(lookupsChunk);
834       addresses.push_back(addressesChunk);
835 
836       if (s->file->isEC()) {
837         auto chunk = make<AuxImportChunk>(s->file);
838         auxIat.push_back(chunk);
839         s->file->impECSym->setLocation(chunk);
840 
841         chunk = make<AuxImportChunk>(s->file);
842         auxIatCopy.push_back(chunk);
843         s->file->auxImpCopySym->setLocation(chunk);
844       } else if (ctx.hybridSymtab) {
845         // Fill the auxiliary IAT with null chunks for native-only imports.
846         auxIat.push_back(make<NullChunk>(ctx));
847         auxIatCopy.push_back(make<NullChunk>(ctx));
848         ++nativeOnly;
849       }
850     }
851     // Terminate with null values.
852     lookups.push_back(lookupsTerminator ? lookupsTerminator
853                                         : make<NullChunk>(ctx));
854     addresses.push_back(addressesTerminator ? addressesTerminator
855                                             : make<NullChunk>(ctx));
856     if (ctx.symtab.isEC()) {
857       auxIat.push_back(make<NullChunk>(ctx));
858       auxIatCopy.push_back(make<NullChunk>(ctx));
859     }
860 
861     for (int i = 0, e = syms.size(); i < e; ++i) {
862       syms[i]->setLocation(addresses[base + i]);
863       if (syms[i]->file->hybridFile)
864         syms[i]->file->hybridFile->impSym->setLocation(addresses[base + i]);
865     }
866 
867     // Create the import table header.
868     dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
869     auto *dir = make<ImportDirectoryChunk>(dllNames.back());
870 
871     if (ctx.hybridSymtab && nativeOnly) {
872       if (ctx.config.machine != ARM64X)
873         // On pure ARM64EC targets, skip native-only imports in the import
874         // directory.
875         base += nativeOnly;
876       else if (nativeOnly) {
877         // If native-only imports exist, they will appear as a prefix to all
878         // imports. Emit ARM64X relocations to skip them in the EC view.
879         ctx.dynamicRelocs->add(
880             IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
881             Arm64XRelocVal(
882                 dir, offsetof(ImportDirectoryTableEntry, ImportLookupTableRVA)),
883             nativeOnly * sizeof(uint64_t));
884         ctx.dynamicRelocs->add(
885             IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
886             Arm64XRelocVal(dir, offsetof(ImportDirectoryTableEntry,
887                                          ImportAddressTableRVA)),
888             nativeOnly * sizeof(uint64_t));
889       }
890     }
891 
892     dir->lookupTab = lookups[base];
893     dir->addressTab = addresses[base];
894     dirs.push_back(dir);
895   }
896   // Add null terminator.
897   dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry), 4));
898 }
899 
getChunks()900 std::vector<Chunk *> DelayLoadContents::getChunks() {
901   std::vector<Chunk *> v;
902   v.insert(v.end(), dirs.begin(), dirs.end());
903   v.insert(v.end(), names.begin(), names.end());
904   v.insert(v.end(), hintNames.begin(), hintNames.end());
905   v.insert(v.end(), dllNames.begin(), dllNames.end());
906   return v;
907 }
908 
getDataChunks()909 std::vector<Chunk *> DelayLoadContents::getDataChunks() {
910   std::vector<Chunk *> v;
911   v.insert(v.end(), moduleHandles.begin(), moduleHandles.end());
912   v.insert(v.end(), addresses.begin(), addresses.end());
913   return v;
914 }
915 
getDirSize()916 uint64_t DelayLoadContents::getDirSize() {
917   return dirs.size() * sizeof(delay_import_directory_table_entry);
918 }
919 
create()920 void DelayLoadContents::create() {
921   std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
922 
923   // Create .didat contents for each DLL.
924   for (std::vector<DefinedImportData *> &syms : v) {
925     // Create the delay import table header.
926     dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
927     auto *dir = make<DelayDirectoryChunk>(dllNames.back());
928 
929     size_t base = addresses.size();
930     ctx.forEachSymtab([&](SymbolTable &symtab) {
931       if (symtab.isEC()) {
932         if (ctx.config.machine == ARM64X) {
933           // For hybrid images, emit null-terminated native import entries
934           // followed by null-terminated EC entries. If a view is missing
935           // imports for a given module, only terminators are emitted. Emit
936           // ARM64X relocations to skip native entries in the EC view.
937           ctx.dynamicRelocs->add(
938               IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
939               Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
940                                            DelayImportAddressTable)),
941               (addresses.size() - base) * sizeof(uint64_t));
942           ctx.dynamicRelocs->add(
943               IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
944               Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
945                                            DelayImportNameTable)),
946               (addresses.size() - base) * sizeof(uint64_t));
947         } else {
948           base = addresses.size();
949         }
950       }
951 
952       Chunk *tm = nullptr;
953 
954       for (DefinedImportData *s : syms) {
955         // Process only the symbols belonging to the current symtab.
956         if (symtab.isEC() != s->file->isEC())
957           continue;
958 
959         if (!tm) {
960           tm = newTailMergeChunk(symtab, dir);
961           Chunk *pdataChunk = newTailMergePDataChunk(symtab, tm);
962           if (pdataChunk)
963             pdata.push_back(pdataChunk);
964         }
965 
966         Chunk *t = newThunkChunk(s, tm);
967         auto *a = make<DelayAddressChunk>(ctx, t);
968         addresses.push_back(a);
969         s->setLocation(a);
970         thunks.push_back(t);
971         StringRef extName = s->getExternalName();
972         if (extName.empty()) {
973           names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));
974         } else {
975           auto *c = make<HintNameChunk>(extName, 0);
976           names.push_back(make<LookupChunk>(ctx, c));
977           hintNames.push_back(c);
978           // Add a synthetic symbol for this load thunk, using the
979           // "__imp___load" prefix, in case this thunk needs to be added to the
980           // list of valid call targets for Control Flow Guard.
981           StringRef symName = saver().save("__imp___load_" + extName);
982           s->loadThunkSym =
983               cast<DefinedSynthetic>(symtab.addSynthetic(symName, t));
984         }
985 
986         if (symtab.isEC()) {
987           auto chunk = make<AuxImportChunk>(s->file);
988           auxIat.push_back(chunk);
989           s->file->impECSym->setLocation(chunk);
990 
991           chunk = make<AuxImportChunk>(s->file);
992           auxIatCopy.push_back(chunk);
993           s->file->auxImpCopySym->setLocation(chunk);
994         } else if (ctx.config.machine == ARM64X) {
995           // Fill the auxiliary IAT with null chunks for native imports.
996           auxIat.push_back(make<NullChunk>(ctx));
997           auxIatCopy.push_back(make<NullChunk>(ctx));
998         }
999       }
1000 
1001       if (tm) {
1002         thunks.push_back(tm);
1003         StringRef tmName =
1004             saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
1005         symtab.addSynthetic(tmName, tm);
1006       }
1007 
1008       // Skip terminators on pure ARM64EC target if there are no native imports.
1009       if (!tm && !symtab.isEC() && ctx.config.machine != ARM64X)
1010         return;
1011 
1012       // Terminate with null values.
1013       addresses.push_back(make<NullChunk>(ctx, 8));
1014       names.push_back(make<NullChunk>(ctx, 8));
1015       if (ctx.symtab.isEC()) {
1016         auxIat.push_back(make<NullChunk>(ctx, 8));
1017         auxIatCopy.push_back(make<NullChunk>(ctx, 8));
1018       }
1019     });
1020 
1021     auto *mh = make<NullChunk>(8, 8);
1022     moduleHandles.push_back(mh);
1023 
1024     // Fill the delay import table header fields.
1025     dir->moduleHandle = mh;
1026     dir->addressTab = addresses[base];
1027     dir->nameTab = names[base];
1028     dirs.push_back(dir);
1029   }
1030 
1031   ctx.forEachSymtab([&](SymbolTable &symtab) {
1032     if (symtab.tailMergeUnwindInfoChunk)
1033       unwindinfo.push_back(symtab.tailMergeUnwindInfoChunk);
1034   });
1035   // Add null terminator.
1036   dirs.push_back(
1037       make<NullChunk>(sizeof(delay_import_directory_table_entry), 4));
1038 }
1039 
newTailMergeChunk(SymbolTable & symtab,Chunk * dir)1040 Chunk *DelayLoadContents::newTailMergeChunk(SymbolTable &symtab, Chunk *dir) {
1041   auto helper = cast_or_null<Defined>(symtab.delayLoadHelper);
1042   switch (symtab.machine) {
1043   case AMD64:
1044   case ARM64EC:
1045     return make<TailMergeChunkX64>(dir, helper);
1046   case I386:
1047     return make<TailMergeChunkX86>(ctx, dir, helper);
1048   case ARMNT:
1049     return make<TailMergeChunkARM>(ctx, dir, helper);
1050   case ARM64:
1051     return make<TailMergeChunkARM64>(dir, helper);
1052   default:
1053     llvm_unreachable("unsupported machine type");
1054   }
1055 }
1056 
newTailMergePDataChunk(SymbolTable & symtab,Chunk * tm)1057 Chunk *DelayLoadContents::newTailMergePDataChunk(SymbolTable &symtab,
1058                                                  Chunk *tm) {
1059   switch (symtab.machine) {
1060   case AMD64:
1061   case ARM64EC:
1062     if (!symtab.tailMergeUnwindInfoChunk)
1063       symtab.tailMergeUnwindInfoChunk = make<TailMergeUnwindInfoX64>();
1064     return make<TailMergePDataChunkX64>(tm, symtab.tailMergeUnwindInfoChunk);
1065     // FIXME: Add support for other architectures.
1066   default:
1067     return nullptr; // Just don't generate unwind info.
1068   }
1069 }
1070 
newThunkChunk(DefinedImportData * s,Chunk * tailMerge)1071 Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
1072                                         Chunk *tailMerge) {
1073   switch (s->file->getMachineType()) {
1074   case AMD64:
1075   case ARM64EC:
1076     return make<ThunkChunkX64>(s, tailMerge);
1077   case I386:
1078     return make<ThunkChunkX86>(ctx, s, tailMerge);
1079   case ARMNT:
1080     return make<ThunkChunkARM>(ctx, s, tailMerge);
1081   case ARM64:
1082     return make<ThunkChunkARM64>(s, tailMerge);
1083   default:
1084     llvm_unreachable("unsupported machine type");
1085   }
1086 }
1087 
createEdataChunks(SymbolTable & symtab,std::vector<Chunk * > & chunks)1088 void createEdataChunks(SymbolTable &symtab, std::vector<Chunk *> &chunks) {
1089   unsigned baseOrdinal = 1 << 16, maxOrdinal = 0;
1090   for (Export &e : symtab.exports) {
1091     baseOrdinal = std::min(baseOrdinal, (unsigned)e.ordinal);
1092     maxOrdinal = std::max(maxOrdinal, (unsigned)e.ordinal);
1093   }
1094   // Ordinals must start at 1 as suggested in:
1095   // https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function?view=msvc-170
1096   assert(baseOrdinal >= 1);
1097 
1098   auto *dllName =
1099       make<StringChunk>(sys::path::filename(symtab.ctx.config.outputFile));
1100   auto *addressTab = make<AddressTableChunk>(symtab, baseOrdinal, maxOrdinal);
1101   std::vector<Chunk *> names;
1102   for (Export &e : symtab.exports)
1103     if (!e.noname)
1104       names.push_back(make<StringChunk>(e.exportName));
1105 
1106   std::vector<Chunk *> forwards;
1107   for (Export &e : symtab.exports) {
1108     if (e.forwardTo.empty())
1109       continue;
1110     e.forwardChunk = make<StringChunk>(e.forwardTo);
1111     forwards.push_back(e.forwardChunk);
1112   }
1113 
1114   auto *nameTab = make<NamePointersChunk>(names);
1115   auto *ordinalTab =
1116       make<ExportOrdinalChunk>(symtab, baseOrdinal, names.size());
1117   auto *dir =
1118       make<ExportDirectoryChunk>(baseOrdinal, maxOrdinal, names.size(), dllName,
1119                                  addressTab, nameTab, ordinalTab);
1120   chunks.push_back(dir);
1121   chunks.push_back(dllName);
1122   chunks.push_back(addressTab);
1123   chunks.push_back(nameTab);
1124   chunks.push_back(ordinalTab);
1125   chunks.insert(chunks.end(), names.begin(), names.end());
1126   chunks.insert(chunks.end(), forwards.begin(), forwards.end());
1127 }
1128 
1129 } // namespace lld::coff
1130