//===- Writer.cpp ---------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Config.h" #include "Writer.h" #include "lld/Core/Error.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; static const int PageSize = 4096; static const int FileAlignment = 512; static const int SectionAlignment = 4096; static const int DOSStubSize = 64; static const int NumberfOfDataDirectory = 16; static const int HeaderSize = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + sizeof(pe32plus_header) + sizeof(data_directory) * NumberfOfDataDirectory; namespace lld { namespace coff { OutputSection::OutputSection(StringRef N, uint32_t SI) : Name(N), SectionIndex(SI) { memset(&Header, 0, sizeof(Header)); strncpy(Header.Name, Name.data(), std::min(Name.size(), size_t(8))); } void OutputSection::setRVA(uint64_t RVA) { Header.VirtualAddress = RVA; for (Chunk *C : Chunks) C->setRVA(C->getRVA() + RVA); } void OutputSection::setFileOffset(uint64_t Off) { // If a section has no actual data (i.e. BSS section), we want to // set 0 to its PointerToRawData. Otherwise the output is rejected // by the loader. if (Header.SizeOfRawData == 0) return; Header.PointerToRawData = Off; for (Chunk *C : Chunks) C->setFileOff(C->getFileOff() + Off); } void OutputSection::addChunk(Chunk *C) { Chunks.push_back(C); uint64_t Off = Header.VirtualSize; Off = RoundUpToAlignment(Off, C->getAlign()); C->setRVA(Off); C->setFileOff(Off); Off += C->getSize(); Header.VirtualSize = Off; if (C->hasData()) Header.SizeOfRawData = RoundUpToAlignment(Off, FileAlignment); } void OutputSection::addPermissions(uint32_t C) { Header.Characteristics = Header.Characteristics | (C & PermMask); } void Writer::markLive() { Entry = cast(Symtab->find(Config->EntryName)); Entry->markLive(); for (Chunk *C : Symtab->getChunks()) if (C->isRoot()) C->markLive(); } void Writer::createSections() { std::map> Map; for (Chunk *C : Symtab->getChunks()) { if (!C->isLive()) { if (Config->Verbose) C->printDiscardedMessage(); continue; } // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. Map[C->getSectionName().split('$').first].push_back(C); } // Input sections are ordered by their names including '$' parts, // which gives you some control over the output layout. auto Comp = [](Chunk *A, Chunk *B) { return A->getSectionName() < B->getSectionName(); }; for (auto &P : Map) { StringRef SectionName = P.first; std::vector &Chunks = P.second; std::stable_sort(Chunks.begin(), Chunks.end(), Comp); auto Sec = llvm::make_unique(SectionName, OutputSections.size()); for (Chunk *C : Chunks) { C->setOutputSection(Sec.get()); Sec->addChunk(C); Sec->addPermissions(C->getPermissions()); } OutputSections.push_back(std::move(Sec)); } } std::map> Writer::binImports() { // Group DLL-imported symbols by DLL name because that's how symbols // are layed out in the import descriptor table. std::map> Res; OutputSection *Text = createSection(".text"); for (std::unique_ptr &P : Symtab->ImportFiles) { for (SymbolBody *B : P->getSymbols()) { if (auto *Import = dyn_cast(B)) { Res[Import->getDLLName()].push_back(Import); continue; } // Linker-created function thunks for DLL symbols are added to // .text section. Text->addChunk(cast(B)->getChunk()); } } // Sort symbols by name for each group. auto Comp = [](DefinedImportData *A, DefinedImportData *B) { return A->getName() < B->getName(); }; for (auto &P : Res) { std::vector &V = P.second; std::sort(V.begin(), V.end(), Comp); } return Res; } // Create .idata section contents. void Writer::createImportTables() { if (Symtab->ImportFiles.empty()) return; std::vector Tabs; for (auto &P : binImports()) { StringRef DLLName = P.first; std::vector &Imports = P.second; Tabs.emplace_back(DLLName, Imports); } OutputSection *Idata = createSection(".idata"); size_t NumChunks = Idata->getChunks().size(); // Add the directory tables. for (ImportTable &T : Tabs) Idata->addChunk(T.DirTab); Idata->addChunk(new NullChunk(sizeof(ImportDirectoryTableEntry))); ImportDirectoryTableSize = (Tabs.size() + 1) * sizeof(ImportDirectoryTableEntry); // Add the import lookup tables. for (ImportTable &T : Tabs) { for (LookupChunk *C : T.LookupTables) Idata->addChunk(C); Idata->addChunk(new NullChunk(sizeof(uint64_t))); } // Add the import address tables. Their contents are the same as the // lookup tables. for (ImportTable &T : Tabs) { for (LookupChunk *C : T.AddressTables) Idata->addChunk(C); Idata->addChunk(new NullChunk(sizeof(uint64_t))); ImportAddressTableSize += (T.AddressTables.size() + 1) * sizeof(uint64_t); } ImportAddressTable = Tabs[0].AddressTables[0]; // Add the hint name table. for (ImportTable &T : Tabs) for (HintNameChunk *C : T.HintNameTables) Idata->addChunk(C); // Add DLL names. for (ImportTable &T : Tabs) Idata->addChunk(T.DLLName); // Claim ownership of all chunks in the .idata section. for (size_t I = NumChunks, E = Idata->getChunks().size(); I < E; ++I) Chunks.push_back(std::unique_ptr(Idata->getChunks()[I])); } // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { auto IsEmpty = [](const std::unique_ptr &S) { return S->getVirtualSize() == 0; }; OutputSections.erase( std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty), OutputSections.end()); } // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { SizeOfHeaders = RoundUpToAlignment( HeaderSize + sizeof(coff_section) * OutputSections.size(), PageSize); uint64_t RVA = 0x1000; // The first page is kept unmapped. uint64_t FileOff = SizeOfHeaders; for (std::unique_ptr &Sec : OutputSections) { Sec->setRVA(RVA); Sec->setFileOffset(FileOff); RVA += RoundUpToAlignment(Sec->getVirtualSize(), PageSize); FileOff += RoundUpToAlignment(Sec->getRawSize(), FileAlignment); } SizeOfImage = SizeOfHeaders + RoundUpToAlignment(RVA - 0x1000, PageSize); FileSize = SizeOfHeaders + RoundUpToAlignment(FileOff - SizeOfHeaders, FileAlignment); } void Writer::writeHeader() { // Write DOS stub uint8_t *Buf = Buffer->getBufferStart(); auto *DOS = reinterpret_cast(Buf); Buf += DOSStubSize; DOS->Magic[0] = 'M'; DOS->Magic[1] = 'Z'; DOS->AddressOfRelocationTable = sizeof(dos_header); DOS->AddressOfNewExeHeader = DOSStubSize; // Write PE magic memcpy(Buf, PEMagic, sizeof(PEMagic)); Buf += sizeof(PEMagic); // Write COFF header auto *COFF = reinterpret_cast(Buf); Buf += sizeof(*COFF); COFF->Machine = IMAGE_FILE_MACHINE_AMD64; COFF->NumberOfSections = OutputSections.size(); COFF->Characteristics = (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_LARGE_ADDRESS_AWARE); COFF->SizeOfOptionalHeader = sizeof(pe32plus_header) + sizeof(data_directory) * NumberfOfDataDirectory; // Write PE header auto *PE = reinterpret_cast(Buf); Buf += sizeof(*PE); PE->Magic = PE32Header::PE32_PLUS; PE->ImageBase = Config->ImageBase; PE->SectionAlignment = SectionAlignment; PE->FileAlignment = FileAlignment; PE->MajorOperatingSystemVersion = 6; PE->MajorSubsystemVersion = 6; PE->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; PE->SizeOfImage = SizeOfImage; PE->SizeOfHeaders = SizeOfHeaders; PE->AddressOfEntryPoint = Entry->getRVA(); PE->SizeOfStackReserve = 1024 * 1024; PE->SizeOfStackCommit = 4096; PE->SizeOfHeapReserve = 1024 * 1024; PE->SizeOfHeapCommit = 4096; PE->NumberOfRvaAndSize = NumberfOfDataDirectory; if (OutputSection *Text = findSection(".text")) { PE->BaseOfCode = Text->getRVA(); PE->SizeOfCode = Text->getRawSize(); } PE->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory auto *DataDirectory = reinterpret_cast(Buf); Buf += sizeof(*DataDirectory) * NumberfOfDataDirectory; if (OutputSection *Idata = findSection(".idata")) { using namespace llvm::COFF; DataDirectory[IMPORT_TABLE].RelativeVirtualAddress = Idata->getRVA(); DataDirectory[IMPORT_TABLE].Size = ImportDirectoryTableSize; DataDirectory[IAT].RelativeVirtualAddress = ImportAddressTable->getRVA(); DataDirectory[IAT].Size = ImportAddressTableSize; } // Write section table coff_section *SectionTable = reinterpret_cast(Buf); int Idx = 0; for (std::unique_ptr &Sec : OutputSections) SectionTable[Idx++] = Sec->getHeader(); } std::error_code Writer::openFile(StringRef Path) { if (auto EC = FileOutputBuffer::create(Path, FileSize, Buffer, FileOutputBuffer::F_executable)) return make_dynamic_error_code(Twine("Failed to open ") + Path + ": " + EC.message()); return std::error_code(); } // Write section contents to a mmap'ed file. void Writer::writeSections() { uint8_t *Buf = Buffer->getBufferStart(); for (std::unique_ptr &Sec : OutputSections) { // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as // ADD instructions). if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE) memset(Buf + Sec->getFileOff(), 0xCC, Sec->getRawSize()); for (Chunk *C : Sec->getChunks()) if (C->hasData()) memcpy(Buf + C->getFileOff(), C->getData(), C->getSize()); } } OutputSection *Writer::findSection(StringRef Name) { for (std::unique_ptr &Sec : OutputSections) if (Sec->getName() == Name) return Sec.get(); return nullptr; } uint32_t Writer::getSizeOfInitializedData() { uint32_t Res = 0; for (std::unique_ptr &S : OutputSections) if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA) Res += S->getRawSize(); return Res; } // Returns an existing section or create a new one if not found. OutputSection *Writer::createSection(StringRef Name) { if (auto *Sec = findSection(Name)) return Sec; const auto R = IMAGE_SCN_MEM_READ; const auto W = IMAGE_SCN_MEM_WRITE; const auto E = IMAGE_SCN_MEM_EXECUTE; uint32_t Perm = StringSwitch(Name) .Case(".bss", IMAGE_SCN_CNT_UNINITIALIZED_DATA | R | W) .Case(".data", IMAGE_SCN_CNT_INITIALIZED_DATA | R | W) .Case(".idata", IMAGE_SCN_CNT_INITIALIZED_DATA | R) .Case(".rdata", IMAGE_SCN_CNT_INITIALIZED_DATA | R) .Case(".text", IMAGE_SCN_CNT_CODE | R | E) .Default(0); if (!Perm) llvm_unreachable("unknown section name"); auto Sec = new OutputSection(Name, OutputSections.size()); Sec->addPermissions(Perm); OutputSections.push_back(std::unique_ptr(Sec)); return Sec; } void Writer::applyRelocations() { uint8_t *Buf = Buffer->getBufferStart(); for (std::unique_ptr &Sec : OutputSections) for (Chunk *C : Sec->getChunks()) C->applyRelocations(Buf); } std::error_code Writer::write(StringRef OutputPath) { markLive(); createSections(); createImportTables(); assignAddresses(); removeEmptySections(); if (auto EC = openFile(OutputPath)) return EC; writeHeader(); writeSections(); applyRelocations(); if (auto EC = Buffer->commit()) return EC; return std::error_code(); } } // namespace coff } // namespace lld