1 //===- COFFImportFile.cpp - COFF short import file implementation ---------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines the writeImportLibrary function.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/Object/COFFImportFile.h"
15 #include "llvm/ADT/ArrayRef.h"
16 #include "llvm/Object/Archive.h"
17 #include "llvm/Object/ArchiveWriter.h"
18 #include "llvm/Object/COFF.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/Path.h"
21 
22 #include <cstdint>
23 #include <string>
24 #include <vector>
25 
26 using namespace llvm::COFF;
27 using namespace llvm::object;
28 using namespace llvm;
29 
30 namespace llvm {
31 namespace object {
32 
33 static bool is32bit(MachineTypes Machine) {
34   switch (Machine) {
35   default:
36     llvm_unreachable("unsupported machine");
37   case IMAGE_FILE_MACHINE_ARM64:
38   case IMAGE_FILE_MACHINE_AMD64:
39     return false;
40   case IMAGE_FILE_MACHINE_ARMNT:
41   case IMAGE_FILE_MACHINE_I386:
42     return true;
43   }
44 }
45 
46 static uint16_t getImgRelRelocation(MachineTypes Machine) {
47   switch (Machine) {
48   default:
49     llvm_unreachable("unsupported machine");
50   case IMAGE_FILE_MACHINE_AMD64:
51     return IMAGE_REL_AMD64_ADDR32NB;
52   case IMAGE_FILE_MACHINE_ARMNT:
53     return IMAGE_REL_ARM_ADDR32NB;
54   case IMAGE_FILE_MACHINE_ARM64:
55     return IMAGE_REL_ARM64_ADDR32NB;
56   case IMAGE_FILE_MACHINE_I386:
57     return IMAGE_REL_I386_DIR32NB;
58   }
59 }
60 
61 template <class T> static void append(std::vector<uint8_t> &B, const T &Data) {
62   size_t S = B.size();
63   B.resize(S + sizeof(T));
64   memcpy(&B[S], &Data, sizeof(T));
65 }
66 
67 static void writeStringTable(std::vector<uint8_t> &B,
68                              ArrayRef<const std::string> Strings) {
69   // The COFF string table consists of a 4-byte value which is the size of the
70   // table, including the length field itself.  This value is followed by the
71   // string content itself, which is an array of null-terminated C-style
72   // strings.  The termination is important as they are referenced to by offset
73   // by the symbol entity in the file format.
74 
75   size_t Pos = B.size();
76   size_t Offset = B.size();
77 
78   // Skip over the length field, we will fill it in later as we will have
79   // computed the length while emitting the string content itself.
80   Pos += sizeof(uint32_t);
81 
82   for (const auto &S : Strings) {
83     B.resize(Pos + S.length() + 1);
84     strcpy(reinterpret_cast<char *>(&B[Pos]), S.c_str());
85     Pos += S.length() + 1;
86   }
87 
88   // Backfill the length of the table now that it has been computed.
89   support::ulittle32_t Length(B.size() - Offset);
90   support::endian::write32le(&B[Offset], Length);
91 }
92 
93 static ImportNameType getNameType(StringRef Sym, StringRef ExtName,
94                                   MachineTypes Machine) {
95   if (Sym != ExtName)
96     return IMPORT_NAME_UNDECORATE;
97   if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.startswith("_"))
98     return IMPORT_NAME_NOPREFIX;
99   return IMPORT_NAME;
100 }
101 
102 static Expected<std::string> replace(StringRef S, StringRef From,
103                                      StringRef To) {
104   size_t Pos = S.find(From);
105 
106   // From and To may be mangled, but substrings in S may not.
107   if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) {
108     From = From.substr(1);
109     To = To.substr(1);
110     Pos = S.find(From);
111   }
112 
113   if (Pos == StringRef::npos) {
114     return make_error<StringError>(
115       StringRef(Twine(S + ": replacing '" + From +
116         "' with '" + To + "' failed").str()), object_error::parse_failed);
117   }
118 
119   return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
120 }
121 
122 static const std::string NullImportDescriptorSymbolName =
123     "__NULL_IMPORT_DESCRIPTOR";
124 
125 namespace {
126 // This class constructs various small object files necessary to support linking
127 // symbols imported from a DLL.  The contents are pretty strictly defined and
128 // nearly entirely static.  The details of the structures files are defined in
129 // WINNT.h and the PE/COFF specification.
130 class ObjectFactory {
131   using u16 = support::ulittle16_t;
132   using u32 = support::ulittle32_t;
133   MachineTypes Machine;
134   BumpPtrAllocator Alloc;
135   StringRef ImportName;
136   StringRef Library;
137   std::string ImportDescriptorSymbolName;
138   std::string NullThunkSymbolName;
139 
140 public:
141   ObjectFactory(StringRef S, MachineTypes M)
142       : Machine(M), ImportName(S), Library(S.drop_back(4)),
143         ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()),
144         NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").str()) {}
145 
146   // Creates an Import Descriptor.  This is a small object file which contains a
147   // reference to the terminators and contains the library name (entry) for the
148   // import name table.  It will force the linker to construct the necessary
149   // structure to import symbols from the DLL.
150   NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer);
151 
152   // Creates a NULL import descriptor.  This is a small object file whcih
153   // contains a NULL import descriptor.  It is used to terminate the imports
154   // from a specific DLL.
155   NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer);
156 
157   // Create a NULL Thunk Entry.  This is a small object file which contains a
158   // NULL Import Address Table entry and a NULL Import Lookup Table Entry.  It
159   // is used to terminate the IAT and ILT.
160   NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer);
161 
162   // Create a short import file which is described in PE/COFF spec 7. Import
163   // Library Format.
164   NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
165                                      ImportType Type, ImportNameType NameType);
166 
167   // Create a weak external file which is described in PE/COFF Aux Format 3.
168   NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp);
169 };
170 } // namespace
171 
172 NewArchiveMember
173 ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) {
174   const uint32_t NumberOfSections = 2;
175   const uint32_t NumberOfSymbols = 7;
176   const uint32_t NumberOfRelocations = 3;
177 
178   // COFF Header
179   coff_file_header Header{
180       u16(Machine),
181       u16(NumberOfSections),
182       u32(0),
183       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
184           // .idata$2
185           sizeof(coff_import_directory_table_entry) +
186           NumberOfRelocations * sizeof(coff_relocation) +
187           // .idata$4
188           (ImportName.size() + 1)),
189       u32(NumberOfSymbols),
190       u16(0),
191       u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0),
192   };
193   append(Buffer, Header);
194 
195   // Section Header Table
196   const coff_section SectionTable[NumberOfSections] = {
197       {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
198        u32(0),
199        u32(0),
200        u32(sizeof(coff_import_directory_table_entry)),
201        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
202        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
203            sizeof(coff_import_directory_table_entry)),
204        u32(0),
205        u16(NumberOfRelocations),
206        u16(0),
207        u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
208            IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
209       {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'},
210        u32(0),
211        u32(0),
212        u32(ImportName.size() + 1),
213        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
214            sizeof(coff_import_directory_table_entry) +
215            NumberOfRelocations * sizeof(coff_relocation)),
216        u32(0),
217        u32(0),
218        u16(0),
219        u16(0),
220        u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
221            IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
222   };
223   append(Buffer, SectionTable);
224 
225   // .idata$2
226   const coff_import_directory_table_entry ImportDescriptor{
227       u32(0), u32(0), u32(0), u32(0), u32(0),
228   };
229   append(Buffer, ImportDescriptor);
230 
231   const coff_relocation RelocationTable[NumberOfRelocations] = {
232       {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2),
233        u16(getImgRelRelocation(Machine))},
234       {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)),
235        u32(3), u16(getImgRelRelocation(Machine))},
236       {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)),
237        u32(4), u16(getImgRelRelocation(Machine))},
238   };
239   append(Buffer, RelocationTable);
240 
241   // .idata$6
242   auto S = Buffer.size();
243   Buffer.resize(S + ImportName.size() + 1);
244   memcpy(&Buffer[S], ImportName.data(), ImportName.size());
245   Buffer[S + ImportName.size()] = '\0';
246 
247   // Symbol Table
248   coff_symbol16 SymbolTable[NumberOfSymbols] = {
249       {{{0, 0, 0, 0, 0, 0, 0, 0}},
250        u32(0),
251        u16(1),
252        u16(0),
253        IMAGE_SYM_CLASS_EXTERNAL,
254        0},
255       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
256        u32(0),
257        u16(1),
258        u16(0),
259        IMAGE_SYM_CLASS_SECTION,
260        0},
261       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
262        u32(0),
263        u16(2),
264        u16(0),
265        IMAGE_SYM_CLASS_STATIC,
266        0},
267       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
268        u32(0),
269        u16(0),
270        u16(0),
271        IMAGE_SYM_CLASS_SECTION,
272        0},
273       {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
274        u32(0),
275        u16(0),
276        u16(0),
277        IMAGE_SYM_CLASS_SECTION,
278        0},
279       {{{0, 0, 0, 0, 0, 0, 0, 0}},
280        u32(0),
281        u16(0),
282        u16(0),
283        IMAGE_SYM_CLASS_EXTERNAL,
284        0},
285       {{{0, 0, 0, 0, 0, 0, 0, 0}},
286        u32(0),
287        u16(0),
288        u16(0),
289        IMAGE_SYM_CLASS_EXTERNAL,
290        0},
291   };
292   // TODO: Name.Offset.Offset here and in the all similar places below
293   // suggests a names refactoring. Maybe StringTableOffset.Value?
294   SymbolTable[0].Name.Offset.Offset =
295       sizeof(uint32_t);
296   SymbolTable[5].Name.Offset.Offset =
297       sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1;
298   SymbolTable[6].Name.Offset.Offset =
299       sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 +
300       NullImportDescriptorSymbolName.length() + 1;
301   append(Buffer, SymbolTable);
302 
303   // String Table
304   writeStringTable(Buffer,
305                    {ImportDescriptorSymbolName, NullImportDescriptorSymbolName,
306                     NullThunkSymbolName});
307 
308   StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
309   return {MemoryBufferRef(F, ImportName)};
310 }
311 
312 NewArchiveMember
313 ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) {
314   const uint32_t NumberOfSections = 1;
315   const uint32_t NumberOfSymbols = 1;
316 
317   // COFF Header
318   coff_file_header Header{
319       u16(Machine),
320       u16(NumberOfSections),
321       u32(0),
322       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
323           // .idata$3
324           sizeof(coff_import_directory_table_entry)),
325       u32(NumberOfSymbols),
326       u16(0),
327       u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0),
328   };
329   append(Buffer, Header);
330 
331   // Section Header Table
332   const coff_section SectionTable[NumberOfSections] = {
333       {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
334        u32(0),
335        u32(0),
336        u32(sizeof(coff_import_directory_table_entry)),
337        u32(sizeof(coff_file_header) +
338            (NumberOfSections * sizeof(coff_section))),
339        u32(0),
340        u32(0),
341        u16(0),
342        u16(0),
343        u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
344            IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
345   };
346   append(Buffer, SectionTable);
347 
348   // .idata$3
349   const coff_import_directory_table_entry ImportDescriptor{
350       u32(0), u32(0), u32(0), u32(0), u32(0),
351   };
352   append(Buffer, ImportDescriptor);
353 
354   // Symbol Table
355   coff_symbol16 SymbolTable[NumberOfSymbols] = {
356       {{{0, 0, 0, 0, 0, 0, 0, 0}},
357        u32(0),
358        u16(1),
359        u16(0),
360        IMAGE_SYM_CLASS_EXTERNAL,
361        0},
362   };
363   SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
364   append(Buffer, SymbolTable);
365 
366   // String Table
367   writeStringTable(Buffer, {NullImportDescriptorSymbolName});
368 
369   StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
370   return {MemoryBufferRef(F, ImportName)};
371 }
372 
373 NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
374   const uint32_t NumberOfSections = 2;
375   const uint32_t NumberOfSymbols = 1;
376   uint32_t VASize = is32bit(Machine) ? 4 : 8;
377 
378   // COFF Header
379   coff_file_header Header{
380       u16(Machine),
381       u16(NumberOfSections),
382       u32(0),
383       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
384           // .idata$5
385           VASize +
386           // .idata$4
387           VASize),
388       u32(NumberOfSymbols),
389       u16(0),
390       u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0),
391   };
392   append(Buffer, Header);
393 
394   // Section Header Table
395   const coff_section SectionTable[NumberOfSections] = {
396       {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
397        u32(0),
398        u32(0),
399        u32(VASize),
400        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
401        u32(0),
402        u32(0),
403        u16(0),
404        u16(0),
405        u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES
406                              : IMAGE_SCN_ALIGN_8BYTES) |
407            IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
408            IMAGE_SCN_MEM_WRITE)},
409       {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
410        u32(0),
411        u32(0),
412        u32(VASize),
413        u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
414            VASize),
415        u32(0),
416        u32(0),
417        u16(0),
418        u16(0),
419        u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES
420                              : IMAGE_SCN_ALIGN_8BYTES) |
421            IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
422            IMAGE_SCN_MEM_WRITE)},
423   };
424   append(Buffer, SectionTable);
425 
426   // .idata$5, ILT
427   append(Buffer, u32(0));
428   if (!is32bit(Machine))
429     append(Buffer, u32(0));
430 
431   // .idata$4, IAT
432   append(Buffer, u32(0));
433   if (!is32bit(Machine))
434     append(Buffer, u32(0));
435 
436   // Symbol Table
437   coff_symbol16 SymbolTable[NumberOfSymbols] = {
438       {{{0, 0, 0, 0, 0, 0, 0, 0}},
439        u32(0),
440        u16(1),
441        u16(0),
442        IMAGE_SYM_CLASS_EXTERNAL,
443        0},
444   };
445   SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
446   append(Buffer, SymbolTable);
447 
448   // String Table
449   writeStringTable(Buffer, {NullThunkSymbolName});
450 
451   StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
452   return {MemoryBufferRef{F, ImportName}};
453 }
454 
455 NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
456                                                   uint16_t Ordinal,
457                                                   ImportType ImportType,
458                                                   ImportNameType NameType) {
459   size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs
460   size_t Size = sizeof(coff_import_header) + ImpSize;
461   char *Buf = Alloc.Allocate<char>(Size);
462   memset(Buf, 0, Size);
463   char *P = Buf;
464 
465   // Write short import library.
466   auto *Imp = reinterpret_cast<coff_import_header *>(P);
467   P += sizeof(*Imp);
468   Imp->Sig2 = 0xFFFF;
469   Imp->Machine = Machine;
470   Imp->SizeOfData = ImpSize;
471   if (Ordinal > 0)
472     Imp->OrdinalHint = Ordinal;
473   Imp->TypeInfo = (NameType << 2) | ImportType;
474 
475   // Write symbol name and DLL name.
476   memcpy(P, Sym.data(), Sym.size());
477   P += Sym.size() + 1;
478   memcpy(P, ImportName.data(), ImportName.size());
479 
480   return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
481 }
482 
483 NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym,
484                                                    StringRef Weak, bool Imp) {
485   std::vector<uint8_t> Buffer;
486   const uint32_t NumberOfSections = 1;
487   const uint32_t NumberOfSymbols = 5;
488 
489   // COFF Header
490   coff_file_header Header{
491       u16(0),
492       u16(NumberOfSections),
493       u32(0),
494       u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))),
495       u32(NumberOfSymbols),
496       u16(0),
497       u16(0),
498   };
499   append(Buffer, Header);
500 
501   // Section Header Table
502   const coff_section SectionTable[NumberOfSections] = {
503       {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
504        u32(0),
505        u32(0),
506        u32(0),
507        u32(0),
508        u32(0),
509        u32(0),
510        u16(0),
511        u16(0),
512        u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}};
513   append(Buffer, SectionTable);
514 
515   // Symbol Table
516   coff_symbol16 SymbolTable[NumberOfSymbols] = {
517       {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
518        u32(0),
519        u16(0xFFFF),
520        u16(0),
521        IMAGE_SYM_CLASS_STATIC,
522        0},
523       {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
524        u32(0),
525        u16(0xFFFF),
526        u16(0),
527        IMAGE_SYM_CLASS_STATIC,
528        0},
529       {{{0, 0, 0, 0, 0, 0, 0, 0}},
530        u32(0),
531        u16(0),
532        u16(0),
533        IMAGE_SYM_CLASS_EXTERNAL,
534        0},
535       {{{0, 0, 0, 0, 0, 0, 0, 0}},
536        u32(0),
537        u16(0),
538        u16(0),
539        IMAGE_SYM_CLASS_WEAK_EXTERNAL,
540        1},
541       {{{2, 0, 0, 0, 3, 0, 0, 0}}, u32(0), u16(0), u16(0), uint8_t(0), 0},
542   };
543   SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t);
544 
545   //__imp_ String Table
546   StringRef Prefix = Imp ? "__imp_" : "";
547   SymbolTable[3].Name.Offset.Offset =
548       sizeof(uint32_t) + Sym.size() + Prefix.size() + 1;
549   append(Buffer, SymbolTable);
550   writeStringTable(Buffer, {(Prefix + Sym).str(),
551                             (Prefix + Weak).str()});
552 
553   // Copied here so we can still use writeStringTable
554   char *Buf = Alloc.Allocate<char>(Buffer.size());
555   memcpy(Buf, Buffer.data(), Buffer.size());
556   return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)};
557 }
558 
559 Error writeImportLibrary(StringRef ImportName, StringRef Path,
560                          ArrayRef<COFFShortExport> Exports,
561                          MachineTypes Machine, bool MakeWeakAliases) {
562 
563   std::vector<NewArchiveMember> Members;
564   ObjectFactory OF(llvm::sys::path::filename(ImportName), Machine);
565 
566   std::vector<uint8_t> ImportDescriptor;
567   Members.push_back(OF.createImportDescriptor(ImportDescriptor));
568 
569   std::vector<uint8_t> NullImportDescriptor;
570   Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor));
571 
572   std::vector<uint8_t> NullThunk;
573   Members.push_back(OF.createNullThunk(NullThunk));
574 
575   for (COFFShortExport E : Exports) {
576     if (E.Private)
577       continue;
578 
579     if (E.isWeak() && MakeWeakAliases) {
580       Members.push_back(OF.createWeakExternal(E.Name, E.ExtName, false));
581       Members.push_back(OF.createWeakExternal(E.Name, E.ExtName, true));
582       continue;
583     }
584 
585     ImportType ImportType = IMPORT_CODE;
586     if (E.Data)
587       ImportType = IMPORT_DATA;
588     if (E.Constant)
589       ImportType = IMPORT_CONST;
590 
591     StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName;
592     ImportNameType NameType = getNameType(SymbolName, E.Name, Machine);
593     Expected<std::string> Name = E.ExtName.empty()
594                                      ? SymbolName
595                                      : replace(SymbolName, E.Name, E.ExtName);
596 
597     if (!Name)
598       return Name.takeError();
599 
600     Members.push_back(
601         OF.createShortImport(*Name, E.Ordinal, ImportType, NameType));
602   }
603 
604   return writeArchive(Path, Members, /*WriteSymtab*/ true,
605                       object::Archive::K_GNU,
606                       /*Deterministic*/ true, /*Thin*/ false);
607 }
608 
609 } // namespace object
610 } // namespace llvm
611