[llvm-readelf] Support dumping of stack sizes sections with readelf --stack-sizes
Reviewers: jhenderson, grimar, rupprecht
Differential Revision: https://reviews.llvm.org/D65313
llvm-svn: 367942
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 029dd7d..0e81958 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/STLExtras.h"
@@ -36,6 +37,7 @@
#include "llvm/Object/ELFTypes.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/RelocationResolver.h"
#include "llvm/Object/StackMapParser.h"
#include "llvm/Support/AMDGPUMetadata.h"
#include "llvm/Support/ARMAttributeParser.h"
@@ -182,6 +184,7 @@
void printNotes() override;
void printELFLinkerOptions() override;
+ void printStackSizes() override;
const object::ELFObjectFile<ELFT> *getElfObject() const { return ObjF; };
@@ -347,6 +350,7 @@
public:
using Elf_Shdr = typename ELFT::Shdr;
using Elf_Sym = typename ELFT::Sym;
+ using Elf_Addr = typename ELFT::Addr;
DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) {}
virtual ~DumpStyle() = default;
@@ -379,6 +383,20 @@
virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0;
virtual void printNotes(const ELFFile<ELFT> *Obj) = 0;
virtual void printELFLinkerOptions(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printStackSizes(const ELFObjectFile<ELFT> *Obj) = 0;
+ void printNonRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj,
+ std::function<void()> PrintHeader);
+ void printRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj,
+ std::function<void()> PrintHeader);
+ void printFunctionStackSize(const ELFObjectFile<ELFT> *Obj, uint64_t SymValue,
+ SectionRef FunctionSec,
+ const StringRef SectionName, DataExtractor Data,
+ uint64_t *Offset);
+ void printStackSize(const ELFObjectFile<ELFT> *Obj, RelocationRef Rel,
+ SectionRef FunctionSec,
+ const StringRef &StackSizeSectionName,
+ const RelocationResolver &Resolver, DataExtractor Data);
+ virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0;
virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0;
virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0;
const ELFDumper<ELFT> *dumper() const { return Dumper; }
@@ -423,6 +441,8 @@
void printAddrsig(const ELFFile<ELFT> *Obj) override;
void printNotes(const ELFFile<ELFT> *Obj) override;
void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override;
+ void printStackSizes(const ELFObjectFile<ELFT> *Obj) override;
+ void printStackSizeEntry(uint64_t Size, StringRef FuncName) override;
void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -526,6 +546,8 @@
void printAddrsig(const ELFFile<ELFT> *Obj) override;
void printNotes(const ELFFile<ELFT> *Obj) override;
void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override;
+ void printStackSizes(const ELFObjectFile<ELFT> *Obj) override;
+ void printStackSizeEntry(uint64_t Size, StringRef FuncName) override;
void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -1715,6 +1737,10 @@
ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile());
}
+template <class ELFT> void ELFDumper<ELFT>::printStackSizes() {
+ ELFDumperStyle->printStackSizes(ObjF);
+}
+
#define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \
{ #enum, prefix##_##enum }
@@ -4357,6 +4383,296 @@
OS << "printELFLinkerOptions not implemented!\n";
}
+// FIXME: As soon as the DataExtractor interface handles uint64_t *, this
+// should be eliminated. See upstream review https://reviews.llvm.org/D64006.
+inline uint32_t *AdjustPtr(uint64_t *Offset) {
+ uint32_t *Ptr = reinterpret_cast<uint32_t *>(Offset);
+ if (sys::IsBigEndianHost)
+ Ptr++;
+ return Ptr;
+}
+
+template <class ELFT>
+void DumpStyle<ELFT>::printFunctionStackSize(
+ const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, SectionRef FunctionSec,
+ const StringRef SectionName, DataExtractor Data, uint64_t *Offset) {
+ // This function ignores potentially erroneous input, unless it is directly
+ // related to stack size reporting.
+ SymbolRef FuncSym;
+ for (const ELFSymbolRef &Symbol : Obj->symbols()) {
+ Expected<uint64_t> SymAddrOrErr = Symbol.getAddress();
+ if (!SymAddrOrErr) {
+ consumeError(SymAddrOrErr.takeError());
+ continue;
+ }
+ if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) {
+ // Check if the symbol is in the right section.
+ if (FunctionSec.containsSymbol(Symbol)) {
+ FuncSym = Symbol;
+ break;
+ }
+ }
+ }
+
+ StringRef FileStr = Obj->getFileName();
+ std::string FuncName = "?";
+ // A valid SymbolRef has a non-null object file pointer.
+ if (FuncSym.BasicSymbolRef::getObject()) {
+ // Extract the symbol name.
+ Expected<StringRef> FuncNameOrErr = FuncSym.getName();
+ if (FuncNameOrErr)
+ FuncName = maybeDemangle(*FuncNameOrErr);
+ else
+ consumeError(FuncNameOrErr.takeError());
+ } else
+ reportWarning(" '" + FileStr +
+ "': could not identify function symbol for stack size entry");
+
+ // Extract the size. The expectation is that Offset is pointing to the right
+ // place, i.e. past the function address.
+ uint64_t PrevOffset = *Offset;
+ uint64_t StackSize = Data.getULEB128(AdjustPtr(Offset));
+ // getULEB128() does not advance Offset if it is not able to extract a valid
+ // integer.
+ if (*Offset == PrevOffset)
+ reportError(
+ FileStr,
+ createStringError(object_error::parse_failed,
+ "could not extract a valid stack size in section %s",
+ SectionName.data()));
+
+ printStackSizeEntry(StackSize, FuncName);
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) {
+ OS.PadToColumn(2);
+ OS << format_decimal(Size, 11);
+ OS.PadToColumn(18);
+ OS << FuncName << "\n";
+}
+
+template <class ELFT>
+void DumpStyle<ELFT>::printStackSize(const ELFObjectFile<ELFT> *Obj,
+ RelocationRef Reloc,
+ SectionRef FunctionSec,
+ const StringRef &StackSizeSectionName,
+ const RelocationResolver &Resolver,
+ DataExtractor Data) {
+ // This function ignores potentially erroneous input, unless it is directly
+ // related to stack size reporting.
+ object::symbol_iterator RelocSym = Reloc.getSymbol();
+ uint64_t RelocSymValue = 0;
+ StringRef FileStr = Obj->getFileName();
+ if (RelocSym != Obj->symbol_end()) {
+ // Ensure that the relocation symbol is in the function section, i.e. the
+ // section where the functions whose stack sizes we are reporting are
+ // located.
+ StringRef SymName = "?";
+ Expected<StringRef> NameOrErr = RelocSym->getName();
+ if (NameOrErr)
+ SymName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ auto SectionOrErr = RelocSym->getSection();
+ if (!SectionOrErr) {
+ reportWarning(" '" + FileStr +
+ "': cannot identify the section for relocation symbol " +
+ SymName);
+ consumeError(SectionOrErr.takeError());
+ } else if (*SectionOrErr != FunctionSec) {
+ reportWarning(" '" + FileStr + "': relocation symbol " + SymName +
+ " is not in the expected section");
+ // Pretend that the symbol is in the correct section and report its
+ // stack size anyway.
+ FunctionSec = **SectionOrErr;
+ }
+
+ Expected<uint64_t> RelocSymValueOrErr = RelocSym->getValue();
+ if (RelocSymValueOrErr)
+ RelocSymValue = *RelocSymValueOrErr;
+ else
+ consumeError(RelocSymValueOrErr.takeError());
+ }
+
+ uint64_t Offset = Reloc.getOffset();
+ if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1))
+ reportError(FileStr, createStringError(
+ object_error::parse_failed,
+ "found invalid relocation offset into section %s "
+ "while trying to extract a stack size entry",
+ StackSizeSectionName.data()));
+
+ uint64_t Addend = Data.getAddress(AdjustPtr(&Offset));
+ uint64_t SymValue = Resolver(Reloc, RelocSymValue, Addend);
+ this->printFunctionStackSize(Obj, SymValue, FunctionSec, StackSizeSectionName,
+ Data, &Offset);
+}
+
+template <class ELFT>
+SectionRef toSectionRef(const ObjectFile *Obj, const typename ELFT::Shdr *Sec) {
+ DataRefImpl DRI;
+ DRI.p = reinterpret_cast<uintptr_t>(Sec);
+ return SectionRef(DRI, Obj);
+}
+
+template <class ELFT>
+void DumpStyle<ELFT>::printNonRelocatableStackSizes(
+ const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader) {
+ // This function ignores potentially erroneous input, unless it is directly
+ // related to stack size reporting.
+ const ELFFile<ELFT> *EF = Obj->getELFFile();
+ StringRef FileStr = Obj->getFileName();
+ for (const SectionRef &Sec : Obj->sections()) {
+ StringRef SectionName;
+ Sec.getName(SectionName);
+ const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl());
+ if (!SectionName.startswith(".stack_sizes"))
+ continue;
+ PrintHeader();
+ ArrayRef<uint8_t> Contents = unwrapOrError(EF->getSectionContents(ElfSec));
+ DataExtractor Data(
+ StringRef(reinterpret_cast<const char *>(Contents.data()),
+ Contents.size()),
+ Obj->isLittleEndian(), sizeof(Elf_Addr));
+ // A .stack_sizes section header's sh_link field is supposed to point
+ // to the section that contains the functions whose stack sizes are
+ // described in it.
+ const Elf_Shdr *FunctionELFSec =
+ unwrapOrError(EF->getSection(ElfSec->sh_link));
+ uint64_t Offset = 0;
+ while (Offset < Contents.size()) {
+ // The function address is followed by a ULEB representing the stack
+ // size. Check for an extra byte before we try to process the entry.
+ if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) {
+ reportError(
+ FileStr,
+ createStringError(
+ object_error::parse_failed,
+ "section %s ended while trying to extract a stack size entry",
+ SectionName.data()));
+ }
+ uint64_t SymValue = Data.getAddress(AdjustPtr(&Offset));
+ printFunctionStackSize(Obj, SymValue,
+ toSectionRef<ELFT>(Obj, FunctionELFSec),
+ SectionName, Data, &Offset);
+ }
+ }
+}
+
+template <class ELFT>
+void DumpStyle<ELFT>::printRelocatableStackSizes(
+ const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader) {
+ const ELFFile<ELFT> *EF = Obj->getELFFile();
+ StringRef FileStr = Obj->getFileName();
+ // Build a map between stack size sections and their corresponding relocation
+ // sections.
+ llvm::MapVector<SectionRef, SectionRef> StackSizeRelocMap;
+ const SectionRef NullSection;
+
+ for (const SectionRef &Sec : Obj->sections()) {
+ StringRef SectionName;
+ Sec.getName(SectionName);
+ // A stack size section that we haven't encountered yet is mapped to the
+ // null section until we find its corresponding relocation section.
+ if (SectionName.startswith(".stack_sizes"))
+ if (StackSizeRelocMap.count(Sec) == 0) {
+ StackSizeRelocMap[Sec] = NullSection;
+ continue;
+ }
+
+ // Check relocation sections if they are relocating contents of a
+ // stack sizes section.
+ const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl());
+ uint32_t SectionType = ElfSec->sh_type;
+ if (SectionType != ELF::SHT_RELA && SectionType != ELF::SHT_REL)
+ continue;
+
+ SectionRef Contents = *Sec.getRelocatedSection();
+ const Elf_Shdr *ContentsSec = Obj->getSection(Contents.getRawDataRefImpl());
+ Expected<StringRef> ContentsSectionNameOrErr =
+ EF->getSectionName(ContentsSec);
+ if (!ContentsSectionNameOrErr) {
+ consumeError(ContentsSectionNameOrErr.takeError());
+ continue;
+ }
+ if (!ContentsSectionNameOrErr->startswith(".stack_sizes"))
+ continue;
+ // Insert a mapping from the stack sizes section to its relocation section.
+ StackSizeRelocMap[toSectionRef<ELFT>(Obj, ContentsSec)] = Sec;
+ }
+
+ for (const auto &StackSizeMapEntry : StackSizeRelocMap) {
+ PrintHeader();
+ const SectionRef &StackSizesSec = StackSizeMapEntry.first;
+ const SectionRef &RelocSec = StackSizeMapEntry.second;
+
+ // Warn about stack size sections without a relocation section.
+ StringRef StackSizeSectionName;
+ StackSizesSec.getName(StackSizeSectionName);
+ if (RelocSec == NullSection) {
+ reportWarning(" '" + FileStr + "': section " + StackSizeSectionName +
+ " does not have a corresponding "
+ "relocation section");
+ continue;
+ }
+
+ // A .stack_sizes section header's sh_link field is supposed to point
+ // to the section that contains the functions whose stack sizes are
+ // described in it.
+ const Elf_Shdr *StackSizesELFSec =
+ Obj->getSection(StackSizesSec.getRawDataRefImpl());
+ const SectionRef FunctionSec = toSectionRef<ELFT>(
+ Obj, unwrapOrError(EF->getSection(StackSizesELFSec->sh_link)));
+
+ bool (*IsSupportedFn)(uint64_t);
+ RelocationResolver Resolver;
+ std::tie(IsSupportedFn, Resolver) = getRelocationResolver(*Obj);
+ auto Contents = unwrapOrError(StackSizesSec.getContents());
+ DataExtractor Data(
+ StringRef(reinterpret_cast<const char *>(Contents.data()),
+ Contents.size()),
+ Obj->isLittleEndian(), sizeof(Elf_Addr));
+ for (const RelocationRef &Reloc : RelocSec.relocations()) {
+ if (!IsSupportedFn(Reloc.getType())) {
+ StringRef RelocSectionName;
+ RelocSec.getName(RelocSectionName);
+ StringRef RelocName = EF->getRelocationTypeName(Reloc.getType());
+ reportError(
+ FileStr,
+ createStringError(object_error::parse_failed,
+ "unsupported relocation type in section %s: %s",
+ RelocSectionName.data(), RelocName.data()));
+ }
+ this->printStackSize(Obj, Reloc, FunctionSec, StackSizeSectionName,
+ Resolver, Data);
+ }
+ }
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printStackSizes(const ELFObjectFile<ELFT> *Obj) {
+ bool HeaderHasBeenPrinted = false;
+ auto PrintHeader = [&]() {
+ if (HeaderHasBeenPrinted)
+ return;
+ OS << "\nStack Sizes:\n";
+ OS.PadToColumn(9);
+ OS << "Size";
+ OS.PadToColumn(18);
+ OS << "Function\n";
+ HeaderHasBeenPrinted = true;
+ };
+
+ // For non-relocatable objects, look directly for sections whose name starts
+ // with .stack_sizes and process the contents.
+ if (Obj->isRelocatableObject())
+ this->printRelocatableStackSizes(Obj, PrintHeader);
+ else
+ this->printNonRelocatableStackSizes(Obj, PrintHeader);
+}
+
template <class ELFT>
void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) {
size_t Bias = ELFT::Is64Bits ? 8 : 0;
@@ -5223,6 +5539,17 @@
}
template <class ELFT>
+void LLVMStyle<ELFT>::printStackSizes(const ELFObjectFile<ELFT> *Obj) {
+ W.printString(
+ "Dumping of stack sizes in LLVM style is not implemented yet\n");
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) {
+ // FIXME: Implement this function for LLVM-style dumping.
+}
+
+template <class ELFT>
void LLVMStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) {
auto PrintEntry = [&](const Elf_Addr *E) {
W.printHex("Address", Parser.getGotAddress(E));