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