1 //===- COFFImportFile.cpp - COFF short import file implementation ---------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines the writeImportLibrary function.
12 //===----------------------------------------------------------------------===//
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"
28 using namespace llvm::COFF
;
29 using namespace llvm::object
;
35 static bool is32bit(MachineTypes Machine
) {
38 llvm_unreachable("unsupported machine");
39 case IMAGE_FILE_MACHINE_ARM64
:
40 case IMAGE_FILE_MACHINE_AMD64
:
42 case IMAGE_FILE_MACHINE_ARMNT
:
43 case IMAGE_FILE_MACHINE_I386
:
48 static uint16_t getImgRelRelocation(MachineTypes Machine
) {
51 llvm_unreachable("unsupported machine");
52 case IMAGE_FILE_MACHINE_AMD64
:
53 return IMAGE_REL_AMD64_ADDR32NB
;
54 case IMAGE_FILE_MACHINE_ARMNT
:
55 return IMAGE_REL_ARM_ADDR32NB
;
56 case IMAGE_FILE_MACHINE_ARM64
:
57 return IMAGE_REL_ARM64_ADDR32NB
;
58 case IMAGE_FILE_MACHINE_I386
:
59 return IMAGE_REL_I386_DIR32NB
;
63 template <class T
> static void append(std::vector
<uint8_t> &B
, const T
&Data
) {
65 B
.resize(S
+ sizeof(T
));
66 memcpy(&B
[S
], &Data
, sizeof(T
));
69 static void writeStringTable(std::vector
<uint8_t> &B
,
70 ArrayRef
<const std::string
> Strings
) {
71 // The COFF string table consists of a 4-byte value which is the size of the
72 // table, including the length field itself. This value is followed by the
73 // string content itself, which is an array of null-terminated C-style
74 // strings. The termination is important as they are referenced to by offset
75 // by the symbol entity in the file format.
77 size_t Pos
= B
.size();
78 size_t Offset
= B
.size();
80 // Skip over the length field, we will fill it in later as we will have
81 // computed the length while emitting the string content itself.
82 Pos
+= sizeof(uint32_t);
84 for (const auto &S
: Strings
) {
85 B
.resize(Pos
+ S
.length() + 1);
86 strcpy(reinterpret_cast<char *>(&B
[Pos
]), S
.c_str());
87 Pos
+= S
.length() + 1;
90 // Backfill the length of the table now that it has been computed.
91 support::ulittle32_t
Length(B
.size() - Offset
);
92 support::endian::write32le(&B
[Offset
], Length
);
95 static ImportNameType
getNameType(StringRef Sym
, StringRef ExtName
,
96 MachineTypes Machine
) {
98 return IMPORT_NAME_UNDECORATE
;
99 if (Machine
== IMAGE_FILE_MACHINE_I386
&& Sym
.startswith("_"))
100 return IMPORT_NAME_NOPREFIX
;
104 static Expected
<std::string
> replace(StringRef S
, StringRef From
,
106 size_t Pos
= S
.find(From
);
108 // From and To may be mangled, but substrings in S may not.
109 if (Pos
== StringRef::npos
&& From
.startswith("_") && To
.startswith("_")) {
110 From
= From
.substr(1);
115 if (Pos
== StringRef::npos
) {
116 return make_error
<StringError
>(
117 StringRef(Twine(S
+ ": replacing '" + From
+
118 "' with '" + To
+ "' failed").str()), object_error::parse_failed
);
121 return (Twine(S
.substr(0, Pos
)) + To
+ S
.substr(Pos
+ From
.size())).str();
124 static const std::string NullImportDescriptorSymbolName
=
125 "__NULL_IMPORT_DESCRIPTOR";
128 // This class constructs various small object files necessary to support linking
129 // symbols imported from a DLL. The contents are pretty strictly defined and
130 // nearly entirely static. The details of the structures files are defined in
131 // WINNT.h and the PE/COFF specification.
132 class ObjectFactory
{
133 using u16
= support::ulittle16_t
;
134 using u32
= support::ulittle32_t
;
135 MachineTypes Machine
;
136 BumpPtrAllocator Alloc
;
137 StringRef ImportName
;
139 std::string ImportDescriptorSymbolName
;
140 std::string NullThunkSymbolName
;
143 ObjectFactory(StringRef S
, MachineTypes M
)
144 : Machine(M
), ImportName(S
), Library(S
.drop_back(4)),
145 ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library
).str()),
146 NullThunkSymbolName(("\x7f" + Library
+ "_NULL_THUNK_DATA").str()) {}
148 // Creates an Import Descriptor. This is a small object file which contains a
149 // reference to the terminators and contains the library name (entry) for the
150 // import name table. It will force the linker to construct the necessary
151 // structure to import symbols from the DLL.
152 NewArchiveMember
createImportDescriptor(std::vector
<uint8_t> &Buffer
);
154 // Creates a NULL import descriptor. This is a small object file whcih
155 // contains a NULL import descriptor. It is used to terminate the imports
156 // from a specific DLL.
157 NewArchiveMember
createNullImportDescriptor(std::vector
<uint8_t> &Buffer
);
159 // Create a NULL Thunk Entry. This is a small object file which contains a
160 // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It
161 // is used to terminate the IAT and ILT.
162 NewArchiveMember
createNullThunk(std::vector
<uint8_t> &Buffer
);
164 // Create a short import file which is described in PE/COFF spec 7. Import
166 NewArchiveMember
createShortImport(StringRef Sym
, uint16_t Ordinal
,
167 ImportType Type
, ImportNameType NameType
);
169 // Create a weak external file which is described in PE/COFF Aux Format 3.
170 NewArchiveMember
createWeakExternal(StringRef Sym
, StringRef Weak
, bool Imp
);
175 ObjectFactory::createImportDescriptor(std::vector
<uint8_t> &Buffer
) {
176 const uint32_t NumberOfSections
= 2;
177 const uint32_t NumberOfSymbols
= 7;
178 const uint32_t NumberOfRelocations
= 3;
181 coff_file_header Header
{
183 u16(NumberOfSections
),
185 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
187 sizeof(coff_import_directory_table_entry
) +
188 NumberOfRelocations
* sizeof(coff_relocation
) +
190 (ImportName
.size() + 1)),
191 u32(NumberOfSymbols
),
193 u16(is32bit(Machine
) ? IMAGE_FILE_32BIT_MACHINE
: 0),
195 append(Buffer
, Header
);
197 // Section Header Table
198 const coff_section SectionTable
[NumberOfSections
] = {
199 {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
202 u32(sizeof(coff_import_directory_table_entry
)),
203 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
)),
204 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
205 sizeof(coff_import_directory_table_entry
)),
207 u16(NumberOfRelocations
),
209 u32(IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
210 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
211 {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'},
214 u32(ImportName
.size() + 1),
215 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
216 sizeof(coff_import_directory_table_entry
) +
217 NumberOfRelocations
* sizeof(coff_relocation
)),
222 u32(IMAGE_SCN_ALIGN_2BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
223 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
225 append(Buffer
, SectionTable
);
228 const coff_import_directory_table_entry ImportDescriptor
{
229 u32(0), u32(0), u32(0), u32(0), u32(0),
231 append(Buffer
, ImportDescriptor
);
233 const coff_relocation RelocationTable
[NumberOfRelocations
] = {
234 {u32(offsetof(coff_import_directory_table_entry
, NameRVA
)), u32(2),
235 u16(getImgRelRelocation(Machine
))},
236 {u32(offsetof(coff_import_directory_table_entry
, ImportLookupTableRVA
)),
237 u32(3), u16(getImgRelRelocation(Machine
))},
238 {u32(offsetof(coff_import_directory_table_entry
, ImportAddressTableRVA
)),
239 u32(4), u16(getImgRelRelocation(Machine
))},
241 append(Buffer
, RelocationTable
);
244 auto S
= Buffer
.size();
245 Buffer
.resize(S
+ ImportName
.size() + 1);
246 memcpy(&Buffer
[S
], ImportName
.data(), ImportName
.size());
247 Buffer
[S
+ ImportName
.size()] = '\0';
250 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
251 {{{0, 0, 0, 0, 0, 0, 0, 0}},
255 IMAGE_SYM_CLASS_EXTERNAL
,
257 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
261 IMAGE_SYM_CLASS_SECTION
,
263 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
267 IMAGE_SYM_CLASS_STATIC
,
269 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
273 IMAGE_SYM_CLASS_SECTION
,
275 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
279 IMAGE_SYM_CLASS_SECTION
,
281 {{{0, 0, 0, 0, 0, 0, 0, 0}},
285 IMAGE_SYM_CLASS_EXTERNAL
,
287 {{{0, 0, 0, 0, 0, 0, 0, 0}},
291 IMAGE_SYM_CLASS_EXTERNAL
,
294 // TODO: Name.Offset.Offset here and in the all similar places below
295 // suggests a names refactoring. Maybe StringTableOffset.Value?
296 SymbolTable
[0].Name
.Offset
.Offset
=
298 SymbolTable
[5].Name
.Offset
.Offset
=
299 sizeof(uint32_t) + ImportDescriptorSymbolName
.length() + 1;
300 SymbolTable
[6].Name
.Offset
.Offset
=
301 sizeof(uint32_t) + ImportDescriptorSymbolName
.length() + 1 +
302 NullImportDescriptorSymbolName
.length() + 1;
303 append(Buffer
, SymbolTable
);
306 writeStringTable(Buffer
,
307 {ImportDescriptorSymbolName
, NullImportDescriptorSymbolName
,
308 NullThunkSymbolName
});
310 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
311 return {MemoryBufferRef(F
, ImportName
)};
315 ObjectFactory::createNullImportDescriptor(std::vector
<uint8_t> &Buffer
) {
316 const uint32_t NumberOfSections
= 1;
317 const uint32_t NumberOfSymbols
= 1;
320 coff_file_header Header
{
322 u16(NumberOfSections
),
324 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
326 sizeof(coff_import_directory_table_entry
)),
327 u32(NumberOfSymbols
),
329 u16(is32bit(Machine
) ? IMAGE_FILE_32BIT_MACHINE
: 0),
331 append(Buffer
, Header
);
333 // Section Header Table
334 const coff_section SectionTable
[NumberOfSections
] = {
335 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
338 u32(sizeof(coff_import_directory_table_entry
)),
339 u32(sizeof(coff_file_header
) +
340 (NumberOfSections
* sizeof(coff_section
))),
345 u32(IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
346 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
348 append(Buffer
, SectionTable
);
351 const coff_import_directory_table_entry ImportDescriptor
{
352 u32(0), u32(0), u32(0), u32(0), u32(0),
354 append(Buffer
, ImportDescriptor
);
357 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
358 {{{0, 0, 0, 0, 0, 0, 0, 0}},
362 IMAGE_SYM_CLASS_EXTERNAL
,
365 SymbolTable
[0].Name
.Offset
.Offset
= sizeof(uint32_t);
366 append(Buffer
, SymbolTable
);
369 writeStringTable(Buffer
, {NullImportDescriptorSymbolName
});
371 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
372 return {MemoryBufferRef(F
, ImportName
)};
375 NewArchiveMember
ObjectFactory::createNullThunk(std::vector
<uint8_t> &Buffer
) {
376 const uint32_t NumberOfSections
= 2;
377 const uint32_t NumberOfSymbols
= 1;
378 uint32_t VASize
= is32bit(Machine
) ? 4 : 8;
381 coff_file_header Header
{
383 u16(NumberOfSections
),
385 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
390 u32(NumberOfSymbols
),
392 u16(is32bit(Machine
) ? IMAGE_FILE_32BIT_MACHINE
: 0),
394 append(Buffer
, Header
);
396 // Section Header Table
397 const coff_section SectionTable
[NumberOfSections
] = {
398 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
402 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
)),
407 u32((is32bit(Machine
) ? IMAGE_SCN_ALIGN_4BYTES
408 : IMAGE_SCN_ALIGN_8BYTES
) |
409 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
|
410 IMAGE_SCN_MEM_WRITE
)},
411 {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
415 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
421 u32((is32bit(Machine
) ? IMAGE_SCN_ALIGN_4BYTES
422 : IMAGE_SCN_ALIGN_8BYTES
) |
423 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
|
424 IMAGE_SCN_MEM_WRITE
)},
426 append(Buffer
, SectionTable
);
429 append(Buffer
, u32(0));
430 if (!is32bit(Machine
))
431 append(Buffer
, u32(0));
434 append(Buffer
, u32(0));
435 if (!is32bit(Machine
))
436 append(Buffer
, u32(0));
439 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
440 {{{0, 0, 0, 0, 0, 0, 0, 0}},
444 IMAGE_SYM_CLASS_EXTERNAL
,
447 SymbolTable
[0].Name
.Offset
.Offset
= sizeof(uint32_t);
448 append(Buffer
, SymbolTable
);
451 writeStringTable(Buffer
, {NullThunkSymbolName
});
453 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
454 return {MemoryBufferRef
{F
, ImportName
}};
457 NewArchiveMember
ObjectFactory::createShortImport(StringRef Sym
,
459 ImportType ImportType
,
460 ImportNameType NameType
) {
461 size_t ImpSize
= ImportName
.size() + Sym
.size() + 2; // +2 for NULs
462 size_t Size
= sizeof(coff_import_header
) + ImpSize
;
463 char *Buf
= Alloc
.Allocate
<char>(Size
);
464 memset(Buf
, 0, Size
);
467 // Write short import library.
468 auto *Imp
= reinterpret_cast<coff_import_header
*>(P
);
471 Imp
->Machine
= Machine
;
472 Imp
->SizeOfData
= ImpSize
;
474 Imp
->OrdinalHint
= Ordinal
;
475 Imp
->TypeInfo
= (NameType
<< 2) | ImportType
;
477 // Write symbol name and DLL name.
478 memcpy(P
, Sym
.data(), Sym
.size());
480 memcpy(P
, ImportName
.data(), ImportName
.size());
482 return {MemoryBufferRef(StringRef(Buf
, Size
), ImportName
)};
485 NewArchiveMember
ObjectFactory::createWeakExternal(StringRef Sym
,
486 StringRef Weak
, bool Imp
) {
487 std::vector
<uint8_t> Buffer
;
488 const uint32_t NumberOfSections
= 1;
489 const uint32_t NumberOfSymbols
= 5;
492 coff_file_header Header
{
494 u16(NumberOfSections
),
496 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
))),
497 u32(NumberOfSymbols
),
501 append(Buffer
, Header
);
503 // Section Header Table
504 const coff_section SectionTable
[NumberOfSections
] = {
505 {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
514 u32(IMAGE_SCN_LNK_INFO
| IMAGE_SCN_LNK_REMOVE
)}};
515 append(Buffer
, SectionTable
);
518 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
519 {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
523 IMAGE_SYM_CLASS_STATIC
,
525 {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
529 IMAGE_SYM_CLASS_STATIC
,
531 {{{0, 0, 0, 0, 0, 0, 0, 0}},
535 IMAGE_SYM_CLASS_EXTERNAL
,
537 {{{0, 0, 0, 0, 0, 0, 0, 0}},
541 IMAGE_SYM_CLASS_WEAK_EXTERNAL
,
543 {{{2, 0, 0, 0, 3, 0, 0, 0}}, u32(0), u16(0), u16(0), uint8_t(0), 0},
545 SymbolTable
[2].Name
.Offset
.Offset
= sizeof(uint32_t);
547 //__imp_ String Table
548 StringRef Prefix
= Imp
? "__imp_" : "";
549 SymbolTable
[3].Name
.Offset
.Offset
=
550 sizeof(uint32_t) + Sym
.size() + Prefix
.size() + 1;
551 append(Buffer
, SymbolTable
);
552 writeStringTable(Buffer
, {(Prefix
+ Sym
).str(),
553 (Prefix
+ Weak
).str()});
555 // Copied here so we can still use writeStringTable
556 char *Buf
= Alloc
.Allocate
<char>(Buffer
.size());
557 memcpy(Buf
, Buffer
.data(), Buffer
.size());
558 return {MemoryBufferRef(StringRef(Buf
, Buffer
.size()), ImportName
)};
561 Error
writeImportLibrary(StringRef ImportName
, StringRef Path
,
562 ArrayRef
<COFFShortExport
> Exports
,
563 MachineTypes Machine
, bool MakeWeakAliases
) {
565 std::vector
<NewArchiveMember
> Members
;
566 ObjectFactory
OF(llvm::sys::path::filename(ImportName
), Machine
);
568 std::vector
<uint8_t> ImportDescriptor
;
569 Members
.push_back(OF
.createImportDescriptor(ImportDescriptor
));
571 std::vector
<uint8_t> NullImportDescriptor
;
572 Members
.push_back(OF
.createNullImportDescriptor(NullImportDescriptor
));
574 std::vector
<uint8_t> NullThunk
;
575 Members
.push_back(OF
.createNullThunk(NullThunk
));
577 for (COFFShortExport E
: Exports
) {
581 if (E
.isWeak() && MakeWeakAliases
) {
582 Members
.push_back(OF
.createWeakExternal(E
.Name
, E
.ExtName
, false));
583 Members
.push_back(OF
.createWeakExternal(E
.Name
, E
.ExtName
, true));
587 ImportType ImportType
= IMPORT_CODE
;
589 ImportType
= IMPORT_DATA
;
591 ImportType
= IMPORT_CONST
;
593 StringRef SymbolName
= E
.SymbolName
.empty() ? E
.Name
: E
.SymbolName
;
594 ImportNameType NameType
= getNameType(SymbolName
, E
.Name
, Machine
);
595 Expected
<std::string
> Name
= E
.ExtName
.empty()
597 : replace(SymbolName
, E
.Name
, E
.ExtName
);
600 return Name
.takeError();
603 OF
.createShortImport(*Name
, E
.Ordinal
, ImportType
, NameType
));
606 return writeArchive(Path
, Members
, /*WriteSymtab*/ true,
607 object::Archive::K_GNU
,
608 /*Deterministic*/ true, /*Thin*/ false);
611 } // namespace object