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" @@ -551,12 +552,26 @@ 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" || S == "zlib-gabi") + return zlib::isAvailable(); + // We support only default zlib compression style now. + 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 : Joined<["--"], "compress-debug-sections=">, + HelpText<"Compresses 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 @@ -87,6 +87,13 @@ void assignOffsets(); std::vector Sections; + // Contains compressed relocated content, used for implementation of + // --compress-debug-sections option. + std::vector CompressedData; + + // Size of decompressed data. Only has meaning if compression enabled. + size_t DecompressedSize; + // 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" @@ -67,7 +68,13 @@ OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags) : SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type, /*Info*/ 0, - /*Link*/ 0) {} + /*Link*/ 0) { + if (!Config->CompressDebugSections || !Name.startswith(".debug")) + return; + // SHF_COMPRESSED can not be used in conjunction with SHF_ALLOC flag. + if (!(Flags & SHF_ALLOC)) + this->Flags |= SHF_COMPRESSED; +} static bool compareByFilePosition(InputSection *A, InputSection *B) { // Synthetic doesn't have link order dependecy, stable_sort will keep it last @@ -96,6 +103,33 @@ this->Link = D->OutSec->SectionIndex; } + if (this->Flags & SHF_COMPRESSED) { + assignOffsets(); + + std::unique_ptr Compressor = + check(zlib::StreamCompression::create(1024)); + for (size_t I = 0, E = Sections.size(); I != E; ++I) { + InputSection *Sec = Sections[I]; + size_t Size; + if (I == E - 1) + Size = Sec->OutSecOff + Sec->getSize(); + else + Size = Sections[I + 1]->OutSecOff - Sec->OutSecOff; + + std::vector RelData(Size); + Sec->relocate(RelData.data(), RelData.data() + Size); + + Compressor->compress(RelData, I == E - 1, [&](ArrayRef Piece) { + CompressedData.insert(CompressedData.end(), Piece.begin(), Piece.end()); + }); + } + + this->DecompressedSize = this->Size; + this->Size = this->CompressedData.size() + Config->Is64 + ? sizeof(Elf64_Chdr) + : sizeof(Elf32_Chdr); + } + uint32_t Type = this->Type; if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL)) return; @@ -247,9 +281,40 @@ return 0; } +static void writeCompressed(uint8_t *Buf, ArrayRef Data, + size_t OrigSize, uint32_t OrigAlignment) { + llvm::support::endianness E = Config->Endianness; + write32(Buf, ELFCOMPRESS_ZLIB, E); + Buf += 4; + + if (Config->Is64) { + Buf += sizeof(Elf64_Word); // Skip ch_reserved field. + write64(Buf, OrigSize, E); + Buf += sizeof(Elf64_Xword); + write64(Buf, OrigAlignment, E); + Buf += sizeof(Elf64_Xword); + } else { + write32(Buf, OrigSize, E); + Buf += sizeof(Elf32_Word); + write32(Buf, OrigAlignment, E); + Buf += sizeof(Elf32_Word); + } + + memcpy(Buf, Data.data(), Data.size()); +} + template void OutputSection::writeTo(uint8_t *Buf) { Loc = Buf; + // If we have compressed output section here, we should write + // header and data that we already prepared in finilize(). + // Sections compression header format is explaned here: + // https://docs.oracle.com/cd/E53394_01/html/E54813/section_compression.html + if (!CompressedData.empty()) { + writeCompressed(Buf, CompressedData, DecompressedSize, Alignment); + return; + } + uint32_t Filler = getFill(); // Write leading padding. Index: test/ELF/compress-debug-sections.s =================================================================== --- test/ELF/compress-debug-sections.s +++ test/ELF/compress-debug-sections.s @@ -0,0 +1,54 @@ +# 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 +# RUN: llvm-readobj -s %t1 | FileCheck %s --check-prefix=ZLIBFLAGS +# RUN: ld.lld %t.o -o %t2 --compress-debug-sections=zlib-gabi +# RUN: llvm-objdump -s %t2 | FileCheck %s --check-prefix=ZLIBCONTENT +# RUN: llvm-readobj -s %t2 | FileCheck %s --check-prefix=ZLIBFLAGS + +# ZLIBCONTENT: Contents of section .debug_str: +# ZLIBCONTENT-NEXT: 0000 01000000 00000000 38000000 00000000 +# ZLIBCONTENT-NEXT: 0010 01000000 00000000 789c6360 200f0000 +# ZLIBCONTENT-NEXT: 0020 00380001 +# ZLIBFLAGS: Section { +# ZLIBFLAGS: Index: +# ZLIBFLAGS: Name: .debug_str +# ZLIBFLAGS-NEXT: Type: SHT_PROGBITS +# ZLIBFLAGS-NEXT: Flags [ +# ZLIBFLAGS-NEXT: SHF_COMPRESSED +# ZLIBFLAGS-NEXT: SHF_MERGE +# ZLIBFLAGS-NEXT: SHF_STRINGS +# ZLIBFLAGS-NEXT: ] +# ZLIBFLAGS-NEXT: Address: +# ZLIBFLAGS-NEXT: Offset: +# ZLIBFLAGS-NEXT: Size: 36 + +# RUN: ld.lld %t.o -o %t3 --compress-debug-sections=none +# RUN: llvm-objdump -s %t3 | FileCheck %s --check-prefix=CONTENT +# RUN: llvm-readobj -s %t3 | FileCheck %s --check-prefix=FLAGS + +# CONTENT: Contents of section .debug_str: +# CONTENT-NEXT: 0000 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA +# CONTENT-NEXT: 0010 41414141 41414141 41414100 42424242 AAAAAAAAAAA.BBBB +# CONTENT-NEXT: 0020 42424242 42424242 42424242 42424242 BBBBBBBBBBBBBBBB +# CONTENT-NEXT: 0030 42424242 42424200 BBBBBBB. +# FLAGS: Section { +# FLAGS: Index: +# FLAGS: Name: .debug_str +# FLAGS-NEXT: Type: SHT_PROGBITS +# FLAGS-NEXT: Flags [ +# FLAGS-NEXT: SHF_MERGE +# FLAGS-NEXT: SHF_STRINGS +# FLAGS-NEXT: ] +# FLAGS-NEXT: Address: 0x0 +# FLAGS-NEXT: Offset: 0x1000 +# FLAGS-NEXT: Size: 56 + +.section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "AAAAAAAAAAAAAAAAAAAAAAAAAAA" +.Linfo_string1: + .asciz "BBBBBBBBBBBBBBBBBBBBBBBBBBB"