Path: blob/main/contrib/llvm-project/lld/COFF/DLL.cpp
34870 views
//===- DLL.cpp ------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines various types of chunks for the DLL import or export9// descriptor tables. They are inherently Windows-specific.10// You need to read Microsoft PE/COFF spec to understand details11// about the data structures.12//13// If you are not particularly interested in linking against Windows14// DLL, you can skip this file, and you should still be able to15// understand the rest of the linker.16//17//===----------------------------------------------------------------------===//1819#include "DLL.h"20#include "COFFLinkerContext.h"21#include "Chunks.h"22#include "SymbolTable.h"23#include "llvm/ADT/STLExtras.h"24#include "llvm/Object/COFF.h"25#include "llvm/Support/Endian.h"26#include "llvm/Support/Path.h"2728using namespace llvm;29using namespace llvm::object;30using namespace llvm::support::endian;31using namespace llvm::COFF;3233namespace lld::coff {34namespace {3536// Import table3738// A chunk for the import descriptor table.39class HintNameChunk : public NonSectionChunk {40public:41HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {}4243size_t getSize() const override {44// Starts with 2 byte Hint field, followed by a null-terminated string,45// ends with 0 or 1 byte padding.46return alignTo(name.size() + 3, 2);47}4849void writeTo(uint8_t *buf) const override {50memset(buf, 0, getSize());51write16le(buf, hint);52memcpy(buf + 2, name.data(), name.size());53}5455private:56StringRef name;57uint16_t hint;58};5960// A chunk for the import descriptor table.61class LookupChunk : public NonSectionChunk {62public:63explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c)64: hintName(c), ctx(ctx) {65setAlignment(ctx.config.wordsize);66}67size_t getSize() const override { return ctx.config.wordsize; }6869void writeTo(uint8_t *buf) const override {70if (ctx.config.is64())71write64le(buf, hintName->getRVA());72else73write32le(buf, hintName->getRVA());74}7576Chunk *hintName;7778private:79COFFLinkerContext &ctx;80};8182// A chunk for the import descriptor table.83// This chunk represent import-by-ordinal symbols.84// See Microsoft PE/COFF spec 7.1. Import Header for details.85class OrdinalOnlyChunk : public NonSectionChunk {86public:87explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v)88: ordinal(v), ctx(c) {89setAlignment(ctx.config.wordsize);90}91size_t getSize() const override { return ctx.config.wordsize; }9293void writeTo(uint8_t *buf) const override {94// An import-by-ordinal slot has MSB 1 to indicate that95// this is import-by-ordinal (and not import-by-name).96if (ctx.config.is64()) {97write64le(buf, (1ULL << 63) | ordinal);98} else {99write32le(buf, (1ULL << 31) | ordinal);100}101}102103uint16_t ordinal;104105private:106COFFLinkerContext &ctx;107};108109// A chunk for the import descriptor table.110class ImportDirectoryChunk : public NonSectionChunk {111public:112explicit ImportDirectoryChunk(Chunk *n) : dllName(n) { setAlignment(4); }113size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }114115void writeTo(uint8_t *buf) const override {116memset(buf, 0, getSize());117118auto *e = (coff_import_directory_table_entry *)(buf);119e->ImportLookupTableRVA = lookupTab->getRVA();120e->NameRVA = dllName->getRVA();121e->ImportAddressTableRVA = addressTab->getRVA();122}123124Chunk *dllName;125Chunk *lookupTab;126Chunk *addressTab;127};128129// A chunk representing null terminator in the import table.130// Contents of this chunk is always null bytes.131class NullChunk : public NonSectionChunk {132public:133explicit NullChunk(size_t n) : size(n) { hasData = false; }134size_t getSize() const override { return size; }135136void writeTo(uint8_t *buf) const override {137memset(buf, 0, size);138}139140private:141size_t size;142};143144static std::vector<std::vector<DefinedImportData *>>145binImports(COFFLinkerContext &ctx,146const std::vector<DefinedImportData *> &imports) {147// Group DLL-imported symbols by DLL name because that's how148// symbols are laid out in the import descriptor table.149auto less = [&ctx](const std::string &a, const std::string &b) {150return ctx.config.dllOrder[a] < ctx.config.dllOrder[b];151};152std::map<std::string, std::vector<DefinedImportData *>, decltype(less)> m(153less);154for (DefinedImportData *sym : imports)155m[sym->getDLLName().lower()].push_back(sym);156157std::vector<std::vector<DefinedImportData *>> v;158for (auto &kv : m) {159// Sort symbols by name for each group.160std::vector<DefinedImportData *> &syms = kv.second;161llvm::sort(syms, [](DefinedImportData *a, DefinedImportData *b) {162return a->getName() < b->getName();163});164v.push_back(std::move(syms));165}166return v;167}168169// See Microsoft PE/COFF spec 4.3 for details.170171// A chunk for the delay import descriptor table etnry.172class DelayDirectoryChunk : public NonSectionChunk {173public:174explicit DelayDirectoryChunk(Chunk *n) : dllName(n) { setAlignment(4); }175176size_t getSize() const override {177return sizeof(delay_import_directory_table_entry);178}179180void writeTo(uint8_t *buf) const override {181memset(buf, 0, getSize());182183auto *e = (delay_import_directory_table_entry *)(buf);184e->Attributes = 1;185e->Name = dllName->getRVA();186e->ModuleHandle = moduleHandle->getRVA();187e->DelayImportAddressTable = addressTab->getRVA();188e->DelayImportNameTable = nameTab->getRVA();189}190191Chunk *dllName;192Chunk *moduleHandle;193Chunk *addressTab;194Chunk *nameTab;195};196197// Initial contents for delay-loaded functions.198// This code calls __delayLoadHelper2 function to resolve a symbol199// which then overwrites its jump table slot with the result200// for subsequent function calls.201static const uint8_t thunkX64[] = {2020x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]2030xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>204};205206static const uint8_t tailMergeX64[] = {2070x51, // push rcx2080x52, // push rdx2090x41, 0x50, // push r82100x41, 0x51, // push r92110x48, 0x83, 0xEC, 0x48, // sub rsp, 48h2120x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm02130x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm12140x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm22150x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm32160x48, 0x8B, 0xD0, // mov rdx, rax2170x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...]2180xE8, 0, 0, 0, 0, // call __delayLoadHelper22190x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp]2200x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h]2210x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h]2220x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h]2230x48, 0x83, 0xC4, 0x48, // add rsp, 48h2240x41, 0x59, // pop r92250x41, 0x58, // pop r82260x5A, // pop rdx2270x59, // pop rcx2280xFF, 0xE0, // jmp rax229};230231static const uint8_t tailMergeUnwindInfoX64[] = {2320x01, // Version=1, Flags=UNW_FLAG_NHANDLER2330x0a, // Size of prolog2340x05, // Count of unwind codes2350x00, // No frame register2360x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48)2370x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8)2380x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8)2390x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8)2400x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8)2410x00, 0x00 // Padding to align on 32-bits242};243244static const uint8_t thunkX86[] = {2450xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME>2460xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>247};248249static const uint8_t tailMergeX86[] = {2500x51, // push ecx2510x52, // push edx2520x50, // push eax2530x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll2540xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@82550x5A, // pop edx2560x59, // pop ecx2570xFF, 0xE0, // jmp eax258};259260static const uint8_t thunkARM[] = {2610x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME>2620xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME>2630x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_<lib>264};265266static const uint8_t tailMergeARM[] = {2670x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr}2680x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #162690x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7}2700x61, 0x46, // mov r1, ip2710x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR2720xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR2730x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper22740x84, 0x46, // mov ip, r02750xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7}2760xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr}2770x60, 0x47, // bx ip278};279280static const uint8_t thunkARM64[] = {2810x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME>2820x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME>2830x00, 0x00, 0x00, 0x14, // b __tailMerge_<lib>284};285286static const uint8_t tailMergeARM64[] = {2870xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]!2880xfd, 0x03, 0x00, 0x91, // mov x29, sp2890xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16]2900xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32]2910xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48]2920xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64]2930xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80]2940xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112]2950xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144]2960xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176]2970xe1, 0x03, 0x11, 0xaa, // mov x1, x172980x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR2990x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR3000x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper23010xf0, 0x03, 0x00, 0xaa, // mov x16, x03020xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176]3030xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144]3040xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112]3050xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80]3060xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64]3070xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48]3080xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32]3090xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16]3100xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #2083110x00, 0x02, 0x1f, 0xd6, // br x16312};313314// A chunk for the delay import thunk.315class ThunkChunkX64 : public NonSectionCodeChunk {316public:317ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}318319size_t getSize() const override { return sizeof(thunkX64); }320MachineTypes getMachine() const override { return AMD64; }321322void writeTo(uint8_t *buf) const override {323memcpy(buf, thunkX64, sizeof(thunkX64));324write32le(buf + 3, imp->getRVA() - rva - 7);325write32le(buf + 8, tailMerge->getRVA() - rva - 12);326}327328Defined *imp = nullptr;329Chunk *tailMerge = nullptr;330};331332class TailMergeChunkX64 : public NonSectionCodeChunk {333public:334TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {}335336size_t getSize() const override { return sizeof(tailMergeX64); }337MachineTypes getMachine() const override { return AMD64; }338339void writeTo(uint8_t *buf) const override {340memcpy(buf, tailMergeX64, sizeof(tailMergeX64));341write32le(buf + 39, desc->getRVA() - rva - 43);342write32le(buf + 44, helper->getRVA() - rva - 48);343}344345Chunk *desc = nullptr;346Defined *helper = nullptr;347};348349class TailMergePDataChunkX64 : public NonSectionChunk {350public:351TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) {352// See353// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function354setAlignment(4);355}356357size_t getSize() const override { return 3 * sizeof(uint32_t); }358359void writeTo(uint8_t *buf) const override {360write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA361write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA362write32le(buf + 8, unwind->getRVA()); // UnwindInfo RVA363}364365Chunk *tm = nullptr;366Chunk *unwind = nullptr;367};368369class TailMergeUnwindInfoX64 : public NonSectionChunk {370public:371TailMergeUnwindInfoX64() {372// See373// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info374setAlignment(4);375}376377size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); }378379void writeTo(uint8_t *buf) const override {380memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64));381}382};383384class ThunkChunkX86 : public NonSectionCodeChunk {385public:386ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)387: imp(i), tailMerge(tm), ctx(ctx) {}388389size_t getSize() const override { return sizeof(thunkX86); }390MachineTypes getMachine() const override { return I386; }391392void writeTo(uint8_t *buf) const override {393memcpy(buf, thunkX86, sizeof(thunkX86));394write32le(buf + 1, imp->getRVA() + ctx.config.imageBase);395write32le(buf + 6, tailMerge->getRVA() - rva - 10);396}397398void getBaserels(std::vector<Baserel> *res) override {399res->emplace_back(rva + 1, ctx.config.machine);400}401402Defined *imp = nullptr;403Chunk *tailMerge = nullptr;404405private:406const COFFLinkerContext &ctx;407};408409class TailMergeChunkX86 : public NonSectionCodeChunk {410public:411TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h)412: desc(d), helper(h), ctx(ctx) {}413414size_t getSize() const override { return sizeof(tailMergeX86); }415MachineTypes getMachine() const override { return I386; }416417void writeTo(uint8_t *buf) const override {418memcpy(buf, tailMergeX86, sizeof(tailMergeX86));419write32le(buf + 4, desc->getRVA() + ctx.config.imageBase);420write32le(buf + 9, helper->getRVA() - rva - 13);421}422423void getBaserels(std::vector<Baserel> *res) override {424res->emplace_back(rva + 4, ctx.config.machine);425}426427Chunk *desc = nullptr;428Defined *helper = nullptr;429430private:431const COFFLinkerContext &ctx;432};433434class ThunkChunkARM : public NonSectionCodeChunk {435public:436ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm)437: imp(i), tailMerge(tm), ctx(ctx) {438setAlignment(2);439}440441size_t getSize() const override { return sizeof(thunkARM); }442MachineTypes getMachine() const override { return ARMNT; }443444void writeTo(uint8_t *buf) const override {445memcpy(buf, thunkARM, sizeof(thunkARM));446applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase);447applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);448}449450void getBaserels(std::vector<Baserel> *res) override {451res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T);452}453454Defined *imp = nullptr;455Chunk *tailMerge = nullptr;456457private:458const COFFLinkerContext &ctx;459};460461class TailMergeChunkARM : public NonSectionCodeChunk {462public:463TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h)464: desc(d), helper(h), ctx(ctx) {465setAlignment(2);466}467468size_t getSize() const override { return sizeof(tailMergeARM); }469MachineTypes getMachine() const override { return ARMNT; }470471void writeTo(uint8_t *buf) const override {472memcpy(buf, tailMergeARM, sizeof(tailMergeARM));473applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase);474applyBranch24T(buf + 22, helper->getRVA() - rva - 26);475}476477void getBaserels(std::vector<Baserel> *res) override {478res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T);479}480481Chunk *desc = nullptr;482Defined *helper = nullptr;483484private:485const COFFLinkerContext &ctx;486};487488class ThunkChunkARM64 : public NonSectionCodeChunk {489public:490ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {491setAlignment(4);492}493494size_t getSize() const override { return sizeof(thunkARM64); }495MachineTypes getMachine() const override { return ARM64; }496497void writeTo(uint8_t *buf) const override {498memcpy(buf, thunkARM64, sizeof(thunkARM64));499applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12);500applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0);501applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8);502}503504Defined *imp = nullptr;505Chunk *tailMerge = nullptr;506};507508class TailMergeChunkARM64 : public NonSectionCodeChunk {509public:510TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {511setAlignment(4);512}513514size_t getSize() const override { return sizeof(tailMergeARM64); }515MachineTypes getMachine() const override { return ARM64; }516517void writeTo(uint8_t *buf) const override {518memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64));519applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12);520applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0);521applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52);522}523524Chunk *desc = nullptr;525Defined *helper = nullptr;526};527528// A chunk for the import descriptor table.529class DelayAddressChunk : public NonSectionChunk {530public:531explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c)532: thunk(c), ctx(ctx) {533setAlignment(ctx.config.wordsize);534}535size_t getSize() const override { return ctx.config.wordsize; }536537void writeTo(uint8_t *buf) const override {538if (ctx.config.is64()) {539write64le(buf, thunk->getRVA() + ctx.config.imageBase);540} else {541uint32_t bit = 0;542// Pointer to thumb code must have the LSB set, so adjust it.543if (ctx.config.machine == ARMNT)544bit = 1;545write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit);546}547}548549void getBaserels(std::vector<Baserel> *res) override {550res->emplace_back(rva, ctx.config.machine);551}552553Chunk *thunk;554555private:556const COFFLinkerContext &ctx;557};558559// Export table560// Read Microsoft PE/COFF spec 5.3 for details.561562// A chunk for the export descriptor table.563class ExportDirectoryChunk : public NonSectionChunk {564public:565ExportDirectoryChunk(int baseOrdinal, int maxOrdinal, int nameTabSize,566Chunk *d, Chunk *a, Chunk *n, Chunk *o)567: baseOrdinal(baseOrdinal), maxOrdinal(maxOrdinal),568nameTabSize(nameTabSize), dllName(d), addressTab(a), nameTab(n),569ordinalTab(o) {}570571size_t getSize() const override {572return sizeof(export_directory_table_entry);573}574575void writeTo(uint8_t *buf) const override {576memset(buf, 0, getSize());577578auto *e = (export_directory_table_entry *)(buf);579e->NameRVA = dllName->getRVA();580e->OrdinalBase = baseOrdinal;581e->AddressTableEntries = (maxOrdinal - baseOrdinal) + 1;582e->NumberOfNamePointers = nameTabSize;583e->ExportAddressTableRVA = addressTab->getRVA();584e->NamePointerRVA = nameTab->getRVA();585e->OrdinalTableRVA = ordinalTab->getRVA();586}587588uint16_t baseOrdinal;589uint16_t maxOrdinal;590uint16_t nameTabSize;591Chunk *dllName;592Chunk *addressTab;593Chunk *nameTab;594Chunk *ordinalTab;595};596597class AddressTableChunk : public NonSectionChunk {598public:599explicit AddressTableChunk(COFFLinkerContext &ctx, size_t baseOrdinal,600size_t maxOrdinal)601: baseOrdinal(baseOrdinal), size((maxOrdinal - baseOrdinal) + 1),602ctx(ctx) {}603size_t getSize() const override { return size * 4; }604605void writeTo(uint8_t *buf) const override {606memset(buf, 0, getSize());607608for (const Export &e : ctx.config.exports) {609assert(e.ordinal >= baseOrdinal && "Export symbol has invalid ordinal");610// Subtract the OrdinalBase to get the index.611uint8_t *p = buf + (e.ordinal - baseOrdinal) * 4;612uint32_t bit = 0;613// Pointer to thumb code must have the LSB set, so adjust it.614if (ctx.config.machine == ARMNT && !e.data)615bit = 1;616if (e.forwardChunk) {617write32le(p, e.forwardChunk->getRVA() | bit);618} else {619assert(cast<Defined>(e.sym)->getRVA() != 0 &&620"Exported symbol unmapped");621write32le(p, cast<Defined>(e.sym)->getRVA() | bit);622}623}624}625626private:627size_t baseOrdinal;628size_t size;629const COFFLinkerContext &ctx;630};631632class NamePointersChunk : public NonSectionChunk {633public:634explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {}635size_t getSize() const override { return chunks.size() * 4; }636637void writeTo(uint8_t *buf) const override {638for (Chunk *c : chunks) {639write32le(buf, c->getRVA());640buf += 4;641}642}643644private:645std::vector<Chunk *> chunks;646};647648class ExportOrdinalChunk : public NonSectionChunk {649public:650explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t baseOrdinal,651size_t tableSize)652: baseOrdinal(baseOrdinal), size(tableSize), ctx(ctx) {}653size_t getSize() const override { return size * 2; }654655void writeTo(uint8_t *buf) const override {656for (const Export &e : ctx.config.exports) {657if (e.noname)658continue;659assert(e.ordinal >= baseOrdinal && "Export symbol has invalid ordinal");660// This table stores unbiased indices, so subtract OrdinalBase.661write16le(buf, e.ordinal - baseOrdinal);662buf += 2;663}664}665666private:667size_t baseOrdinal;668size_t size;669const COFFLinkerContext &ctx;670};671672} // anonymous namespace673674void IdataContents::create(COFFLinkerContext &ctx) {675std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);676677// Create .idata contents for each DLL.678for (std::vector<DefinedImportData *> &syms : v) {679// Create lookup and address tables. If they have external names,680// we need to create hintName chunks to store the names.681// If they don't (if they are import-by-ordinals), we store only682// ordinal values to the table.683size_t base = lookups.size();684for (DefinedImportData *s : syms) {685uint16_t ord = s->getOrdinal();686if (s->getExternalName().empty()) {687lookups.push_back(make<OrdinalOnlyChunk>(ctx, ord));688addresses.push_back(make<OrdinalOnlyChunk>(ctx, ord));689continue;690}691auto *c = make<HintNameChunk>(s->getExternalName(), ord);692lookups.push_back(make<LookupChunk>(ctx, c));693addresses.push_back(make<LookupChunk>(ctx, c));694hints.push_back(c);695}696// Terminate with null values.697lookups.push_back(make<NullChunk>(ctx.config.wordsize));698addresses.push_back(make<NullChunk>(ctx.config.wordsize));699700for (int i = 0, e = syms.size(); i < e; ++i)701syms[i]->setLocation(addresses[base + i]);702703// Create the import table header.704dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));705auto *dir = make<ImportDirectoryChunk>(dllNames.back());706dir->lookupTab = lookups[base];707dir->addressTab = addresses[base];708dirs.push_back(dir);709}710// Add null terminator.711dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry)));712}713714std::vector<Chunk *> DelayLoadContents::getChunks() {715std::vector<Chunk *> v;716v.insert(v.end(), dirs.begin(), dirs.end());717v.insert(v.end(), names.begin(), names.end());718v.insert(v.end(), hintNames.begin(), hintNames.end());719v.insert(v.end(), dllNames.begin(), dllNames.end());720return v;721}722723std::vector<Chunk *> DelayLoadContents::getDataChunks() {724std::vector<Chunk *> v;725v.insert(v.end(), moduleHandles.begin(), moduleHandles.end());726v.insert(v.end(), addresses.begin(), addresses.end());727return v;728}729730uint64_t DelayLoadContents::getDirSize() {731return dirs.size() * sizeof(delay_import_directory_table_entry);732}733734void DelayLoadContents::create(Defined *h) {735helper = h;736std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);737738Chunk *unwind = newTailMergeUnwindInfoChunk();739740// Create .didat contents for each DLL.741for (std::vector<DefinedImportData *> &syms : v) {742// Create the delay import table header.743dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));744auto *dir = make<DelayDirectoryChunk>(dllNames.back());745746size_t base = addresses.size();747Chunk *tm = newTailMergeChunk(dir);748Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr;749for (DefinedImportData *s : syms) {750Chunk *t = newThunkChunk(s, tm);751auto *a = make<DelayAddressChunk>(ctx, t);752addresses.push_back(a);753thunks.push_back(t);754StringRef extName = s->getExternalName();755if (extName.empty()) {756names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));757} else {758auto *c = make<HintNameChunk>(extName, 0);759names.push_back(make<LookupChunk>(ctx, c));760hintNames.push_back(c);761// Add a synthetic symbol for this load thunk, using the "__imp___load"762// prefix, in case this thunk needs to be added to the list of valid763// call targets for Control Flow Guard.764StringRef symName = saver().save("__imp___load_" + extName);765s->loadThunkSym =766cast<DefinedSynthetic>(ctx.symtab.addSynthetic(symName, t));767}768}769thunks.push_back(tm);770if (pdataChunk)771pdata.push_back(pdataChunk);772StringRef tmName =773saver().save("__tailMerge_" + syms[0]->getDLLName().lower());774ctx.symtab.addSynthetic(tmName, tm);775// Terminate with null values.776addresses.push_back(make<NullChunk>(8));777names.push_back(make<NullChunk>(8));778779for (int i = 0, e = syms.size(); i < e; ++i)780syms[i]->setLocation(addresses[base + i]);781auto *mh = make<NullChunk>(8);782mh->setAlignment(8);783moduleHandles.push_back(mh);784785// Fill the delay import table header fields.786dir->moduleHandle = mh;787dir->addressTab = addresses[base];788dir->nameTab = names[base];789dirs.push_back(dir);790}791792if (unwind)793unwindinfo.push_back(unwind);794// Add null terminator.795dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));796}797798Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {799switch (ctx.config.machine) {800case AMD64:801return make<TailMergeChunkX64>(dir, helper);802case I386:803return make<TailMergeChunkX86>(ctx, dir, helper);804case ARMNT:805return make<TailMergeChunkARM>(ctx, dir, helper);806case ARM64:807return make<TailMergeChunkARM64>(dir, helper);808default:809llvm_unreachable("unsupported machine type");810}811}812813Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() {814switch (ctx.config.machine) {815case AMD64:816return make<TailMergeUnwindInfoX64>();817// FIXME: Add support for other architectures.818default:819return nullptr; // Just don't generate unwind info.820}821}822Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) {823switch (ctx.config.machine) {824case AMD64:825return make<TailMergePDataChunkX64>(tm, unwind);826// FIXME: Add support for other architectures.827default:828return nullptr; // Just don't generate unwind info.829}830}831832Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,833Chunk *tailMerge) {834switch (ctx.config.machine) {835case AMD64:836return make<ThunkChunkX64>(s, tailMerge);837case I386:838return make<ThunkChunkX86>(ctx, s, tailMerge);839case ARMNT:840return make<ThunkChunkARM>(ctx, s, tailMerge);841case ARM64:842return make<ThunkChunkARM64>(s, tailMerge);843default:844llvm_unreachable("unsupported machine type");845}846}847848EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) {849unsigned baseOrdinal = 1 << 16, maxOrdinal = 0;850for (Export &e : ctx.config.exports) {851baseOrdinal = std::min(baseOrdinal, (unsigned)e.ordinal);852maxOrdinal = std::max(maxOrdinal, (unsigned)e.ordinal);853}854// Ordinals must start at 1 as suggested in:855// https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function?view=msvc-170856assert(baseOrdinal >= 1);857858auto *dllName = make<StringChunk>(sys::path::filename(ctx.config.outputFile));859auto *addressTab = make<AddressTableChunk>(ctx, baseOrdinal, maxOrdinal);860std::vector<Chunk *> names;861for (Export &e : ctx.config.exports)862if (!e.noname)863names.push_back(make<StringChunk>(e.exportName));864865std::vector<Chunk *> forwards;866for (Export &e : ctx.config.exports) {867if (e.forwardTo.empty())868continue;869e.forwardChunk = make<StringChunk>(e.forwardTo);870forwards.push_back(e.forwardChunk);871}872873auto *nameTab = make<NamePointersChunk>(names);874auto *ordinalTab = make<ExportOrdinalChunk>(ctx, baseOrdinal, names.size());875auto *dir =876make<ExportDirectoryChunk>(baseOrdinal, maxOrdinal, names.size(), dllName,877addressTab, nameTab, ordinalTab);878chunks.push_back(dir);879chunks.push_back(dllName);880chunks.push_back(addressTab);881chunks.push_back(nameTab);882chunks.push_back(ordinalTab);883chunks.insert(chunks.end(), names.begin(), names.end());884chunks.insert(chunks.end(), forwards.begin(), forwards.end());885}886887} // namespace lld::coff888889890