Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -39,6 +39,7 @@ struct Configuration { Symbol *EntrySym = nullptr; InputFile *FirstElf = nullptr; + llvm::StringRef CompressDebugSections; llvm::StringRef DynamicLinker; llvm::StringRef Entry; llvm::StringRef Emulation; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -310,6 +310,7 @@ Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->CompressDebugSections = getString(Args, OPT_compress_debug_sections, "none"); Config->Demangle = !Args.hasArg(OPT_no_demangle); Config->DisableVerify = Args.hasArg(OPT_disable_verify); Config->DiscardAll = Args.hasArg(OPT_discard_all); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -29,6 +29,9 @@ def as_needed : Flag<["--"], "as-needed">; +def compress_debug_sections : Joined<["--"], "compress-debug-sections=">, + HelpText<"Force undefined symbol during linking">; + def disable_new_dtags : Flag<["--"], "disable-new-dtags">, HelpText<"Disable new dynamic tags">; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -296,7 +296,7 @@ typedef typename ELFT::Rel Elf_Rel; typedef typename ELFT::Rela Elf_Rela; typedef typename ELFT::uint uintX_t; - OutputSection(StringRef Name, uint32_t Type, uintX_t Flags); + OutputSection(StringRef Name, uint32_t Type, uintX_t Flags, bool Compress); void addSection(InputSectionBase *C) override; void sortInitFini(); void sortCtorsDtors(); @@ -305,6 +305,14 @@ void forEachInputSection(std::function *)> F) override; std::vector *> Sections; + +private: + void finalizeCompressed(); + void writeCompressed(uint8_t *Buf); + + bool Compress; + SmallVector CompressedData; + uint64_t UncompressedSize = 0; }; template Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -13,6 +13,7 @@ #include "SymbolTable.h" #include "Target.h" #include "lld/Core/Parallel.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" @@ -815,15 +816,73 @@ } template -OutputSection::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags) - : OutputSectionBase(Name, Type, Flags) { +OutputSection::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags, + bool Compress) + : OutputSectionBase(Name, Type, Flags), Compress(Compress) { if (Type == SHT_RELA) this->Header.sh_entsize = sizeof(Elf_Rela); else if (Type == SHT_REL) this->Header.sh_entsize = sizeof(Elf_Rel); + + if (!Compress) + return; + + // Compress candidate sections using ZLIB compression. The resulting output + // sections have the SHF_COMPRESSED section flag set to identify the use of + // compression. + if (Config->CompressDebugSections == "zlib-gabi" || + Config->CompressDebugSections == "zlib") { + Header.sh_flags |= SHF_COMPRESSED; + } + else { + // Compress all candidate sections using ZLIB compression, using the GNU + // section compression format. This format requires candidate sections to + // have a name that begins with.debug. The resulting output sections are + // renamed to start with.zdebug to identify the use of compression. + StringSaver Saver(ScriptConfig->Alloc); + this->Name = Saver.save(Twine(".z") + Name.drop_front(1)); + } +} + +template static size_t getCompressionHeaderSize() { + if (Config->CompressDebugSections == "none") + return 0; + // The gnu-style header consist from 'zlib' signature and + // 8 bytes length of uncompressed data: + // https://docs.oracle.com/cd/E53394_01/html/E54813/section_compression.html + if (Config->CompressDebugSections == "zlib-gnu") + return 12; + return ELFT::Is64Bits ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr); +} + +template void OutputSection::finalizeCompressed() { + std::vector Uncompressed(getSize()); + uint8_t *Data = Uncompressed.data(); + for (InputSection *C : Sections) + C->writeTo(Data); + + SmallVector Compressed; + zlib::Status Success = + zlib::compress(StringRef((const char *)Data, getSize()), Compressed, + zlib::BestSizeCompression); + + // If not successfully compressed or total size is greater than + // size before applying compression, then cancel compressed output. + size_t TotalSize = Compressed.size() + getCompressionHeaderSize(); + Compress = Success == zlib::Status::StatusOK && TotalSize < getSize(); + if (!Compress) + return; + + CompressedData.swap(Compressed); + UncompressedSize = getSize(); + setSize(TotalSize); } template void OutputSection::finalize() { + if (Compress) { + finalizeCompressed(); + return; + } uint32_t Type = this->Header.sh_type; if (Type != SHT_RELA && Type != SHT_REL) return; @@ -950,10 +1009,35 @@ memcpy(Buf + I, A.data(), Size - I); } +template void OutputSection::writeCompressed(uint8_t *Buf) { + const endianness E = ELFT::TargetEndianness; + if (Config->CompressDebugSections == "zlib-gnu") { + // 4 bytes magic + size of uncompressed data in big endian. + memcpy(Buf, "ZLIB", 4); + write64be(Buf + 4, UncompressedSize); + } else { + // "zlib-gabi" or "zlib". + write32(Buf, ELFCOMPRESS_ZLIB); + if (ELFT::Is64Bits) + write64(Buf + 2 * sizeof(Elf64_Word), UncompressedSize); + else + write32(Buf + sizeof(Elf32_Word), UncompressedSize); + } + + memcpy(Buf + getCompressionHeaderSize(), CompressedData.data(), + CompressedData.size()); +} + template void OutputSection::writeTo(uint8_t *Buf) { + if (Compress) { + writeCompressed(Buf); + return; + } + ArrayRef Filler = Script::X->getFiller(this->Name); if (!Filler.empty()) fill(Buf, this->getSize(), Filler); + if (Config->Threads) { parallel_for_each(Sections.begin(), Sections.end(), [=](InputSection *C) { C->writeTo(Buf); }); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/StringSaver.h" @@ -178,7 +179,8 @@ // See "Dynamic section" in Chapter 5 in the following document: // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf MipsRldMap.reset(new OutputSection(".rld_map", SHT_PROGBITS, - SHF_ALLOC | SHF_WRITE)); + SHF_ALLOC | SHF_WRITE, + false /* Compression */)); MipsRldMap->setSize(sizeof(uintX_t)); MipsRldMap->updateAlign(sizeof(uintX_t)); } @@ -1002,8 +1004,8 @@ template void Writer::ensureBss() { if (Out::Bss) return; - Out::Bss = - new OutputSection(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); + Out::Bss = new OutputSection( + ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE, false /* Compression */); OwningSections.emplace_back(Out::Bss); OutputSections.push_back(Out::Bss); } @@ -1157,6 +1159,20 @@ } template +static bool isCompressionCandidate(InputSectionBase *C, + StringRef OutSecName) { + if (Config->CompressDebugSections == "none") + return false; + // SHF_COMPRESSED applies only to non - allocable sections, and + // cannot be used in conjunction with SHF_ALLOC. + if (C->getSectionHdr()->sh_flags & SHF_ALLOC) + return false; + if (!OutSecName.startswith(".debug")) + return false; + return zlib::isAvailable(); +} + +template std::pair *, bool> OutputSectionFactory::create(InputSectionBase *C, StringRef OutsecName) { @@ -1167,7 +1183,8 @@ switch (C->SectionKind) { case InputSectionBase::Regular: - Sec = new OutputSection(Key.Name, Key.Type, Key.Flags); + Sec = new OutputSection(Key.Name, Key.Type, Key.Flags, + isCompressionCandidate(C, OutsecName)); break; case InputSectionBase::EHFrame: Sec = new EHOutputSection(Key.Name, Key.Type, Key.Flags);