[llvm] f97019a - [llvm-readobj/elf] - Add a testing for --stackmap and refine the implementation.

Georgii Rymar via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 03:09:34 PDT 2020


Author: Georgii Rymar
Date: 2020-08-05T13:09:04+03:00
New Revision: f97019ad6e3a96995dda3f759ee692eb81abcc4c

URL: https://github.com/llvm/llvm-project/commit/f97019ad6e3a96995dda3f759ee692eb81abcc4c
DIFF: https://github.com/llvm/llvm-project/commit/f97019ad6e3a96995dda3f759ee692eb81abcc4c.diff

LOG: [llvm-readobj/elf] - Add a testing for --stackmap and refine the implementation.

Currently, we only test the `--stackmap` option here:
https://github.com/llvm/llvm-project/blob/master/llvm/test/Object/stackmap-dump.test
it uses a precompiled MachO binary currently and I've found no tests for this option for ELF.

The implementation also has issues. For example, it might assert on a wrong version
of the .llvm-stackmaps section. Or it might crash on an empty or truncated section.

This patch introduces a new tools/llvm-readobj/ELF test file as well as implements a few
basic checks to catch simple crashes/issues

It also eliminates `unwrapOrError` calls in `printStackMap()`.

Differential revision: https://reviews.llvm.org/D85208

Added: 
    llvm/test/tools/llvm-readobj/ELF/stackmap.test

Modified: 
    llvm/include/llvm/Object/StackMapParser.h
    llvm/lib/CodeGen/StackMaps.cpp
    llvm/tools/llvm-readobj/COFFDumper.cpp
    llvm/tools/llvm-readobj/ELFDumper.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Object/StackMapParser.h b/llvm/include/llvm/Object/StackMapParser.h
index b408f4041034..83926c6471c0 100644
--- a/llvm/include/llvm/Object/StackMapParser.h
+++ b/llvm/include/llvm/Object/StackMapParser.h
@@ -11,6 +11,7 @@
 
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/iterator_range.h"
+#include "llvm/Object/ELF.h"
 #include "llvm/Support/Endian.h"
 #include <cassert>
 #include <cstddef>
@@ -318,6 +319,23 @@ class StackMapParser {
     }
   }
 
+  /// Validates the header of the specified stack map section.
+  static Error validateHeader(ArrayRef<uint8_t> StackMapSection) {
+    // See the comment for StackMaps::emitStackmapHeader().
+    if (StackMapSection.size() < 16)
+      return object::createError(
+          "the stack map section size (" + Twine(StackMapSection.size()) +
+          ") is less than the minimum possible size of its header (16)");
+
+    unsigned Version = StackMapSection[0];
+    if (Version != 3)
+      return object::createError(
+          "the version (" + Twine(Version) +
+          ") of the stack map section is unsupported, the "
+          "supported version is 3");
+    return Error::success();
+  }
+
   using function_iterator = AccessorIterator<FunctionAccessor>;
   using constant_iterator = AccessorIterator<ConstantAccessor>;
   using record_iterator = AccessorIterator<RecordAccessor>;

diff  --git a/llvm/lib/CodeGen/StackMaps.cpp b/llvm/lib/CodeGen/StackMaps.cpp
index 1e060ecbeb43..113d477ec80a 100644
--- a/llvm/lib/CodeGen/StackMaps.cpp
+++ b/llvm/lib/CodeGen/StackMaps.cpp
@@ -404,7 +404,7 @@ void StackMaps::recordStatepoint(const MCSymbol &L, const MachineInstr &MI) {
 /// Emit the stackmap header.
 ///
 /// Header {
-///   uint8  : Stack Map Version (currently 2)
+///   uint8  : Stack Map Version (currently 3)
 ///   uint8  : Reserved (expected to be 0)
 ///   uint16 : Reserved (expected to be 0)
 /// }

diff  --git a/llvm/test/tools/llvm-readobj/ELF/stackmap.test b/llvm/test/tools/llvm-readobj/ELF/stackmap.test
new file mode 100644
index 000000000000..22a1bd1bef8d
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/stackmap.test
@@ -0,0 +1,86 @@
+## Here we test how the --stackmap option can be used to dump .llvm_stackmaps sections.
+
+## Check we are able to dump an empty .llvm_stackmaps section. Document that
+## we are only trying to dump the first stack map section and ignore others if any.
+
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-readobj %t --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=EMPTY --implicit-check-not=warning:
+# RUN: llvm-readelf %t --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=EMPTY --implicit-check-not=warning:
+
+# EMPTY:      LLVM StackMap Version: 3
+# EMPTY-NEXT: Num Functions: 0
+# EMPTY-NEXT: Num Constants: 0
+# EMPTY-NEXT: Num Records: 0
+# EMPTY-NOT:  {{.}}
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_X86_64
+Sections:
+  - Name:         [[NAME=.llvm_stackmaps]]
+    Type:         SHT_PROGBITS
+    ContentArray: [ [[VERSION=0x3]] ]
+    Size:         [[SIZE=16]]
+    ShSize:       [[SHSIZE=<none>]]
+    ShOffset:     [[SHOFFSET=<none>]]
+## An arbitrary second broken .llvm_stackmaps section.
+  - Name:         .llvm_stackmaps (1)
+    Type:         SHT_PROGBITS
+    ContentArray: [ 0xFF ]
+    Size:         0x1
+
+## Hide the first stack map section to allow dumpers to locate and validate the second one, which is broken.
+## Check we are able to find it and report a warning properly.
+
+# RUN: yaml2obj %s -DNAME=.foo -o %t.second
+# RUN: llvm-readobj %t.second --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=SECOND -DFILE=%t.second --implicit-check-not=warning:
+# RUN: llvm-readelf %t.second --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=SECOND -DFILE=%t.second --implicit-check-not=warning:
+
+# SECOND: warning: '[[FILE]]': unable to read the stack map from SHT_PROGBITS section with index 2: the stack map section size (1) is less than the minimum possible size of its header (16)
+
+## Check we report a warning when the size of the .llvm_stackmaps section is less
+## than the minimum possible size of its header.
+
+# RUN: yaml2obj %s -DSHSIZE=0 -o %t.trunc0
+# RUN: llvm-readobj %t.trunc0 --stackmap 2>&1 | FileCheck %s -DFILE=%t.trunc0 --check-prefix=TRUNC -DVAL=0
+# RUN: llvm-readelf %t.trunc0 --stackmap 2>&1 | FileCheck %s -DFILE=%t.trunc0 --check-prefix=TRUNC -DVAL=0
+
+# RUN: yaml2obj %s -DSIZE=1 -o %t.trunc1
+# RUN: llvm-readobj %t.trunc1 --stackmap 2>&1 | FileCheck %s -DFILE=%t.trunc1 --check-prefix=TRUNC -DVAL=1
+# RUN: llvm-readelf %t.trunc1 --stackmap 2>&1 | FileCheck %s -DFILE=%t.trunc1 --check-prefix=TRUNC -DVAL=1
+
+# RUN: yaml2obj %s -DSIZE=15 -o %t.trunc15
+# RUN: llvm-readobj %t.trunc15 --stackmap 2>&1 | FileCheck %s -DFILE=%t.trunc15 --check-prefix=TRUNC -DVAL=15
+# RUN: llvm-readelf %t.trunc15 --stackmap 2>&1 | FileCheck %s -DFILE=%t.trunc15 --check-prefix=TRUNC -DVAL=15
+
+# TRUNC: warning: '[[FILE]]': unable to read the stack map from SHT_PROGBITS section with index 1: the stack map section size ([[VAL]]) is less than the minimum possible size of its header (16)
+
+## Check that we report a warning when the version of the stack map section is not supported.
+
+# RUN: yaml2obj %s -DVERSION=2 -o %t.ver2
+# RUN: llvm-readobj %t.ver2 --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=VERSION -DFILE=%t.ver2 --implicit-check-not=warning: -DVERSION=2
+# RUN: llvm-readelf %t.ver2 --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=VERSION -DFILE=%t.ver2 --implicit-check-not=warning: -DVERSION=2
+
+# RUN: yaml2obj %s -DVERSION=4 -o %t.ver4
+# RUN: llvm-readobj %t.ver4 --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=VERSION -DFILE=%t.ver4 --implicit-check-not=warning: -DVERSION=4
+# RUN: llvm-readelf %t.ver4 --stackmap 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=VERSION -DFILE=%t.ver4 --implicit-check-not=warning: -DVERSION=4
+
+# VERSION: warning: '[[FILE]]': unable to read the stack map from SHT_PROGBITS section with index 1: the version ([[VERSION]]) of the stack map section is unsupported, the supported version is 3
+
+## Check that we report a warning when we are unable to read the content of the stack map section.
+# RUN: yaml2obj %s -DSHOFFSET=0xffff -o %t.offset
+# RUN: llvm-readobj %t.offset --stackmap 2>&1 | FileCheck %s -DFILE=%t.offset --check-prefix=OFFSET
+# RUN: llvm-readelf %t.offset --stackmap 2>&1 | FileCheck %s -DFILE=%t.offset --check-prefix=OFFSET
+
+# OFFSET: warning: '[[FILE]]': unable to read the stack map from SHT_PROGBITS section with index 1: section [index 1] has a sh_offset (0xffff) + sh_size (0x10) that is greater than the file size (0x1b8)

diff  --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 89a904f53ae7..39549efc040c 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -60,10 +60,6 @@ using namespace llvm::codeview;
 using namespace llvm::support;
 using namespace llvm::Win64EH;
 
-static inline Error createError(const Twine &Err) {
-  return make_error<StringError>(Err, object_error::parse_failed);
-}
-
 namespace {
 
 struct LoadConfigTables {

diff  --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 53ebfd5663c9..78d47b540ab6 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -3423,24 +3423,30 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() {
 
 template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
   const ELFFile<ELFT> *Obj = ObjF->getELFFile();
-  const Elf_Shdr *StackMapSection = nullptr;
-  for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
-    StringRef Name =
-        unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec));
-    if (Name == ".llvm_stackmaps") {
-      StackMapSection = &Sec;
-      break;
-    }
-  }
-
+  const Elf_Shdr *StackMapSection = findSectionByName(".llvm_stackmaps");
   if (!StackMapSection)
     return;
 
-  ArrayRef<uint8_t> StackMapContentsArray = unwrapOrError(
-      ObjF->getFileName(), Obj->getSectionContents(StackMapSection));
+  auto Warn = [&](Error &&E) {
+    this->reportUniqueWarning(createError("unable to read the stack map from " +
+                                          describe(*StackMapSection) + ": " +
+                                          toString(std::move(E))));
+  };
+
+  Expected<ArrayRef<uint8_t>> ContentOrErr =
+      Obj->getSectionContents(StackMapSection);
+  if (!ContentOrErr) {
+    Warn(ContentOrErr.takeError());
+    return;
+  }
+
+  if (Error E = StackMapParser<ELFT::TargetEndianness>::validateHeader(
+          *ContentOrErr)) {
+    Warn(std::move(E));
+    return;
+  }
 
-  prettyPrintStackMap(
-      W, StackMapParser<ELFT::TargetEndianness>(StackMapContentsArray));
+  prettyPrintStackMap(W, StackMapParser<ELFT::TargetEndianness>(*ContentOrErr));
 }
 
 template <class ELFT> void ELFDumper<ELFT>::printGroupSections() {


        


More information about the llvm-commits mailing list