Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -104,6 +104,7 @@ bool Bsymbolic; bool BsymbolicFunctions; bool ColorDiagnostics = false; + bool CompressDebugSections; bool DefineCommon; bool Demangle = true; bool DisableVerify; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -45,6 +45,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Object/Decompressor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" @@ -564,12 +565,25 @@ return Ret; } +static bool getCompressDebugSections(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_compress_debug_sections)) { + StringRef S = Arg->getValue(); + if (S == "none") + return false; + if (S == "zlib") + return zlib::isAvailable(); + error("unknown --compress-debug-sections value: " + S); + } + return false; +} + // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->AuxiliaryList = getArgs(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->CompressDebugSections = getCompressDebugSections(Args); Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common, !Args.hasArg(OPT_relocatable)); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -22,6 +22,9 @@ def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">; +def compress_debug_sections : J<"compress-debug-sections=">, + HelpText<"Compress DWARF debug sections">; + def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, HelpText<"Add a directory to the library search path">; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -84,9 +84,14 @@ uint32_t getFiller(); template void writeTo(uint8_t *Buf); template void finalize(); + template void maybeCompress(); void assignOffsets(); std::vector Sections; + // Used for implementation of --compress-debug-sections option. + llvm::SmallVector CompressedData; + std::vector CompressedHeader; + // Location in the output buffer. uint8_t *Loc = nullptr; }; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -16,6 +16,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Threads.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" @@ -83,6 +84,52 @@ return LA->OutSecOff < LB->OutSecOff; } +// Compressed sections has header which we create in this function. +// Format is explaned here: +// https://docs.oracle.com/cd/E53394_01/html/E54813/section_compression.html +template +static std::vector createHeader(size_t Size, uint32_t Alignment) { + const endianness E = ELFT::TargetEndianness; + + std::vector Ret(sizeof(ELFT::Chdr)); + uint8_t *Buf = &Ret[0]; + write32(Buf, ELFCOMPRESS_ZLIB); + Buf += 4; + + if (Config->Is64) { + Buf += sizeof(Elf64_Word); // Skip ch_reserved field. + write64(Buf, Size); + Buf += sizeof(Elf64_Xword); + write64(Buf, Alignment); + Buf += sizeof(Elf64_Xword); + } else { + write32(Buf, Size); + Buf += sizeof(Elf32_Word); + write32(Buf, Alignment); + Buf += sizeof(Elf32_Word); + } + + return Ret; +} + +template void OutputSection::maybeCompress() { + // If -compress-debug-sections is specified, we compress output debug + // sections. + if (!Config->CompressDebugSections || !Name.startswith(".debug_") || + (Flags & SHF_ALLOC)) + return; + + this->Flags |= SHF_COMPRESSED; + CompressedHeader = createHeader(this->Size, this->Alignment); + + // Here we write relocated content of sections and compress it. + std::vector Data(this->Size); + this->writeTo(&Data[0]); + zlib::compress(StringRef((char *)Data.data(), Data.size()), CompressedData); + + this->Size = this->CompressedHeader.size() + this->CompressedData.size(); +} + template void OutputSection::finalize() { if ((this->Flags & SHF_LINK_ORDER) && !this->Sections.empty()) { std::sort(Sections.begin(), Sections.end(), compareByFilePosition); @@ -245,6 +292,15 @@ template void OutputSection::writeTo(uint8_t *Buf) { Loc = Buf; + // We may have already rendered compressed content when using + // -compress-debug-sections option. Write it together with header. + if (!CompressedData.empty()) { + memcpy(Buf, CompressedHeader.data(), CompressedHeader.size()); + Buf += CompressedHeader.size(); + memcpy(Buf, CompressedData.data(), CompressedData.size()); + return; + } + // Write leading padding. uint32_t Filler = getFiller(); if (Filler) @@ -422,6 +478,11 @@ template void OutputSection::finalize(); template void OutputSection::finalize(); +template void OutputSection::maybeCompress(); +template void OutputSection::maybeCompress(); +template void OutputSection::maybeCompress(); +template void OutputSection::maybeCompress(); + template void OutputSection::writeTo(uint8_t *Buf); template void OutputSection::writeTo(uint8_t *Buf); template void OutputSection::writeTo(uint8_t *Buf); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -19,6 +19,7 @@ #include "SymbolTable.h" #include "SyntheticSections.h" #include "Target.h" +#include "Threads.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/FileOutputBuffer.h" @@ -1216,6 +1217,11 @@ for (OutputSection *Sec : OutputSections) Sec->finalize(); + // Some sections may require compression. That happens for + // debug sections when --compress-debug-sections option used. + parallelForEach(OutputSections.begin(), OutputSections.end(), + [](OutputSection *S) { S->maybeCompress(); }); + // createThunks may have added local symbols to the static symbol table applySynthetic({In::SymTab, In::ShStrTab, In::StrTab}, [](SyntheticSection *SS) { SS->postThunkContents(); }); Index: test/ELF/compress-debug-output.s =================================================================== --- test/ELF/compress-debug-output.s +++ test/ELF/compress-debug-output.s @@ -0,0 +1,33 @@ +# REQUIRES: x86 +# REQUIRES: zlib + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t1 --compress-debug-sections=zlib + +# RUN: llvm-objdump -s %t1 | FileCheck %s --check-prefix=ZLIBCONTENT +# ZLIBCONTENT: Contents of section .debug_str: +# ZLIBCONTENT-NOT: AAAAAAAAA + +# RUN: llvm-readobj -s %t1 | FileCheck %s --check-prefix=ZLIBFLAGS +# ZLIBFLAGS: Section { +# ZLIBFLAGS: Index: +# ZLIBFLAGS: Name: .debug_str +# ZLIBFLAGS-NEXT: Type: SHT_PROGBITS +# ZLIBFLAGS-NEXT: Flags [ +# ZLIBFLAGS-NEXT: SHF_COMPRESSED + +# RUN: llvm-dwarfdump %t1 -debug-dump=str | \ +# RUN: FileCheck %s --check-prefix=DEBUGSTR +# DEBUGSTR: .debug_str contents: +# DEBUGSTR-NEXT: AAAAAAAAAAAAAAAAAAAAAAAAAAA +# DEBUGSTR-NEXT: BBBBBBBBBBBBBBBBBBBBBBBBBBB + +# RUN: not ld.lld %t.o -o %t1 --compress-debug-sections=zlib-gabi 2>&1 | \ +# RUN: FileCheck -check-prefix=ERR %s +# ERR: unknown --compress-debug-sections value: zlib-gabi + +.section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "AAAAAAAAAAAAAAAAAAAAAAAAAAA" +.Linfo_string1: + .asciz "BBBBBBBBBBBBBBBBBBBBBBBBBBB"