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

source code of llvm/lib/Object/COFFImportFile.cpp