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: 42 HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {} 43 44 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 50 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: 64 explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c) 65 : hintName(c), ctx(ctx) { 66 setAlignment(ctx.config.wordsize); 67 } 68 size_t getSize() const override { return ctx.config.wordsize; } 69 70 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: 88 explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v) 89 : ordinal(v), ctx(c) { 90 setAlignment(ctx.config.wordsize); 91 } 92 size_t getSize() const override { return ctx.config.wordsize; } 93 94 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: 113 explicit ImportDirectoryChunk(Chunk *n) : dllName(n) { setAlignment(4); } 114 size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); } 115 116 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: 134 explicit NullChunk(size_t n, uint32_t align) : size(n) { 135 setAlignment(align); 136 } 137 explicit NullChunk(COFFLinkerContext &ctx) 138 : NullChunk(ctx.config.wordsize, ctx.config.wordsize) {} 139 explicit NullChunk(COFFLinkerContext &ctx, size_t n) 140 : NullChunk(n, ctx.config.wordsize) {} 141 size_t getSize() const override { return size; } 142 143 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: 154 explicit AuxImportChunk(ImportFile *file) : file(file) { 155 setAlignment(sizeof(uint64_t)); 156 } 157 size_t getSize() const override { return sizeof(uint64_t); } 158 159 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 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 *>> 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: 214 explicit DelayDirectoryChunk(Chunk *n) : dllName(n) { setAlignment(4); } 215 216 size_t getSize() const override { 217 return sizeof(delay_import_directory_table_entry); 218 } 219 220 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: 357 ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} 358 359 size_t getSize() const override { return sizeof(thunkX64); } 360 MachineTypes getMachine() const override { return AMD64; } 361 362 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: 374 TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {} 375 376 size_t getSize() const override { return sizeof(tailMergeX64); } 377 MachineTypes getMachine() const override { return AMD64; } 378 379 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: 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 397 size_t getSize() const override { return 3 * sizeof(uint32_t); } 398 MachineTypes getMachine() const override { return AMD64; } 399 400 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: 412 TailMergeUnwindInfoX64() { 413 // See 414 // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info 415 setAlignment(4); 416 } 417 418 size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); } 419 MachineTypes getMachine() const override { return AMD64; } 420 421 void writeTo(uint8_t *buf) const override { 422 memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64)); 423 } 424 }; 425 426 class ThunkChunkX86 : public NonSectionCodeChunk { 427 public: 428 ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm) 429 : imp(i), tailMerge(tm), ctx(ctx) {} 430 431 size_t getSize() const override { return sizeof(thunkX86); } 432 MachineTypes getMachine() const override { return I386; } 433 434 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 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: 453 TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h) 454 : desc(d), helper(h), ctx(ctx) {} 455 456 size_t getSize() const override { return sizeof(tailMergeX86); } 457 MachineTypes getMachine() const override { return I386; } 458 459 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 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: 478 ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm) 479 : imp(i), tailMerge(tm), ctx(ctx) { 480 setAlignment(2); 481 } 482 483 size_t getSize() const override { return sizeof(thunkARM); } 484 MachineTypes getMachine() const override { return ARMNT; } 485 486 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 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: 505 TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h) 506 : desc(d), helper(h), ctx(ctx) { 507 setAlignment(2); 508 } 509 510 size_t getSize() const override { return sizeof(tailMergeARM); } 511 MachineTypes getMachine() const override { return ARMNT; } 512 513 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 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: 532 ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) { 533 setAlignment(4); 534 } 535 536 size_t getSize() const override { return sizeof(thunkARM64); } 537 MachineTypes getMachine() const override { return ARM64; } 538 539 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: 552 TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) { 553 setAlignment(4); 554 } 555 556 size_t getSize() const override { return sizeof(tailMergeARM64); } 557 MachineTypes getMachine() const override { return ARM64; } 558 559 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: 576 explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c) 577 : thunk(c), ctx(ctx) { 578 setAlignment(ctx.config.wordsize); 579 } 580 size_t getSize() const override { return ctx.config.wordsize; } 581 582 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 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: 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 616 size_t getSize() const override { 617 return sizeof(export_directory_table_entry); 618 } 619 620 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: 644 explicit AddressTableChunk(SymbolTable &symtab, size_t baseOrdinal, 645 size_t maxOrdinal) 646 : baseOrdinal(baseOrdinal), size((maxOrdinal - baseOrdinal) + 1), 647 symtab(symtab) {} 648 size_t getSize() const override { return size * 4; } 649 650 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: 679 explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {} 680 size_t getSize() const override { return chunks.size() * 4; } 681 682 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: 695 explicit ExportOrdinalChunk(const SymbolTable &symtab, size_t baseOrdinal, 696 size_t tableSize) 697 : baseOrdinal(baseOrdinal), size(tableSize), symtab(symtab) {} 698 size_t getSize() const override { return size * 2; } 699 700 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 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 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 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 916 uint64_t DelayLoadContents::getDirSize() { 917 return dirs.size() * sizeof(delay_import_directory_table_entry); 918 } 919 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 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 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 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 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