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. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/Object/COFFImportFile.h" |
14 | #include "llvm/ADT/ArrayRef.h" |
15 | #include "llvm/ADT/SmallVector.h" |
16 | #include "llvm/ADT/StringMap.h" |
17 | #include "llvm/ADT/Twine.h" |
18 | #include "llvm/Object/Archive.h" |
19 | #include "llvm/Object/ArchiveWriter.h" |
20 | #include "llvm/Object/COFF.h" |
21 | #include "llvm/Support/Allocator.h" |
22 | #include "llvm/Support/Endian.h" |
23 | #include "llvm/Support/Error.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | #include "llvm/Support/Path.h" |
26 | |
27 | #include <cstdint> |
28 | #include <string> |
29 | #include <vector> |
30 | |
31 | using namespace llvm::COFF; |
32 | using namespace llvm::object; |
33 | using namespace llvm; |
34 | |
35 | namespace llvm { |
36 | namespace object { |
37 | |
38 | StringRef COFFImportFile::getFileFormatName() const { |
39 | switch (getMachine()) { |
40 | case COFF::IMAGE_FILE_MACHINE_I386: |
41 | return "COFF-import-file-i386" ; |
42 | case COFF::IMAGE_FILE_MACHINE_AMD64: |
43 | return "COFF-import-file-x86-64" ; |
44 | case COFF::IMAGE_FILE_MACHINE_ARMNT: |
45 | return "COFF-import-file-ARM" ; |
46 | case COFF::IMAGE_FILE_MACHINE_ARM64: |
47 | return "COFF-import-file-ARM64" ; |
48 | case COFF::IMAGE_FILE_MACHINE_ARM64EC: |
49 | return "COFF-import-file-ARM64EC" ; |
50 | case COFF::IMAGE_FILE_MACHINE_ARM64X: |
51 | return "COFF-import-file-ARM64X" ; |
52 | default: |
53 | return "COFF-import-file-<unknown arch>" ; |
54 | } |
55 | } |
56 | |
57 | static StringRef applyNameType(ImportNameType Type, StringRef name) { |
58 | auto ltrim1 = [](StringRef s, StringRef chars) { |
59 | return !s.empty() && chars.contains(C: s[0]) ? s.substr(Start: 1) : s; |
60 | }; |
61 | |
62 | switch (Type) { |
63 | case IMPORT_NAME_NOPREFIX: |
64 | name = ltrim1(name, "?@_" ); |
65 | break; |
66 | case IMPORT_NAME_UNDECORATE: |
67 | name = ltrim1(name, "?@_" ); |
68 | name = name.substr(Start: 0, N: name.find(C: '@')); |
69 | break; |
70 | default: |
71 | break; |
72 | } |
73 | return name; |
74 | } |
75 | |
76 | StringRef COFFImportFile::getExportName() const { |
77 | const coff_import_header *hdr = getCOFFImportHeader(); |
78 | StringRef name = Data.getBuffer().substr(Start: sizeof(*hdr)).split(Separator: '\0').first; |
79 | |
80 | switch (hdr->getNameType()) { |
81 | case IMPORT_ORDINAL: |
82 | name = "" ; |
83 | break; |
84 | case IMPORT_NAME_NOPREFIX: |
85 | case IMPORT_NAME_UNDECORATE: |
86 | name = applyNameType(Type: static_cast<ImportNameType>(hdr->getNameType()), name); |
87 | break; |
88 | case IMPORT_NAME_EXPORTAS: { |
89 | // Skip DLL name |
90 | name = Data.getBuffer().substr(Start: sizeof(*hdr) + name.size() + 1); |
91 | name = name.split(Separator: '\0').second.split(Separator: '\0').first; |
92 | break; |
93 | } |
94 | default: |
95 | break; |
96 | } |
97 | |
98 | return name; |
99 | } |
100 | |
101 | Error COFFImportFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const { |
102 | switch (Symb.p) { |
103 | case ImpSymbol: |
104 | OS << "__imp_" ; |
105 | break; |
106 | case ECAuxSymbol: |
107 | OS << "__imp_aux_" ; |
108 | break; |
109 | } |
110 | const char *Name = Data.getBufferStart() + sizeof(coff_import_header); |
111 | if (Symb.p != ECThunkSymbol && COFF::isArm64EC(Machine: getMachine())) { |
112 | if (std::optional<std::string> DemangledName = |
113 | getArm64ECDemangledFunctionName(Name)) { |
114 | OS << StringRef(*DemangledName); |
115 | return Error::success(); |
116 | } |
117 | } |
118 | OS << StringRef(Name); |
119 | return Error::success(); |
120 | } |
121 | |
122 | static uint16_t getImgRelRelocation(MachineTypes Machine) { |
123 | switch (Machine) { |
124 | default: |
125 | llvm_unreachable("unsupported machine" ); |
126 | case IMAGE_FILE_MACHINE_AMD64: |
127 | return IMAGE_REL_AMD64_ADDR32NB; |
128 | case IMAGE_FILE_MACHINE_ARMNT: |
129 | return IMAGE_REL_ARM_ADDR32NB; |
130 | case IMAGE_FILE_MACHINE_ARM64: |
131 | case IMAGE_FILE_MACHINE_ARM64EC: |
132 | case IMAGE_FILE_MACHINE_ARM64X: |
133 | return IMAGE_REL_ARM64_ADDR32NB; |
134 | case IMAGE_FILE_MACHINE_I386: |
135 | return IMAGE_REL_I386_DIR32NB; |
136 | case IMAGE_FILE_MACHINE_R4000: |
137 | return IMAGE_REL_MIPS_REFWORDNB; |
138 | } |
139 | } |
140 | |
141 | template <class T> static void append(std::vector<uint8_t> &B, const T &Data) { |
142 | size_t S = B.size(); |
143 | B.resize(new_size: S + sizeof(T)); |
144 | memcpy(&B[S], &Data, sizeof(T)); |
145 | } |
146 | |
147 | static void writeStringTable(std::vector<uint8_t> &B, |
148 | ArrayRef<const std::string_view> Strings) { |
149 | // The COFF string table consists of a 4-byte value which is the size of the |
150 | // table, including the length field itself. This value is followed by the |
151 | // string content itself, which is an array of null-terminated C-style |
152 | // strings. The termination is important as they are referenced to by offset |
153 | // by the symbol entity in the file format. |
154 | |
155 | size_t Pos = B.size(); |
156 | size_t Offset = B.size(); |
157 | |
158 | // Skip over the length field, we will fill it in later as we will have |
159 | // computed the length while emitting the string content itself. |
160 | Pos += sizeof(uint32_t); |
161 | |
162 | for (const auto &S : Strings) { |
163 | B.resize(new_size: Pos + S.length() + 1); |
164 | std::copy(first: S.begin(), last: S.end(), result: std::next(x: B.begin(), n: Pos)); |
165 | B[Pos + S.length()] = 0; |
166 | Pos += S.length() + 1; |
167 | } |
168 | |
169 | // Backfill the length of the table now that it has been computed. |
170 | support::ulittle32_t Length(B.size() - Offset); |
171 | support::endian::write32le(P: &B[Offset], V: Length); |
172 | } |
173 | |
174 | static ImportNameType getNameType(StringRef Sym, StringRef ExtName, |
175 | MachineTypes Machine, bool MinGW) { |
176 | // A decorated stdcall function in MSVC is exported with the |
177 | // type IMPORT_NAME, and the exported function name includes the |
178 | // the leading underscore. In MinGW on the other hand, a decorated |
179 | // stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX). |
180 | // See the comment in isDecorated in COFFModuleDefinition.cpp for more |
181 | // details. |
182 | if (ExtName.starts_with(Prefix: "_" ) && ExtName.contains(C: '@') && !MinGW) |
183 | return IMPORT_NAME; |
184 | if (Sym != ExtName) |
185 | return IMPORT_NAME_UNDECORATE; |
186 | if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with(Prefix: "_" )) |
187 | return IMPORT_NAME_NOPREFIX; |
188 | return IMPORT_NAME; |
189 | } |
190 | |
191 | static Expected<std::string> replace(StringRef S, StringRef From, |
192 | StringRef To) { |
193 | size_t Pos = S.find(Str: From); |
194 | |
195 | // From and To may be mangled, but substrings in S may not. |
196 | if (Pos == StringRef::npos && From.starts_with(Prefix: "_" ) && To.starts_with(Prefix: "_" )) { |
197 | From = From.substr(Start: 1); |
198 | To = To.substr(Start: 1); |
199 | Pos = S.find(Str: From); |
200 | } |
201 | |
202 | if (Pos == StringRef::npos) { |
203 | return make_error<StringError>( |
204 | Args: StringRef(Twine(S + ": replacing '" + From + |
205 | "' with '" + To + "' failed" ).str()), Args: object_error::parse_failed); |
206 | } |
207 | |
208 | return (Twine(S.substr(Start: 0, N: Pos)) + To + S.substr(Start: Pos + From.size())).str(); |
209 | } |
210 | |
211 | namespace { |
212 | // This class constructs various small object files necessary to support linking |
213 | // symbols imported from a DLL. The contents are pretty strictly defined and |
214 | // nearly entirely static. The details of the structures files are defined in |
215 | // WINNT.h and the PE/COFF specification. |
216 | class ObjectFactory { |
217 | using u16 = support::ulittle16_t; |
218 | using u32 = support::ulittle32_t; |
219 | MachineTypes NativeMachine; |
220 | BumpPtrAllocator Alloc; |
221 | StringRef ImportName; |
222 | StringRef Library; |
223 | std::string ImportDescriptorSymbolName; |
224 | std::string NullThunkSymbolName; |
225 | |
226 | public: |
227 | ObjectFactory(StringRef S, MachineTypes M) |
228 | : NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(path: S)), |
229 | ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()), |
230 | NullThunkSymbolName( |
231 | (NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {} |
232 | |
233 | // Creates an Import Descriptor. This is a small object file which contains a |
234 | // reference to the terminators and contains the library name (entry) for the |
235 | // import name table. It will force the linker to construct the necessary |
236 | // structure to import symbols from the DLL. |
237 | NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); |
238 | |
239 | // Creates a NULL import descriptor. This is a small object file whcih |
240 | // contains a NULL import descriptor. It is used to terminate the imports |
241 | // from a specific DLL. |
242 | NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); |
243 | |
244 | // Create a NULL Thunk Entry. This is a small object file which contains a |
245 | // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It |
246 | // is used to terminate the IAT and ILT. |
247 | NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); |
248 | |
249 | // Create a short import file which is described in PE/COFF spec 7. Import |
250 | // Library Format. |
251 | NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, |
252 | ImportType Type, ImportNameType NameType, |
253 | StringRef ExportName, |
254 | MachineTypes Machine); |
255 | |
256 | // Create a weak external file which is described in PE/COFF Aux Format 3. |
257 | NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp, |
258 | MachineTypes Machine); |
259 | |
260 | bool is64Bit() const { return COFF::is64Bit(Machine: NativeMachine); } |
261 | }; |
262 | } // namespace |
263 | |
264 | NewArchiveMember |
265 | ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { |
266 | const uint32_t NumberOfSections = 2; |
267 | const uint32_t NumberOfSymbols = 7; |
268 | const uint32_t NumberOfRelocations = 3; |
269 | |
270 | // COFF Header |
271 | coff_file_header { |
272 | .Machine: u16(NativeMachine), |
273 | .NumberOfSections: u16(NumberOfSections), |
274 | .TimeDateStamp: u32(0), |
275 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + |
276 | // .idata$2 |
277 | sizeof(coff_import_directory_table_entry) + |
278 | NumberOfRelocations * sizeof(coff_relocation) + |
279 | // .idata$4 |
280 | (ImportName.size() + 1)), |
281 | .NumberOfSymbols: u32(NumberOfSymbols), |
282 | .SizeOfOptionalHeader: u16(0), |
283 | .Characteristics: u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), |
284 | }; |
285 | append(B&: Buffer, Data: Header); |
286 | |
287 | // Section Header Table |
288 | const coff_section SectionTable[NumberOfSections] = { |
289 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, |
290 | .VirtualSize: u32(0), |
291 | .VirtualAddress: u32(0), |
292 | .SizeOfRawData: u32(sizeof(coff_import_directory_table_entry)), |
293 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), |
294 | .PointerToRelocations: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + |
295 | sizeof(coff_import_directory_table_entry)), |
296 | .PointerToLinenumbers: u32(0), |
297 | .NumberOfRelocations: u16(NumberOfRelocations), |
298 | .NumberOfLinenumbers: u16(0), |
299 | .Characteristics: u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | |
300 | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, |
301 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, |
302 | .VirtualSize: u32(0), |
303 | .VirtualAddress: u32(0), |
304 | .SizeOfRawData: u32(ImportName.size() + 1), |
305 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + |
306 | sizeof(coff_import_directory_table_entry) + |
307 | NumberOfRelocations * sizeof(coff_relocation)), |
308 | .PointerToRelocations: u32(0), |
309 | .PointerToLinenumbers: u32(0), |
310 | .NumberOfRelocations: u16(0), |
311 | .NumberOfLinenumbers: u16(0), |
312 | .Characteristics: u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | |
313 | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, |
314 | }; |
315 | append(B&: Buffer, Data: SectionTable); |
316 | |
317 | // .idata$2 |
318 | const coff_import_directory_table_entry ImportDescriptor{ |
319 | .ImportLookupTableRVA: u32(0), .TimeDateStamp: u32(0), .ForwarderChain: u32(0), .NameRVA: u32(0), .ImportAddressTableRVA: u32(0), |
320 | }; |
321 | append(B&: Buffer, Data: ImportDescriptor); |
322 | |
323 | const coff_relocation RelocationTable[NumberOfRelocations] = { |
324 | {.VirtualAddress: u32(offsetof(coff_import_directory_table_entry, NameRVA)), .SymbolTableIndex: u32(2), |
325 | .Type: u16(getImgRelRelocation(Machine: NativeMachine))}, |
326 | {.VirtualAddress: u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), |
327 | .SymbolTableIndex: u32(3), .Type: u16(getImgRelRelocation(Machine: NativeMachine))}, |
328 | {.VirtualAddress: u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), |
329 | .SymbolTableIndex: u32(4), .Type: u16(getImgRelRelocation(Machine: NativeMachine))}, |
330 | }; |
331 | append(B&: Buffer, Data: RelocationTable); |
332 | |
333 | // .idata$6 |
334 | auto S = Buffer.size(); |
335 | Buffer.resize(new_size: S + ImportName.size() + 1); |
336 | memcpy(dest: &Buffer[S], src: ImportName.data(), n: ImportName.size()); |
337 | Buffer[S + ImportName.size()] = '\0'; |
338 | |
339 | // Symbol Table |
340 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
341 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
342 | .Value: u32(0), |
343 | .SectionNumber: u16(1), |
344 | .Type: u16(0), |
345 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
346 | .NumberOfAuxSymbols: 0}, |
347 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, |
348 | .Value: u32(0), |
349 | .SectionNumber: u16(1), |
350 | .Type: u16(0), |
351 | .StorageClass: IMAGE_SYM_CLASS_SECTION, |
352 | .NumberOfAuxSymbols: 0}, |
353 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, |
354 | .Value: u32(0), |
355 | .SectionNumber: u16(2), |
356 | .Type: u16(0), |
357 | .StorageClass: IMAGE_SYM_CLASS_STATIC, |
358 | .NumberOfAuxSymbols: 0}, |
359 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, |
360 | .Value: u32(0), |
361 | .SectionNumber: u16(0), |
362 | .Type: u16(0), |
363 | .StorageClass: IMAGE_SYM_CLASS_SECTION, |
364 | .NumberOfAuxSymbols: 0}, |
365 | {.Name: {.ShortName: {'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, |
366 | .Value: u32(0), |
367 | .SectionNumber: u16(0), |
368 | .Type: u16(0), |
369 | .StorageClass: IMAGE_SYM_CLASS_SECTION, |
370 | .NumberOfAuxSymbols: 0}, |
371 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
372 | .Value: u32(0), |
373 | .SectionNumber: u16(0), |
374 | .Type: u16(0), |
375 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
376 | .NumberOfAuxSymbols: 0}, |
377 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
378 | .Value: u32(0), |
379 | .SectionNumber: u16(0), |
380 | .Type: u16(0), |
381 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
382 | .NumberOfAuxSymbols: 0}, |
383 | }; |
384 | // TODO: Name.Offset.Offset here and in the all similar places below |
385 | // suggests a names refactoring. Maybe StringTableOffset.Value? |
386 | SymbolTable[0].Name.Offset.Offset = |
387 | sizeof(uint32_t); |
388 | SymbolTable[5].Name.Offset.Offset = |
389 | sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; |
390 | SymbolTable[6].Name.Offset.Offset = |
391 | sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + |
392 | NullImportDescriptorSymbolName.length() + 1; |
393 | append(B&: Buffer, Data: SymbolTable); |
394 | |
395 | // String Table |
396 | writeStringTable(B&: Buffer, |
397 | Strings: {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, |
398 | NullThunkSymbolName}); |
399 | |
400 | StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; |
401 | return {MemoryBufferRef(F, ImportName)}; |
402 | } |
403 | |
404 | NewArchiveMember |
405 | ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { |
406 | const uint32_t NumberOfSections = 1; |
407 | const uint32_t NumberOfSymbols = 1; |
408 | |
409 | // COFF Header |
410 | coff_file_header { |
411 | .Machine: u16(NativeMachine), |
412 | .NumberOfSections: u16(NumberOfSections), |
413 | .TimeDateStamp: u32(0), |
414 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + |
415 | // .idata$3 |
416 | sizeof(coff_import_directory_table_entry)), |
417 | .NumberOfSymbols: u32(NumberOfSymbols), |
418 | .SizeOfOptionalHeader: u16(0), |
419 | .Characteristics: u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), |
420 | }; |
421 | append(B&: Buffer, Data: Header); |
422 | |
423 | // Section Header Table |
424 | const coff_section SectionTable[NumberOfSections] = { |
425 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, |
426 | .VirtualSize: u32(0), |
427 | .VirtualAddress: u32(0), |
428 | .SizeOfRawData: u32(sizeof(coff_import_directory_table_entry)), |
429 | .PointerToRawData: u32(sizeof(coff_file_header) + |
430 | (NumberOfSections * sizeof(coff_section))), |
431 | .PointerToRelocations: u32(0), |
432 | .PointerToLinenumbers: u32(0), |
433 | .NumberOfRelocations: u16(0), |
434 | .NumberOfLinenumbers: u16(0), |
435 | .Characteristics: u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | |
436 | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, |
437 | }; |
438 | append(B&: Buffer, Data: SectionTable); |
439 | |
440 | // .idata$3 |
441 | const coff_import_directory_table_entry ImportDescriptor{ |
442 | .ImportLookupTableRVA: u32(0), .TimeDateStamp: u32(0), .ForwarderChain: u32(0), .NameRVA: u32(0), .ImportAddressTableRVA: u32(0), |
443 | }; |
444 | append(B&: Buffer, Data: ImportDescriptor); |
445 | |
446 | // Symbol Table |
447 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
448 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
449 | .Value: u32(0), |
450 | .SectionNumber: u16(1), |
451 | .Type: u16(0), |
452 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
453 | .NumberOfAuxSymbols: 0}, |
454 | }; |
455 | SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); |
456 | append(B&: Buffer, Data: SymbolTable); |
457 | |
458 | // String Table |
459 | writeStringTable(B&: Buffer, Strings: {NullImportDescriptorSymbolName}); |
460 | |
461 | StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; |
462 | return {MemoryBufferRef(F, ImportName)}; |
463 | } |
464 | |
465 | NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { |
466 | const uint32_t NumberOfSections = 2; |
467 | const uint32_t NumberOfSymbols = 1; |
468 | uint32_t VASize = is64Bit() ? 8 : 4; |
469 | |
470 | // COFF Header |
471 | coff_file_header { |
472 | .Machine: u16(NativeMachine), |
473 | .NumberOfSections: u16(NumberOfSections), |
474 | .TimeDateStamp: u32(0), |
475 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + |
476 | // .idata$5 |
477 | VASize + |
478 | // .idata$4 |
479 | VASize), |
480 | .NumberOfSymbols: u32(NumberOfSymbols), |
481 | .SizeOfOptionalHeader: u16(0), |
482 | .Characteristics: u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), |
483 | }; |
484 | append(B&: Buffer, Data: Header); |
485 | |
486 | // Section Header Table |
487 | const coff_section SectionTable[NumberOfSections] = { |
488 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, |
489 | .VirtualSize: u32(0), |
490 | .VirtualAddress: u32(0), |
491 | .SizeOfRawData: u32(VASize), |
492 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), |
493 | .PointerToRelocations: u32(0), |
494 | .PointerToLinenumbers: u32(0), |
495 | .NumberOfRelocations: u16(0), |
496 | .NumberOfLinenumbers: u16(0), |
497 | .Characteristics: u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | |
498 | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
499 | IMAGE_SCN_MEM_WRITE)}, |
500 | {.Name: {'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, |
501 | .VirtualSize: u32(0), |
502 | .VirtualAddress: u32(0), |
503 | .SizeOfRawData: u32(VASize), |
504 | .PointerToRawData: u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + |
505 | VASize), |
506 | .PointerToRelocations: u32(0), |
507 | .PointerToLinenumbers: u32(0), |
508 | .NumberOfRelocations: u16(0), |
509 | .NumberOfLinenumbers: u16(0), |
510 | .Characteristics: u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | |
511 | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
512 | IMAGE_SCN_MEM_WRITE)}, |
513 | }; |
514 | append(B&: Buffer, Data: SectionTable); |
515 | |
516 | // .idata$5, ILT |
517 | append(B&: Buffer, Data: u32(0)); |
518 | if (is64Bit()) |
519 | append(B&: Buffer, Data: u32(0)); |
520 | |
521 | // .idata$4, IAT |
522 | append(B&: Buffer, Data: u32(0)); |
523 | if (is64Bit()) |
524 | append(B&: Buffer, Data: u32(0)); |
525 | |
526 | // Symbol Table |
527 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
528 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
529 | .Value: u32(0), |
530 | .SectionNumber: u16(1), |
531 | .Type: u16(0), |
532 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
533 | .NumberOfAuxSymbols: 0}, |
534 | }; |
535 | SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); |
536 | append(B&: Buffer, Data: SymbolTable); |
537 | |
538 | // String Table |
539 | writeStringTable(B&: Buffer, Strings: {NullThunkSymbolName}); |
540 | |
541 | StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; |
542 | return {MemoryBufferRef{F, ImportName}}; |
543 | } |
544 | |
545 | NewArchiveMember |
546 | ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal, |
547 | ImportType ImportType, ImportNameType NameType, |
548 | StringRef ExportName, MachineTypes Machine) { |
549 | size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs |
550 | if (!ExportName.empty()) |
551 | ImpSize += ExportName.size() + 1; |
552 | size_t Size = sizeof(coff_import_header) + ImpSize; |
553 | char *Buf = Alloc.Allocate<char>(Num: Size); |
554 | memset(s: Buf, c: 0, n: Size); |
555 | char *P = Buf; |
556 | |
557 | // Write short import library. |
558 | auto *Imp = reinterpret_cast<coff_import_header *>(P); |
559 | P += sizeof(*Imp); |
560 | Imp->Sig2 = 0xFFFF; |
561 | Imp->Machine = Machine; |
562 | Imp->SizeOfData = ImpSize; |
563 | if (Ordinal > 0) |
564 | Imp->OrdinalHint = Ordinal; |
565 | Imp->TypeInfo = (NameType << 2) | ImportType; |
566 | |
567 | // Write symbol name and DLL name. |
568 | memcpy(dest: P, src: Sym.data(), n: Sym.size()); |
569 | P += Sym.size() + 1; |
570 | memcpy(dest: P, src: ImportName.data(), n: ImportName.size()); |
571 | if (!ExportName.empty()) { |
572 | P += ImportName.size() + 1; |
573 | memcpy(dest: P, src: ExportName.data(), n: ExportName.size()); |
574 | } |
575 | |
576 | return {MemoryBufferRef(StringRef(Buf, Size), ImportName)}; |
577 | } |
578 | |
579 | NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, |
580 | StringRef Weak, bool Imp, |
581 | MachineTypes Machine) { |
582 | std::vector<uint8_t> Buffer; |
583 | const uint32_t NumberOfSections = 1; |
584 | const uint32_t NumberOfSymbols = 5; |
585 | |
586 | // COFF Header |
587 | coff_file_header { |
588 | .Machine: u16(Machine), |
589 | .NumberOfSections: u16(NumberOfSections), |
590 | .TimeDateStamp: u32(0), |
591 | .PointerToSymbolTable: u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))), |
592 | .NumberOfSymbols: u32(NumberOfSymbols), |
593 | .SizeOfOptionalHeader: u16(0), |
594 | .Characteristics: u16(0), |
595 | }; |
596 | append(B&: Buffer, Data: Header); |
597 | |
598 | // Section Header Table |
599 | const coff_section SectionTable[NumberOfSections] = { |
600 | {.Name: {'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'}, |
601 | .VirtualSize: u32(0), |
602 | .VirtualAddress: u32(0), |
603 | .SizeOfRawData: u32(0), |
604 | .PointerToRawData: u32(0), |
605 | .PointerToRelocations: u32(0), |
606 | .PointerToLinenumbers: u32(0), |
607 | .NumberOfRelocations: u16(0), |
608 | .NumberOfLinenumbers: u16(0), |
609 | .Characteristics: u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}}; |
610 | append(B&: Buffer, Data: SectionTable); |
611 | |
612 | // Symbol Table |
613 | coff_symbol16 SymbolTable[NumberOfSymbols] = { |
614 | {.Name: {.ShortName: {'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}}, |
615 | .Value: u32(0), |
616 | .SectionNumber: u16(0xFFFF), |
617 | .Type: u16(0), |
618 | .StorageClass: IMAGE_SYM_CLASS_STATIC, |
619 | .NumberOfAuxSymbols: 0}, |
620 | {.Name: {.ShortName: {'@', 'f', 'e', 'a', 't', '.', '0', '0'}}, |
621 | .Value: u32(0), |
622 | .SectionNumber: u16(0xFFFF), |
623 | .Type: u16(0), |
624 | .StorageClass: IMAGE_SYM_CLASS_STATIC, |
625 | .NumberOfAuxSymbols: 0}, |
626 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
627 | .Value: u32(0), |
628 | .SectionNumber: u16(0), |
629 | .Type: u16(0), |
630 | .StorageClass: IMAGE_SYM_CLASS_EXTERNAL, |
631 | .NumberOfAuxSymbols: 0}, |
632 | {.Name: {.ShortName: {0, 0, 0, 0, 0, 0, 0, 0}}, |
633 | .Value: u32(0), |
634 | .SectionNumber: u16(0), |
635 | .Type: u16(0), |
636 | .StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL, |
637 | .NumberOfAuxSymbols: 1}, |
638 | {.Name: {.ShortName: {2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}}, |
639 | .Value: u32(0), |
640 | .SectionNumber: u16(0), |
641 | .Type: u16(0), |
642 | .StorageClass: IMAGE_SYM_CLASS_NULL, |
643 | .NumberOfAuxSymbols: 0}, |
644 | }; |
645 | SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t); |
646 | |
647 | //__imp_ String Table |
648 | StringRef Prefix = Imp ? "__imp_" : "" ; |
649 | SymbolTable[3].Name.Offset.Offset = |
650 | sizeof(uint32_t) + Sym.size() + Prefix.size() + 1; |
651 | append(B&: Buffer, Data: SymbolTable); |
652 | writeStringTable(B&: Buffer, Strings: {(Prefix + Sym).str(), |
653 | (Prefix + Weak).str()}); |
654 | |
655 | // Copied here so we can still use writeStringTable |
656 | char *Buf = Alloc.Allocate<char>(Num: Buffer.size()); |
657 | memcpy(dest: Buf, src: Buffer.data(), n: Buffer.size()); |
658 | return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)}; |
659 | } |
660 | |
661 | Error writeImportLibrary(StringRef ImportName, StringRef Path, |
662 | ArrayRef<COFFShortExport> Exports, |
663 | MachineTypes Machine, bool MinGW, |
664 | ArrayRef<COFFShortExport> NativeExports) { |
665 | |
666 | MachineTypes NativeMachine = Machine; |
667 | if (isArm64EC(Machine)) { |
668 | NativeMachine = IMAGE_FILE_MACHINE_ARM64; |
669 | Machine = IMAGE_FILE_MACHINE_ARM64EC; |
670 | } |
671 | |
672 | std::vector<NewArchiveMember> Members; |
673 | ObjectFactory OF(llvm::sys::path::filename(path: ImportName), NativeMachine); |
674 | |
675 | std::vector<uint8_t> ImportDescriptor; |
676 | Members.push_back(x: OF.createImportDescriptor(Buffer&: ImportDescriptor)); |
677 | |
678 | std::vector<uint8_t> NullImportDescriptor; |
679 | Members.push_back(x: OF.createNullImportDescriptor(Buffer&: NullImportDescriptor)); |
680 | |
681 | std::vector<uint8_t> NullThunk; |
682 | Members.push_back(x: OF.createNullThunk(Buffer&: NullThunk)); |
683 | |
684 | auto addExports = [&](ArrayRef<COFFShortExport> Exp, |
685 | MachineTypes M) -> Error { |
686 | StringMap<std::string> RegularImports; |
687 | struct Deferred { |
688 | std::string Name; |
689 | ImportType ImpType; |
690 | const COFFShortExport *Export; |
691 | }; |
692 | SmallVector<Deferred, 0> Renames; |
693 | for (const COFFShortExport &E : Exp) { |
694 | if (E.Private) |
695 | continue; |
696 | |
697 | ImportType ImportType = IMPORT_CODE; |
698 | if (E.Data) |
699 | ImportType = IMPORT_DATA; |
700 | if (E.Constant) |
701 | ImportType = IMPORT_CONST; |
702 | |
703 | StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName; |
704 | std::string Name; |
705 | |
706 | if (E.ExtName.empty()) { |
707 | Name = std::string(SymbolName); |
708 | } else { |
709 | Expected<std::string> ReplacedName = |
710 | object::replace(S: SymbolName, From: E.Name, To: E.ExtName); |
711 | if (!ReplacedName) |
712 | return ReplacedName.takeError(); |
713 | Name.swap(s&: *ReplacedName); |
714 | } |
715 | |
716 | ImportNameType NameType; |
717 | std::string ExportName; |
718 | if (E.Noname) { |
719 | NameType = IMPORT_ORDINAL; |
720 | } else if (!E.ExportAs.empty()) { |
721 | NameType = IMPORT_NAME_EXPORTAS; |
722 | ExportName = E.ExportAs; |
723 | } else if (!E.ImportName.empty()) { |
724 | // If we need to import from a specific ImportName, we may need to use |
725 | // a weak alias (which needs another import to point at). But if we can |
726 | // express ImportName based on the symbol name and a specific NameType, |
727 | // prefer that over an alias. |
728 | if (Machine == IMAGE_FILE_MACHINE_I386 && |
729 | applyNameType(Type: IMPORT_NAME_UNDECORATE, name: Name) == E.ImportName) |
730 | NameType = IMPORT_NAME_UNDECORATE; |
731 | else if (Machine == IMAGE_FILE_MACHINE_I386 && |
732 | applyNameType(Type: IMPORT_NAME_NOPREFIX, name: Name) == E.ImportName) |
733 | NameType = IMPORT_NAME_NOPREFIX; |
734 | else if (isArm64EC(Machine: M)) { |
735 | NameType = IMPORT_NAME_EXPORTAS; |
736 | ExportName = E.ImportName; |
737 | } else if (Name == E.ImportName) |
738 | NameType = IMPORT_NAME; |
739 | else { |
740 | Deferred D; |
741 | D.Name = Name; |
742 | D.ImpType = ImportType; |
743 | D.Export = &E; |
744 | Renames.push_back(Elt: D); |
745 | continue; |
746 | } |
747 | } else { |
748 | NameType = getNameType(Sym: SymbolName, ExtName: E.Name, Machine: M, MinGW); |
749 | } |
750 | |
751 | // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols. |
752 | if (ImportType == IMPORT_CODE && isArm64EC(Machine: M)) { |
753 | if (std::optional<std::string> MangledName = |
754 | getArm64ECMangledFunctionName(Name)) { |
755 | if (!E.Noname && ExportName.empty()) { |
756 | NameType = IMPORT_NAME_EXPORTAS; |
757 | ExportName.swap(s&: Name); |
758 | } |
759 | Name = std::move(*MangledName); |
760 | } else if (!E.Noname && ExportName.empty()) { |
761 | std::optional<std::string> DemangledName = |
762 | getArm64ECDemangledFunctionName(Name); |
763 | if (!DemangledName) |
764 | return make_error<StringError>( |
765 | Args: StringRef(Twine("Invalid ARM64EC function name '" + Name + "'" ) |
766 | .str()), |
767 | Args: object_error::parse_failed); |
768 | NameType = IMPORT_NAME_EXPORTAS; |
769 | ExportName = std::move(*DemangledName); |
770 | } |
771 | } |
772 | |
773 | RegularImports[applyNameType(Type: NameType, name: Name)] = Name; |
774 | Members.push_back(x: OF.createShortImport(Sym: Name, Ordinal: E.Ordinal, ImportType, |
775 | NameType, ExportName, Machine: M)); |
776 | } |
777 | for (const auto &D : Renames) { |
778 | auto It = RegularImports.find(Key: D.Export->ImportName); |
779 | if (It != RegularImports.end()) { |
780 | // We have a regular import entry for a symbol with the name we |
781 | // want to reference; produce an alias pointing at that. |
782 | StringRef Symbol = It->second; |
783 | if (D.ImpType == IMPORT_CODE) |
784 | Members.push_back(x: OF.createWeakExternal(Sym: Symbol, Weak: D.Name, Imp: false, Machine: M)); |
785 | Members.push_back(x: OF.createWeakExternal(Sym: Symbol, Weak: D.Name, Imp: true, Machine: M)); |
786 | } else { |
787 | Members.push_back(x: OF.createShortImport(Sym: D.Name, Ordinal: D.Export->Ordinal, |
788 | ImportType: D.ImpType, NameType: IMPORT_NAME_EXPORTAS, |
789 | ExportName: D.Export->ImportName, Machine: M)); |
790 | } |
791 | } |
792 | return Error::success(); |
793 | }; |
794 | |
795 | if (Error e = addExports(Exports, Machine)) |
796 | return e; |
797 | if (Error e = addExports(NativeExports, NativeMachine)) |
798 | return e; |
799 | |
800 | return writeArchive(ArcName: Path, NewMembers: Members, WriteSymtab: SymtabWritingMode::NormalSymtab, |
801 | Kind: object::Archive::K_COFF, |
802 | /*Deterministic*/ true, /*Thin*/ false, |
803 | /*OldArchiveBuf*/ nullptr, IsEC: isArm64EC(Machine)); |
804 | } |
805 | |
806 | } // namespace object |
807 | } // namespace llvm |
808 | |