ObjectFileELF: Add support for compressed sections
authorPavel Labath <labath@google.com>
Fri, 15 Dec 2017 14:23:58 +0000 (14:23 +0000)
committerPavel Labath <labath@google.com>
Fri, 15 Dec 2017 14:23:58 +0000 (14:23 +0000)
Summary:
We use the llvm decompressor to decompress SHF_COMPRESSED sections. This enables
us to read data from debug info sections, which are sometimes compressed,
particuarly in the split-dwarf case.  This functionality is only available if
llvm is compiled with zlib support.

Reviewers: clayborg, zturner

Subscribers: emaste, mgorny, aprantl, lldb-commits

Differential Revision: https://reviews.llvm.org/D40616

llvm-svn: 320813

lldb/include/lldb/Symbol/ObjectFile.h
lldb/lit/CMakeLists.txt
lldb/lit/Modules/compressed-sections.yaml [new file with mode: 0644]
lldb/lit/Modules/lit.local.cfg [new file with mode: 0644]
lldb/lit/lit.cfg
lldb/lit/lit.site.cfg.in
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
lldb/tools/lldb-test/lldb-test.cpp
lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp

index 0708b7c47f34db1adfa58562dcabf69a6297e0ef..3f9250af7e08a2eeebbc34e89631ca8b4068c387 100644 (file)
@@ -793,15 +793,24 @@ public:
   static lldb::DataBufferSP ReadMemory(const lldb::ProcessSP &process_sp,
                                        lldb::addr_t addr, size_t byte_size);
 
+  // This function returns raw file contents. Do not use it if you want
+  // transparent decompression of section contents.
   size_t GetData(lldb::offset_t offset, size_t length,
                  DataExtractor &data) const;
 
+  // This function returns raw file contents. Do not use it if you want
+  // transparent decompression of section contents.
   size_t CopyData(lldb::offset_t offset, size_t length, void *dst) const;
 
+  // This function will transparently decompress section data if the section if
+  // compressed.
   virtual size_t ReadSectionData(Section *section,
                                  lldb::offset_t section_offset, void *dst,
                                  size_t dst_len);
 
+  // This function will transparently decompress section data if the section if
+  // compressed. Note that for compressed section the resulting data size may be
+  // larger than what Section::GetFileSize reports.
   virtual size_t ReadSectionData(Section *section,
                                  DataExtractor &section_data);
 
index 925b833d5943eea16ff3966fd189fbf5c177e290..5488154318a9e51a15a7160b91c7b96a17b1a24e 100644 (file)
@@ -22,10 +22,11 @@ configure_lit_site_cfg(
 set(LLDB_TEST_DEPS
   LLDBUnitTests
   lldb
+  lldb-test
   )
 
 if(NOT LLDB_BUILT_STANDALONE)
-  list(APPEND LLDB_TEST_DEPS FileCheck not)
+  list(APPEND LLDB_TEST_DEPS FileCheck not yaml2obj)
 endif()
   
 # lldb-server is not built on every platform.
diff --git a/lldb/lit/Modules/compressed-sections.yaml b/lldb/lit/Modules/compressed-sections.yaml
new file mode 100644 (file)
index 0000000..0eabd99
--- /dev/null
@@ -0,0 +1,30 @@
+# REQUIRES: zlib
+# RUN: yaml2obj %s > %t
+# RUN: lldb-test module-sections --contents %t | FileCheck %s
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS32
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_386
+Sections:
+  - Name:            .hello_elf
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_COMPRESSED ]
+    Content:         010000000800000001000000789c5330700848286898000009c802c1
+  - Name:            .bogus
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_COMPRESSED ]
+    Content:         deadbeefbaadf00d
+
+# CHECK: Name: .hello_elf
+# CHECK-NEXT: VM size: 0
+# CHECK-NEXT: File size: 28
+# CHECK-NEXT: Data:
+# CHECK-NEXT: 20304050 60708090
+
+# CHECK: Name: .bogus
+# CHECK-NEXT: VM size: 0
+# CHECK-NEXT: File size: 8
+# CHECK-NEXT: Data:
+# CHECK-NEXT: DEADBEEF BAADF00D
diff --git a/lldb/lit/Modules/lit.local.cfg b/lldb/lit/Modules/lit.local.cfg
new file mode 100644 (file)
index 0000000..8169b9f
--- /dev/null
@@ -0,0 +1 @@
+config.suffixes = ['.yaml']
index 51f795313f3950d59b147a6ce257933acdf4390c..402d03947ca8afc8dfaba147953d9820d022ddbc 100644 (file)
@@ -9,6 +9,9 @@ import locale
 import lit.formats
 import lit.util
 
+def binary_feature(on, feature, off_prefix):
+  return feature if on else off_prefix + feature
+
 # Configuration file for the 'lit' test runner.
 
 # name: The name of this test suite.
@@ -81,6 +84,8 @@ if debugserver is not None:
     config.substitutions.append(('%debugserver', debugserver))
 
 for pattern in [r"\bFileCheck\b",
+                r"\blldb-test\b",
+                r"\byaml2obj\b",
                 r"\| \bnot\b"]:
     tool_match = re.match(r"^(\\)?((\| )?)\W+b([0-9A-Za-z-_]+)\\b\W*$",
                           pattern)
@@ -125,6 +130,8 @@ elif re.match(r'gcc', config.cc):
 elif re.match(r'cl', config.cc):
     config.available_features.add("compiler-msvc")
 
+config.available_features.add(binary_feature(config.have_zlib, "zlib", "no"))
+
 # llvm-config knows whether it is compiled with asserts (and)
 # whether we are operating in release/debug mode.
 import subprocess
index 4088bce470facfc5532244afa2b2adc5cc8c0e7e..2cfa677651a113e001e7d7dd9a7561d11691d26f 100644 (file)
@@ -12,6 +12,7 @@ config.target_triple = "@TARGET_TRIPLE@"
 config.python_executable = "@PYTHON_EXECUTABLE@"
 config.cc = "@LLDB_TEST_C_COMPILER@"
 config.cxx = "@LLDB_TEST_CXX_COMPILER@"
+config.have_zlib = @HAVE_LIBZ@
 
 # Support substitution of the tools and libs dirs with user parameters. This is
 # used when we can't determine the tool dir at configuration time.
index fc42dde8284382259b939d0f294b790934976c80..9869a94f06b0bda02506366a301b8779b275e0c5 100644 (file)
@@ -23,6 +23,7 @@
 #include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
 #include "lldb/Utility/DataBufferLLVM.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/Status.h"
@@ -31,6 +32,7 @@
 
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Decompressor.h"
 #include "llvm/Support/ARMBuildAttributes.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -3460,3 +3462,56 @@ ObjectFile::Strata ObjectFileELF::CalculateStrata() {
   }
   return eStrataUnknown;
 }
+
+size_t ObjectFileELF::ReadSectionData(Section *section,
+                       lldb::offset_t section_offset, void *dst,
+                       size_t dst_len) {
+  // If some other objectfile owns this data, pass this to them.
+  if (section->GetObjectFile() != this)
+    return section->GetObjectFile()->ReadSectionData(section, section_offset,
+                                                     dst, dst_len);
+
+  if (!section->Test(SHF_COMPRESSED))
+    return ObjectFile::ReadSectionData(section, section_offset, dst, dst_len);
+
+  // For compressed sections we need to read to full data to be able to
+  // decompress.
+  DataExtractor data;
+  ReadSectionData(section, data);
+  return data.CopyData(section_offset, dst_len, dst);
+}
+
+size_t ObjectFileELF::ReadSectionData(Section *section,
+                                      DataExtractor &section_data) {
+  // If some other objectfile owns this data, pass this to them.
+  if (section->GetObjectFile() != this)
+    return section->GetObjectFile()->ReadSectionData(section, section_data);
+
+  Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES);
+
+  size_t result = ObjectFile::ReadSectionData(section, section_data);
+  if (result == 0 || !section->Test(SHF_COMPRESSED))
+    return result;
+
+  auto Decompressor = llvm::object::Decompressor::create(
+      section->GetName().GetStringRef(),
+      {reinterpret_cast<const char *>(section_data.GetDataStart()),
+       section_data.GetByteSize()},
+      GetByteOrder() == eByteOrderLittle, GetAddressByteSize() == 8);
+  if (!Decompressor) {
+    LLDB_LOG(log, "Unable to initialize decompressor for section {0}: {1}",
+             section->GetName(), llvm::toString(Decompressor.takeError()));
+    return result;
+  }
+  auto buffer_sp =
+      std::make_shared<DataBufferHeap>(Decompressor->getDecompressedSize(), 0);
+  if (auto Error = Decompressor->decompress(
+          {reinterpret_cast<char *>(buffer_sp->GetBytes()),
+           buffer_sp->GetByteSize()})) {
+    LLDB_LOG(log, "Decompression of section {0} failed: {1}",
+             section->GetName(), llvm::toString(std::move(Error)));
+    return result;
+  }
+  section_data.SetData(buffer_sp);
+  return buffer_sp->GetByteSize();
+}
index e3a267a8d850e57a2645b95b54e0c1d0471bfd7d..2909f4e52e4aab2233b181413307881841446470 100644 (file)
@@ -140,6 +140,13 @@ public:
 
   ObjectFile::Strata CalculateStrata() override;
 
+  size_t ReadSectionData(lldb_private::Section *section,
+                         lldb::offset_t section_offset, void *dst,
+                         size_t dst_len) override;
+
+  size_t ReadSectionData(lldb_private::Section *section,
+                         lldb_private::DataExtractor &section_data) override;
+
   // Returns number of program headers found in the ELF file.
   size_t GetProgramHeaderCount();
 
index 723093dee05c3faf0f45591c3347669ba8db95ff..1fe2f5b63859456cb752754a462a9e8a27574aaf 100644 (file)
@@ -89,7 +89,8 @@ static void dumpModules(Debugger &Dbg) {
       assert(S);
       Printer.formatLine("Index: {0}", I);
       Printer.formatLine("Name: {0}", S->GetName().GetStringRef());
-      Printer.formatLine("Length: {0}", S->GetByteSize());
+      Printer.formatLine("VM size: {0}", S->GetByteSize());
+      Printer.formatLine("File size: {0}", S->GetFileSize());
 
       if (opts::module::SectionContents) {
         DataExtractor Data;
index 16fa5f000a98061667d53ccd5d8f8c208e37a9e2..056799ee9191c62310b9725254caf55264f85a46 100644 (file)
 
 #include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
 #include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h"
+#include "TestingSupport/TestUtilities.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Host/HostInfo.h"
-#include "TestingSupport/TestUtilities.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/Support/Compression.h"
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"