1 | //===- COFFImportFile.cpp - COFF short import file implementation ---------===// |
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 the writeImportLibrary function. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/Object/COFFImportFile.h" |
14 | #include "llvm/ADT/ArrayRef.h" |
15 | #include "llvm/ADT/SmallVector.h" |
16 | #include "llvm/ADT/StringMap.h" |
17 | #include "llvm/ADT/Twine.h" |
18 | #include "llvm/Object/Archive.h" |
19 | #include "llvm/Object/ArchiveWriter.h" |
20 | #include "llvm/Object/COFF.h" |
21 | #include "llvm/Support/Allocator.h" |
22 | #include "llvm/Support/Endian.h" |
23 | #include "llvm/Support/Error.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | #include "llvm/Support/Path.h" |
26 | |
27 | #include <cstdint> |
28 | #include <string> |
29 | #include <vector> |
30 | |
31 | using namespace llvm::COFF; |
32 | using namespace llvm::object; |
33 | using namespace llvm; |
34 | |
35 | namespace llvm { |
36 | namespace object { |
37 | |
38 | StringRef COFFImportFile::getFileFormatName() const { |
39 | switch (getMachine()) { |
40 | case COFF::IMAGE_FILE_MACHINE_I386: |
41 | return "COFF-import-file-i386" ; |
42 | case COFF::IMAGE_FILE_MACHINE_AMD64: |
43 | return "COFF-import-file-x86-64" ; |
44 | case COFF::IMAGE_FILE_MACHINE_ARMNT: |
45 | return "COFF-import-file-ARM" ; |
46 | case COFF::IMAGE_FILE_MACHINE_ARM64: |
47 | return "COFF-import-file-ARM64" ; |
48 | case COFF::IMAGE_FILE_MACHINE_ARM64EC: |
49 | return "COFF-import-file-ARM64EC" ; |
50 | case COFF::IMAGE_FILE_MACHINE_ARM64X: |
51 | return "COFF-import-file-ARM64X" ; |
52 | default: |
53 | return "COFF-import-file-<unknown arch>" ; |
54 | } |
55 | } |
56 | |
57 | static StringRef applyNameType(ImportNameType Type, StringRef name) { |
58 | auto ltrim1 = [](StringRef s, StringRef chars) { |
59 | return !s.empty() && chars.contains(C: s[0]) ? s.substr(Start: 1) : s; |
60 | }; |
61 | |
62 | switch (Type) { |
63 | case IMPORT_NAME_NOPREFIX: |
64 | name = ltrim1(name, "?@_" ); |
65 | break; |
66 | case IMPORT_NAME_UNDECORATE: |
67 | name = ltrim1(name, "?@_" ); |
68 | name = name.substr(Start: 0, N: name.find(C: '@')); |
69 | break; |
70 | default: |
71 | break; |
72 | } |
73 | return name; |
74 | } |
75 | |
76 | StringRef COFFImportFile::getExportName() const { |
77 | const coff_import_header *hdr = getCOFFImportHeader(); |
78 | StringRef name = Data.getBuffer().substr(Start: sizeof(*hdr)).split(Separator: '\0').first; |
79 | |
80 | switch (hdr->getNameType()) { |
81 | case IMPORT_ORDINAL: |
82 | name = "" ; |
83 | break; |
84 | case IMPORT_NAME_NOPREFIX: |
85 | case IMPORT_NAME_UNDECORATE: |
86 | name = applyNameType(Type: static_cast<ImportNameType>(hdr->getNameType()), name); |
87 | break; |
88 | case IMPORT_NAME_EXPORTAS: { |
89 | // Skip DLL name |
90 | name = Data.getBuffer().substr(Start: sizeof(*hdr) + name.size() + 1); |
91 | name = name.split(Separator: '\0').second.split(Separator: '\0').first; |
92 | break; |
93 | } |
94 | default: |
95 | break; |
96 | } |
97 | |
98 | return name; |
99 | } |
100 | |
101 | Error COFFImportFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const { |
102 | switch (Symb.p) { |
103 | case ImpSymbol: |
104 | OS << "__imp_" ; |
105 | break; |
106 | case ECAuxSymbol: |
107 | OS << "__imp_aux_" ; |
108 | break; |
109 | } |
110 | const char *Name = Data.getBufferStart() + sizeof(coff_import_header); |
111 | if (Symb.p != ECThunkSymbol && COFF::isArm64EC(Machine: getMachine())) { |
112 | if (std::optional<std::string> DemangledName = |
113 | getArm64ECDemangledFunctionName(Name)) { |
114 | OS << StringRef(*DemangledName); |
115 | return Error::success(); |
116 | } |
117 | } |
118 | OS << StringRef(Name); |
119 | return Error::success(); |
120 | } |
121 | |
122 | static uint16_t getImgRelRelocation(MachineTypes Machine) { |
123 | switch (Machine) { |
124 | default: |
125 | llvm_unreachable("unsupported machine" ); |
126 | case IMAGE_FILE_MACHINE_AMD64: |
127 | return IMAGE_REL_AMD64_ADDR32NB; |
128 | case IMAGE_FILE_MACHINE_ARMNT: |
129 | return IMAGE_REL_ARM_ADDR32NB; |
130 | case IMAGE_FILE_MACHINE_ARM64: |
131 | case IMAGE_FILE_MACHINE_ARM64EC: |
132 | case IMAGE_FILE_MACHINE_ARM64X: |
133 | return IMAGE_REL_ARM64_ADDR32NB; |
134 | case IMAGE_FILE_MACHINE_I386: |
135 | return IMAGE_REL_I386_DIR32NB; |
136 | } |
137 | } |
138 | |
139 | template <class T> static void append(std::vector<uint8_t> &B, const T &Data) { |
140 | size_t S = B.size(); |
141 | B.resize(new_size: S + sizeof(T)); |
142 | memcpy(&B[S], &Data, sizeof(T)); |
143 | } |
144 | |
145 | static void writeStringTable(std::vector<uint8_t> &B, |
146 | ArrayRef<const std::string_view> Strings) { |
147 | // The COFF string table consists of a 4-byte value which is the size of the |
148 | // table, including the length field itself. This value is followed by the |
149 | // string content itself, which is an array of null-terminated C-style |
150 | // strings. The termination is important as they are referenced to by offset |
151 | // by the symbol entity in the file format. |
152 | |
153 | size_t Pos = B.size(); |
154 | size_t Offset = B.size(); |
155 | |
156 | // Skip over the length field, we will fill it in later as we will have |
157 | // computed the length while emitting the string content itself. |
158 | Pos += sizeof(uint32_t); |
159 | |
160 | for (const auto &S : Strings) { |
161 | B.resize(new_size: Pos + S.length() + 1); |
162 | std::copy(S.begin(), S.end(), std::next(x: B.begin(), n: Pos)); |
163 | B[Pos + S.length()] = 0; |
164 | Pos += S.length() + 1; |
165 | } |
166 | |
167 | // Backfill the length of the table now that it has been computed. |
168 | support::ulittle32_t Length(B.size() - Offset); |
169 | support::endian::write32le(P: &B[Offset], V: Length); |
170 | } |
171 | |
172 | static ImportNameType getNameType(StringRef Sym, StringRef ExtName, |
173 | MachineTypes Machine, bool MinGW) { |
174 | // A decorated stdcall function in MSVC is exported with the |
175 | // type IMPORT_NAME, and the exported function name includes the |
176 | // the leading underscore. In MinGW on the other hand, a decorated |
177 | // stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX). |
178 | // See the comment in isDecorated in COFFModuleDefinition.cpp for more |
179 | // details. |
180 | if (ExtName.starts_with(Prefix: "_" ) && ExtName.contains(C: '@') && !MinGW) |
181 | return IMPORT_NAME; |
182 | if (Sym != ExtName) |
183 | return IMPORT_NAME_UNDECORATE; |
184 | if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with(Prefix: "_" )) |
185 | return IMPORT_NAME_NOPREFIX; |
186 | return IMPORT_NAME; |
187 | } |
188 | |
189 | static Expected<std::string> replace(StringRef S, StringRef From, |
190 | StringRef To) { |
191 | size_t Pos = S.find(Str: From); |
192 | |
193 | // From and To may be mangled, but substrings in S may not. |
194 | if (Pos == StringRef::npos && From.starts_with(Prefix: "_" ) && To.starts_with(Prefix: "_" )) { |
195 | From = From.substr(Start: 1); |
196 | To = To.substr(Start: 1); |
197 | Pos = S.find(Str: From); |
198 | } |
199 | |
200 | if (Pos == StringRef::npos) { |
201 | return make_error<StringError>( |
202 | Args: StringRef(Twine(S + ": replacing '" + From + |
203 | "' with '" + To + "' failed" ).str()), Args: object_error::parse_failed); |
204 | } |
205 | |
206 | return (Twine(S.substr(Start: 0, N: Pos)) + To + S.substr(Start: Pos + From.size())).str(); |
207 | } |
208 | |
209 | namespace { |
210 | // This class constructs various small object files necessary to support linking |
211 | // symbols imported from a DLL. The contents are pretty strictly defined and |
212 | // nearly entirely static. The details of the structures files are defined in |
213 | // WINNT.h and the PE/COFF specification. |
214 | class ObjectFactory { |
215 | using u16 = support::ulittle16_t; |
216 | using u32 = support::ulittle32_t; |
217 | MachineTypes NativeMachine; |
218 | BumpPtrAllocator Alloc; |
219 | StringRef ImportName; |
220 | StringRef Library; |
221 | std::string ImportDescriptorSymbolName; |
222 | std::string NullThunkSymbolName; |
223 | |
224 | public: |
225 | ObjectFactory(StringRef S, MachineTypes M) |
226 | : NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(path: S)), |
227 | ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()), |
228 | NullThunkSymbolName( |
229 | (NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {} |
230 | |
231 | // Creates an Import Descriptor. This is a small object file which contains a |
232 | // reference to the terminators and contains the library name (entry) for the |
233 | // import name table. It will force the linker to construct the necessary |
234 | // structure to import symbols from the DLL. |
235 | NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); |
236 | |
237 | // Creates a NULL import descriptor. This is a small object file whcih |
238 | // contains a NULL import descriptor. It is used to terminate the imports |
239 | // from a specific DLL. |
240 | NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); |
241 | |
242 | // Create a NULL Thunk Entry. This is a small object file which contains a |
243 | // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It |
244 | // is used to terminate the IAT and ILT. |
245 | NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); |
246 | |
247 | // Create a short import file which is described in PE/COFF spec 7. Import |
248 | // Library Format. |
249 | NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, |
250 | ImportType Type, ImportNameType NameType, |
251 | StringRef ExportName, |
252 | MachineTypes Machine); |
253 | |
254 | // Create a weak external file which is described in PE/COFF Aux Format 3. |
255 | NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp, |
256 | MachineTypes Machine); |
257 | |
258 | bool is64Bit() const { return COFF::is64Bit(Machine: NativeMachine); } |
259 | }; |
260 | } // namespace |
261 | |
262 | NewArchiveMember |
263 | ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { |
264 | const uint32_t NumberOfSections = 2; |
265 | const uint32_t NumberOfSymbols = 7; |
266 | const uint32_t NumberOfRelocations = 3; |
267 | |
268 | // COFF Header |
269 | coff_file_header { |
270 | .Machine: u16(NativeMachine), |
271 | .NumberOfSections: u16(NumberOfSections), |
272 | .TimeDateStamp: u32(0), |
273 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + |
274 | // .idata$2 |
275 | sizeof(coff_import_directory_table_entry) + |
276 | NumberOfRelocations * sizeof(coff_relocation) + |
277 | // .idata$4 |
278 | (ImportName.size() + 1)), |
279 | .NumberOfSymbols: u32(NumberOfSymbols), |
280 | .SizeOfOptionalHeader: u16(0), |
281 | .Characteristics: u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), |
282 | }; |
283 | append(B&: Buffer, Data: Header); |
284 | |
285 | // Section Header Table |
286 | const coff_section SectionTable[NumberOfSections] = { |
287 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, |
288 | .VirtualSize: u32(0), |
289 | .VirtualAddress: u32(0), |
290 | .SizeOfRawData: u32(sizeof(coff_import_directory_table_entry)), |
291 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), |
292 | .PointerToRelocations: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + |
293 | sizeof(coff_import_directory_table_entry)), |
294 | .PointerToLinenumbers: u32(0), |
295 | .NumberOfRelocations: u16(NumberOfRelocations), |
296 | .NumberOfLinenumbers: u16(0), |
297 | .Characteristics: u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | |
298 | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, |
299 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, |
300 | .VirtualSize: u32(0), |
301 | .VirtualAddress: u32(0), |
302 | .SizeOfRawData: u32(ImportName.size() + 1), |
303 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + |
304 | sizeof(coff_import_directory_table_entry) + |
305 | NumberOfRelocations * sizeof(coff_relocation)), |
306 | .PointerToRelocations: u32(0), |
307 | .PointerToLinenumbers: u32(0), |
308 | .NumberOfRelocations: u16(0), |
309 | .NumberOfLinenumbers: u16(0), |
310 | .Characteristics: u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | |
311 | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, |
312 | }; |
313 | append(B&: Buffer, Data: SectionTable); |
314 | |
315 | // .idata$2 |
316 | const coff_import_directory_table_entry ImportDescriptor{ |
317 | .ImportLookupTableRVA: u32(0), .TimeDateStamp: u32(0), .ForwarderChain: u32(0), .NameRVA: u32(0), .ImportAddressTableRVA: u32(0), |
318 | }; |
319 | append(B&: Buffer, Data: ImportDescriptor); |
320 | |
321 | const coff_relocation RelocationTable[NumberOfRelocations] = { |
322 | {.VirtualAddress: u32(offsetof(coff_import_directory_table_entry, NameRVA)), .SymbolTableIndex: u32(2), |
323 | .Type: u16(getImgRelRelocation(Machine: NativeMachine))}, |
324 | {.VirtualAddress: u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), |
325 | .SymbolTableIndex: u32(3), .Type: u16(getImgRelRelocation(Machine: NativeMachine))}, |
326 | {.VirtualAddress: u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), |
327 | .SymbolTableIndex: u32(4), .Type: u16(getImgRelRelocation(Machine: NativeMachine))}, |
328 | }; |
329 | append(B&: Buffer, Data: RelocationTable); |
330 | |
331 | // .idata$6 |
332 | auto S = Buffer.size(); |
333 | Buffer.resize(new_size: S + ImportName.size() + 1); |
334 | memcpy(dest: &Buffer[S], src: ImportName.data(), n: ImportName.size()); |
335 | Buffer[S + ImportName.size()] = '\0'; |
336 | |
337 | // Symbol Table |
338 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
339 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
340 | .Value: u32(0), |
341 | .SectionNumber: u16(1), |
342 | .Type: u16(0), |
343 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
344 | .NumberOfAuxSymbols: 0}, |
345 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, |
346 | .Value: u32(0), |
347 | .SectionNumber: u16(1), |
348 | .Type: u16(0), |
349 | .StorageClass: IMAGE_SYM_CLASS_SECTION, |
350 | .NumberOfAuxSymbols: 0}, |
351 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, |
352 | .Value: u32(0), |
353 | .SectionNumber: u16(2), |
354 | .Type: u16(0), |
355 | .StorageClass: IMAGE_SYM_CLASS_STATIC, |
356 | .NumberOfAuxSymbols: 0}, |
357 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, |
358 | .Value: u32(0), |
359 | .SectionNumber: u16(0), |
360 | .Type: u16(0), |
361 | .StorageClass: IMAGE_SYM_CLASS_SECTION, |
362 | .NumberOfAuxSymbols: 0}, |
363 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, |
364 | .Value: u32(0), |
365 | .SectionNumber: u16(0), |
366 | .Type: u16(0), |
367 | .StorageClass: IMAGE_SYM_CLASS_SECTION, |
368 | .NumberOfAuxSymbols: 0}, |
369 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
370 | .Value: u32(0), |
371 | .SectionNumber: u16(0), |
372 | .Type: u16(0), |
373 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
374 | .NumberOfAuxSymbols: 0}, |
375 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
376 | .Value: u32(0), |
377 | .SectionNumber: u16(0), |
378 | .Type: u16(0), |
379 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
380 | .NumberOfAuxSymbols: 0}, |
381 | }; |
382 | // TODO: Name.Offset.Offset here and in the all similar places below |
383 | // suggests a names refactoring. Maybe StringTableOffset.Value? |
384 | SymbolTable[0].Name.Offset.Offset = |
385 | sizeof(uint32_t); |
386 | SymbolTable[5].Name.Offset.Offset = |
387 | sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; |
388 | SymbolTable[6].Name.Offset.Offset = |
389 | sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + |
390 | NullImportDescriptorSymbolName.length() + 1; |
391 | append(B&: Buffer, Data: SymbolTable); |
392 | |
393 | // String Table |
394 | writeStringTable(B&: Buffer, |
395 | Strings: {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, |
396 | NullThunkSymbolName}); |
397 | |
398 | StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; |
399 | return {MemoryBufferRef(F, ImportName)}; |
400 | } |
401 | |
402 | NewArchiveMember |
403 | ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { |
404 | const uint32_t NumberOfSections = 1; |
405 | const uint32_t NumberOfSymbols = 1; |
406 | |
407 | // COFF Header |
408 | coff_file_header { |
409 | .Machine: u16(NativeMachine), |
410 | .NumberOfSections: u16(NumberOfSections), |
411 | .TimeDateStamp: u32(0), |
412 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + |
413 | // .idata$3 |
414 | sizeof(coff_import_directory_table_entry)), |
415 | .NumberOfSymbols: u32(NumberOfSymbols), |
416 | .SizeOfOptionalHeader: u16(0), |
417 | .Characteristics: u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), |
418 | }; |
419 | append(B&: Buffer, Data: Header); |
420 | |
421 | // Section Header Table |
422 | const coff_section SectionTable[NumberOfSections] = { |
423 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, |
424 | .VirtualSize: u32(0), |
425 | .VirtualAddress: u32(0), |
426 | .SizeOfRawData: u32(sizeof(coff_import_directory_table_entry)), |
427 | .PointerToRawData: u32(sizeof(coff_file_header) + |
428 | (NumberOfSections * sizeof(coff_section))), |
429 | .PointerToRelocations: u32(0), |
430 | .PointerToLinenumbers: u32(0), |
431 | .NumberOfRelocations: u16(0), |
432 | .NumberOfLinenumbers: u16(0), |
433 | .Characteristics: u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | |
434 | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, |
435 | }; |
436 | append(B&: Buffer, Data: SectionTable); |
437 | |
438 | // .idata$3 |
439 | const coff_import_directory_table_entry ImportDescriptor{ |
440 | .ImportLookupTableRVA: u32(0), .TimeDateStamp: u32(0), .ForwarderChain: u32(0), .NameRVA: u32(0), .ImportAddressTableRVA: u32(0), |
441 | }; |
442 | append(B&: Buffer, Data: ImportDescriptor); |
443 | |
444 | // Symbol Table |
445 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
446 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
447 | .Value: u32(0), |
448 | .SectionNumber: u16(1), |
449 | .Type: u16(0), |
450 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
451 | .NumberOfAuxSymbols: 0}, |
452 | }; |
453 | SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); |
454 | append(B&: Buffer, Data: SymbolTable); |
455 | |
456 | // String Table |
457 | writeStringTable(B&: Buffer, Strings: {NullImportDescriptorSymbolName}); |
458 | |
459 | StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; |
460 | return {MemoryBufferRef(F, ImportName)}; |
461 | } |
462 | |
463 | NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { |
464 | const uint32_t NumberOfSections = 2; |
465 | const uint32_t NumberOfSymbols = 1; |
466 | uint32_t VASize = is64Bit() ? 8 : 4; |
467 | |
468 | // COFF Header |
469 | coff_file_header { |
470 | .Machine: u16(NativeMachine), |
471 | .NumberOfSections: u16(NumberOfSections), |
472 | .TimeDateStamp: u32(0), |
473 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + |
474 | // .idata$5 |
475 | VASize + |
476 | // .idata$4 |
477 | VASize), |
478 | .NumberOfSymbols: u32(NumberOfSymbols), |
479 | .SizeOfOptionalHeader: u16(0), |
480 | .Characteristics: u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), |
481 | }; |
482 | append(B&: Buffer, Data: Header); |
483 | |
484 | // Section Header Table |
485 | const coff_section SectionTable[NumberOfSections] = { |
486 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, |
487 | .VirtualSize: u32(0), |
488 | .VirtualAddress: u32(0), |
489 | .SizeOfRawData: u32(VASize), |
490 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), |
491 | .PointerToRelocations: u32(0), |
492 | .PointerToLinenumbers: u32(0), |
493 | .NumberOfRelocations: u16(0), |
494 | .NumberOfLinenumbers: u16(0), |
495 | .Characteristics: u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | |
496 | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
497 | IMAGE_SCN_MEM_WRITE)}, |
498 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, |
499 | .VirtualSize: u32(0), |
500 | .VirtualAddress: u32(0), |
501 | .SizeOfRawData: u32(VASize), |
502 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + |
503 | VASize), |
504 | .PointerToRelocations: u32(0), |
505 | .PointerToLinenumbers: u32(0), |
506 | .NumberOfRelocations: u16(0), |
507 | .NumberOfLinenumbers: u16(0), |
508 | .Characteristics: u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | |
509 | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
510 | IMAGE_SCN_MEM_WRITE)}, |
511 | }; |
512 | append(B&: Buffer, Data: SectionTable); |
513 | |
514 | // .idata$5, ILT |
515 | append(B&: Buffer, Data: u32(0)); |
516 | if (is64Bit()) |
517 | append(B&: Buffer, Data: u32(0)); |
518 | |
519 | // .idata$4, IAT |
520 | append(B&: Buffer, Data: u32(0)); |
521 | if (is64Bit()) |
522 | append(B&: Buffer, Data: u32(0)); |
523 | |
524 | // Symbol Table |
525 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
526 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
527 | .Value: u32(0), |
528 | .SectionNumber: u16(1), |
529 | .Type: u16(0), |
530 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
531 | .NumberOfAuxSymbols: 0}, |
532 | }; |
533 | SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); |
534 | append(B&: Buffer, Data: SymbolTable); |
535 | |
536 | // String Table |
537 | writeStringTable(B&: Buffer, Strings: {NullThunkSymbolName}); |
538 | |
539 | StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; |
540 | return {MemoryBufferRef{F, ImportName}}; |
541 | } |
542 | |
543 | NewArchiveMember |
544 | ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal, |
545 | ImportType ImportType, ImportNameType NameType, |
546 | StringRef ExportName, MachineTypes Machine) { |
547 | size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs |
548 | if (!ExportName.empty()) |
549 | ImpSize += ExportName.size() + 1; |
550 | size_t Size = sizeof(coff_import_header) + ImpSize; |
551 | char *Buf = Alloc.Allocate<char>(Num: Size); |
552 | memset(s: Buf, c: 0, n: Size); |
553 | char *P = Buf; |
554 | |
555 | // Write short import library. |
556 | auto *Imp = reinterpret_cast<coff_import_header *>(P); |
557 | P += sizeof(*Imp); |
558 | Imp->Sig2 = 0xFFFF; |
559 | Imp->Machine = Machine; |
560 | Imp->SizeOfData = ImpSize; |
561 | if (Ordinal > 0) |
562 | Imp->OrdinalHint = Ordinal; |
563 | Imp->TypeInfo = (NameType << 2) | ImportType; |
564 | |
565 | // Write symbol name and DLL name. |
566 | memcpy(dest: P, src: Sym.data(), n: Sym.size()); |
567 | P += Sym.size() + 1; |
568 | memcpy(dest: P, src: ImportName.data(), n: ImportName.size()); |
569 | if (!ExportName.empty()) { |
570 | P += ImportName.size() + 1; |
571 | memcpy(dest: P, src: ExportName.data(), n: ExportName.size()); |
572 | } |
573 | |
574 | return {MemoryBufferRef(StringRef(Buf, Size), ImportName)}; |
575 | } |
576 | |
577 | NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, |
578 | StringRef Weak, bool Imp, |
579 | MachineTypes Machine) { |
580 | std::vector<uint8_t> Buffer; |
581 | const uint32_t NumberOfSections = 1; |
582 | const uint32_t NumberOfSymbols = 5; |
583 | |
584 | // COFF Header |
585 | coff_file_header { |
586 | .Machine: u16(Machine), |
587 | .NumberOfSections: u16(NumberOfSections), |
588 | .TimeDateStamp: u32(0), |
589 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))), |
590 | .NumberOfSymbols: u32(NumberOfSymbols), |
591 | .SizeOfOptionalHeader: u16(0), |
592 | .Characteristics: u16(0), |
593 | }; |
594 | append(B&: Buffer, Data: Header); |
595 | |
596 | // Section Header Table |
597 | const coff_section SectionTable[NumberOfSections] = { |
598 | {.Name: {'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'}, |
599 | .VirtualSize: u32(0), |
600 | .VirtualAddress: u32(0), |
601 | .SizeOfRawData: u32(0), |
602 | .PointerToRawData: u32(0), |
603 | .PointerToRelocations: u32(0), |
604 | .PointerToLinenumbers: u32(0), |
605 | .NumberOfRelocations: u16(0), |
606 | .NumberOfLinenumbers: u16(0), |
607 | .Characteristics: u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}}; |
608 | append(B&: Buffer, Data: SectionTable); |
609 | |
610 | // Symbol Table |
611 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
612 | {.Name: {.ShortName: {'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}}, |
613 | .Value: u32(0), |
614 | .SectionNumber: u16(0xFFFF), |
615 | .Type: u16(0), |
616 | .StorageClass: IMAGE_SYM_CLASS_STATIC, |
617 | .NumberOfAuxSymbols: 0}, |
618 | {.Name: {.ShortName: {'@', 'f', 'e', 'a', 't', '.', '0', '0'}}, |
619 | .Value: u32(0), |
620 | .SectionNumber: u16(0xFFFF), |
621 | .Type: u16(0), |
622 | .StorageClass: IMAGE_SYM_CLASS_STATIC, |
623 | .NumberOfAuxSymbols: 0}, |
624 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
625 | .Value: u32(0), |
626 | .SectionNumber: u16(0), |
627 | .Type: u16(0), |
628 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
629 | .NumberOfAuxSymbols: 0}, |
630 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
631 | .Value: u32(0), |
632 | .SectionNumber: u16(0), |
633 | .Type: u16(0), |
634 | .StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL, |
635 | .NumberOfAuxSymbols: 1}, |
636 | {.Name: {.ShortName: {2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}}, |
637 | .Value: u32(0), |
638 | .SectionNumber: u16(0), |
639 | .Type: u16(0), |
640 | .StorageClass: IMAGE_SYM_CLASS_NULL, |
641 | .NumberOfAuxSymbols: 0}, |
642 | }; |
643 | SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t); |
644 | |
645 | //__imp_ String Table |
646 | StringRef Prefix = Imp ? "__imp_" : "" ; |
647 | SymbolTable[3].Name.Offset.Offset = |
648 | sizeof(uint32_t) + Sym.size() + Prefix.size() + 1; |
649 | append(B&: Buffer, Data: SymbolTable); |
650 | writeStringTable(B&: Buffer, Strings: {(Prefix + Sym).str(), |
651 | (Prefix + Weak).str()}); |
652 | |
653 | // Copied here so we can still use writeStringTable |
654 | char *Buf = Alloc.Allocate<char>(Num: Buffer.size()); |
655 | memcpy(dest: Buf, src: Buffer.data(), n: Buffer.size()); |
656 | return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)}; |
657 | } |
658 | |
659 | Error writeImportLibrary(StringRef ImportName, StringRef Path, |
660 | ArrayRef<COFFShortExport> Exports, |
661 | MachineTypes Machine, bool MinGW, |
662 | ArrayRef<COFFShortExport> NativeExports) { |
663 | |
664 | MachineTypes NativeMachine = Machine; |
665 | if (isArm64EC(Machine)) { |
666 | NativeMachine = IMAGE_FILE_MACHINE_ARM64; |
667 | Machine = IMAGE_FILE_MACHINE_ARM64EC; |
668 | } |
669 | |
670 | std::vector<NewArchiveMember> Members; |
671 | ObjectFactory OF(llvm::sys::path::filename(path: ImportName), NativeMachine); |
672 | |
673 | std::vector<uint8_t> ImportDescriptor; |
674 | Members.push_back(x: OF.createImportDescriptor(Buffer&: ImportDescriptor)); |
675 | |
676 | std::vector<uint8_t> NullImportDescriptor; |
677 | Members.push_back(x: OF.createNullImportDescriptor(Buffer&: NullImportDescriptor)); |
678 | |
679 | std::vector<uint8_t> NullThunk; |
680 | Members.push_back(x: OF.createNullThunk(Buffer&: NullThunk)); |
681 | |
682 | auto addExports = [&](ArrayRef<COFFShortExport> Exp, |
683 | MachineTypes M) -> Error { |
684 | StringMap<std::string> RegularImports; |
685 | struct Deferred { |
686 | std::string Name; |
687 | ImportType ImpType; |
688 | const COFFShortExport *Export; |
689 | }; |
690 | SmallVector<Deferred, 0> Renames; |
691 | for (const COFFShortExport &E : Exp) { |
692 | if (E.Private) |
693 | continue; |
694 | |
695 | ImportType ImportType = IMPORT_CODE; |
696 | if (E.Data) |
697 | ImportType = IMPORT_DATA; |
698 | if (E.Constant) |
699 | ImportType = IMPORT_CONST; |
700 | |
701 | StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName; |
702 | std::string Name; |
703 | |
704 | if (E.ExtName.empty()) { |
705 | Name = std::string(SymbolName); |
706 | } else { |
707 | Expected<std::string> ReplacedName = |
708 | replace(S: SymbolName, From: E.Name, To: E.ExtName); |
709 | if (!ReplacedName) |
710 | return ReplacedName.takeError(); |
711 | Name.swap(s&: *ReplacedName); |
712 | } |
713 | |
714 | ImportNameType NameType; |
715 | std::string ExportName; |
716 | if (E.Noname) { |
717 | NameType = IMPORT_ORDINAL; |
718 | } else if (!E.ExportAs.empty()) { |
719 | NameType = IMPORT_NAME_EXPORTAS; |
720 | ExportName = E.ExportAs; |
721 | } else if (!E.ImportName.empty()) { |
722 | // If we need to import from a specific ImportName, we may need to use |
723 | // a weak alias (which needs another import to point at). But if we can |
724 | // express ImportName based on the symbol name and a specific NameType, |
725 | // prefer that over an alias. |
726 | if (Machine == IMAGE_FILE_MACHINE_I386 && |
727 | applyNameType(Type: IMPORT_NAME_UNDECORATE, name: Name) == E.ImportName) |
728 | NameType = IMPORT_NAME_UNDECORATE; |
729 | else if (Machine == IMAGE_FILE_MACHINE_I386 && |
730 | applyNameType(Type: IMPORT_NAME_NOPREFIX, name: Name) == E.ImportName) |
731 | NameType = IMPORT_NAME_NOPREFIX; |
732 | else if (isArm64EC(Machine: M)) { |
733 | NameType = IMPORT_NAME_EXPORTAS; |
734 | ExportName = E.ImportName; |
735 | } else if (Name == E.ImportName) |
736 | NameType = IMPORT_NAME; |
737 | else { |
738 | Deferred D; |
739 | D.Name = Name; |
740 | D.ImpType = ImportType; |
741 | D.Export = &E; |
742 | Renames.push_back(Elt: D); |
743 | continue; |
744 | } |
745 | } else { |
746 | NameType = getNameType(Sym: SymbolName, ExtName: E.Name, Machine: M, MinGW); |
747 | } |
748 | |
749 | // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols. |
750 | if (ImportType == IMPORT_CODE && isArm64EC(Machine: M)) { |
751 | if (std::optional<std::string> MangledName = |
752 | getArm64ECMangledFunctionName(Name)) { |
753 | if (!E.Noname && ExportName.empty()) { |
754 | NameType = IMPORT_NAME_EXPORTAS; |
755 | ExportName.swap(s&: Name); |
756 | } |
757 | Name = std::move(*MangledName); |
758 | } else if (!E.Noname && ExportName.empty()) { |
759 | NameType = IMPORT_NAME_EXPORTAS; |
760 | ExportName = std::move(*getArm64ECDemangledFunctionName(Name)); |
761 | } |
762 | } |
763 | |
764 | RegularImports[applyNameType(Type: NameType, name: Name)] = Name; |
765 | Members.push_back(x: OF.createShortImport(Sym: Name, Ordinal: E.Ordinal, ImportType, |
766 | NameType, ExportName, Machine: M)); |
767 | } |
768 | for (const auto &D : Renames) { |
769 | auto It = RegularImports.find(Key: D.Export->ImportName); |
770 | if (It != RegularImports.end()) { |
771 | // We have a regular import entry for a symbol with the name we |
772 | // want to reference; produce an alias pointing at that. |
773 | StringRef Symbol = It->second; |
774 | if (D.ImpType == IMPORT_CODE) |
775 | Members.push_back(x: OF.createWeakExternal(Sym: Symbol, Weak: D.Name, Imp: false, Machine: M)); |
776 | Members.push_back(x: OF.createWeakExternal(Sym: Symbol, Weak: D.Name, Imp: true, Machine: M)); |
777 | } else { |
778 | Members.push_back(x: OF.createShortImport(Sym: D.Name, Ordinal: D.Export->Ordinal, |
779 | ImportType: D.ImpType, NameType: IMPORT_NAME_EXPORTAS, |
780 | ExportName: D.Export->ImportName, Machine: M)); |
781 | } |
782 | } |
783 | return Error::success(); |
784 | }; |
785 | |
786 | if (Error e = addExports(Exports, Machine)) |
787 | return e; |
788 | if (Error e = addExports(NativeExports, NativeMachine)) |
789 | return e; |
790 | |
791 | return writeArchive(ArcName: Path, NewMembers: Members, WriteSymtab: SymtabWritingMode::NormalSymtab, |
792 | Kind: object::Archive::K_COFF, |
793 | /*Deterministic*/ true, /*Thin*/ false, |
794 | /*OldArchiveBuf*/ nullptr, IsEC: isArm64EC(Machine)); |
795 | } |
796 | |
797 | } // namespace object |
798 | } // namespace llvm |
799 | |