Index: include/llvm/Object/Compressor.h =================================================================== --- /dev/null +++ include/llvm/Object/Compressor.h @@ -0,0 +1,92 @@ +//===-- Compressor.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_OBJECT_COMPRESSOR_H +#define LLVM_OBJECT_COMPRESSOR_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/EndianStream.h" + +namespace llvm { +namespace object { + +/// Return the gnu style compressed section name. +StringRef getDebugSectionName(const StringRef Name, bool IsGnuStyle); + +/// Returns if the section can be compressed based on its name (must have a +/// debug name, starts with .*debug. +bool isCompressable(StringRef Name); + +/// Returns if the section is already compressed based on the section contents +/// Name and Flags. +bool isCompressed(StringRef Name, StringRef Contents, uint64_t Flags); + +template +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, unsigned Align); + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align); + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align); + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align); + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align); + +/// Compressor helps to handle compression of compressed sections. +template class Compressor { +public: + void writeHeader(support::endian::Writer &W, uint64_t DecompressedSize, + unsigned Alignment, bool IsGnuStyle) { + if (IsGnuStyle) { + const StringRef Magic = "ZLIB"; + W.OS << Magic; + support::endian::write(W.OS, DecompressedSize, support::big); + return; + } + + produceCompressedZLibHeader(W, DecompressedSize, Alignment); + } + + /// Uncompress section data to raw buffer provided. + /// @param W Destination buffer stream. + Error writeCompressedSectionData(support::endian::Writer &W) { + SmallVector CompressedBuffer; + auto E = zlib::compress(SectionData, CompressedBuffer); + W.OS << StringRef(CompressedBuffer.data(), CompressedBuffer.size()).str(); + return E; + } + + explicit Compressor(StringRef Data) : SectionData(Data) {} + +private: + StringRef SectionData; +}; + +} // end namespace object +} // end namespace llvm + +#endif // LLVM_OBJECT_COMPRESSOR_H Index: lib/Object/CMakeLists.txt =================================================================== --- lib/Object/CMakeLists.txt +++ lib/Object/CMakeLists.txt @@ -21,10 +21,11 @@ SymbolSize.cpp WasmObjectFile.cpp WindowsResource.cpp + Compressor.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Object DEPENDS intrinsics_gen llvm_vcsrevision_h Index: lib/Object/Compressor.cpp =================================================================== --- /dev/null +++ lib/Object/Compressor.cpp @@ -0,0 +1,106 @@ +//===-- Compressor.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/Compressor.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" + +using namespace llvm; +using namespace llvm::support::endian; +using namespace object; + +namespace llvm { +namespace object { + +StringRef getDebugSectionName(const StringRef Name, bool IsGnuStyle) { + + if (!Name.startswith(".debug") && !Name.startswith(".zdebug")) + return ""; + + std::string LookupName = Name.substr(Name.startswith(".debug") ? 1 : 2).str(); + std::string Dot("."); + + static std::map> NameMap; + auto I = NameMap.find(LookupName); + if (I == NameMap.end()) { + NameMap.insert(std::pair>( + LookupName, + std::make_tuple(Dot + LookupName, (Dot + "z") + LookupName))); + I = NameMap.find(LookupName); + } + + StringRef debugName, zdebugName; + std::tie(debugName, zdebugName) = I->second; + return IsGnuStyle ? zdebugName : debugName; +} + +bool isCompressable(StringRef Name) { + StringRef DotDebugName = getDebugSectionName(Name, false /* IsGnuStyle */); + return DotDebugName.startswith(".debug"); +} + +bool isCompressed(StringRef Name, StringRef Contents, uint64_t Flags) { + if (!Name.startswith(".debug") && !Name.startswith(".zdebug")) + return false; + + StringRef magic = Contents.substr(4); + return Name.startswith(".zdebug") || (magic == "ZLIB") || + (Flags & ELF::SHF_COMPRESSED); +} + +template void writeReserved(support::endian::Writer &W) {} +template <> void writeReserved(support::endian::Writer &W) { + W.write(static_cast(0)); // ch_reserved field. +} + +template +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, unsigned Align) { + T Elf_Chr; + W.write(static_cast(ELF::ELFCOMPRESS_ZLIB)); + writeReserved(W); + W.write(static_cast(DecompressedSize)); + W.write(static_cast(Align)); +} + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align) { + produceCompressedZLibHeader(W, DecompressedSize, Align); +} + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align) { + produceCompressedZLibHeader(W, DecompressedSize, Align); +} + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align) { + produceCompressedZLibHeader(W, DecompressedSize, Align); +} + +template <> +void produceCompressedZLibHeader(support::endian::Writer &W, + uint64_t DecompressedSize, + unsigned Align) { + produceCompressedZLibHeader(W, DecompressedSize, Align); +} + +} // end namespace object +} // end namespace llvm Index: test/tools/llvm-objcopy/zlib-dwarf.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/zlib-dwarf.test @@ -0,0 +1,219 @@ +# REQUIRES: shell + +# RUN: yaml2obj %s > %t-clean.o +# RUN: llvm-objdump -s %t-clean.o -section=.debug_str | grep -i clang + +# RUN: cp %t-clean.o %t-clean2.o +# RUN: cp %t-clean.o %t-zlib.o +# RUN: cp %t-clean.o %t-zlib-gnu.o + +# RUN: llvm-objcopy %t-clean2.o +# RUN: llvm-objcopy --compress-debug-sections=zlib %t-zlib.o +# RUN: llvm-objcopy --compress-debug-sections=zlib-gnu %t-zlib-gnu.o + +# RUN: llvm-objdump -s %t-zlib.o -section=.debug_str | grep -vi clang +# RUN: llvm-objdump -s %t-zlib.o | grep -v ZLIB +# RUN: llvm-objdump -s %t-zlib.o | grep -v "\.zdebug" + +# RUN: llvm-objdump -s %t-zlib-gnu.o -section=.debug_str | grep -vi clang +# RUN: llvm-objdump -s %t-zlib-gnu.o | grep ZLIB +# RUN: llvm-objdump -s %t-zlib-gnu.o | grep "\.zdebug" + +# RUN: llvm-objcopy --decompress-debug-sections %t-zlib.o +# RUN: llvm-objcopy --decompress-debug-sections %t-zlib-gnu.o + +# RUN: llvm-objdump -s %t-clean2.o | grep " section \." | cut -f1 -d':' | cut -f4 -d' ' | xargs -I% llvm-objdump -s -section=% %t-clean2.o | grep -v "file format" > %t-clean2.o.txt +# RUN: llvm-objdump -s %t-zlib.o | grep " section \." | cut -f1 -d':' | cut -f4 -d' ' | xargs -I% llvm-objdump -s -section=% %t-zlib.o | grep -v "file format" > %t-zlib.o.txt +# RUN: llvm-objdump -s %t-zlib-gnu.o | grep " section \." | cut -f1 -d':' | cut -f4 -d' ' | xargs -I% llvm-objdump -s -section=% %t-zlib-gnu.o | grep -v "file format" > %t-zlib-gnu.o.txt + +# RUN: cmp %t-clean2.o.txt %t-zlib.o.txt +# RUN: cmp %t-clean2.o.txt %t-zlib-gnu.o.txt + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E54883EC1048B80000000000000000897DFC8975F88B75FC8B55F84889C7B000E8000000008B55FC0355F88945F489D04883C4105DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x000000000000000A + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + - Offset: 0x0000000000000024 + Symbol: printf + Type: R_X86_64_PC32 + Addend: -4 + - Name: .rodata.str1.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 2564202B2025640A00 + - Name: .debug_str + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 46616365626F6F6B20636C616E672076657273696F6E20362E302E3020286C6C766D3A20643161616332376537326138353765393761316432373932383032623335363664373130383161362C206366653A20316431376134323466656137313738613363343434373732303766363033353764386339366335632C20636F6D70696C65722D72743A20663033656661636664666431666533363735383563653962663432313439376661356632653163632C206C6C643A206366396536643666653134313833343339346162306337396233666664643631623732323534613020316431376134323466656137313738613363343434373732303766363033353764386339366335632920287373683A2F2F6769742D726F2E7669702E66616365626F6F6B2E636F6D2F646174612F6769747265706F732F6F736D6574612F65787465726E616C2F6C6C766D20643161616332376537326138353765393761316432373932383032623335363664373130383161362920286261736564206F6E204C4C564D20362E302E302900666F6F2E63002F686F6D652F706C6F7466692F50726F6A656374732F6C6C766D2D7374616E64616C6F6E6500666F6F00696E740061006200 + - Name: .debug_abbrev + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 011101250E1305030E10171B0EB44219110112060000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042400030E3E0B0B0B000000 + - Name: .debug_info + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 640000000400000000000801000000000C00000000000000000000000000000000000000000039000000020000000000000000390000000156000000000102600000000302917C000000000102600000000302917800000000010260000000000400000000050400 + - Name: .rela.debug_info + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_info + Relocations: + - Offset: 0x0000000000000006 + Symbol: .debug_abbrev + Type: R_X86_64_32 + - Offset: 0x000000000000000C + Symbol: .debug_str + Type: R_X86_64_32 + - Offset: 0x0000000000000012 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 398 + - Offset: 0x0000000000000016 + Symbol: .debug_line + Type: R_X86_64_32 + - Offset: 0x000000000000001A + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 404 + - Offset: 0x000000000000001E + Symbol: .text + Type: R_X86_64_64 + - Offset: 0x000000000000002B + Symbol: .text + Type: R_X86_64_64 + - Offset: 0x0000000000000039 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 442 + - Offset: 0x0000000000000047 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 450 + - Offset: 0x0000000000000055 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 452 + - Offset: 0x0000000000000061 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 446 + - Name: .debug_ranges + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .debug_macinfo + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '00' + - Name: .debug_pubnames + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 16000000020000000000680000002A000000666F6F0000000000 + - Name: .rela.debug_pubnames + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_pubnames + Relocations: + - Offset: 0x0000000000000006 + Symbol: .debug_info + Type: R_X86_64_32 + - Name: .debug_pubtypes + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '160000000200000000006800000060000000696E740000000000' + - Name: .rela.debug_pubtypes + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_pubtypes + Relocations: + - Offset: 0x0000000000000006 + Symbol: .debug_info + Type: R_X86_64_32 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 0046616365626F6F6B20636C616E672076657273696F6E20362E302E3020286C6C766D3A20643161616332376537326138353765393761316432373932383032623335363664373130383161362C206366653A20316431376134323466656137313738613363343434373732303766363033353764386339366335632C20636F6D70696C65722D72743A20663033656661636664666431666533363735383563653962663432313439376661356632653163632C206C6C643A206366396536643666653134313833343339346162306337396233666664643631623732323534613020316431376134323466656137313738613363343434373732303766363033353764386339366335632920287373683A2F2F6769742D726F2E7669702E66616365626F6F6B2E636F6D2F646174612F6769747265706F732F6F736D6574612F65787465726E616C2F6C6C766D20643161616332376537326138353765393761316432373932383032623335363664373130383161362920286261736564206F6E204C4C564D20362E302E302900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_X86_64_UNWIND + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000000000003900000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + - Name: .debug_line + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 4B00000004001D000000010101FB0E0D00010101010000000100000100666F6F2E63000000000000090200000000000000001305170A0875051A063C05033C050A069F050C063C05033C020B000101 + - Name: .rela.debug_line + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_line + Relocations: + - Offset: 0x000000000000002A + Symbol: .text + Type: R_X86_64_64 +Symbols: + Local: + - Name: foo.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .rodata.str1.1 + Type: STT_SECTION + Section: .rodata.str1.1 + - Name: .debug_str + Type: STT_SECTION + Section: .debug_str + - Name: .debug_abbrev + Type: STT_SECTION + Section: .debug_abbrev + - Name: .debug_info + Type: STT_SECTION + Section: .debug_info + - Name: .debug_line + Type: STT_SECTION + Section: .debug_line + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Size: 0x0000000000000039 + - Name: printf +DynamicSymbols: +... Index: tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- tools/llvm-objcopy/ObjcopyOpts.td +++ tools/llvm-objcopy/ObjcopyOpts.td @@ -16,6 +16,11 @@ defm output_target : Eq<"output-target">, HelpText<"Format of the output file">, Values<"binary">; +defm compress_debug_sections : Eq<"compress-debug-sections">, + MetaVarName<"[ none | zlib | zlib-gnu (deprecated) ]">, + HelpText<"Enable zlib-gnu or zlib Compression of DWARF debug sections.">; +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 @@ -14,7 +14,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/Compressor.h" +#include "llvm/Object/Decompressor.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/JamCRC.h" @@ -43,31 +46,69 @@ class Object; struct Symbol; +struct CopyConfig { + StringRef OutputFilename; + StringRef InputFilename; + StringRef OutputFormat; + StringRef InputFormat; + StringRef BinaryArch; + + StringRef SplitDWO; + StringRef AddGnuDebugLink; + std::vector ToRemove; + std::vector Keep; + std::vector OnlyKeep; + std::vector AddSection; + std::vector SymbolsToLocalize; + std::vector SymbolsToGlobalize; + std::vector SymbolsToWeaken; + std::vector SymbolsToRemove; + std::vector SymbolsToKeep; + StringMap SectionsToRename; + StringMap SymbolsToRename; + bool StripAll = false; + bool StripAllGNU = false; + bool StripDebug = false; + bool StripSections = false; + bool StripNonAlloc = false; + bool StripDWO = false; + bool StripUnneeded = false; + bool ExtractDWO = false; + bool LocalizeHidden = false; + bool Weaken = false; + bool DiscardAll = false; + bool OnlyKeepDebug = false; + bool KeepFileSymbols = false; + bool DecompressDebugSections = false; + + DebugCompressionType CompressDebugSections = DebugCompressionType::None; +}; + class SectionTableRef { MutableArrayRef> Sections; public: using iterator = pointee_iterator *>; explicit SectionTableRef(MutableArrayRef> Secs) : Sections(Secs) {} SectionTableRef(const SectionTableRef &) = default; iterator begin() { return iterator(Sections.data()); } iterator end() { return iterator(Sections.data() + Sections.size()); } SectionBase *getSection(uint32_t Index, Twine ErrMsg); template T *getSectionOfType(uint32_t Index, Twine IndexErrMsg, Twine TypeErrMsg); }; enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE }; class SectionVisitor { public: virtual ~SectionVisitor(); virtual void visit(const Section &Sec) = 0; virtual void visit(const OwnedDataSection &Sec) = 0; virtual void visit(const StringTableSection &Sec) = 0; @@ -190,47 +231,53 @@ template class ELFWriter : public Writer { private: + DebugCompressionType CompressDebugSections = DebugCompressionType::None; + bool DecompressDebugSections = false; + using Elf_Shdr = typename ELFT::Shdr; using Elf_Phdr = typename ELFT::Phdr; using Elf_Ehdr = typename ELFT::Ehdr; void writeEhdr(); void writePhdr(const Segment &Seg); void writeShdr(const SectionBase &Sec); void writePhdrs(); void writeShdrs(); void writeSectionData(); void assignOffsets(); std::unique_ptr> SecWriter; size_t totalSize() const; public: virtual ~ELFWriter() {} bool WriteSectionHeaders = true; void finalize() override; void write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH) - : Writer(Obj, Buf), WriteSectionHeaders(WSH) {} + ELFWriter(Object &Obj, Buffer &Buf, const CopyConfig &Config) + : Writer(Obj, Buf), WriteSectionHeaders(!Config.StripSections) { + this->CompressDebugSections = Config.CompressDebugSections; + this->DecompressDebugSections = Config.DecompressDebugSections; + } }; class BinaryWriter : public Writer { private: std::unique_ptr SecWriter; uint64_t TotalSize; public: ~BinaryWriter() {} void finalize() override; void write() override; BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} }; class SectionBase { public: StringRef Name; @@ -259,8 +306,15 @@ virtual void removeSymbols(function_ref ToRemove); virtual void accept(SectionVisitor &Visitor) const = 0; virtual void markSymbols(); + virtual ArrayRef *getContents() { + assert(false && "Section has no Contents."); + return nullptr; + } + virtual void updateContents(std::unique_ptr> Contents) { + assert(false && "Section has no Contents."); + } }; class Segment { private: struct SectionCompare { @@ -308,22 +362,25 @@ MAKE_SEC_WRITER_FRIEND ArrayRef Contents; + std::unique_ptr> ModifiedContents; SectionBase *LinkSection = nullptr; public: explicit Section(ArrayRef Data) : Contents(Data) {} void accept(SectionVisitor &Visitor) const override; void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; + ArrayRef *getContents() override { return &Contents; } + void updateContents(std::unique_ptr> Update) override; }; class OwnedDataSection : public SectionBase { MAKE_SEC_WRITER_FRIEND std::vector Data; public: OwnedDataSection(StringRef SecName, ArrayRef Data) : Data(std::begin(Data), std::end(Data)) { Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -491,6 +491,13 @@ void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; } +void Section::updateContents(std::unique_ptr> Update) { + ModifiedContents = std::move(Update); + Size = ModifiedContents->size(); + const uint8_t *ContentsPtr = (const uint8_t *)ModifiedContents->data(); + Contents = ArrayRef(ContentsPtr, Size); +} + void GnuDebugLinkSection::init(StringRef File, StringRef Data) { FileName = sys::path::filename(File); // The format for the .gnu_debuglink starts with the file name and is @@ -1213,6 +1220,51 @@ reportError(Buf.getName(), errorToErrorCode(std::move(E))); } +template +std::unique_ptr> decompress(StringRef &Name, + StringRef Contents) { + + auto ModifiedContents = make_unique>(); + + bool Is64Bit = T::Is64Bits; + bool IsLittle = (T::TargetEndianness == endianness::little); + auto D = object::Decompressor::create(Name, Contents, IsLittle, Is64Bit); + if (!D) + consumeError(D.takeError()); + + auto E = D->resizeAndDecompress(*ModifiedContents.get()); + consumeError(std::move(E)); + + if (const bool IsGnuStyle = Name.startswith(".zdebug")) + Name = object::getDebugSectionName(Name, !IsGnuStyle); + + return std::move(ModifiedContents); +} + +template +std::unique_ptr> +compress(StringRef &Name, StringRef Contents, uint64_t Align, bool IsGnuStyle) { + + auto ModifiedContents = make_unique>(); + + object::endianness E = T::TargetEndianness; + raw_svector_ostream OS(*ModifiedContents.get()); + support::endian::Writer W(OS, E); + + auto C = object::Compressor(Contents); + C.writeHeader(W, Contents.size(), Align, IsGnuStyle); + auto Error = C.writeCompressedSectionData(W); + consumeError(std::move(Error)); + + if (ModifiedContents->size() < Contents.size()) { + if (IsGnuStyle) + Name = object::getDebugSectionName(Name, IsGnuStyle); + return std::move(ModifiedContents); + } + + return nullptr; +} + template void ELFWriter::finalize() { // It could happen that SectionNames has been removed and yet the user wants // a section header table output. We need to throw an error if a user tries @@ -1256,26 +1308,51 @@ } } + bool DoDecompress = DecompressDebugSections; + bool IsGnuStyle = (CompressDebugSections == DebugCompressionType::GNU); + bool DoCompress = (CompressDebugSections != DebugCompressionType::None) && + !DecompressDebugSections; + // Make sure we add the names of all the sections. Importantly this must be // done after we decide to add or remove SectionIndexes. if (Obj.SectionNames != nullptr) - for (const auto &Section : Obj.sections()) { + for (auto &Section : Obj.sections()) { + bool isCompressable = object::isCompressable(Section.Name); + if (isCompressable && (DoCompress || DoDecompress)) { + StringRef Contents((const char *)Section.getContents()->data(), + Section.getContents()->size()); + bool isCompressed = + object::isCompressed(Section.Name, Contents, Section.Flags); + if (DoDecompress && isCompressed) { + auto ModifiedContents = decompress(Section.Name, Contents); + Section.updateContents(std::move(ModifiedContents)); + } else if (DoCompress && !isCompressed) { + auto ModifiedContents = + compress(Section.Name, Contents, Section.Align, IsGnuStyle); + if (ModifiedContents != nullptr) { + Section.updateContents(std::move(ModifiedContents)); + if (!IsGnuStyle) + Section.Flags |= ELF::SHF_COMPRESSED; + } + } + } + Obj.SectionNames->addString(Section.Name); } // Before we can prepare for layout the indexes need to be finalized. uint64_t Index = 0; for (auto &Sec : Obj.sections()) Sec.Index = Index++; // The symbol table does not update all other sections on update. For // instance, symbol names are not added as new symbols are added. This means // that some sections, like .strtab, don't yet have their final size. if (Obj.SymbolTable != nullptr) Obj.SymbolTable->prepareForLayout(); assignOffsets(); // Finalize SectionNames first so that we can assign name indexes. if (Obj.SectionNames != nullptr) Obj.SectionNames->finalize(); Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -115,63 +115,28 @@ StripOptTable() : OptTable(StripInfoTable, true) {} }; -struct CopyConfig { - StringRef OutputFilename; - StringRef InputFilename; - StringRef OutputFormat; - StringRef InputFormat; - StringRef BinaryArch; - - StringRef SplitDWO; - StringRef AddGnuDebugLink; - std::vector ToRemove; - std::vector Keep; - std::vector OnlyKeep; - std::vector AddSection; - std::vector SymbolsToLocalize; - std::vector SymbolsToGlobalize; - std::vector SymbolsToWeaken; - std::vector SymbolsToRemove; - std::vector SymbolsToKeep; - StringMap SectionsToRename; - StringMap SymbolsToRename; - bool StripAll = false; - bool StripAllGNU = false; - bool StripDebug = false; - bool StripSections = false; - bool StripNonAlloc = false; - bool StripDWO = false; - bool StripUnneeded = false; - bool ExtractDWO = false; - bool LocalizeHidden = false; - bool Weaken = false; - bool DiscardAll = false; - bool OnlyKeepDebug = false; - bool KeepFileSymbols = false; -}; - using SectionPred = std::function; } // namespace namespace llvm { namespace objcopy { // The name this program was invoked as. StringRef ToolName; LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { errs() << ToolName << ": " << Message << ".\n"; errs().flush(); exit(1); } LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { assert(EC); errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n"; exit(1); } LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { assert(E); std::string Buf; @@ -212,21 +177,17 @@ // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: - return llvm::make_unique>(Obj, Buf, - !Config.StripSections); + return llvm::make_unique>(Obj, Buf, Config); case ELFT_ELF64LE: - return llvm::make_unique>(Obj, Buf, - !Config.StripSections); + return llvm::make_unique>(Obj, Buf, Config); case ELFT_ELF32BE: - return llvm::make_unique>(Obj, Buf, - !Config.StripSections); + return llvm::make_unique>(Obj, Buf, Config); case ELFT_ELF64BE: - return llvm::make_unique>(Obj, Buf, - !Config.StripSections); + return llvm::make_unique>(Obj, Buf, Config); } llvm_unreachable("Invalid output format"); } static void SplitDWOToFile(const CopyConfig &Config, const Reader &Reader, StringRef File, ElfType OutputElfType) { auto DWOFile = Reader.create(); @@ -588,9 +549,16 @@ Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); Config.BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); + Config.CompressDebugSections = + StringSwitch( + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections)) + .Case("zlib-gnu", DebugCompressionType::GNU) + .Case("zlib", DebugCompressionType::Z) + .Default(DebugCompressionType::None); + Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { if (!StringRef(Arg->getValue()).contains('=')) error("Bad format for --redefine-sym"); @@ -628,6 +596,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_globalize_symbol)) @@ -639,9 +609,15 @@ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) Config.SymbolsToKeep.push_back(Arg->getValue()); + if (Config.DecompressDebugSections && + Config.CompressDebugSections != DebugCompressionType::None) { + error("Cannot specify --compress-debug-sections as well as " + "--decompress-debug-sections at the same time."); + } + return Config; } // ParseStripOptions returns the config and sets the input arguments. If a // help flag is set then ParseStripOptions will print the help messege and // exit.