xref: /llvm-project-15.0.7/lld/COFF/DLL.cpp (revision 95213c31)
1 //===- DLL.cpp ------------------------------------------------------------===//
2 //
3 //                             The LLVM Linker
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines various types of chunks for the DLL import
11 // descriptor table. They are inherently Windows-specific.
12 // You need to read Microsoft PE/COFF spec to understand details
13 // about the data structures.
14 //
15 // If you are not particularly interested in linking against Windows
16 // DLL, you can skip this file, and you should still be able to
17 // understand the rest of the linker.
18 //
19 //===----------------------------------------------------------------------===//
20 
21 #include "Chunks.h"
22 #include "DLL.h"
23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/Object/COFF.h"
25 #include "llvm/Support/Endian.h"
26 
27 using namespace llvm;
28 using namespace llvm::object;
29 using namespace llvm::support::endian;
30 using namespace llvm::COFF;
31 using llvm::RoundUpToAlignment;
32 
33 static const size_t LookupChunkSize = sizeof(uint64_t);
34 static const size_t DirectoryChunkSize = sizeof(ImportDirectoryTableEntry);
35 
36 namespace lld {
37 namespace coff {
38 
39 // A chunk for the import descriptor table.
40 class HintNameChunk : public Chunk {
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 RoundUpToAlignment(Name.size() + 3, 2);
48   }
49 
50   void writeTo(uint8_t *Buf) override {
51     write16le(Buf + FileOff, Hint);
52     memcpy(Buf + FileOff + 2, Name.data(), Name.size());
53   }
54 
55 private:
56   StringRef Name;
57   uint16_t Hint;
58 };
59 
60 // A chunk for the import descriptor table.
61 class LookupChunk : public Chunk {
62 public:
63   explicit LookupChunk(Chunk *C) : HintName(C) {}
64   size_t getSize() const override { return LookupChunkSize; }
65 
66   void writeTo(uint8_t *Buf) override {
67     write32le(Buf + FileOff, HintName->getRVA());
68   }
69 
70   Chunk *HintName;
71 };
72 
73 // A chunk for the import descriptor table.
74 // This chunk represent import-by-ordinal symbols.
75 // See Microsoft PE/COFF spec 7.1. Import Header for details.
76 class OrdinalOnlyChunk : public Chunk {
77 public:
78   explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {}
79   size_t getSize() const override { return sizeof(uint64_t); }
80 
81   void writeTo(uint8_t *Buf) override {
82     // An import-by-ordinal slot has MSB 1 to indicate that
83     // this is import-by-ordinal (and not import-by-name).
84     write64le(Buf + FileOff, (uint64_t(1) << 63) | Ordinal);
85   }
86 
87   uint16_t Ordinal;
88 };
89 
90 // A chunk for the import descriptor table.
91 class DirectoryChunk : public Chunk {
92 public:
93   explicit DirectoryChunk(Chunk *N) : DLLName(N) {}
94   size_t getSize() const override { return DirectoryChunkSize; }
95 
96   void writeTo(uint8_t *Buf) override {
97     auto *E = (coff_import_directory_table_entry *)(Buf + FileOff);
98     E->ImportLookupTableRVA = LookupTab->getRVA();
99     E->NameRVA = DLLName->getRVA();
100     E->ImportAddressTableRVA = AddressTab->getRVA();
101   }
102 
103   Chunk *DLLName;
104   Chunk *LookupTab;
105   Chunk *AddressTab;
106 };
107 
108 // A chunk representing null terminator in the import table.
109 // Contents of this chunk is always null bytes.
110 class NullChunk : public Chunk {
111 public:
112   explicit NullChunk(size_t N) : Size(N) {}
113   bool hasData() const override { return false; }
114   size_t getSize() const override { return Size; }
115 
116 private:
117   size_t Size;
118 };
119 
120 uint64_t IdataContents::getDirSize() {
121   return Dirs.size() * DirectoryChunkSize;
122 }
123 
124 uint64_t IdataContents::getIATSize() {
125   return Addresses.size() * LookupChunkSize;
126 }
127 
128 // Returns a list of .idata contents.
129 // See Microsoft PE/COFF spec 5.4 for details.
130 std::vector<Chunk *> IdataContents::getChunks() {
131   create();
132   std::vector<Chunk *> V;
133   // The loader assumes a specific order of data.
134   // Add each type in the correct order.
135   for (std::unique_ptr<Chunk> &C : Dirs)
136     V.push_back(C.get());
137   for (std::unique_ptr<Chunk> &C : Lookups)
138     V.push_back(C.get());
139   for (std::unique_ptr<Chunk> &C : Addresses)
140     V.push_back(C.get());
141   for (std::unique_ptr<Chunk> &C : Hints)
142     V.push_back(C.get());
143   for (auto &P : DLLNames) {
144     std::unique_ptr<Chunk> &C = P.second;
145     V.push_back(C.get());
146   }
147   return V;
148 }
149 
150 void IdataContents::create() {
151   // Group DLL-imported symbols by DLL name because that's how
152   // symbols are layed out in the import descriptor table.
153   std::map<StringRef, std::vector<DefinedImportData *>> Map;
154   for (DefinedImportData *Sym : Imports)
155     Map[Sym->getDLLName()].push_back(Sym);
156 
157   // Create .idata contents for each DLL.
158   for (auto &P : Map) {
159     StringRef Name = P.first;
160     std::vector<DefinedImportData *> &Syms = P.second;
161 
162     // Sort symbols by name for each group.
163     std::sort(Syms.begin(), Syms.end(),
164               [](DefinedImportData *A, DefinedImportData *B) {
165                 return A->getName() < B->getName();
166               });
167 
168     // Create lookup and address tables. If they have external names,
169     // we need to create HintName chunks to store the names.
170     // If they don't (if they are import-by-ordinals), we store only
171     // ordinal values to the table.
172     size_t Base = Lookups.size();
173     for (DefinedImportData *S : Syms) {
174       uint16_t Ord = S->getOrdinal();
175       if (S->getExternalName().empty()) {
176         Lookups.push_back(make_unique<OrdinalOnlyChunk>(Ord));
177         Addresses.push_back(make_unique<OrdinalOnlyChunk>(Ord));
178         continue;
179       }
180       auto C = make_unique<HintNameChunk>(S->getExternalName(), Ord);
181       Lookups.push_back(make_unique<LookupChunk>(C.get()));
182       Addresses.push_back(make_unique<LookupChunk>(C.get()));
183       Hints.push_back(std::move(C));
184     }
185     // Terminate with null values.
186     Lookups.push_back(make_unique<NullChunk>(LookupChunkSize));
187     Addresses.push_back(make_unique<NullChunk>(LookupChunkSize));
188 
189     for (int I = 0, E = Syms.size(); I < E; ++I)
190       Syms[I]->setLocation(Addresses[Base + I].get());
191 
192     // Create the import table header.
193     if (!DLLNames.count(Name))
194       DLLNames[Name] = make_unique<StringChunk>(Name);
195     auto Dir = make_unique<DirectoryChunk>(DLLNames[Name].get());
196     Dir->LookupTab = Lookups[Base].get();
197     Dir->AddressTab = Addresses[Base].get();
198     Dirs.push_back(std::move(Dir));
199   }
200   // Add null terminator.
201   Dirs.push_back(make_unique<NullChunk>(DirectoryChunkSize));
202 }
203 
204 } // namespace coff
205 } // namespace lld
206