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"