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