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