#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>
}
}
+ /// 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>;
--- /dev/null
+## 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)
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() {