diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1776,6 +1776,7 @@ // Legal values for ch_type field of compressed section header. enum { ELFCOMPRESS_ZLIB = 1, // ZLIB/DEFLATE algorithm. + ELFCOMPRESS_ZSTD = 2, // Zstandard algorithm ELFCOMPRESS_LOOS = 0x60000000, // Start of OS-specific. ELFCOMPRESS_HIOS = 0x6fffffff, // End of OS-specific. ELFCOMPRESS_LOPROC = 0x70000000, // Start of processor-specific. diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h --- a/llvm/include/llvm/MC/MCTargetOptions.h +++ b/llvm/include/llvm/MC/MCTargetOptions.h @@ -27,7 +27,8 @@ enum class DebugCompressionType { None, ///< No compression - Z, ///< zlib style complession + Z, ///< zlib + Zstd, ///< Zstandard }; enum class EmitDwarfUnwindType { diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h --- a/llvm/lib/ObjCopy/ELF/ELFObject.h +++ b/llvm/lib/ObjCopy/ELF/ELFObject.h @@ -536,6 +536,7 @@ class CompressedSection : public SectionBase { MAKE_SEC_WRITER_FRIEND + uint32_t OrigChType = 0; DebugCompressionType CompressionType; uint64_t DecompressedSize; uint64_t DecompressedAlign; @@ -544,11 +545,12 @@ public: CompressedSection(const SectionBase &Sec, DebugCompressionType CompressionType); - CompressedSection(ArrayRef CompressedData, uint64_t DecompressedSize, - uint64_t DecompressedAlign); + CompressedSection(ArrayRef CompressedData, uint32_t OrigChType, + uint64_t DecompressedSize, uint64_t DecompressedAlign); uint64_t getDecompressedSize() const { return DecompressedSize; } uint64_t getDecompressedAlign() const { return DecompressedAlign; } + uint64_t getOrigChType() const { return OrigChType; } Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; @@ -562,8 +564,9 @@ MAKE_SEC_WRITER_FRIEND public: + uint32_t ChType; explicit DecompressedSection(const CompressedSection &Sec) - : SectionBase(Sec) { + : SectionBase(Sec), ChType(Sec.getOrigChType()) { Size = Sec.getDecompressedSize(); Align = Sec.getDecompressedAlign(); Flags = OriginalFlags = (Flags & ~ELF::SHF_COMPRESSED); diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp --- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp +++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp @@ -434,29 +434,35 @@ return Error::success(); } -template -static std::tuple -getDecompressedSizeAndAlignment(ArrayRef Data) { - const uint64_t DecompressedSize = - reinterpret_cast *>(Data.data())->ch_size; - const uint64_t DecompressedAlign = - reinterpret_cast *>(Data.data())->ch_addralign; - - return std::make_tuple(DecompressedSize, DecompressedAlign); -} - template Error ELFSectionWriter::visit(const DecompressedSection &Sec) { ArrayRef Compressed = Sec.OriginalData.slice(sizeof(Elf_Chdr_Impl)); - SmallVector DecompressedContent; - if (Error Err = compression::zlib::uncompress(Compressed, DecompressedContent, - static_cast(Sec.Size))) - return createStringError(errc::invalid_argument, - "'" + Sec.Name + "': " + toString(std::move(Err))); + SmallVector Decompressed; + switch (Sec.ChType) { + case ELFCOMPRESS_ZLIB: + if (Error Err = compression::zlib::uncompress( + Compressed, Decompressed, static_cast(Sec.Size))) + return createStringError(errc::invalid_argument, + "'" + Sec.Name + + "': " + toString(std::move(Err))); + break; + case ELFCOMPRESS_ZSTD: + if (Error Err = compression::zstd::uncompress( + Compressed, Decompressed, static_cast(Sec.Size))) + return createStringError(errc::invalid_argument, + "'" + Sec.Name + + "': " + toString(std::move(Err))); + break; + default: + return createStringError(errc::operation_not_permitted, + "--decompress-debug-sections: ch_type (" + + Twine(Sec.ChType) + ") of section '" + + Sec.Name + "' is unsupported"); + } uint8_t *Buf = reinterpret_cast(Out.getBufferStart()) + Sec.Offset; - std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); + std::copy(Decompressed.begin(), Decompressed.end(), Buf); return Error::success(); } @@ -509,6 +515,9 @@ case DebugCompressionType::Z: Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB; break; + case DebugCompressionType::Zstd: + Chdr.ch_type = ELF::ELFCOMPRESS_ZSTD; + break; } Chdr.ch_size = Sec.DecompressedSize; Chdr.ch_addralign = Sec.DecompressedAlign; @@ -523,9 +532,17 @@ DebugCompressionType CompressionType) : SectionBase(Sec), CompressionType(CompressionType), DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { - compression::zlib::compress(OriginalData, CompressedData); + switch (CompressionType) { + case DebugCompressionType::Z: + compression::zlib::compress(OriginalData, CompressedData); + break; + case DebugCompressionType::Zstd: + compression::zstd::compress(OriginalData, CompressedData); + break; + default: + llvm_unreachable("unsupported CompressionType should have been errored"); + } - assert(CompressionType != DebugCompressionType::None); Flags |= ELF::SHF_COMPRESSED; size_t ChdrSize = std::max(std::max(sizeof(object::Elf_Chdr_Impl), @@ -537,9 +554,10 @@ } CompressedSection::CompressedSection(ArrayRef CompressedData, + uint32_t OrigChType, uint64_t DecompressedSize, uint64_t DecompressedAlign) - : CompressionType(DebugCompressionType::None), + : OrigChType(OrigChType), CompressionType(DebugCompressionType::None), DecompressedSize(DecompressedSize), DecompressedAlign(DecompressedAlign) { OriginalData = CompressedData; } @@ -1714,15 +1732,11 @@ if (!Name) return Name.takeError(); - if (Shdr.sh_flags & ELF::SHF_COMPRESSED) { - uint64_t DecompressedSize, DecompressedAlign; - std::tie(DecompressedSize, DecompressedAlign) = - getDecompressedSizeAndAlignment(*Data); - return Obj.addSection( - CompressedSection(*Data, DecompressedSize, DecompressedAlign)); - } - - return Obj.addSection
(*Data); + if (!(Shdr.sh_flags & ELF::SHF_COMPRESSED)) + return Obj.addSection
(*Data); + auto *Chdr = reinterpret_cast *>(Data->data()); + return Obj.addSection(CompressedSection( + *Data, Chdr->ch_type, Chdr->ch_size, Chdr->ch_addralign)); } } } diff --git a/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-default.test b/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-default.test --- a/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-default.test +++ b/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-default.test @@ -2,12 +2,15 @@ # RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t.o # RUN: llvm-objcopy --compress-debug-sections %t.o %t-compressed.o -# RUN: llvm-readobj -S %t-compressed.o | FileCheck %s +# RUN: llvm-readobj -S --sd %t-compressed.o | FileCheck %s # CHECK: Name: .debug_foo # CHECK-NEXT: Type: SHT_PROGBITS # CHECK-NEXT: Flags [ # CHECK-NEXT: SHF_COMPRESSED # CHECK-NEXT: ] +# CHECK: SectionData ( +## ch_type = ELFCOMPRESS_ZLIB (1) +# CHECK-NEXT: 0000: 01000000 {{.*}} # CHECK-NOT: Name: .debug_foo diff --git a/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-zstd-err.test b/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-zstd-err.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-zstd-err.test @@ -0,0 +1,5 @@ +# UNSUPPORTED: zstd +# RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t +# RUN: not llvm-objcopy --compress-debug-sections=zstd %t /dev/null 2>&1 | FileCheck %s + +# CHECK: error: LLVM was not compiled with LLVM_ENABLE_ZSTD: can not compress diff --git a/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-zstd.test b/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-zstd.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/compress-debug-sections-zstd.test @@ -0,0 +1,33 @@ +# REQUIRES: zstd +## Test --compress-debug-sections=zstd and uncompression. + +# RUN: yaml2obj %p/Inputs/compress-debug-sections.yaml -o %t.o && llvm-objcopy %t.o +# RUN: llvm-objcopy --compress-debug-sections=zstd %t.o %t-zstd.o +# RUN: llvm-objcopy --decompress-debug-sections %t-zstd.o %t-un.o +# RUN: cmp %t.o %t-un.o + +# RUN: llvm-readelf -S -r -x .debug_foo %t-zstd.o | FileCheck %s --check-prefixes=CHECK,COMPRESSED +# RUN: llvm-readelf -S -r -x .debug_foo %t-un.o | FileCheck %s --check-prefixes=CHECK,UNCOMPRESSED + +# CHECK: Name Type Address Off Size ES Flg Lk Inf Al +# COMPRESSED: .debug_foo PROGBITS 0000000000000000 000040 {{.*}} 00 C 0 0 8 +# COMPRESSED-NEXT: .notdebug_foo PROGBITS 0000000000000000 {{.*}} 000008 00 0 0 0 +# UNCOMPRESSED: .debug_foo PROGBITS 0000000000000000 000040 000008 00 0 0 0 +# UNCOMPRESSED-NEXT: .notdebug_foo PROGBITS 0000000000000000 {{.*}} 000008 00 0 0 0 + +## Relocations do not change. +# CHECK: Relocation section '.rela.debug_foo' at offset {{.*}} contains 2 entries: +# CHECK-NEXT: Offset +# CHECK-NEXT: 0000000000000001 000000010000000a R_X86_64_32 0000000000000000 .debug_foo + 0 +# CHECK-NEXT: 0000000000000002 000000020000000a R_X86_64_32 0000000000000000 .notdebug_foo + 0 + +# COMPRESSED: Hex dump of section '.debug_foo': +## ch_type == ELFCOMPRESS_ZSTD (2) +# COMPRESSED-NEXT: 0x00000000 02000000 00000000 08000000 00000000 +# COMPRESSED-NEXT: 0x00000010 00000000 00000000 {{.*}} + +## --compress-debug-sections does not update a compressed section. Its compression +## type does not change. +# RUN: llvm-objcopy --compress-debug-sections=zstd %t-zstd.o %t-zstd-zstd.o +# RUN: cmp %t-zstd.o %t-zstd-zstd.o +# RUN: %if zlib %{ llvm-objcopy --compress-debug-sections=zlib %t-zstd.o %t-zstd-zlib.o && cmp %t-zstd.o %t-zstd-zlib.o %} diff --git a/llvm/test/tools/llvm-objcopy/ELF/decompress-debug-sections-err.test b/llvm/test/tools/llvm-objcopy/ELF/decompress-debug-sections-err.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/decompress-debug-sections-err.test @@ -0,0 +1,24 @@ +# REQUIRES: zlib +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objcopy --decompress-debug-sections %t /dev/null 2>&1 | FileCheck %s -DFILE=%t + +# CHECK: error: '[[FILE]]': --decompress-debug-sections: ch_type (3) of section '.debug_info' is unsupported +# CHECK-EMPTY: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .debug_info + Type: SHT_PROGBITS + Flags: [ SHF_COMPRESSED ] + Content: 030000000000000004000000000000000000000000000000789c6360 + AddressAlign: 8 + - Name: .debug_str + Type: SHT_PROGBITS + Flags: [ SHF_COMPRESSED ] + Content: 030000000000000004000000000000000000000000000000789c6360 + AddressAlign: 8 diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -728,6 +728,7 @@ StringSwitch( InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)) .Case("zlib", DebugCompressionType::Z) + .Case("zstd", DebugCompressionType::Zstd) .Default(DebugCompressionType::None); if (Config.CompressionType == DebugCompressionType::None) return createStringError( @@ -741,6 +742,11 @@ return createStringError( errc::invalid_argument, "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); + if (Config.CompressionType == DebugCompressionType::Zstd && + !compression::zstd::isAvailable()) + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZSTD: can not compress"); } Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); diff --git a/llvm/utils/lit/lit/llvm/config.py b/llvm/utils/lit/lit/llvm/config.py --- a/llvm/utils/lit/lit/llvm/config.py +++ b/llvm/utils/lit/lit/llvm/config.py @@ -108,6 +108,9 @@ have_zlib = getattr(config, 'have_zlib', None) if have_zlib: features.add('zlib') + have_zstd = getattr(config, 'have_zstd', None) + if have_zstd: + features.add('zstd') # Check if we should run long running tests. long_tests = lit_config.params.get('run_long_tests', None)