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