// Merge identical COMDAT sections. // Two sections are considered the same if their section headers, // contents and relocations are all the same. void ICF::run(const std::vector<Chunk *> &Vec) { // Collect only mergeable sections and group by hash value. parallel_for_each(Vec.begin(), Vec.end(), [&](Chunk *C) { if (auto *SC = dyn_cast<SectionChunk>(C)) { bool Global = SC->Sym && SC->Sym->isExternal(); bool Writable = SC->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE; if (SC->isCOMDAT() && SC->isLive() && Global && !Writable) SC->GroupID = getHash(SC) | (uint64_t(1) << 63); } }); std::vector<SectionChunk *> Chunks; for (Chunk *C : Vec) { if (auto *SC = dyn_cast<SectionChunk>(C)) { if (SC->GroupID) { Chunks.push_back(SC); } else { SC->GroupID = NextID++; } } } // From now on, sections in Chunks are ordered so that sections in // the same group are consecutive in the vector. std::sort(Chunks.begin(), Chunks.end(), [](SectionChunk *A, SectionChunk *B) { return A->GroupID < B->GroupID; }); // Split groups until we get a convergence. int Cnt = 1; forEachGroup(Chunks, equalsConstant); for (;;) { if (!forEachGroup(Chunks, equalsVariable)) break; ++Cnt; } if (Config->Verbose) llvm::outs() << "\nICF needed " << Cnt << " iterations.\n"; // Merge sections in the same group. for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) { SectionChunk *Head = *It++; auto Bound = std::find_if(It, End, [&](SectionChunk *SC) { return Head->GroupID != SC->GroupID; }); if (It == Bound) continue; if (Config->Verbose) llvm::outs() << "Selected " << Head->getDebugName() << "\n"; while (It != Bound) { SectionChunk *SC = *It++; if (Config->Verbose) llvm::outs() << " Removed " << SC->getDebugName() << "\n"; Head->replace(SC); } } }
static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) { SectionChunk *Sec = findByName(File->getDebugChunks(), SecName); if (!Sec) return {}; // First 4 bytes are section magic. ArrayRef<uint8_t> Data = Sec->getContents(); if (Data.size() < 4) fatal(SecName + " too short"); if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) fatal(SecName + " has an invalid magic"); return Data.slice(4); }
// Set live bit on for each reachable chunk. Unmarked (unreachable) // COMDAT chunks will be ignored by Writer, so they will be excluded // from the final output. void markLive(ArrayRef<Chunk *> Chunks) { ScopedTimer T(GCTimer); // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark // as we push, so sections never appear twice in the list. SmallVector<SectionChunk *, 256> Worklist; // COMDAT section chunks are dead by default. Add non-COMDAT chunks. for (Chunk *C : Chunks) if (auto *SC = dyn_cast<SectionChunk>(C)) if (SC->isLive()) Worklist.push_back(SC); auto Enqueue = [&](SectionChunk *C) { if (C->isLive()) return; C->markLive(); Worklist.push_back(C); }; auto AddSym = [&](Symbol *B) { if (auto *Sym = dyn_cast<DefinedRegular>(B)) Enqueue(Sym->getChunk()); else if (auto *Sym = dyn_cast<DefinedImportData>(B)) Sym->File->Live = true; else if (auto *Sym = dyn_cast<DefinedImportThunk>(B)) Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true; }; // Add GC root chunks. for (Symbol *B : Config->GCRoot) AddSym(B); while (!Worklist.empty()) { SectionChunk *SC = Worklist.pop_back_val(); assert(SC->isLive() && "We mark as live when pushing onto the worklist!"); // Mark all symbols listed in the relocation table for this section. for (Symbol *B : SC->symbols()) if (B) AddSym(B); // Mark associative sections if any. for (SectionChunk *C : SC->children()) Enqueue(C); } }
template <typename PEHeaderTy> void Writer::writeHeader() { // Write DOS stub uint8_t *Buf = Buffer->getBufferStart(); auto *DOS = reinterpret_cast<dos_header *>(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<coff_file_header *>(Buf); Buf += sizeof(*COFF); COFF->Machine = Config->Machine; COFF->NumberOfSections = OutputSections.size(); COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; if (Config->LargeAddressAware) COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; if (!Config->is64()) COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE; if (Config->DLL) COFF->Characteristics |= IMAGE_FILE_DLL; if (!Config->Relocatable) COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; COFF->SizeOfOptionalHeader = sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory; // Write PE header auto *PE = reinterpret_cast<PEHeaderTy *>(Buf); Buf += sizeof(*PE); PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; PE->ImageBase = Config->ImageBase; PE->SectionAlignment = PageSize; PE->FileAlignment = SectorSize; PE->MajorImageVersion = Config->MajorImageVersion; PE->MinorImageVersion = Config->MinorImageVersion; PE->MajorOperatingSystemVersion = Config->MajorOSVersion; PE->MinorOperatingSystemVersion = Config->MinorOSVersion; PE->MajorSubsystemVersion = Config->MajorOSVersion; PE->MinorSubsystemVersion = Config->MinorOSVersion; PE->Subsystem = Config->Subsystem; PE->SizeOfImage = SizeOfImage; PE->SizeOfHeaders = SizeOfHeaders; if (!Config->NoEntry) { Defined *Entry = cast<Defined>(Config->Entry->repl()); PE->AddressOfEntryPoint = Entry->getRVA(); // Pointer to thumb code must have the LSB set, so adjust it. if (Config->Machine == ARMNT) PE->AddressOfEntryPoint |= 1; } PE->SizeOfStackReserve = Config->StackReserve; PE->SizeOfStackCommit = Config->StackCommit; PE->SizeOfHeapReserve = Config->HeapReserve; PE->SizeOfHeapCommit = Config->HeapCommit; if (Config->DynamicBase) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; if (Config->HighEntropyVA) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; if (!Config->AllowBind) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; if (Config->NxCompat) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; if (!Config->AllowIsolation) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; if (Config->TerminalServerAware) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; PE->NumberOfRvaAndSize = NumberfOfDataDirectory; if (OutputSection *Text = findSection(".text")) { PE->BaseOfCode = Text->getRVA(); PE->SizeOfCode = Text->getRawSize(); } PE->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory auto *Dir = reinterpret_cast<data_directory *>(Buf); Buf += sizeof(*Dir) * NumberfOfDataDirectory; if (OutputSection *Sec = findSection(".edata")) { Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA(); Dir[EXPORT_TABLE].Size = Sec->getVirtualSize(); } if (!Idata.empty()) { Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA(); Dir[IMPORT_TABLE].Size = Idata.getDirSize(); Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA(); Dir[IAT].Size = Idata.getIATSize(); } if (!DelayIdata.empty()) { Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = DelayIdata.getDirRVA(); Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize(); } if (OutputSection *Sec = findSection(".rsrc")) { Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA(); Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize(); } if (OutputSection *Sec = findSection(".reloc")) { Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA(); Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); } if (OutputSection *Sec = findSection(".pdata")) { Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA(); Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize(); } if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { if (Defined *B = dyn_cast<Defined>(Sym->Body)) { Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); Dir[TLS_TABLE].Size = Config->is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); } } if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { if (auto *B = dyn_cast<DefinedRegular>(Sym->Body)) { SectionChunk *SC = B->getChunk(); assert(B->getRVA() >= SC->getRVA()); uint64_t OffsetInChunk = B->getRVA() - SC->getRVA(); if (!SC->hasData() || OffsetInChunk + 4 > SC->getSize()) fatal("_load_config_used is malformed"); ArrayRef<uint8_t> SecContents = SC->getContents(); uint32_t LoadConfigSize = *reinterpret_cast<const ulittle32_t *>(&SecContents[OffsetInChunk]); if (OffsetInChunk + LoadConfigSize > SC->getSize()) fatal("_load_config_used is too large"); Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA(); Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize; } } // Write section table for (OutputSection *Sec : OutputSections) { Sec->writeHeaderTo(Buf); Buf += sizeof(coff_section); } if (OutputSymtab.empty()) return; COFF->PointerToSymbolTable = PointerToSymbolTable; uint32_t NumberOfSymbols = OutputSymtab.size(); COFF->NumberOfSymbols = NumberOfSymbols; auto *SymbolTable = reinterpret_cast<coff_symbol16 *>( Buffer->getBufferStart() + COFF->PointerToSymbolTable); for (size_t I = 0; I != NumberOfSymbols; ++I) SymbolTable[I] = OutputSymtab[I]; // Create the string table, it follows immediately after the symbol table. // The first 4 bytes is length including itself. Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]); write32le(Buf, Strtab.size() + 4); if (!Strtab.empty()) memcpy(Buf + 4, Strtab.data(), Strtab.size()); }