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/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"
25 using namespace llvm::COFF
;
26 using namespace llvm::object
;
32 static bool is32bit(MachineTypes Machine
) {
35 llvm_unreachable("unsupported machine");
36 case IMAGE_FILE_MACHINE_ARM64
:
37 case IMAGE_FILE_MACHINE_AMD64
:
39 case IMAGE_FILE_MACHINE_ARMNT
:
40 case IMAGE_FILE_MACHINE_I386
:
45 static uint16_t getImgRelRelocation(MachineTypes Machine
) {
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
;
60 template <class T
> static void append(std::vector
<uint8_t> &B
, const T
&Data
) {
62 B
.resize(S
+ sizeof(T
));
63 memcpy(&B
[S
], &Data
, sizeof(T
));
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.
74 size_t Pos
= B
.size();
75 size_t Offset
= B
.size();
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);
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;
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
);
92 static 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
100 if (ExtName
.startswith("_") && ExtName
.contains('@') && !MinGW
)
103 return IMPORT_NAME_UNDECORATE
;
104 if (Machine
== IMAGE_FILE_MACHINE_I386
&& Sym
.startswith("_"))
105 return IMPORT_NAME_NOPREFIX
;
109 static Expected
<std::string
> replace(StringRef S
, StringRef From
,
111 size_t Pos
= S
.find(From
);
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);
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
);
126 return (Twine(S
.substr(0, Pos
)) + To
+ S
.substr(Pos
+ From
.size())).str();
129 static const std::string NullImportDescriptorSymbolName
=
130 "__NULL_IMPORT_DESCRIPTOR";
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.
137 class ObjectFactory
{
138 using u16
= support::ulittle16_t
;
139 using u32
= support::ulittle32_t
;
140 MachineTypes Machine
;
141 BumpPtrAllocator Alloc
;
142 StringRef ImportName
;
144 std::string ImportDescriptorSymbolName
;
145 std::string NullThunkSymbolName
;
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()) {}
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
);
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
);
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
);
169 // Create a short import file which is described in PE/COFF spec 7. Import
171 NewArchiveMember
createShortImport(StringRef Sym
, uint16_t Ordinal
,
172 ImportType Type
, ImportNameType NameType
);
174 // Create a weak external file which is described in PE/COFF Aux Format 3.
175 NewArchiveMember
createWeakExternal(StringRef Sym
, StringRef Weak
, bool Imp
);
180 ObjectFactory::createImportDescriptor(std::vector
<uint8_t> &Buffer
) {
181 const uint32_t NumberOfSections
= 2;
182 const uint32_t NumberOfSymbols
= 7;
183 const uint32_t NumberOfRelocations
= 3;
186 coff_file_header Header
{
188 u16(NumberOfSections
),
190 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
192 sizeof(coff_import_directory_table_entry
) +
193 NumberOfRelocations
* sizeof(coff_relocation
) +
195 (ImportName
.size() + 1)),
196 u32(NumberOfSymbols
),
198 u16(is32bit(Machine
) ? IMAGE_FILE_32BIT_MACHINE
: C_Invalid
),
200 append(Buffer
, Header
);
202 // Section Header Table
203 const coff_section SectionTable
[NumberOfSections
] = {
204 {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
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
)),
212 u16(NumberOfRelocations
),
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'},
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
)),
227 u32(IMAGE_SCN_ALIGN_2BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
228 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
230 append(Buffer
, SectionTable
);
233 const coff_import_directory_table_entry ImportDescriptor
{
234 u32(0), u32(0), u32(0), u32(0), u32(0),
236 append(Buffer
, ImportDescriptor
);
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
))},
246 append(Buffer
, RelocationTable
);
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';
255 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
256 {{{0, 0, 0, 0, 0, 0, 0, 0}},
260 IMAGE_SYM_CLASS_EXTERNAL
,
262 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
266 IMAGE_SYM_CLASS_SECTION
,
268 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
272 IMAGE_SYM_CLASS_STATIC
,
274 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
278 IMAGE_SYM_CLASS_SECTION
,
280 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
284 IMAGE_SYM_CLASS_SECTION
,
286 {{{0, 0, 0, 0, 0, 0, 0, 0}},
290 IMAGE_SYM_CLASS_EXTERNAL
,
292 {{{0, 0, 0, 0, 0, 0, 0, 0}},
296 IMAGE_SYM_CLASS_EXTERNAL
,
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
=
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
);
311 writeStringTable(Buffer
,
312 {ImportDescriptorSymbolName
, NullImportDescriptorSymbolName
,
313 NullThunkSymbolName
});
315 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
316 return {MemoryBufferRef(F
, ImportName
)};
320 ObjectFactory::createNullImportDescriptor(std::vector
<uint8_t> &Buffer
) {
321 const uint32_t NumberOfSections
= 1;
322 const uint32_t NumberOfSymbols
= 1;
325 coff_file_header Header
{
327 u16(NumberOfSections
),
329 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
331 sizeof(coff_import_directory_table_entry
)),
332 u32(NumberOfSymbols
),
334 u16(is32bit(Machine
) ? IMAGE_FILE_32BIT_MACHINE
: C_Invalid
),
336 append(Buffer
, Header
);
338 // Section Header Table
339 const coff_section SectionTable
[NumberOfSections
] = {
340 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
343 u32(sizeof(coff_import_directory_table_entry
)),
344 u32(sizeof(coff_file_header
) +
345 (NumberOfSections
* sizeof(coff_section
))),
350 u32(IMAGE_SCN_ALIGN_4BYTES
| IMAGE_SCN_CNT_INITIALIZED_DATA
|
351 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
)},
353 append(Buffer
, SectionTable
);
356 const coff_import_directory_table_entry ImportDescriptor
{
357 u32(0), u32(0), u32(0), u32(0), u32(0),
359 append(Buffer
, ImportDescriptor
);
362 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
363 {{{0, 0, 0, 0, 0, 0, 0, 0}},
367 IMAGE_SYM_CLASS_EXTERNAL
,
370 SymbolTable
[0].Name
.Offset
.Offset
= sizeof(uint32_t);
371 append(Buffer
, SymbolTable
);
374 writeStringTable(Buffer
, {NullImportDescriptorSymbolName
});
376 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
377 return {MemoryBufferRef(F
, ImportName
)};
380 NewArchiveMember
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;
386 coff_file_header Header
{
388 u16(NumberOfSections
),
390 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
)) +
395 u32(NumberOfSymbols
),
397 u16(is32bit(Machine
) ? IMAGE_FILE_32BIT_MACHINE
: C_Invalid
),
399 append(Buffer
, Header
);
401 // Section Header Table
402 const coff_section SectionTable
[NumberOfSections
] = {
403 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
407 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
)),
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'},
420 u32(sizeof(coff_file_header
) + NumberOfSections
* sizeof(coff_section
) +
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
)},
431 append(Buffer
, SectionTable
);
434 append(Buffer
, u32(0));
435 if (!is32bit(Machine
))
436 append(Buffer
, u32(0));
439 append(Buffer
, u32(0));
440 if (!is32bit(Machine
))
441 append(Buffer
, u32(0));
444 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
445 {{{0, 0, 0, 0, 0, 0, 0, 0}},
449 IMAGE_SYM_CLASS_EXTERNAL
,
452 SymbolTable
[0].Name
.Offset
.Offset
= sizeof(uint32_t);
453 append(Buffer
, SymbolTable
);
456 writeStringTable(Buffer
, {NullThunkSymbolName
});
458 StringRef F
{reinterpret_cast<const char *>(Buffer
.data()), Buffer
.size()};
459 return {MemoryBufferRef
{F
, ImportName
}};
462 NewArchiveMember
ObjectFactory::createShortImport(StringRef Sym
,
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
);
472 // Write short import library.
473 auto *Imp
= reinterpret_cast<coff_import_header
*>(P
);
476 Imp
->Machine
= Machine
;
477 Imp
->SizeOfData
= ImpSize
;
479 Imp
->OrdinalHint
= Ordinal
;
480 Imp
->TypeInfo
= (NameType
<< 2) | ImportType
;
482 // Write symbol name and DLL name.
483 memcpy(P
, Sym
.data(), Sym
.size());
485 memcpy(P
, ImportName
.data(), ImportName
.size());
487 return {MemoryBufferRef(StringRef(Buf
, Size
), ImportName
)};
490 NewArchiveMember
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;
497 coff_file_header Header
{
499 u16(NumberOfSections
),
501 u32(sizeof(Header
) + (NumberOfSections
* sizeof(coff_section
))),
502 u32(NumberOfSymbols
),
506 append(Buffer
, Header
);
508 // Section Header Table
509 const coff_section SectionTable
[NumberOfSections
] = {
510 {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
519 u32(IMAGE_SCN_LNK_INFO
| IMAGE_SCN_LNK_REMOVE
)}};
520 append(Buffer
, SectionTable
);
523 coff_symbol16 SymbolTable
[NumberOfSymbols
] = {
524 {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
528 IMAGE_SYM_CLASS_STATIC
,
530 {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
534 IMAGE_SYM_CLASS_STATIC
,
536 {{{0, 0, 0, 0, 0, 0, 0, 0}},
540 IMAGE_SYM_CLASS_EXTERNAL
,
542 {{{0, 0, 0, 0, 0, 0, 0, 0}},
546 IMAGE_SYM_CLASS_WEAK_EXTERNAL
,
548 {{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS
, 0, 0, 0}},
552 IMAGE_SYM_CLASS_NULL
,
555 SymbolTable
[2].Name
.Offset
.Offset
= sizeof(uint32_t);
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()});
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
)};
571 Error
writeImportLibrary(StringRef ImportName
, StringRef Path
,
572 ArrayRef
<COFFShortExport
> Exports
,
573 MachineTypes Machine
, bool MinGW
) {
575 std::vector
<NewArchiveMember
> Members
;
576 ObjectFactory
OF(llvm::sys::path::filename(ImportName
), Machine
);
578 std::vector
<uint8_t> ImportDescriptor
;
579 Members
.push_back(OF
.createImportDescriptor(ImportDescriptor
));
581 std::vector
<uint8_t> NullImportDescriptor
;
582 Members
.push_back(OF
.createNullImportDescriptor(NullImportDescriptor
));
584 std::vector
<uint8_t> NullThunk
;
585 Members
.push_back(OF
.createNullThunk(NullThunk
));
587 for (COFFShortExport E
: Exports
) {
591 ImportType ImportType
= IMPORT_CODE
;
593 ImportType
= IMPORT_DATA
;
595 ImportType
= IMPORT_CONST
;
597 StringRef SymbolName
= E
.SymbolName
.empty() ? E
.Name
: E
.SymbolName
;
598 ImportNameType NameType
= getNameType(SymbolName
, E
.Name
, Machine
, MinGW
);
599 Expected
<std::string
> Name
= E
.ExtName
.empty()
601 : replace(SymbolName
, E
.Name
, E
.ExtName
);
604 return Name
.takeError();
606 if (!E
.AliasTarget
.empty() && *Name
!= E
.AliasTarget
) {
607 Members
.push_back(OF
.createWeakExternal(E
.AliasTarget
, *Name
, false));
608 Members
.push_back(OF
.createWeakExternal(E
.AliasTarget
, *Name
, true));
613 OF
.createShortImport(*Name
, E
.Ordinal
, ImportType
, NameType
));
616 return writeArchive(Path
, Members
, /*WriteSymtab*/ true,
617 object::Archive::K_GNU
,
618 /*Deterministic*/ true, /*Thin*/ false);
621 } // namespace object