Index: test/tools/llvm-objcopy/compress-and-decompress-debug-sections-error.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/compress-and-decompress-debug-sections-error.test @@ -0,0 +1,5 @@ +# RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t.o +# RUN: not llvm-objcopy --compress-debug-sections=zlib --decompress-debug-sections %t.o 2>&1 | FileCheck %s + +# CHECK: Cannot specify --compress-debug-sections as well as --decompress-debug-sections at the same time. + Index: test/tools/llvm-objcopy/compress-debug-sections.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/compress-debug-sections.test @@ -0,0 +1,18 @@ +# REQUIRES: zlib + +# RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t.o + +# RUN: llvm-objcopy --compress-debug-sections=zlib %t.o %tz.o +# RUN: llvm-objcopy --compress-debug-sections=zlib-gnu %t.o %tzg.o + +# RUN: llvm-objcopy --decompress-debug-sections %tz.o %t2.o +# RUN: llvm-objcopy --decompress-debug-sections %tzg.o %t3.o + +# Using redirects to avoid llvm-objdump from printing the filename. +# RUN: llvm-objdump -s -section=.debug_str - < %t.o > %t.txt +# RUN: llvm-objdump -s -section=.debug_str - < %t2.o > %t2.txt +# RUN: llvm-objdump -s -section=.debug_str - < %t3.o > %t3.txt + +# RUN: diff %t.txt %t2.txt +# RUN: diff %t.txt %t3.txt + Index: test/tools/llvm-objcopy/decompress-debug-sections-zlib-gnu.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/decompress-debug-sections-zlib-gnu.test @@ -0,0 +1,52 @@ +# REQUIRES: zlib + +# RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t.o +# RUN: llvm-objcopy --compress-debug-sections=zlib-gnu %t.o %t-compressed.o +# RUN: llvm-objcopy --decompress-debug-sections %t-compressed.o %t-decompressed.o + +# RUN: llvm-objdump -s %t.o -section=.debug_foo | FileCheck %s +# RUN: llvm-objdump -s %t-compressed.o | FileCheck %s --check-prefix=CHECK-COMPRESSED +# RUN: llvm-readobj -s -r %t-compressed.o | FileCheck %s --check-prefix=CHECK-FLAGS +# RUN: llvm-objdump -s %t-decompressed.o -section=.debug_foo | FileCheck %s + + +# CHECK: .debug_foo: + +# CHECK-COMPRESSED: .zdebug_foo: +# CHECK-COMPRESSED: ZLIB +# CHECK-COMPRESSED: .notdebug_foo: + +# CHECK-FLAGS-NOT: Name: .debug_foo +# CHECK-FLAGS: Index: 1 +# CHECK-FLAGS-NEXT: Name: .zdebug_foo +# CHECK-FLAGS-NEXT: Type: SHT_PROGBITS +# CHECK-FLAGS-NEXT: Flags [ +# CHECK-FLAGS-NEXT: ] +# CHECK-FLAGS-NEXT: Address: +# CHECK-FLAGS-NEXT: Offset: +# CHECK-FLAGS-NEXT: Size: 23 + +# CHECK-FLAGS: Name: .notdebug_foo +# CHECK-FLAGS-NEXT: Type: SHT_PROGBITS +# CHECK-FLAGS-NEXT: Flags [ +# CHECK-FLAGS-NEXT: ] +# CHECK-FLAGS-NEXT: Address: +# CHECK-FLAGS-NEXT: Offset: +# CHECK-FLAGS-NEXT: Size: 8 + +# CHECK-FLAGS: Name: .rela.debug_foo +# CHECK-FLAGS-NEXT: Type: SHT_RELA +# CHECK-FLAGS-NEXT: Flags [ +# CHECK-FLAGS-NEXT: ] +# CHECK-FLAGS-NEXT: Address: +# CHECK-FLAGS-NEXT: Offset: +# CHECK-FLAGS-NEXT: Size: +# CHECK-FLAGS-NEXT: Link: +# CHECK-FLAGS-NEXT: Info: 1 + +# CHECK-FLAGS: Relocations [ +# CHECK-FLAGS-NEXT: .rela.debug_foo { +# CHECK-FLAGS-NEXT: 0x1 R_X86_64_32 - 0x0 +# CHECK-FLAGS-NEXT: } +# CHECK-FLAGS-NEXT: ] + Index: test/tools/llvm-objcopy/decompress-debug-sections-zlib.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/decompress-debug-sections-zlib.test @@ -0,0 +1,50 @@ +# REQUIRES: zlib + +# RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t.o +# RUN: llvm-objcopy --compress-debug-sections=zlib %t.o %t-compressed.o +# RUN: llvm-objcopy --decompress-debug-sections %t-compressed.o %t-decompressed.o + +# RUN: llvm-objdump -s %t.o -section=.debug_foo | FileCheck %s +# RUN: llvm-objdump -s %t-compressed.o | FileCheck %s --check-prefix=CHECK-COMPRESSED +# RUN: llvm-readobj -s -r %t-compressed.o | FileCheck %s --check-prefix=CHECK-FLAGS +# RUN: llvm-objdump -s %t-decompressed.o -section=.debug_foo | FileCheck %s + +# CHECK: .debug_foo: + +# CHECK-COMPRESSED: .notdebug_foo: +# CHECK-COMPRESSED: .debug_foo: + +# CHECK-FLAGS: Index: 1 +# CHECK-FLAGS-NEXT: Name: .debug_foo +# CHECK-FLAGS-NEXT: Type: SHT_PROGBITS +# CHECK-FLAGS-NEXT: Flags [ +# CHECK-FLAGS-NEXT: SHF_COMPRESSED +# CHECK-FLAGS-NEXT: ] +# CHECK-FLAGS-NEXT: Address: +# CHECK-FLAGS-NEXT: Offset: +# CHECK-FLAGS-NEXT: Size: 35 + +# CHECK-FLAGS: Name: .notdebug_foo +# CHECK-FLAGS-NEXT: Type: SHT_PROGBITS +# CHECK-FLAGS-NEXT: Flags [ +# CHECK-FLAGS-NEXT: ] +# CHECK-FLAGS-NEXT: Address: +# CHECK-FLAGS-NEXT: Offset: +# CHECK-FLAGS-NEXT: Size: 8 + +# CHECK-FLAGS: Name: .rela.debug_foo +# CHECK-FLAGS-NEXT: Type: SHT_RELA +# CHECK-FLAGS-NEXT: Flags [ +# CHECK-FLAGS-NEXT: ] +# CHECK-FLAGS-NEXT: Address: +# CHECK-FLAGS-NEXT: Offset: +# CHECK-FLAGS-NEXT: Size: +# CHECK-FLAGS-NEXT: Link: +# CHECK-FLAGS-NEXT: Info: 1 + +# CHECK-FLAGS: Relocations [ +# CHECK-FLAGS-NEXT: .rela.debug_foo { +# CHECK-FLAGS-NEXT: 0x1 R_X86_64_32 - 0x0 +# CHECK-FLAGS-NEXT: } +# CHECK-FLAGS-NEXT: ] + Index: tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- tools/llvm-objcopy/ObjcopyOpts.td +++ tools/llvm-objcopy/ObjcopyOpts.td @@ -23,6 +23,8 @@ HelpText<"Compress DWARF debug sections using " "specified style. Supported styles: " "'zlib-gnu' and 'zlib'">; +def decompress_debug_sections : Flag<["-", "--"], "decompress-debug-sections">, + HelpText<"Decompress DWARF debug sections.">; def O : JoinedOrSeparate<["-"], "O">, Alias; defm split_dwo : Eq<"split-dwo">, Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -99,7 +99,6 @@ virtual ~SectionWriter(){}; void visit(const Section &Sec) override; - void visit(const OwnedDataSection &Sec) override; void visit(const StringTableSection &Sec) override; void visit(const DynamicRelocationSection &Sec) override; virtual void visit(const SymbolTableSection &Sec) override = 0; @@ -108,6 +107,7 @@ virtual void visit(const GroupSection &Sec) override = 0; virtual void visit(const SectionIndexSection &Sec) override = 0; virtual void visit(const CompressedSection &Sec) override = 0; + virtual void visit(const OwnedDataSection &Sec) override = 0; explicit SectionWriter(Buffer &Buf) : Out(Buf) {} }; @@ -127,6 +127,7 @@ void visit(const GroupSection &Sec) override; void visit(const SectionIndexSection &Sec) override; void visit(const CompressedSection &Sec) override; + void visit(const OwnedDataSection &Sec) override; explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; @@ -145,6 +146,7 @@ void visit(const GroupSection &Sec) override; void visit(const SectionIndexSection &Sec) override; void visit(const CompressedSection &Sec) override; + void visit(const OwnedDataSection &Sec) override; explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; @@ -269,6 +271,8 @@ uint64_t Offset = 0; uint64_t Size = 0; uint64_t Type = ELF::SHT_NULL; + uint64_t DecompressedSize = 0; + uint64_t DecompressedAlign = 1; ArrayRef OriginalData; SectionBase() = default; @@ -346,6 +350,7 @@ MAKE_SEC_WRITER_FRIEND std::vector Data; + bool DoDecompression = false; public: OwnedDataSection(StringRef SecName, ArrayRef Data) @@ -356,6 +361,17 @@ OriginalOffset = std::numeric_limits::max(); } + OwnedDataSection(const SectionBase &Sec, bool DoDecompression = false) + : SectionBase(Sec), DoDecompression(DoDecompression) { + if (!DoDecompression) + return; + + if (StringRef(Name).startswith(".zdebug")) + Name = "." + Name.substr(2); + Size = DecompressedSize; + Flags = (Flags & ~ELF::SHF_COMPRESSED); + } + void accept(SectionVisitor &Sec) const override; }; @@ -363,8 +379,6 @@ MAKE_SEC_WRITER_FRIEND DebugCompressionType CompressionType; - uint64_t DecompressedSize; - uint64_t DecompressedAlign; SmallVector CompressedData; public: @@ -786,4 +800,8 @@ } // end namespace objcopy } // end namespace llvm +bool isCompressedIgnoreContents(const llvm::objcopy::SectionBase &Section); +bool isCompressedGnuContents(const llvm::objcopy::SectionBase &Section); +bool isCompressed(const llvm::objcopy::SectionBase &Section); + #endif // LLVM_TOOLS_OBJCOPY_OBJECT_H Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -131,8 +131,29 @@ void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } -void SectionWriter::visit(const OwnedDataSection &Sec) { +template +void ELFSectionWriter::visit(const OwnedDataSection &Sec) { uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + + if (Sec.DoDecompression) { + size_t DecompressedSize = Sec.DecompressedSize; + SmallVector DecompressedContent; + const uint8_t *CompressedContent = + reinterpret_cast(Sec.OriginalData.data()) + + (isCompressedGnuContents(Sec) + ? (strlen("ZLIB") + sizeof(Sec.DecompressedSize)) + : sizeof(Elf_Chdr_Impl)); + + if (Error E = zlib::uncompress( + StringRef(reinterpret_cast(CompressedContent), + CompressedContent - Sec.OriginalData.data()), + DecompressedContent.data(), DecompressedSize)) + reportError(Sec.Name, std::move(E)); + + std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); + return; + } + std::copy(std::begin(Sec.Data), std::end(Sec.Data), Buf); } @@ -140,6 +161,10 @@ Visitor.visit(*this); } +void BinarySectionWriter::visit(const OwnedDataSection &Sec) { + error("Cannot write owned data section '" + Sec.Name + "' "); +} + void BinarySectionWriter::visit(const CompressedSection &Sec) { error("Cannot write compressed section '" + Sec.Name + "' "); } @@ -176,8 +201,10 @@ CompressedSection::CompressedSection(const SectionBase &Sec, DebugCompressionType CompressionType) - : SectionBase(Sec), CompressionType(CompressionType), - DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { + : SectionBase(Sec), CompressionType(CompressionType) { + + DecompressedSize = Sec.OriginalData.size(); + DecompressedAlign = Sec.Align; if (!zlib::isAvailable()) { CompressionType = DebugCompressionType::None; @@ -975,6 +1002,34 @@ } } +bool isCompressedIgnoreContents(const SectionBase &Section) { + return (StringRef(Section.Name).startswith(".zdebug")) || + (Section.Flags & ELF::SHF_COMPRESSED); +} + +bool isCompressedGnuContents(const SectionBase &Section) { + const char *Magic = "ZLIB"; + return Section.OriginalData.size() > strlen(Magic) && + !strncmp(reinterpret_cast(Section.OriginalData.data()), + Magic, strlen(Magic)); +} + +bool isCompressed(const SectionBase &Section) { + return isCompressedIgnoreContents(Section) || + isCompressedGnuContents(Section); +} + +template uint64_t getDecompressedSize(const SectionBase &Sec) { + if (StringRef(Sec.Name).startswith(".zdebug") && + isCompressedGnuContents(Sec)) { + return support::endian::read64be(reinterpret_cast( + Sec.OriginalData.data() + strlen("ZLIB"))); + } + + return reinterpret_cast *>(Sec.OriginalData.data()) + ->ch_size; +} + template void ELFBuilder::readSectionHeaders() { uint32_t Index = 0; for (const auto &Shdr : unwrapOrError(ElfFile.sections())) { @@ -998,6 +1053,16 @@ Sec.OriginalData = ArrayRef(ElfFile.base() + Shdr.sh_offset, (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); + + if (isCompressed(Sec)) { + Sec.DecompressedSize = getDecompressedSize(Sec); + if (!StringRef(Sec.Name).startswith(".zdebug") && + !isCompressedGnuContents(Sec)) { + Sec.DecompressedAlign = reinterpret_cast *>( + Sec.OriginalData.data()) + ->ch_addralign; + } + } } // If a section index table exists we'll need to initialize it before we Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -178,6 +178,7 @@ bool StripSections = false; bool StripUnneeded = false; bool Weaken = false; + bool DecompressDebugSections = false; DebugCompressionType CompressionType = DebugCompressionType::None; }; @@ -416,47 +417,38 @@ object_error::parse_failed); } -static bool isCompressed(const SectionBase &Section) { - const char *Magic = "ZLIB"; - return StringRef(Section.Name).startswith(".zdebug") || - (Section.OriginalData.size() > strlen(Magic) && - !strncmp(reinterpret_cast(Section.OriginalData.data()), - Magic, strlen(Magic))) || - (Section.Flags & ELF::SHF_COMPRESSED); -} - static bool isCompressable(const SectionBase &Section) { return !isCompressed(Section) && isDebugSection(Section) && Section.Name != ".gdb_index"; } -static void compressSections(const CopyConfig &Config, Object &Obj, - SectionPred &RemovePred) { - SmallVector ToCompress; +static void +replaceSections(const CopyConfig &Config, Object &Obj, SectionPred &RemovePred, + std::function doReplacement, + std::function addSection) { + SmallVector ToReplace; SmallVector RelocationSections; for (auto &Sec : Obj.sections()) { if (RelocationSection *R = dyn_cast(&Sec)) { - if (isCompressable(*R->getSection())) + if (doReplacement(*R->getSection())) RelocationSections.push_back(R); continue; } - if (isCompressable(Sec)) - ToCompress.push_back(&Sec); + if (doReplacement(Sec)) + ToReplace.push_back(&Sec); } - for (SectionBase *S : ToCompress) { - CompressedSection &CS = - Obj.addSection(*S, Config.CompressionType); - + for (SectionBase *S : ToReplace) { + SectionBase *NS = addSection(*S); for (RelocationSection *RS : RelocationSections) { if (RS->getSection() == S) - RS->setSection(&CS); + RS->setSection(NS); } } - RemovePred = [RemovePred](const SectionBase &Sec) { - return isCompressable(Sec) || RemovePred(Sec); + RemovePred = [doReplacement, RemovePred](const SectionBase &Sec) { + return doReplacement(Sec) || RemovePred(Sec); }; } @@ -671,8 +663,19 @@ }; } - if (Config.CompressionType != DebugCompressionType::None) - compressSections(Config, Obj, RemovePred); + if (Config.CompressionType != DebugCompressionType::None) { + replaceSections(Config, Obj, RemovePred, isCompressable, + [&Config, &Obj](const SectionBase &S) { + return &Obj.addSection( + S, Config.CompressionType); + }); + } else if (Config.DecompressDebugSections) { + replaceSections(Config, Obj, RemovePred, isCompressedIgnoreContents, + [&Obj](const SectionBase &S) { + return &Obj.addSection( + S, true /* DoDecompression */); + }); + } Obj.removeSections(RemovePred); @@ -978,6 +981,8 @@ Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all); Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); + Config.DecompressDebugSections = + InputArgs.hasArg(OBJCOPY_decompress_debug_sections); for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) Config.SymbolsToLocalize.push_back(Arg->getValue()); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) @@ -997,6 +1002,12 @@ DriverConfig DC; DC.CopyConfigs.push_back(std::move(Config)); + if (Config.DecompressDebugSections && + Config.CompressionType != DebugCompressionType::None) { + error("Cannot specify --compress-debug-sections as well as " + "--decompress-debug-sections at the same time"); + } + return DC; }