1 //===- COFFImportFile.cpp - COFF short import file implementation ---------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines the writeImportLibrary function. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Object/COFFImportFile.h" 15 #include "llvm/ADT/ArrayRef.h" 16 #include "llvm/Object/Archive.h" 17 #include "llvm/Object/ArchiveWriter.h" 18 #include "llvm/Object/COFF.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/Path.h" 21 22 #include <cstdint> 23 #include <map> 24 #include <set> 25 #include <string> 26 #include <vector> 27 28 using namespace llvm::COFF; 29 using namespace llvm::object; 30 using namespace llvm; 31 32 namespace llvm { 33 namespace object { 34 35 static bool is32bit(MachineTypes Machine) { 36 switch (Machine) { 37 default: 38 llvm_unreachable("unsupported machine"); 39 case IMAGE_FILE_MACHINE_AMD64: 40 return false; 41 case IMAGE_FILE_MACHINE_ARMNT: 42 case IMAGE_FILE_MACHINE_I386: 43 return true; 44 } 45 } 46 47 static uint16_t getImgRelRelocation(MachineTypes Machine) { 48 switch (Machine) { 49 default: 50 llvm_unreachable("unsupported machine"); 51 case IMAGE_FILE_MACHINE_AMD64: 52 return IMAGE_REL_AMD64_ADDR32NB; 53 case IMAGE_FILE_MACHINE_ARMNT: 54 return IMAGE_REL_ARM_ADDR32NB; 55 case IMAGE_FILE_MACHINE_I386: 56 return IMAGE_REL_I386_DIR32NB; 57 } 58 } 59 60 template <class T> static void append(std::vector<uint8_t> &B, const T &Data) { 61 size_t S = B.size(); 62 B.resize(S + sizeof(T)); 63 memcpy(&B[S], &Data, sizeof(T)); 64 } 65 66 static void writeStringTable(std::vector<uint8_t> &B, 67 ArrayRef<const std::string> Strings) { 68 // The COFF string table consists of a 4-byte value which is the size of the 69 // table, including the length field itself. This value is followed by the 70 // string content itself, which is an array of null-terminated C-style 71 // strings. The termination is important as they are referenced to by offset 72 // by the symbol entity in the file format. 73 74 size_t Pos = B.size(); 75 size_t Offset = B.size(); 76 77 // Skip over the length field, we will fill it in later as we will have 78 // computed the length while emitting the string content itself. 79 Pos += sizeof(uint32_t); 80 81 for (const auto &S : Strings) { 82 B.resize(Pos + S.length() + 1); 83 strcpy(reinterpret_cast<char *>(&B[Pos]), S.c_str()); 84 Pos += S.length() + 1; 85 } 86 87 // Backfill the length of the table now that it has been computed. 88 support::ulittle32_t Length(B.size() - Offset); 89 support::endian::write32le(&B[Offset], Length); 90 } 91 92 static ImportNameType getNameType(StringRef Sym, StringRef ExtName, 93 MachineTypes Machine) { 94 if (Sym != ExtName) 95 return IMPORT_NAME_UNDECORATE; 96 if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.startswith("_")) 97 return IMPORT_NAME_NOPREFIX; 98 return IMPORT_NAME; 99 } 100 101 static Expected<std::string> replace(StringRef S, StringRef From, 102 StringRef To) { 103 size_t Pos = S.find(From); 104 105 // From and To may be mangled, but substrings in S may not. 106 if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) { 107 From = From.substr(1); 108 To = To.substr(1); 109 Pos = S.find(From); 110 } 111 112 if (Pos == StringRef::npos) { 113 return make_error<StringError>( 114 Twine(S + ": replacing '" + From + "' with '" + To + "' failed") 115 .getSingleStringRef(), object_error::parse_failed); 116 } 117 118 return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); 119 } 120 121 static const std::string NullImportDescriptorSymbolName = 122 "__NULL_IMPORT_DESCRIPTOR"; 123 124 namespace { 125 // This class constructs various small object files necessary to support linking 126 // symbols imported from a DLL. The contents are pretty strictly defined and 127 // nearly entirely static. The details of the structures files are defined in 128 // WINNT.h and the PE/COFF specification. 129 class ObjectFactory { 130 using u16 = support::ulittle16_t; 131 using u32 = support::ulittle32_t; 132 MachineTypes Machine; 133 BumpPtrAllocator Alloc; 134 StringRef DLLName; 135 StringRef Library; 136 std::string ImportDescriptorSymbolName; 137 std::string NullThunkSymbolName; 138 139 public: 140 ObjectFactory(StringRef S, MachineTypes M) 141 : Machine(M), DLLName(S), Library(S.drop_back(4)), 142 ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), 143 NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").str()) {} 144 145 // Creates an Import Descriptor. This is a small object file which contains a 146 // reference to the terminators and contains the library name (entry) for the 147 // import name table. It will force the linker to construct the necessary 148 // structure to import symbols from the DLL. 149 NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); 150 151 // Creates a NULL import descriptor. This is a small object file whcih 152 // contains a NULL import descriptor. It is used to terminate the imports 153 // from a specific DLL. 154 NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); 155 156 // Create a NULL Thunk Entry. This is a small object file which contains a 157 // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It 158 // is used to terminate the IAT and ILT. 159 NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); 160 161 // Create a short import file which is described in PE/COFF spec 7. Import 162 // Library Format. 163 NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, 164 ImportType Type, ImportNameType NameType); 165 }; 166 } // namespace 167 168 NewArchiveMember 169 ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { 170 static const uint32_t NumberOfSections = 2; 171 static const uint32_t NumberOfSymbols = 7; 172 static const uint32_t NumberOfRelocations = 3; 173 174 // COFF Header 175 coff_file_header Header{ 176 u16(Machine), 177 u16(NumberOfSections), 178 u32(0), 179 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 180 // .idata$2 181 sizeof(coff_import_directory_table_entry) + 182 NumberOfRelocations * sizeof(coff_relocation) + 183 // .idata$4 184 (DLLName.size() + 1)), 185 u32(NumberOfSymbols), 186 u16(0), 187 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 188 }; 189 append(Buffer, Header); 190 191 // Section Header Table 192 static const coff_section SectionTable[NumberOfSections] = { 193 {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, 194 u32(0), 195 u32(0), 196 u32(sizeof(coff_import_directory_table_entry)), 197 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 198 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 199 sizeof(coff_import_directory_table_entry)), 200 u32(0), 201 u16(NumberOfRelocations), 202 u16(0), 203 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 204 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 205 {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, 206 u32(0), 207 u32(0), 208 u32(DLLName.size() + 1), 209 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 210 sizeof(coff_import_directory_table_entry) + 211 NumberOfRelocations * sizeof(coff_relocation)), 212 u32(0), 213 u32(0), 214 u16(0), 215 u16(0), 216 u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 217 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 218 }; 219 append(Buffer, SectionTable); 220 221 // .idata$2 222 static const coff_import_directory_table_entry ImportDescriptor{ 223 u32(0), u32(0), u32(0), u32(0), u32(0), 224 }; 225 append(Buffer, ImportDescriptor); 226 227 static const coff_relocation RelocationTable[NumberOfRelocations] = { 228 {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2), 229 u16(getImgRelRelocation(Machine))}, 230 {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), 231 u32(3), u16(getImgRelRelocation(Machine))}, 232 {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), 233 u32(4), u16(getImgRelRelocation(Machine))}, 234 }; 235 append(Buffer, RelocationTable); 236 237 // .idata$6 238 auto S = Buffer.size(); 239 Buffer.resize(S + DLLName.size() + 1); 240 memcpy(&Buffer[S], DLLName.data(), DLLName.size()); 241 Buffer[S + DLLName.size()] = '\0'; 242 243 // Symbol Table 244 coff_symbol16 SymbolTable[NumberOfSymbols] = { 245 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 246 u32(0), 247 u16(1), 248 u16(0), 249 IMAGE_SYM_CLASS_EXTERNAL, 250 0}, 251 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, 252 u32(0), 253 u16(1), 254 u16(0), 255 IMAGE_SYM_CLASS_SECTION, 256 0}, 257 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, 258 u32(0), 259 u16(2), 260 u16(0), 261 IMAGE_SYM_CLASS_STATIC, 262 0}, 263 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, 264 u32(0), 265 u16(0), 266 u16(0), 267 IMAGE_SYM_CLASS_SECTION, 268 0}, 269 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, 270 u32(0), 271 u16(0), 272 u16(0), 273 IMAGE_SYM_CLASS_SECTION, 274 0}, 275 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 276 u32(0), 277 u16(0), 278 u16(0), 279 IMAGE_SYM_CLASS_EXTERNAL, 280 0}, 281 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 282 u32(0), 283 u16(0), 284 u16(0), 285 IMAGE_SYM_CLASS_EXTERNAL, 286 0}, 287 }; 288 reinterpret_cast<StringTableOffset &>(SymbolTable[0].Name).Offset = 289 sizeof(uint32_t); 290 reinterpret_cast<StringTableOffset &>(SymbolTable[5].Name).Offset = 291 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; 292 reinterpret_cast<StringTableOffset &>(SymbolTable[6].Name).Offset = 293 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + 294 NullImportDescriptorSymbolName.length() + 1; 295 append(Buffer, SymbolTable); 296 297 // String Table 298 writeStringTable(Buffer, 299 {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, 300 NullThunkSymbolName}); 301 302 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 303 return {MemoryBufferRef(F, DLLName)}; 304 } 305 306 NewArchiveMember 307 ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { 308 static const uint32_t NumberOfSections = 1; 309 static const uint32_t NumberOfSymbols = 1; 310 311 // COFF Header 312 coff_file_header Header{ 313 u16(Machine), 314 u16(NumberOfSections), 315 u32(0), 316 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 317 // .idata$3 318 sizeof(coff_import_directory_table_entry)), 319 u32(NumberOfSymbols), 320 u16(0), 321 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 322 }; 323 append(Buffer, Header); 324 325 // Section Header Table 326 static const coff_section SectionTable[NumberOfSections] = { 327 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, 328 u32(0), 329 u32(0), 330 u32(sizeof(coff_import_directory_table_entry)), 331 u32(sizeof(coff_file_header) + 332 (NumberOfSections * sizeof(coff_section))), 333 u32(0), 334 u32(0), 335 u16(0), 336 u16(0), 337 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 338 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 339 }; 340 append(Buffer, SectionTable); 341 342 // .idata$3 343 static const coff_import_directory_table_entry ImportDescriptor{ 344 u32(0), u32(0), u32(0), u32(0), u32(0), 345 }; 346 append(Buffer, ImportDescriptor); 347 348 // Symbol Table 349 coff_symbol16 SymbolTable[NumberOfSymbols] = { 350 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 351 u32(0), 352 u16(1), 353 u16(0), 354 IMAGE_SYM_CLASS_EXTERNAL, 355 0}, 356 }; 357 reinterpret_cast<StringTableOffset &>(SymbolTable[0].Name).Offset = 358 sizeof(uint32_t); 359 append(Buffer, SymbolTable); 360 361 // String Table 362 writeStringTable(Buffer, {NullImportDescriptorSymbolName}); 363 364 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 365 return {MemoryBufferRef(F, DLLName)}; 366 } 367 368 NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { 369 static const uint32_t NumberOfSections = 2; 370 static const uint32_t NumberOfSymbols = 1; 371 uint32_t VASize = is32bit(Machine) ? 4 : 8; 372 373 // COFF Header 374 coff_file_header Header{ 375 u16(Machine), 376 u16(NumberOfSections), 377 u32(0), 378 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 379 // .idata$5 380 VASize + 381 // .idata$4 382 VASize), 383 u32(NumberOfSymbols), 384 u16(0), 385 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 386 }; 387 append(Buffer, Header); 388 389 // Section Header Table 390 static const coff_section SectionTable[NumberOfSections] = { 391 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, 392 u32(0), 393 u32(0), 394 u32(VASize), 395 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 396 u32(0), 397 u32(0), 398 u16(0), 399 u16(0), 400 u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES 401 : IMAGE_SCN_ALIGN_8BYTES) | 402 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 403 IMAGE_SCN_MEM_WRITE)}, 404 {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, 405 u32(0), 406 u32(0), 407 u32(VASize), 408 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 409 VASize), 410 u32(0), 411 u32(0), 412 u16(0), 413 u16(0), 414 u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES 415 : IMAGE_SCN_ALIGN_8BYTES) | 416 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 417 IMAGE_SCN_MEM_WRITE)}, 418 }; 419 append(Buffer, SectionTable); 420 421 // .idata$5, ILT 422 append(Buffer, u32(0)); 423 if (!is32bit(Machine)) 424 append(Buffer, u32(0)); 425 426 // .idata$4, IAT 427 append(Buffer, u32(0)); 428 if (!is32bit(Machine)) 429 append(Buffer, u32(0)); 430 431 // Symbol Table 432 coff_symbol16 SymbolTable[NumberOfSymbols] = { 433 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 434 u32(0), 435 u16(1), 436 u16(0), 437 IMAGE_SYM_CLASS_EXTERNAL, 438 0}, 439 }; 440 reinterpret_cast<StringTableOffset &>(SymbolTable[0].Name).Offset = 441 sizeof(uint32_t); 442 append(Buffer, SymbolTable); 443 444 // String Table 445 writeStringTable(Buffer, {NullThunkSymbolName}); 446 447 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 448 return {MemoryBufferRef{F, DLLName}}; 449 } 450 451 NewArchiveMember ObjectFactory::createShortImport(StringRef Sym, 452 uint16_t Ordinal, 453 ImportType ImportType, 454 ImportNameType NameType) { 455 size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs 456 size_t Size = sizeof(coff_import_header) + ImpSize; 457 char *Buf = Alloc.Allocate<char>(Size); 458 memset(Buf, 0, Size); 459 char *P = Buf; 460 461 // Write short import library. 462 auto *Imp = reinterpret_cast<coff_import_header *>(P); 463 P += sizeof(*Imp); 464 Imp->Sig2 = 0xFFFF; 465 Imp->Machine = Machine; 466 Imp->SizeOfData = ImpSize; 467 if (Ordinal > 0) 468 Imp->OrdinalHint = Ordinal; 469 Imp->TypeInfo = (NameType << 2) | ImportType; 470 471 // Write symbol name and DLL name. 472 memcpy(P, Sym.data(), Sym.size()); 473 P += Sym.size() + 1; 474 memcpy(P, DLLName.data(), DLLName.size()); 475 476 return {MemoryBufferRef(StringRef(Buf, Size), DLLName)}; 477 } 478 479 std::error_code writeImportLibrary(StringRef DLLName, StringRef Path, 480 ArrayRef<COFFShortExport> Exports, 481 MachineTypes Machine) { 482 483 std::vector<NewArchiveMember> Members; 484 ObjectFactory OF(llvm::sys::path::filename(DLLName), Machine); 485 486 std::vector<uint8_t> ImportDescriptor; 487 Members.push_back(OF.createImportDescriptor(ImportDescriptor)); 488 489 std::vector<uint8_t> NullImportDescriptor; 490 Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor)); 491 492 std::vector<uint8_t> NullThunk; 493 Members.push_back(OF.createNullThunk(NullThunk)); 494 495 for (COFFShortExport E : Exports) { 496 if (E.Private) 497 continue; 498 499 ImportType ImportType = IMPORT_CODE; 500 if (E.Data) 501 ImportType = IMPORT_DATA; 502 if (E.Constant) 503 ImportType = IMPORT_CONST; 504 505 StringRef SymbolName = E.isWeak() ? E.ExtName : E.Name; 506 ImportNameType NameType = getNameType(SymbolName, E.Name, Machine); 507 Expected<std::string> Name = E.ExtName.empty() 508 ? SymbolName 509 : replace(SymbolName, E.Name, E.ExtName); 510 511 if (!Name) { 512 return errorToErrorCode(Name.takeError()); 513 } 514 515 Members.push_back( 516 OF.createShortImport(*Name, E.Ordinal, ImportType, NameType)); 517 } 518 519 std::pair<StringRef, std::error_code> Result = 520 writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, 521 /*Deterministic*/ true, /*Thin*/ false); 522 523 return Result.second; 524 } 525 526 } // namespace object 527 } // namespace llvm 528