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 StringRef(Twine(S + ": replacing '" + From + 115 "' with '" + To + "' failed").str()), 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 // TODO: Name.Offset.Offset here and in the all similar places below 289 // suggests a names refactoring. Maybe StringTableOffset.Value? 290 SymbolTable[0].Name.Offset.Offset = 291 sizeof(uint32_t); 292 SymbolTable[5].Name.Offset.Offset = 293 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; 294 SymbolTable[6].Name.Offset.Offset = 295 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + 296 NullImportDescriptorSymbolName.length() + 1; 297 append(Buffer, SymbolTable); 298 299 // String Table 300 writeStringTable(Buffer, 301 {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, 302 NullThunkSymbolName}); 303 304 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 305 return {MemoryBufferRef(F, DLLName)}; 306 } 307 308 NewArchiveMember 309 ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { 310 static const uint32_t NumberOfSections = 1; 311 static const uint32_t NumberOfSymbols = 1; 312 313 // COFF Header 314 coff_file_header Header{ 315 u16(Machine), 316 u16(NumberOfSections), 317 u32(0), 318 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 319 // .idata$3 320 sizeof(coff_import_directory_table_entry)), 321 u32(NumberOfSymbols), 322 u16(0), 323 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 324 }; 325 append(Buffer, Header); 326 327 // Section Header Table 328 static const coff_section SectionTable[NumberOfSections] = { 329 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, 330 u32(0), 331 u32(0), 332 u32(sizeof(coff_import_directory_table_entry)), 333 u32(sizeof(coff_file_header) + 334 (NumberOfSections * sizeof(coff_section))), 335 u32(0), 336 u32(0), 337 u16(0), 338 u16(0), 339 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 340 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 341 }; 342 append(Buffer, SectionTable); 343 344 // .idata$3 345 static const coff_import_directory_table_entry ImportDescriptor{ 346 u32(0), u32(0), u32(0), u32(0), u32(0), 347 }; 348 append(Buffer, ImportDescriptor); 349 350 // Symbol Table 351 coff_symbol16 SymbolTable[NumberOfSymbols] = { 352 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 353 u32(0), 354 u16(1), 355 u16(0), 356 IMAGE_SYM_CLASS_EXTERNAL, 357 0}, 358 }; 359 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); 360 append(Buffer, SymbolTable); 361 362 // String Table 363 writeStringTable(Buffer, {NullImportDescriptorSymbolName}); 364 365 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 366 return {MemoryBufferRef(F, DLLName)}; 367 } 368 369 NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { 370 static const uint32_t NumberOfSections = 2; 371 static const uint32_t NumberOfSymbols = 1; 372 uint32_t VASize = is32bit(Machine) ? 4 : 8; 373 374 // COFF Header 375 coff_file_header Header{ 376 u16(Machine), 377 u16(NumberOfSections), 378 u32(0), 379 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 380 // .idata$5 381 VASize + 382 // .idata$4 383 VASize), 384 u32(NumberOfSymbols), 385 u16(0), 386 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 387 }; 388 append(Buffer, Header); 389 390 // Section Header Table 391 static const coff_section SectionTable[NumberOfSections] = { 392 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, 393 u32(0), 394 u32(0), 395 u32(VASize), 396 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 397 u32(0), 398 u32(0), 399 u16(0), 400 u16(0), 401 u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES 402 : IMAGE_SCN_ALIGN_8BYTES) | 403 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 404 IMAGE_SCN_MEM_WRITE)}, 405 {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, 406 u32(0), 407 u32(0), 408 u32(VASize), 409 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 410 VASize), 411 u32(0), 412 u32(0), 413 u16(0), 414 u16(0), 415 u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES 416 : IMAGE_SCN_ALIGN_8BYTES) | 417 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 418 IMAGE_SCN_MEM_WRITE)}, 419 }; 420 append(Buffer, SectionTable); 421 422 // .idata$5, ILT 423 append(Buffer, u32(0)); 424 if (!is32bit(Machine)) 425 append(Buffer, u32(0)); 426 427 // .idata$4, IAT 428 append(Buffer, u32(0)); 429 if (!is32bit(Machine)) 430 append(Buffer, u32(0)); 431 432 // Symbol Table 433 coff_symbol16 SymbolTable[NumberOfSymbols] = { 434 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 435 u32(0), 436 u16(1), 437 u16(0), 438 IMAGE_SYM_CLASS_EXTERNAL, 439 0}, 440 }; 441 SymbolTable[0].Name.Offset.Offset = 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