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
31using namespace llvm::COFF;
32using namespace llvm::object;
33using namespace llvm;
34
35namespace llvm {
36namespace object {
37
38StringRef 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
57static 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
76StringRef 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
101Error 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
122static 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
139template <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
145static 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
172static 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
189static 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
209namespace {
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.
214class 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
224public:
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
262NewArchiveMember
263ObjectFactory::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 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
402NewArchiveMember
403ObjectFactory::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 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
463NewArchiveMember 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 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
543NewArchiveMember
544ObjectFactory::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
577NewArchiveMember 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 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
659Error 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