diff --git a/include/llvm/Object/COFF.h b/include/llvm/Object/COFF.h index 7553f4bdaf7..aa12418f2d3 100644 --- a/include/llvm/Object/COFF.h +++ b/include/llvm/Object/COFF.h @@ -694,6 +694,7 @@ protected: std::error_code getSectionContents(DataRefImpl Sec, StringRef &Res) const override; uint64_t getSectionAlignment(DataRefImpl Sec) const override; + bool isSectionCompressed(DataRefImpl Sec) const override; bool isSectionText(DataRefImpl Sec) const override; bool isSectionData(DataRefImpl Sec) const override; bool isSectionBSS(DataRefImpl Sec) const override; diff --git a/include/llvm/Object/ELFObjectFile.h b/include/llvm/Object/ELFObjectFile.h index c5f777186aa..52cde1a0f0f 100644 --- a/include/llvm/Object/ELFObjectFile.h +++ b/include/llvm/Object/ELFObjectFile.h @@ -222,6 +222,7 @@ protected: std::error_code getSectionContents(DataRefImpl Sec, StringRef &Res) const override; uint64_t getSectionAlignment(DataRefImpl Sec) const override; + bool isSectionCompressed(DataRefImpl Sec) const override; bool isSectionText(DataRefImpl Sec) const override; bool isSectionData(DataRefImpl Sec) const override; bool isSectionBSS(DataRefImpl Sec) const override; @@ -583,6 +584,11 @@ uint64_t ELFObjectFile::getSectionAlignment(DataRefImpl Sec) const { return getSection(Sec)->sh_addralign; } +template +bool ELFObjectFile::isSectionCompressed(DataRefImpl Sec) const { + return getSection(Sec)->sh_flags & ELF::SHF_COMPRESSED; +} + template bool ELFObjectFile::isSectionText(DataRefImpl Sec) const { return getSection(Sec)->sh_flags & ELF::SHF_EXECINSTR; diff --git a/include/llvm/Object/MachO.h b/include/llvm/Object/MachO.h index 611acf3124d..6a079a964bf 100644 --- a/include/llvm/Object/MachO.h +++ b/include/llvm/Object/MachO.h @@ -222,6 +222,7 @@ public: std::error_code getSectionContents(DataRefImpl Sec, StringRef &Res) const override; uint64_t getSectionAlignment(DataRefImpl Sec) const override; + bool isSectionCompressed(DataRefImpl Sec) const override; bool isSectionText(DataRefImpl Sec) const override; bool isSectionData(DataRefImpl Sec) const override; bool isSectionBSS(DataRefImpl Sec) const override; diff --git a/include/llvm/Object/ObjectFile.h b/include/llvm/Object/ObjectFile.h index 0a902a4590a..0f0f10b8c19 100644 --- a/include/llvm/Object/ObjectFile.h +++ b/include/llvm/Object/ObjectFile.h @@ -89,6 +89,7 @@ public: /// @brief Get the alignment of this section as the actual value (not log 2). uint64_t getAlignment() const; + bool isCompressed() const; bool isText() const; bool isData() const; bool isBSS() const; @@ -214,6 +215,7 @@ protected: virtual std::error_code getSectionContents(DataRefImpl Sec, StringRef &Res) const = 0; virtual uint64_t getSectionAlignment(DataRefImpl Sec) const = 0; + virtual bool isSectionCompressed(DataRefImpl Sec) const = 0; virtual bool isSectionText(DataRefImpl Sec) const = 0; virtual bool isSectionData(DataRefImpl Sec) const = 0; virtual bool isSectionBSS(DataRefImpl Sec) const = 0; @@ -380,6 +382,10 @@ inline uint64_t SectionRef::getAlignment() const { return OwningObject->getSectionAlignment(SectionPimpl); } +inline bool SectionRef::isCompressed() const { + return OwningObject->isSectionCompressed(SectionPimpl); +} + inline bool SectionRef::isText() const { return OwningObject->isSectionText(SectionPimpl); } diff --git a/lib/DebugInfo/DWARF/DWARFContext.cpp b/lib/DebugInfo/DWARF/DWARFContext.cpp index d3b5e736c48..27b8c564d91 100644 --- a/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -15,6 +15,7 @@ #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Dwarf.h" +#include "llvm/Support/ELF.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -590,8 +591,8 @@ DWARFContext::getInliningInfoForAddress(uint64_t Address, return InliningInfo; } -static bool consumeCompressedDebugSectionHeader(StringRef &data, - uint64_t &OriginalSize) { +static bool consumeCompressedGnuHeader(StringRef &data, + uint64_t &OriginalSize) { // Consume "ZLIB" prefix. if (!data.startswith("ZLIB")) return false; @@ -606,6 +607,50 @@ static bool consumeCompressedDebugSectionHeader(StringRef &data, return true; } +static bool consumeCompressedZLibHeader(StringRef &Data, uint64_t &OriginalSize, + bool IsLE, bool Is64Bit) { + using namespace ELF; + uint64_t HdrSize = Is64Bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr); + if (Data.size() < HdrSize) + return false; + + DataExtractor Extractor(Data, IsLE, 0); + uint32_t Offset = 0; + if (Extractor.getUnsigned(&Offset, Is64Bit ? sizeof(Elf64_Word) + : sizeof(Elf32_Word)) != + ELFCOMPRESS_ZLIB) + return false; + + // Skip Elf64_Chdr::ch_reserved field. + if (Is64Bit) + Offset += sizeof(Elf64_Word); + + OriginalSize = Extractor.getUnsigned(&Offset, Is64Bit ? sizeof(Elf64_Xword) + : sizeof(Elf32_Word)); + Data = Data.substr(HdrSize); + return true; +} + +static bool tryDecompress(StringRef &Name, StringRef &Data, + SmallString<32> &Out, bool ZLibStyle, bool IsLE, + bool Is64Bit) { + if (!zlib::isAvailable()) + return false; + + uint64_t OriginalSize; + bool Result = + ZLibStyle ? consumeCompressedZLibHeader(Data, OriginalSize, IsLE, Is64Bit) + : consumeCompressedGnuHeader(Data, OriginalSize); + + if (!Result || zlib::uncompress(Data, Out, OriginalSize) != zlib::StatusOK) + return false; + + // gnu-style names are started from "z", consume that. + if (!ZLibStyle) + Name = Name.substr(1); + return true; +} + DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L) : IsLittleEndian(Obj.isLittleEndian()), @@ -631,20 +676,13 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, name = name.substr(name.find_first_not_of("._")); // Skip . and _ prefixes. - // Check if debug info section is compressed with zlib. - if (name.startswith("zdebug_")) { - uint64_t OriginalSize; - if (!zlib::isAvailable() || - !consumeCompressedDebugSectionHeader(data, OriginalSize)) + bool ZLibStyleCompressed = Section.isCompressed(); + if (ZLibStyleCompressed || name.startswith("zdebug_")) { + SmallString<32> Out; + if (!tryDecompress(name, data, Out, ZLibStyleCompressed, IsLittleEndian, + AddressSize == 8)) continue; - UncompressedSections.resize(UncompressedSections.size() + 1); - if (zlib::uncompress(data, UncompressedSections.back(), OriginalSize) != - zlib::StatusOK) { - UncompressedSections.pop_back(); - continue; - } - // Make data point to uncompressed section contents and save its contents. - name = name.substr(1); + UncompressedSections.emplace_back(std::move(Out)); data = UncompressedSections.back(); } diff --git a/lib/Object/COFFObjectFile.cpp b/lib/Object/COFFObjectFile.cpp index 4b6ab23f5ff..489c69cc4bc 100644 --- a/lib/Object/COFFObjectFile.cpp +++ b/lib/Object/COFFObjectFile.cpp @@ -292,6 +292,10 @@ uint64_t COFFObjectFile::getSectionAlignment(DataRefImpl Ref) const { return Sec->getAlignment(); } +bool COFFObjectFile::isSectionCompressed(DataRefImpl Sec) const { + return false; +} + bool COFFObjectFile::isSectionText(DataRefImpl Ref) const { const coff_section *Sec = toSec(Ref); return Sec->Characteristics & COFF::IMAGE_SCN_CNT_CODE; diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index 760249f56a8..d7ec3e57a58 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -653,6 +653,10 @@ uint64_t MachOObjectFile::getSectionAlignment(DataRefImpl Sec) const { return uint64_t(1) << Align; } +bool MachOObjectFile::isSectionCompressed(DataRefImpl Sec) const { + return false; +} + bool MachOObjectFile::isSectionText(DataRefImpl Sec) const { uint32_t Flags = getSectionFlags(this, Sec); return Flags & MachO::S_ATTR_PURE_INSTRUCTIONS; diff --git a/test/DebugInfo/Inputs/dwarfdump-test-zlib.cc b/test/DebugInfo/Inputs/dwarfdump-test-zlib.cc index 260c3c435c1..966a465f904 100644 --- a/test/DebugInfo/Inputs/dwarfdump-test-zlib.cc +++ b/test/DebugInfo/Inputs/dwarfdump-test-zlib.cc @@ -16,9 +16,12 @@ int main() { return f(2, 3); } -// Built with Clang 3.2 and ld.gold linker: +// Built with Clang 3.9 and GNU gold (GNU Binutils for Ubuntu 2.26) 1.11: +// Note: llvm-symbolizer-zlib.test relies on the path and filename used ! // $ mkdir -p /tmp/dbginfo // $ cp dwarfdump-test-zlib.cc /tmp/dbginfo // $ cd /tmp/dbginfo -// $ clang++ -g dwarfdump-test-zlib.cc -Wl,--compress-debug-sections=zlib \ -// -o +// $ clang++ -g dwarfdump-test-zlib.cc -Wl,--compress-debug-sections=zlib -o dwarfdump-test-zlib.elf-x86-64 +// $ clang++ -g dwarfdump-test-zlib.cc -Wl,--compress-debug-sections=zlib-gnu -o dwarfdump-test-zlibgnu.elf-x86-64 +// llvm-readobj -sections can be used to see that outputs really contain the compressed sections, also output in both +// cases is slightly smaller, that is because of compression. \ No newline at end of file diff --git a/test/DebugInfo/Inputs/dwarfdump-test-zlib.elf-x86-64 b/test/DebugInfo/Inputs/dwarfdump-test-zlib.elf-x86-64 index 16b3153939f..36e630ccae0 100755 Binary files a/test/DebugInfo/Inputs/dwarfdump-test-zlib.elf-x86-64 and b/test/DebugInfo/Inputs/dwarfdump-test-zlib.elf-x86-64 differ diff --git a/test/DebugInfo/Inputs/dwarfdump-test-zlibgnu.elf-x86-64 b/test/DebugInfo/Inputs/dwarfdump-test-zlibgnu.elf-x86-64 new file mode 100644 index 00000000000..0ad7a3d8511 Binary files /dev/null and b/test/DebugInfo/Inputs/dwarfdump-test-zlibgnu.elf-x86-64 differ diff --git a/test/DebugInfo/dwarfdump-zlib.test b/test/DebugInfo/dwarfdump-zlib.test index cbd85ca093c..d3ef806f3f8 100644 --- a/test/DebugInfo/dwarfdump-zlib.test +++ b/test/DebugInfo/dwarfdump-zlib.test @@ -1,6 +1,12 @@ REQUIRES: zlib RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test-zlib.elf-x86-64 | FileCheck %s +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test-zlibgnu.elf-x86-64 | FileCheck %s CHECK: .debug_abbrev contents + +// Dump content of a little section to check that both zlib and zlib gnu styles do +// the decompression correctly and result is the same. This and above also checks +// that sections names are properly shown in zlib-gnu style (without additional 'z' prefix). CHECK: .debug_info contents +CHECK: 0x00000000: Compile Unit: length = 0x00000144 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000148) diff --git a/test/DebugInfo/llvm-symbolizer-zlib.test b/test/DebugInfo/llvm-symbolizer-zlib.test index 0aae7e6c692..2860d090eb8 100644 --- a/test/DebugInfo/llvm-symbolizer-zlib.test +++ b/test/DebugInfo/llvm-symbolizer-zlib.test @@ -1,7 +1,10 @@ REQUIRES: zlib -RUN: echo "%p/Inputs/dwarfdump-test-zlib.elf-x86-64 0x400559" > %t.input -RUN: llvm-symbolizer < %t.input | FileCheck %s +RUN: echo "%p/Inputs/dwarfdump-test-zlib.elf-x86-64 0x4005e9" > %t1.input +RUN: llvm-symbolizer < %t1.input | FileCheck %s + +RUN: echo "%p/Inputs/dwarfdump-test-zlibgnu.elf-x86-64 0x4005e9" > %t2.input +RUN: llvm-symbolizer < %t2.input | FileCheck %s CHECK: main CHECK-NEXT: /tmp/dbginfo{{[/\\]}}dwarfdump-test-zlib.cc:16