xref: /freebsd/contrib/llvm-project/llvm/lib/Object/COFFImportFile.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
getFileFormatName() const38 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 
applyNameType(ImportNameType Type,StringRef name)57 static StringRef applyNameType(ImportNameType Type, StringRef name) {
58   auto ltrim1 = [](StringRef s, StringRef chars) {
59     return !s.empty() && chars.contains(s[0]) ? s.substr(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(0, name.find('@'));
69     break;
70   default:
71     break;
72   }
73   return name;
74 }
75 
getExportName() const76 StringRef COFFImportFile::getExportName() const {
77   const coff_import_header *hdr = getCOFFImportHeader();
78   StringRef name = Data.getBuffer().substr(sizeof(*hdr)).split('\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(static_cast<ImportNameType>(hdr->getNameType()), name);
87     break;
88   case IMPORT_NAME_EXPORTAS: {
89     // Skip DLL name
90     name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1);
91     name = name.split('\0').second.split('\0').first;
92     break;
93   }
94   default:
95     break;
96   }
97 
98   return name;
99 }
100 
printSymbolName(raw_ostream & OS,DataRefImpl Symb) const101 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(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 
getImgRelRelocation(MachineTypes Machine)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 
append(std::vector<uint8_t> & B,const T & Data)139 template <class T> static void append(std::vector<uint8_t> &B, const T &Data) {
140   size_t S = B.size();
141   B.resize(S + sizeof(T));
142   memcpy(&B[S], &Data, sizeof(T));
143 }
144 
writeStringTable(std::vector<uint8_t> & B,ArrayRef<const std::string_view> Strings)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(Pos + S.length() + 1);
162     std::copy(S.begin(), S.end(), std::next(B.begin(), 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(&B[Offset], Length);
170 }
171 
getNameType(StringRef Sym,StringRef ExtName,MachineTypes Machine,bool MinGW)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("_") && ExtName.contains('@') && !MinGW)
181     return IMPORT_NAME;
182   if (Sym != ExtName)
183     return IMPORT_NAME_UNDECORATE;
184   if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with("_"))
185     return IMPORT_NAME_NOPREFIX;
186   return IMPORT_NAME;
187 }
188 
replace(StringRef S,StringRef From,StringRef To)189 static Expected<std::string> replace(StringRef S, StringRef From,
190                                      StringRef To) {
191   size_t Pos = S.find(From);
192 
193   // From and To may be mangled, but substrings in S may not.
194   if (Pos == StringRef::npos && From.starts_with("_") && To.starts_with("_")) {
195     From = From.substr(1);
196     To = To.substr(1);
197     Pos = S.find(From);
198   }
199 
200   if (Pos == StringRef::npos) {
201     return make_error<StringError>(
202       StringRef(Twine(S + ": replacing '" + From +
203         "' with '" + To + "' failed").str()), object_error::parse_failed);
204   }
205 
206   return (Twine(S.substr(0, Pos)) + To + S.substr(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:
ObjectFactory(StringRef S,MachineTypes M)225   ObjectFactory(StringRef S, MachineTypes M)
226       : NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(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 
is64Bit() const258   bool is64Bit() const { return COFF::is64Bit(NativeMachine); }
259 };
260 } // namespace
261 
262 NewArchiveMember
createImportDescriptor(std::vector<uint8_t> & Buffer)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 Header{
270       u16(NativeMachine),
271       u16(NumberOfSections),
272       u32(0),
273       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       u32(NumberOfSymbols),
280       u16(0),
281       u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
282   };
283   append(Buffer, Header);
284 
285   // Section Header Table
286   const coff_section SectionTable[NumberOfSections] = {
287       {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
288        u32(0),
289        u32(0),
290        u32(sizeof(coff_import_directory_table_entry)),
291        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
292        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
293            sizeof(coff_import_directory_table_entry)),
294        u32(0),
295        u16(NumberOfRelocations),
296        u16(0),
297        u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
298            IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
299       {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'},
300        u32(0),
301        u32(0),
302        u32(ImportName.size() + 1),
303        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
304            sizeof(coff_import_directory_table_entry) +
305            NumberOfRelocations * sizeof(coff_relocation)),
306        u32(0),
307        u32(0),
308        u16(0),
309        u16(0),
310        u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
311            IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
312   };
313   append(Buffer, SectionTable);
314 
315   // .idata$2
316   const coff_import_directory_table_entry ImportDescriptor{
317       u32(0), u32(0), u32(0), u32(0), u32(0),
318   };
319   append(Buffer, ImportDescriptor);
320 
321   const coff_relocation RelocationTable[NumberOfRelocations] = {
322       {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2),
323        u16(getImgRelRelocation(NativeMachine))},
324       {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)),
325        u32(3), u16(getImgRelRelocation(NativeMachine))},
326       {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)),
327        u32(4), u16(getImgRelRelocation(NativeMachine))},
328   };
329   append(Buffer, RelocationTable);
330 
331   // .idata$6
332   auto S = Buffer.size();
333   Buffer.resize(S + ImportName.size() + 1);
334   memcpy(&Buffer[S], ImportName.data(), ImportName.size());
335   Buffer[S + ImportName.size()] = '\0';
336 
337   // Symbol Table
338   coff_symbol16 SymbolTable[NumberOfSymbols] = {
339       {{{0, 0, 0, 0, 0, 0, 0, 0}},
340        u32(0),
341        u16(1),
342        u16(0),
343        IMAGE_SYM_CLASS_EXTERNAL,
344        0},
345       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
346        u32(0),
347        u16(1),
348        u16(0),
349        IMAGE_SYM_CLASS_SECTION,
350        0},
351       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
352        u32(0),
353        u16(2),
354        u16(0),
355        IMAGE_SYM_CLASS_STATIC,
356        0},
357       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
358        u32(0),
359        u16(0),
360        u16(0),
361        IMAGE_SYM_CLASS_SECTION,
362        0},
363       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
364        u32(0),
365        u16(0),
366        u16(0),
367        IMAGE_SYM_CLASS_SECTION,
368        0},
369       {{{0, 0, 0, 0, 0, 0, 0, 0}},
370        u32(0),
371        u16(0),
372        u16(0),
373        IMAGE_SYM_CLASS_EXTERNAL,
374        0},
375       {{{0, 0, 0, 0, 0, 0, 0, 0}},
376        u32(0),
377        u16(0),
378        u16(0),
379        IMAGE_SYM_CLASS_EXTERNAL,
380        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(Buffer, SymbolTable);
392 
393   // String Table
394   writeStringTable(Buffer,
395                    {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
createNullImportDescriptor(std::vector<uint8_t> & Buffer)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 Header{
409       u16(NativeMachine),
410       u16(NumberOfSections),
411       u32(0),
412       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
413           // .idata$3
414           sizeof(coff_import_directory_table_entry)),
415       u32(NumberOfSymbols),
416       u16(0),
417       u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
418   };
419   append(Buffer, Header);
420 
421   // Section Header Table
422   const coff_section SectionTable[NumberOfSections] = {
423       {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
424        u32(0),
425        u32(0),
426        u32(sizeof(coff_import_directory_table_entry)),
427        u32(sizeof(coff_file_header) +
428            (NumberOfSections * sizeof(coff_section))),
429        u32(0),
430        u32(0),
431        u16(0),
432        u16(0),
433        u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
434            IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
435   };
436   append(Buffer, SectionTable);
437 
438   // .idata$3
439   const coff_import_directory_table_entry ImportDescriptor{
440       u32(0), u32(0), u32(0), u32(0), u32(0),
441   };
442   append(Buffer, ImportDescriptor);
443 
444   // Symbol Table
445   coff_symbol16 SymbolTable[NumberOfSymbols] = {
446       {{{0, 0, 0, 0, 0, 0, 0, 0}},
447        u32(0),
448        u16(1),
449        u16(0),
450        IMAGE_SYM_CLASS_EXTERNAL,
451        0},
452   };
453   SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
454   append(Buffer, SymbolTable);
455 
456   // String Table
457   writeStringTable(Buffer, {NullImportDescriptorSymbolName});
458 
459   StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
460   return {MemoryBufferRef(F, ImportName)};
461 }
462 
createNullThunk(std::vector<uint8_t> & Buffer)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 Header{
470       u16(NativeMachine),
471       u16(NumberOfSections),
472       u32(0),
473       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
474           // .idata$5
475           VASize +
476           // .idata$4
477           VASize),
478       u32(NumberOfSymbols),
479       u16(0),
480       u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
481   };
482   append(Buffer, Header);
483 
484   // Section Header Table
485   const coff_section SectionTable[NumberOfSections] = {
486       {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
487        u32(0),
488        u32(0),
489        u32(VASize),
490        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
491        u32(0),
492        u32(0),
493        u16(0),
494        u16(0),
495        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       {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
499        u32(0),
500        u32(0),
501        u32(VASize),
502        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
503            VASize),
504        u32(0),
505        u32(0),
506        u16(0),
507        u16(0),
508        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(Buffer, SectionTable);
513 
514   // .idata$5, ILT
515   append(Buffer, u32(0));
516   if (is64Bit())
517     append(Buffer, u32(0));
518 
519   // .idata$4, IAT
520   append(Buffer, u32(0));
521   if (is64Bit())
522     append(Buffer, u32(0));
523 
524   // Symbol Table
525   coff_symbol16 SymbolTable[NumberOfSymbols] = {
526       {{{0, 0, 0, 0, 0, 0, 0, 0}},
527        u32(0),
528        u16(1),
529        u16(0),
530        IMAGE_SYM_CLASS_EXTERNAL,
531        0},
532   };
533   SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
534   append(Buffer, SymbolTable);
535 
536   // String Table
537   writeStringTable(Buffer, {NullThunkSymbolName});
538 
539   StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
540   return {MemoryBufferRef{F, ImportName}};
541 }
542 
543 NewArchiveMember
createShortImport(StringRef Sym,uint16_t Ordinal,ImportType ImportType,ImportNameType NameType,StringRef ExportName,MachineTypes Machine)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>(Size);
552   memset(Buf, 0, 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(P, Sym.data(), Sym.size());
567   P += Sym.size() + 1;
568   memcpy(P, ImportName.data(), ImportName.size());
569   if (!ExportName.empty()) {
570     P += ImportName.size() + 1;
571     memcpy(P, ExportName.data(), ExportName.size());
572   }
573 
574   return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
575 }
576 
createWeakExternal(StringRef Sym,StringRef Weak,bool Imp,MachineTypes Machine)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 Header{
586       u16(Machine),
587       u16(NumberOfSections),
588       u32(0),
589       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))),
590       u32(NumberOfSymbols),
591       u16(0),
592       u16(0),
593   };
594   append(Buffer, Header);
595 
596   // Section Header Table
597   const coff_section SectionTable[NumberOfSections] = {
598       {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
599        u32(0),
600        u32(0),
601        u32(0),
602        u32(0),
603        u32(0),
604        u32(0),
605        u16(0),
606        u16(0),
607        u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}};
608   append(Buffer, SectionTable);
609 
610   // Symbol Table
611   coff_symbol16 SymbolTable[NumberOfSymbols] = {
612       {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
613        u32(0),
614        u16(0xFFFF),
615        u16(0),
616        IMAGE_SYM_CLASS_STATIC,
617        0},
618       {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
619        u32(0),
620        u16(0xFFFF),
621        u16(0),
622        IMAGE_SYM_CLASS_STATIC,
623        0},
624       {{{0, 0, 0, 0, 0, 0, 0, 0}},
625        u32(0),
626        u16(0),
627        u16(0),
628        IMAGE_SYM_CLASS_EXTERNAL,
629        0},
630       {{{0, 0, 0, 0, 0, 0, 0, 0}},
631        u32(0),
632        u16(0),
633        u16(0),
634        IMAGE_SYM_CLASS_WEAK_EXTERNAL,
635        1},
636       {{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}},
637        u32(0),
638        u16(0),
639        u16(0),
640        IMAGE_SYM_CLASS_NULL,
641        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(Buffer, SymbolTable);
650   writeStringTable(Buffer, {(Prefix + Sym).str(),
651                             (Prefix + Weak).str()});
652 
653   // Copied here so we can still use writeStringTable
654   char *Buf = Alloc.Allocate<char>(Buffer.size());
655   memcpy(Buf, Buffer.data(), Buffer.size());
656   return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)};
657 }
658 
writeImportLibrary(StringRef ImportName,StringRef Path,ArrayRef<COFFShortExport> Exports,MachineTypes Machine,bool MinGW,ArrayRef<COFFShortExport> NativeExports)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(ImportName), NativeMachine);
672 
673   std::vector<uint8_t> ImportDescriptor;
674   Members.push_back(OF.createImportDescriptor(ImportDescriptor));
675 
676   std::vector<uint8_t> NullImportDescriptor;
677   Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor));
678 
679   std::vector<uint8_t> NullThunk;
680   Members.push_back(OF.createNullThunk(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(SymbolName, E.Name, E.ExtName);
709         if (!ReplacedName)
710           return ReplacedName.takeError();
711         Name.swap(*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(IMPORT_NAME_UNDECORATE, Name) == E.ImportName)
728           NameType = IMPORT_NAME_UNDECORATE;
729         else if (Machine == IMAGE_FILE_MACHINE_I386 &&
730                  applyNameType(IMPORT_NAME_NOPREFIX, Name) == E.ImportName)
731           NameType = IMPORT_NAME_NOPREFIX;
732         else if (isArm64EC(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(D);
743           continue;
744         }
745       } else {
746         NameType = getNameType(SymbolName, E.Name, M, MinGW);
747       }
748 
749       // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols.
750       if (ImportType == IMPORT_CODE && isArm64EC(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(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(NameType, Name)] = Name;
765       Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
766                                              NameType, ExportName, M));
767     }
768     for (const auto &D : Renames) {
769       auto It = RegularImports.find(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(OF.createWeakExternal(Symbol, D.Name, false, M));
776         Members.push_back(OF.createWeakExternal(Symbol, D.Name, true, M));
777       } else {
778         Members.push_back(OF.createShortImport(D.Name, D.Export->Ordinal,
779                                                D.ImpType, IMPORT_NAME_EXPORTAS,
780                                                D.Export->ImportName, 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(Path, Members, SymtabWritingMode::NormalSymtab,
792                       object::Archive::K_COFF,
793                       /*Deterministic*/ true, /*Thin*/ false,
794                       /*OldArchiveBuf*/ nullptr, isArm64EC(Machine));
795 }
796 
797 } // namespace object
798 } // namespace llvm
799