1 //===- COFFImportFile.cpp - COFF short import file implementation ---------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines the writeImportLibrary function.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/Object/COFFImportFile.h"
14 #include "llvm/ADT/ArrayRef.h"
15 #include "llvm/ADT/Twine.h"
16 #include "llvm/Object/Archive.h"
17 #include "llvm/Object/ArchiveWriter.h"
18 #include "llvm/Object/COFF.h"
19 #include "llvm/Support/Allocator.h"
20 #include "llvm/Support/Endian.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/ErrorHandling.h"
23 #include "llvm/Support/Path.h"
29 using namespace llvm::COFF
;
30 using namespace llvm::object
;
36 static uint16_t getImgRelRelocation(MachineTypes Machine
) {
39 llvm_unreachable("unsupported machine");
40 case IMAGE_FILE_MACHINE_AMD64
:
41 return IMAGE_REL_AMD64_ADDR32NB
;
42 case IMAGE_FILE_MACHINE_ARMNT
:
43 return IMAGE_REL_ARM_ADDR32NB
;
44 case IMAGE_FILE_MACHINE_ARM64
:
45 case IMAGE_FILE_MACHINE_ARM64EC
:
46 case IMAGE_FILE_MACHINE_ARM64X
:
47 return IMAGE_REL_ARM64_ADDR32NB
;
48 case IMAGE_FILE_MACHINE_I386
:
49 return IMAGE_REL_I386_DIR32NB
;
53 template <class T
> static void append(std::vector
<uint8_t> &B
, const T
&Data
) {
55 B
.resize(S
+ sizeof(T
));
56 memcpy(&B
[S
], &Data
, sizeof(T
));
59 static void writeStringTable(std::vector
<uint8_t> &B
,
60 ArrayRef
<const std::string
> Strings
) {
61 // The COFF string table consists of a 4-byte value which is the size of the
62 // table, including the length field itself. This value is followed by the
63 // string content itself, which is an array of null-terminated C-style
64 // strings. The termination is important as they are referenced to by offset
65 // by the symbol entity in the file format.
67 size_t Pos
= B
.size();
68 size_t Offset
= B
.size();
70 // Skip over the length field, we will fill it in later as we will have
71 // computed the length while emitting the string content itself.
72 Pos
+= sizeof(uint32_t);
74 for (const auto &S
: Strings
) {
75 B
.resize(Pos
+ S
.length() + 1);
76 std::copy(S
.begin(), S
.end(), std::next(B
.begin(), Pos
));
77 B
[Pos
+ S
.length()] = 0;
78 Pos
+= S
.length() + 1;
81 // Backfill the length of the table now that it has been computed.
82 support::ulittle32_t
Length(B
.size() - Offset
);
83 support::endian::write32le(&B
[Offset
], Length
);
86 static ImportNameType
getNameType(StringRef Sym
, StringRef ExtName
,
87 MachineTypes Machine
, bool MinGW
) {
88 // A decorated stdcall function in MSVC is exported with the
89 // type IMPORT_NAME, and the exported function name includes the
90 // the leading underscore. In MinGW on the other hand, a decorated
91 // stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX).
92 // See the comment in isDecorated in COFFModuleDefinition.cpp for more
94 if (ExtName
.startswith("_") && ExtName
.contains('@') && !MinGW
)
97 return IMPORT_NAME_UNDECORATE
;
98 if (Machine
== IMAGE_FILE_MACHINE_I386
&& Sym
.startswith("_"))
99 return IMPORT_NAME_NOPREFIX
;
103 static Expected
<std::string
> replace(StringRef S
, StringRef From
,
105 size_t Pos
= S
.find(From
);
107 // From and To may be mangled, but substrings in S may not.
108 if (Pos
== StringRef::npos
&& From
.startswith("_") && To
.startswith("_")) {
109 From
= From
.substr(1);
114 if (Pos
== StringRef::npos
) {
115 return make_error
<StringError
>(
116 StringRef(Twine(S
+ ": replacing '" + From
+
117 "' with '" + To
+ "' failed").str()), object_error::parse_failed
);
120 return (Twine(S
.substr(0, Pos
)) + To
+ S
.substr(Pos
+ From
.size())).str();
123 static const std::string NullImportDescriptorSymbolName
=
124 "__NULL_IMPORT_DESCRIPTOR";
127 // This class constructs various small object files necessary to support linking
128 // symbols imported from a DLL. The contents are pretty strictly defined and
129 // nearly entirely static. The details of the structures files are defined in
130 // WINNT.h and the PE/COFF specification.
131 class ObjectFactory
{
132 using u16
= support::ulittle16_t
;
133 using u32
= support::ulittle32_t
;
134 MachineTypes Machine
;
135 BumpPtrAllocator Alloc
;
136 StringRef ImportName
;
138 std::string ImportDescriptorSymbolName
;
139 std::string NullThunkSymbolName
;
142 ObjectFactory(StringRef S
, MachineTypes M
)
143 : Machine(M
), ImportName(S
), Library(llvm::sys::path::stem(S
)),
144 ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library
).str()),
145 NullThunkSymbolName(("\x7f" + Library
+ "_NULL_THUNK_DATA").str()) {}
147 // Creates an Import Descriptor. This is a small object file which contains a
148 // reference to the terminators and contains the library name (entry) for the
149 // import name table. It will force the linker to construct the necessary
150 // structure to import symbols from the DLL.
151 NewArchiveMember
createImportDescriptor(std::vector
<uint8_t> &Buffer
);
153 // Creates a NULL import descriptor. This is a small object file whcih
154 // contains a NULL import descriptor. It is used to terminate the imports
155 // from a specific DLL.
156 NewArchiveMember
createNullImportDescriptor(std::vector
<uint8_t> &Buffer
);
158 // Create a NULL Thunk Entry. This is a small object file which contains a
159 // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It
160 // is used to terminate the IAT and ILT.
161 NewArchiveMember
createNullThunk(std::vector
<uint8_t> &Buffer
);
163 // Create a short import file which is described in PE/COFF spec 7. Import
165 NewArchiveMember
createShortImport(StringRef Sym
, uint16_t Ordinal
,
166 ImportType Type
, ImportNameType NameType
);
168 // Create a weak external file which is described in PE/COFF Aux Format 3.
169 NewArchiveMember
createWeakExternal(StringRef Sym
, StringRef Weak
, bool Imp
);
174 ObjectFactory::createImportDescriptor(std::vector
<uint8_t> &Buffer
) {
175 const uint32_t NumberOfSections
= 2;
176 const uint32_t NumberOfSymbols
= 7;
177 const uint32_t NumberOfRelocations
= 3;
180 coff_file_header Header
{
182 u16(NumberOfSections
),
184 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
186 sizeof(coff_import_directory_table_entry
) +
187 NumberOfRelocations
* sizeof(coff_relocation
) +
189 (ImportName
.size() + 1)),
190 u32(NumberOfSymbols
),
192 u16(is64Bit(Machine
) ? C_Invalid
: IMAGE_FILE_32BIT_MACHINE
),
194 append(Buffer
, Header
);
196 // Section Header Table
197 const coff_section SectionTable
[NumberOfSections
] = {
198 {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
201 u32(sizeof(coff_import_directory_table_entry
)),
202 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
)),
203 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
204 sizeof(coff_import_directory_table_entry
)),
206 u16(NumberOfRelocations
),
208 u32(IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
209 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
210 {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'},
213 u32(ImportName
.size() + 1),
214 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
215 sizeof(coff_import_directory_table_entry
) +
216 NumberOfRelocations
* sizeof(coff_relocation
)),
221 u32(IMAGE_SCN_ALIGN_2BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
222 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
224 append(Buffer
, SectionTable
);
227 const coff_import_directory_table_entry ImportDescriptor
{
228 u32(0), u32(0), u32(0), u32(0), u32(0),
230 append(Buffer
, ImportDescriptor
);
232 const coff_relocation RelocationTable
[NumberOfRelocations
] = {
233 {u32(offsetof(coff_import_directory_table_entry
, NameRVA
)), u32(2),
234 u16(getImgRelRelocation(Machine
))},
235 {u32(offsetof(coff_import_directory_table_entry
, ImportLookupTableRVA
)),
236 u32(3), u16(getImgRelRelocation(Machine
))},
237 {u32(offsetof(coff_import_directory_table_entry
, ImportAddressTableRVA
)),
238 u32(4), u16(getImgRelRelocation(Machine
))},
240 append(Buffer
, RelocationTable
);
243 auto S
= Buffer
.size();
244 Buffer
.resize(S
+ ImportName
.size() + 1);
245 memcpy(&Buffer
[S
], ImportName
.data(), ImportName
.size());
246 Buffer
[S
+ ImportName
.size()] = '\0';
249 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
250 {{{0, 0, 0, 0, 0, 0, 0, 0}},
254 IMAGE_SYM_CLASS_EXTERNAL
,
256 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
260 IMAGE_SYM_CLASS_SECTION
,
262 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
266 IMAGE_SYM_CLASS_STATIC
,
268 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
272 IMAGE_SYM_CLASS_SECTION
,
274 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
278 IMAGE_SYM_CLASS_SECTION
,
280 {{{0, 0, 0, 0, 0, 0, 0, 0}},
284 IMAGE_SYM_CLASS_EXTERNAL
,
286 {{{0, 0, 0, 0, 0, 0, 0, 0}},
290 IMAGE_SYM_CLASS_EXTERNAL
,
293 // TODO: Name.Offset.Offset here and in the all similar places below
294 // suggests a names refactoring. Maybe StringTableOffset.Value?
295 SymbolTable
[0].Name
.Offset
.Offset
=
297 SymbolTable
[5].Name
.Offset
.Offset
=
298 sizeof(uint32_t) + ImportDescriptorSymbolName
.length() + 1;
299 SymbolTable
[6].Name
.Offset
.Offset
=
300 sizeof(uint32_t) + ImportDescriptorSymbolName
.length() + 1 +
301 NullImportDescriptorSymbolName
.length() + 1;
302 append(Buffer
, SymbolTable
);
305 writeStringTable(Buffer
,
306 {ImportDescriptorSymbolName
, NullImportDescriptorSymbolName
,
307 NullThunkSymbolName
});
309 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
310 return {MemoryBufferRef(F
, ImportName
)};
314 ObjectFactory::createNullImportDescriptor(std::vector
<uint8_t> &Buffer
) {
315 const uint32_t NumberOfSections
= 1;
316 const uint32_t NumberOfSymbols
= 1;
319 coff_file_header Header
{
321 u16(NumberOfSections
),
323 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
325 sizeof(coff_import_directory_table_entry
)),
326 u32(NumberOfSymbols
),
328 u16(is64Bit(Machine
) ? C_Invalid
: IMAGE_FILE_32BIT_MACHINE
),
330 append(Buffer
, Header
);
332 // Section Header Table
333 const coff_section SectionTable
[NumberOfSections
] = {
334 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
337 u32(sizeof(coff_import_directory_table_entry
)),
338 u32(sizeof(coff_file_header
) +
339 (NumberOfSections
* sizeof(coff_section
))),
344 u32(IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
345 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
347 append(Buffer
, SectionTable
);
350 const coff_import_directory_table_entry ImportDescriptor
{
351 u32(0), u32(0), u32(0), u32(0), u32(0),
353 append(Buffer
, ImportDescriptor
);
356 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
357 {{{0, 0, 0, 0, 0, 0, 0, 0}},
361 IMAGE_SYM_CLASS_EXTERNAL
,
364 SymbolTable
[0].Name
.Offset
.Offset
= sizeof(uint32_t);
365 append(Buffer
, SymbolTable
);
368 writeStringTable(Buffer
, {NullImportDescriptorSymbolName
});
370 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
371 return {MemoryBufferRef(F
, ImportName
)};
374 NewArchiveMember
ObjectFactory::createNullThunk(std::vector
<uint8_t> &Buffer
) {
375 const uint32_t NumberOfSections
= 2;
376 const uint32_t NumberOfSymbols
= 1;
377 uint32_t VASize
= is64Bit(Machine
) ? 8 : 4;
380 coff_file_header Header
{
382 u16(NumberOfSections
),
384 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
389 u32(NumberOfSymbols
),
391 u16(is64Bit(Machine
) ? C_Invalid
: IMAGE_FILE_32BIT_MACHINE
),
393 append(Buffer
, Header
);
395 // Section Header Table
396 const coff_section SectionTable
[NumberOfSections
] = {
397 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
401 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
)),
406 u32((is64Bit(Machine
) ? IMAGE_SCN_ALIGN_8BYTES
407 : IMAGE_SCN_ALIGN_4BYTES
) |
408 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
|
409 IMAGE_SCN_MEM_WRITE
)},
410 {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
414 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
420 u32((is64Bit(Machine
) ? IMAGE_SCN_ALIGN_8BYTES
421 : IMAGE_SCN_ALIGN_4BYTES
) |
422 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
|
423 IMAGE_SCN_MEM_WRITE
)},
425 append(Buffer
, SectionTable
);
428 append(Buffer
, u32(0));
429 if (is64Bit(Machine
))
430 append(Buffer
, u32(0));
433 append(Buffer
, u32(0));
434 if (is64Bit(Machine
))
435 append(Buffer
, u32(0));
438 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
439 {{{0, 0, 0, 0, 0, 0, 0, 0}},
443 IMAGE_SYM_CLASS_EXTERNAL
,
446 SymbolTable
[0].Name
.Offset
.Offset
= sizeof(uint32_t);
447 append(Buffer
, SymbolTable
);
450 writeStringTable(Buffer
, {NullThunkSymbolName
});
452 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
453 return {MemoryBufferRef
{F
, ImportName
}};
456 NewArchiveMember
ObjectFactory::createShortImport(StringRef Sym
,
458 ImportType ImportType
,
459 ImportNameType NameType
) {
460 size_t ImpSize
= ImportName
.size() + Sym
.size() + 2; // +2 for NULs
461 size_t Size
= sizeof(coff_import_header
) + ImpSize
;
462 char *Buf
= Alloc
.Allocate
<char>(Size
);
463 memset(Buf
, 0, Size
);
466 // Write short import library.
467 auto *Imp
= reinterpret_cast<coff_import_header
*>(P
);
470 Imp
->Machine
= Machine
;
471 Imp
->SizeOfData
= ImpSize
;
473 Imp
->OrdinalHint
= Ordinal
;
474 Imp
->TypeInfo
= (NameType
<< 2) | ImportType
;
476 // Write symbol name and DLL name.
477 memcpy(P
, Sym
.data(), Sym
.size());
479 memcpy(P
, ImportName
.data(), ImportName
.size());
481 return {MemoryBufferRef(StringRef(Buf
, Size
), ImportName
)};
484 NewArchiveMember
ObjectFactory::createWeakExternal(StringRef Sym
,
485 StringRef Weak
, bool Imp
) {
486 std::vector
<uint8_t> Buffer
;
487 const uint32_t NumberOfSections
= 1;
488 const uint32_t NumberOfSymbols
= 5;
491 coff_file_header Header
{
493 u16(NumberOfSections
),
495 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
))),
496 u32(NumberOfSymbols
),
500 append(Buffer
, Header
);
502 // Section Header Table
503 const coff_section SectionTable
[NumberOfSections
] = {
504 {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
513 u32(IMAGE_SCN_LNK_INFO
| IMAGE_SCN_LNK_REMOVE
)}};
514 append(Buffer
, SectionTable
);
517 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
518 {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
522 IMAGE_SYM_CLASS_STATIC
,
524 {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
528 IMAGE_SYM_CLASS_STATIC
,
530 {{{0, 0, 0, 0, 0, 0, 0, 0}},
534 IMAGE_SYM_CLASS_EXTERNAL
,
536 {{{0, 0, 0, 0, 0, 0, 0, 0}},
540 IMAGE_SYM_CLASS_WEAK_EXTERNAL
,
542 {{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS
, 0, 0, 0}},
546 IMAGE_SYM_CLASS_NULL
,
549 SymbolTable
[2].Name
.Offset
.Offset
= sizeof(uint32_t);
551 //__imp_ String Table
552 StringRef Prefix
= Imp
? "__imp_" : "";
553 SymbolTable
[3].Name
.Offset
.Offset
=
554 sizeof(uint32_t) + Sym
.size() + Prefix
.size() + 1;
555 append(Buffer
, SymbolTable
);
556 writeStringTable(Buffer
, {(Prefix
+ Sym
).str(),
557 (Prefix
+ Weak
).str()});
559 // Copied here so we can still use writeStringTable
560 char *Buf
= Alloc
.Allocate
<char>(Buffer
.size());
561 memcpy(Buf
, Buffer
.data(), Buffer
.size());
562 return {MemoryBufferRef(StringRef(Buf
, Buffer
.size()), ImportName
)};
565 Error
writeImportLibrary(StringRef ImportName
, StringRef Path
,
566 ArrayRef
<COFFShortExport
> Exports
,
567 MachineTypes Machine
, bool MinGW
) {
569 std::vector
<NewArchiveMember
> Members
;
570 ObjectFactory
OF(llvm::sys::path::filename(ImportName
), Machine
);
572 std::vector
<uint8_t> ImportDescriptor
;
573 Members
.push_back(OF
.createImportDescriptor(ImportDescriptor
));
575 std::vector
<uint8_t> NullImportDescriptor
;
576 Members
.push_back(OF
.createNullImportDescriptor(NullImportDescriptor
));
578 std::vector
<uint8_t> NullThunk
;
579 Members
.push_back(OF
.createNullThunk(NullThunk
));
581 for (const COFFShortExport
&E
: Exports
) {
585 ImportType ImportType
= IMPORT_CODE
;
587 ImportType
= IMPORT_DATA
;
589 ImportType
= IMPORT_CONST
;
591 StringRef SymbolName
= E
.SymbolName
.empty() ? E
.Name
: E
.SymbolName
;
592 ImportNameType NameType
= E
.Noname
594 : getNameType(SymbolName
, E
.Name
,
596 Expected
<std::string
> Name
= E
.ExtName
.empty()
597 ? std::string(SymbolName
)
598 : replace(SymbolName
, E
.Name
, E
.ExtName
);
601 return Name
.takeError();
603 if (!E
.AliasTarget
.empty() && *Name
!= E
.AliasTarget
) {
604 Members
.push_back(OF
.createWeakExternal(E
.AliasTarget
, *Name
, false));
605 Members
.push_back(OF
.createWeakExternal(E
.AliasTarget
, *Name
, true));
610 OF
.createShortImport(*Name
, E
.Ordinal
, ImportType
, NameType
));
613 return writeArchive(Path
, Members
, SymtabWritingMode::NormalSymtab
,
614 MinGW
? object::Archive::K_GNU
: object::Archive::K_COFF
,
615 /*Deterministic*/ true, /*Thin*/ false,
616 /*OldArchiveBuf*/ nullptr, isArm64EC(Machine
));
619 } // namespace object