| 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 | |