diff --git a/llvm/include/llvm/Object/StackMapParser.h b/llvm/include/llvm/Object/StackMapParser.h --- 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 #include @@ -318,6 +319,23 @@ } } + /// Validates the header of the specified stack map section. + static Error validateHeader(ArrayRef 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; using constant_iterator = AccessorIterator; using record_iterator = AccessorIterator; diff --git a/llvm/lib/CodeGen/StackMaps.cpp b/llvm/lib/CodeGen/StackMaps.cpp --- a/llvm/lib/CodeGen/StackMaps.cpp +++ b/llvm/lib/CodeGen/StackMaps.cpp @@ -404,7 +404,7 @@ /// 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 --- /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=]] + ShOffset: [[SHOFFSET=]] +## 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 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -60,10 +60,6 @@ using namespace llvm::support; using namespace llvm::Win64EH; -static inline Error createError(const Twine &Err) { - return make_error(Err, object_error::parse_failed); -} - namespace { struct LoadConfigTables { diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -3423,24 +3423,30 @@ template void ELFDumper::printStackMap() const { const ELFFile *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 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> ContentOrErr = + Obj->getSectionContents(StackMapSection); + if (!ContentOrErr) { + Warn(ContentOrErr.takeError()); + return; + } + + if (Error E = StackMapParser::validateHeader( + *ContentOrErr)) { + Warn(std::move(E)); + return; + } - prettyPrintStackMap( - W, StackMapParser(StackMapContentsArray)); + prettyPrintStackMap(W, StackMapParser(*ContentOrErr)); } template void ELFDumper::printGroupSections() {