[InstCombine] Signed saturation tests. NFC
[llvm-complete.git] / lib / Object / COFFImportFile.cpp
blobff4a799be60c77b8b0eec182b15733a2b9eb473e
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.
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"
21 #include <cstdint>
22 #include <string>
23 #include <vector>
25 using namespace llvm::COFF;
26 using namespace llvm::object;
27 using namespace llvm;
29 namespace llvm {
30 namespace object {
32 static 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;
45 static 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;
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));
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
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;
109 static Expected<std::string> replace(StringRef S, StringRef From,
110 StringRef To) {
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);
116 To = To.substr(1);
117 Pos = S.find(From);
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";
132 namespace {
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;
143 StringRef Library;
144 std::string ImportDescriptorSymbolName;
145 std::string NullThunkSymbolName;
147 public:
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
170 // Library Format.
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);
177 } // namespace
179 NewArchiveMember
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;
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),
200 append(Buffer, Header);
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)},
230 append(Buffer, SectionTable);
232 // .idata$2
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);
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';
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,
262 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
263 u32(0),
264 u16(1),
265 u16(0),
266 IMAGE_SYM_CLASS_SECTION,
268 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
269 u32(0),
270 u16(2),
271 u16(0),
272 IMAGE_SYM_CLASS_STATIC,
274 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
275 u32(0),
276 u16(0),
277 u16(0),
278 IMAGE_SYM_CLASS_SECTION,
280 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
281 u32(0),
282 u16(0),
283 u16(0),
284 IMAGE_SYM_CLASS_SECTION,
286 {{{0, 0, 0, 0, 0, 0, 0, 0}},
287 u32(0),
288 u16(0),
289 u16(0),
290 IMAGE_SYM_CLASS_EXTERNAL,
292 {{{0, 0, 0, 0, 0, 0, 0, 0}},
293 u32(0),
294 u16(0),
295 u16(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 =
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);
310 // String Table
311 writeStringTable(Buffer,
312 {ImportDescriptorSymbolName, NullImportDescriptorSymbolName,
313 NullThunkSymbolName});
315 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
316 return {MemoryBufferRef(F, ImportName)};
319 NewArchiveMember
320 ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) {
321 const uint32_t NumberOfSections = 1;
322 const uint32_t NumberOfSymbols = 1;
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),
336 append(Buffer, Header);
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)},
353 append(Buffer, SectionTable);
355 // .idata$3
356 const coff_import_directory_table_entry ImportDescriptor{
357 u32(0), u32(0), u32(0), u32(0), u32(0),
359 append(Buffer, ImportDescriptor);
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,
370 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
371 append(Buffer, SymbolTable);
373 // String Table
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;
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),
399 append(Buffer, Header);
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)},
431 append(Buffer, SectionTable);
433 // .idata$5, ILT
434 append(Buffer, u32(0));
435 if (!is32bit(Machine))
436 append(Buffer, u32(0));
438 // .idata$4, IAT
439 append(Buffer, u32(0));
440 if (!is32bit(Machine))
441 append(Buffer, u32(0));
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,
452 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
453 append(Buffer, SymbolTable);
455 // String Table
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,
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;
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;
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());
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;
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),
506 append(Buffer, Header);
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);
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,
530 {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
531 u32(0),
532 u16(0xFFFF),
533 u16(0),
534 IMAGE_SYM_CLASS_STATIC,
536 {{{0, 0, 0, 0, 0, 0, 0, 0}},
537 u32(0),
538 u16(0),
539 u16(0),
540 IMAGE_SYM_CLASS_EXTERNAL,
542 {{{0, 0, 0, 0, 0, 0, 0, 0}},
543 u32(0),
544 u16(0),
545 u16(0),
546 IMAGE_SYM_CLASS_WEAK_EXTERNAL,
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,
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) {
588 if (E.Private)
589 continue;
591 ImportType ImportType = IMPORT_CODE;
592 if (E.Data)
593 ImportType = IMPORT_DATA;
594 if (E.Constant)
595 ImportType = IMPORT_CONST;
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 ? SymbolName
604 : replace(SymbolName, E.Name, E.ExtName);
606 if (!Name)
607 return Name.takeError();
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;
615 Members.push_back(
616 OF.createShortImport(*Name, E.Ordinal, ImportType, NameType));
619 return writeArchive(Path, Members, /*WriteSymtab*/ true,
620 object::Archive::K_GNU,
621 /*Deterministic*/ true, /*Thin*/ false);
624 } // namespace object
625 } // namespace llvm