Index: llvm/docs/CommandGuide/llvm-readelf.rst =================================================================== --- llvm/docs/CommandGuide/llvm-readelf.rst +++ llvm/docs/CommandGuide/llvm-readelf.rst @@ -160,6 +160,11 @@ Display contents of the stackmap section. +.. option:: --stack-sizes + + Display the contents of the stack sizes section(s), i.e. pairs of function + names and the size of their stack frames. + .. option:: --string-dump=, -p Display the specified section(s) as a list of strings. ``section`` may be a Index: llvm/test/tools/llvm-readobj/stack-sizes.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/stack-sizes.test @@ -0,0 +1,480 @@ +## Check that we correctly display the contents of the .stack_sizes section +## in a relocatable object file. + +# RUN: yaml2obj --docnum=1 %s > %t01 +# RUN: llvm-readelf --stack-sizes %t01 \ +# RUN: | FileCheck %s --check-prefix=RELOC --strict-whitespace --match-full-lines + +# RELOC: Size Function +# RELOC-NEXT: 16 referenced_by_symbol_foo +# RELOC-NEXT: 32 referenced_via_section_bar +# RELOC-NEXT: 8 separate_text_section_baz +# RELOC-NOT:{{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .text.baz + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS +## 2 stack size entries. Each consists of an address (subject to relocation) +## followed by a ULEB for the size. + Content: "000000000000000010000000000000000020" + Link: .text + - Name: .stack_sizes.baz + Type: SHT_PROGBITS +## One stack size entry. + Content: "200000000000000008" + Link: .text.baz + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: +## A symbol relative reference. + - Offset: 0 + Symbol: referenced_by_symbol_foo + Type: R_X86_64_64 +## A section relative reference. + - Offset: 9 + Addend: 16 + Symbol: .text + Type: R_X86_64_64 + - Name: .rela.stack_sizes.baz + Type: SHT_RELA + Info: .stack_sizes.baz + Relocations: + - Offset: 0 + Symbol: separate_text_section_baz + Type: R_X86_64_64 +Symbols: + - Name: separate_text_section_baz + Section: .text.baz + Type: STT_FUNC + - Name: .text + Section: .text + Type: STT_SECTION + - Name: referenced_by_symbol_foo + Section: .text + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: referenced_via_section_bar + Section: .text + Value: 0x10 + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we correctly report the stack sizes in an executable (non-relocatable) +## object file. + +# RUN: yaml2obj --docnum=2 %s > %t02 +# RUN: llvm-readelf --stack-sizes %t02 \ +# RUN: | FileCheck %s --check-prefix=EXEC --strict-whitespace --match-full-lines + +# EXEC: Size Function +# EXEC-NEXT: 16 foo +# EXEC-NEXT: 32 bar +# EXEC-NOT:{{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "100000000000000010200000000000000020" + Link: .text +Symbols: + - Name: foo + Section: .text + Value: 0x10 + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: bar + Section: .text + Value: 0x20 + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we report an error when we find relocations whose offsets point outside +## of the .stack_sizes section. + +# RUN: yaml2obj --docnum=3 %s > %t03 +# RUN: not llvm-readelf --stack-sizes %t03 2>&1 | FileCheck %s --check-prefix=SHORT -DFILE=%t03 + +# SHORT: error: '[[FILE]]': found invalid relocation offset into section .stack_sizes while trying to extract a stack size entry + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS + Size: 1 + Link: .text + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: + - Offset: 1 + Symbol: foo + Type: R_X86_64_64 +Symbols: + - Name: foo + Section: .text + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we warn about a function symbol that is not in the section +## that is referenced by the stack sizes section's sh_link. + +# RUN: yaml2obj --docnum=4 %s > %t04 +# RUN: llvm-readelf --stack-sizes %t04 2> %t04.err | FileCheck %s --check-prefix=WRONGSECTION +# RUN: FileCheck %s < %t04.err --check-prefix=WRONGSECTION-ERR -DFILE=%t04 + +# WRONGSECTION: Size Function +# WRONGSECTION-NEXT: 8 foo +# WRONGSECTION-ERR: warning: '[[FILE]]': relocation symbol foo is not in the expected section + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 8 + - Name: .text2 + Type: SHT_PROGBITS + Size: 8 + Flags: [SHF_ALLOC] + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "000000000000000008" + Link: .text2 + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: + - Offset: 0 + Symbol: foo + Type: R_X86_64_64 +Symbols: + - Name: foo + Section: .text + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we report an error when a stack sizes section ends with a complete stack size entry. + +# RUN: yaml2obj --docnum=5 %s > %t05 +# RUN: not llvm-readelf --stack-sizes %t05 2>&1 %t05.err | \ +# RUN: FileCheck %s --check-prefix=SUDDENEND -DFILE=%t05 + +# SUDDENEND: error: '[[FILE]]': section .stack_sizes ended while trying to extract a stack size entry + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "10000000" + Link: .text +Symbols: + - Name: foo + Section: .text + Value: 0x10 + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we report an invalid stack size, which is represented by a ULEB that +## ends in a byte with the high bit set. + +# RUN: yaml2obj --docnum=6 %s > %t06 +# RUN: not llvm-readelf --stack-sizes %t06 2>&1 | FileCheck %s --check-prefix=BADSIZE -DFILE=%t06 + +# BADSIZE: error: '[[FILE]]': could not extract a valid stack size in section .stack_sizes + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "100000000000000080" + Link: .text +Symbols: + - Name: foo + Section: .text + Value: 0x10 + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we report a warning when a relocation symbol does not belong to a +## valid section. We expect a stack size entry with an unknown symbol in the +## output. + +# RUN: yaml2obj --docnum=7 %s > %t07 +# RUN: llvm-readelf --stack-sizes %t07 2> %t07.err | FileCheck %s --check-prefix=BADSECTION-OUT +# RUN: FileCheck %s < %t07.err --check-prefix=BADSECTION-ERR -DFILE=%t07 + +# BADSECTION-OUT: Size Function +# BADSECTION-OUT: 8 ? +# BADSECTION-ERR: warning: '[[FILE]]': cannot identify the section for relocation symbol foo + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 8 + - Name: .stack_sizes + Type: SHT_PROGBITS + Link: .text + Content: "000000000000000008" + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: + - Offset: 0 + Symbol: foo + Type: R_X86_64_64 +Symbols: + - Name: foo +## An invalid section index. + Index: 10 + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we report a warning when a stack sizes section does not come with +## a corresponding relocation section. + +# RUN: yaml2obj --docnum=8 %s > %t08 +# RUN: llvm-readelf --stack-sizes %t08 2> %t08.err | FileCheck %s --check-prefix=NORELOCSECTION-OUT +# RUN: FileCheck %s < %t08.err --check-prefix=NORELOCSECTION-ERR -DFILE=%t08 + +# NORELOCSECTION-OUT: Size Function +# NORELOCSECTION-OUT-NOT: {{.}} +# NORELOCSECTION-ERR: warning: '[[FILE]]': section .stack_sizes does not have a corresponding relocation section + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 8 + - Name: .stack_sizes + Type: SHT_PROGBITS + Link: .text + Content: "000000000000000008" + +## Check that we handle multiple object files, separately and when they +## are in an archive. This also checks whether we have blank lines between the +## tables. + +# RUN: llvm-ar rc %t1.a %t01 %t02 +# RUN: llvm-readelf --stack-sizes %t01 %t02 | FileCheck %s --check-prefix=MULTIPLE +# RUN: llvm-readelf --stack-sizes %t1.a \ +# RUN: | FileCheck %s --check-prefix=MULTIPLE --check-prefix=ARCHIVE --strict-whitespace\ +# RUN: --match-full-lines -DFILE=%t1.a + +# ARCHIVE:File: [[FILE]]({{.*01}}) +# MULTIPLE:Stack Sizes: +# MULTIPLE-NEXT: Size Function +# MULTIPLE-NEXT: 16 referenced_by_symbol_foo +# MULTIPLE-NEXT: 32 referenced_via_section_bar +# MULTIPLE-NEXT: 8 separate_text_section_baz +# MULTIPLE-EMPTY: +# ARCHIVE:File: [[FILE]]({{.*02}}) +# MULTIPLE-EMPTY: +# MULTIPLE-NEXT:Stack Sizes: +# MULTIPLE-NEXT: Size Function +# MULTIPLE-NEXT: 16 foo +# MULTIPLE-NEXT: 32 bar + +## Check that stack sizes are dumped with the --all switch. + +# RUN: yaml2obj --docnum=1 %s > %t13 +# RUN: llvm-readelf --all %t13 | FileCheck %s --check-prefix=ALL + +# ALL: Stack Sizes: + +## Check that we do not consider symbols that are not function symbols, even though +## a relocation references them. + +# RUN: yaml2obj --docnum=9 %s > %t14 +# RUN: llvm-readelf --all %t14 2> %t14.err | FileCheck %s --check-prefix=NONFUNCTIONSYM +# RUN: FileCheck %s < %t14.err --check-prefix=NONFUNCTIONSYM-ERR -DFILE=%t14 + +# NONFUNCTIONSYM: Stack Sizes: +# NONFUNCTIONSYM: 0 ? +# NONFUNCTIONSYM-ERR: warning: '[[FILE]]': could not identify function symbol for stack size entry + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS + Size: 9 + Link: .text + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: + - Offset: 0 + Symbol: foo + Type: R_X86_64_64 +Symbols: + - Name: foo + Section: .text + Type: STT_OBJECT + Binding: STB_GLOBAL + +## Check that we report an error when we find an unsupported relocation +## in the section that contains the stack size entries' relocations. + +# RUN: yaml2obj --docnum=10 %s > %t15 +# RUN: not llvm-readelf --stack-sizes %t15 2>&1 | FileCheck %s --check-prefix=UNSUPPRELOC -DFILE=%t15 + +# UNSUPPRELOC: error: '[[FILE]]': unsupported relocation type in section .rela.stack_sizes: R_X86_64_RELATIVE + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 8 + - Name: .stack_sizes + Type: SHT_PROGBITS + Size: 16 + Link: .text + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: + - Offset: 0 + Symbol: foo + Type: R_X86_64_RELATIVE +Symbols: + - Name: foo + Section: .text + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that warning messages in archives do not impact other members. In the following +## test, the first archive member generates a warning and we make sure all the information +## is still dumped. + +# RUN: llvm-ar rc %t2.a %t04 %t01 +# RUN: llvm-readelf --stack-sizes %t2.a 2>&1 | FileCheck %s --check-prefix=ARCHIVEWARN \ +# RUN: -DFILE=%t2.a + +# ARCHIVEWARN: File: [[FILE]]({{.*04}}) +# ARCHIVEWARN: Stack Sizes: +# ARCHIVEWARN-NEXT: Size Function +# ARCHIVEWARN: 8 foo +# ARCHIVEWARN: File: [[FILE]]({{.*01}}) +# ARCHIVEWARN: Stack Sizes: +# ARCHIVEWARN-NEXT: Size Function +# ARCHIVEWARN-NEXT: 16 referenced_by_symbol_foo +# ARCHIVEWARN-NEXT: 32 referenced_via_section_bar +# ARCHIVEWARN-NEXT: 8 separate_text_section_baz +# ARCHIVEWARN-NOT: {{.}} +# ARCHIVEWARN-ERR: warning: '[[FILE]]': relocation symbol foo is not in the expected section + +## Check that we demangle function names when requested. + +# RUN: yaml2obj --docnum=11 %s > %t16 +# RUN: llvm-readelf --stack-sizes --demangle %t16 | FileCheck %s --check-prefix=DEMANGLE + +# DEMANGLE: 16 foo(float) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + Size: 16 + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "100000000000000010" + Link: .text +Symbols: + - Name: _Z3foof + Section: .text + Value: 0x10 + Type: STT_FUNC + Binding: STB_GLOBAL + +## Check that we emit a 'not implemented' message for an attempt to dump stack-sizes +## sections LLVM-style, i.e. when invoking llvm-readobj. +## FIXME: Replace this test with something functional when the feature is implemented. + +# RUN: llvm-readobj --stack-sizes %t01 | FileCheck %s --check-prefix=NOTIMPLEMENTED + +# NOTIMPLEMENTED: Dumping of stack sizes in LLVM style is not implemented yet Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ 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 *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 *Dumper) : Dumper(Dumper) {} virtual ~DumpStyle() = default; @@ -379,6 +383,19 @@ virtual void printAddrsig(const ELFFile *Obj) = 0; virtual void printNotes(const ELFFile *Obj) = 0; virtual void printELFLinkerOptions(const ELFFile *Obj) = 0; + virtual void printStackSizes(const ELFObjectFile *Obj) = 0; + void printNonRelocatableStackSizes(const ELFObjectFile *Obj, + std::function PrintHeader); + void printRelocatableStackSizes(const ELFObjectFile *Obj, + std::function PrintHeader); + void printStackSize(const ELFObjectFile *Obj, uint64_t SymValue, + SectionRef FunctionSec, const StringRef SectionName, + DataExtractor Data, uint64_t *Offset); + void printStackSize(const ELFObjectFile *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 &Parser) = 0; virtual void printMipsPLT(const MipsGOTParser &Parser) = 0; const ELFDumper *dumper() const { return Dumper; } @@ -423,6 +440,8 @@ void printAddrsig(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; void printELFLinkerOptions(const ELFFile *Obj) override; + void printStackSizes(const ELFObjectFile *Obj) override; + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; @@ -526,6 +545,8 @@ void printAddrsig(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; void printELFLinkerOptions(const ELFFile *Obj) override; + void printStackSizes(const ELFObjectFile *Obj) override; + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; @@ -1715,6 +1736,10 @@ ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile()); } +template void ELFDumper::printStackSizes() { + ELFDumperStyle->printStackSizes(ObjF); +} + #define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ { #enum, prefix##_##enum } @@ -4275,6 +4300,304 @@ 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(Offset); + if (sys::IsBigEndianHost) + Ptr++; + return Ptr; +} + +template +void DumpStyle::printStackSize(const ELFObjectFile *Obj, + uint64_t SymValue, SectionRef FunctionSec, + const StringRef SectionName, + DataExtractor Data, uint64_t *Offset) { + SymbolRef FuncSym; + for (const ELFSymbolRef &Symbol : Obj->symbols()) { + Expected SymAddrOrErr = Symbol.getAddress(); + if (!SymAddrOrErr) { + // Ignore problems with extracting symbol addresses. We only report + // issues directly related to stack size dumping. + 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 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. We expect 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 +void GNUStyle::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + OS.PadToColumn(2); + OS << format_decimal(Size, 11); + OS.PadToColumn(18); + OS << FuncName << "\n"; +} + +template +void DumpStyle::printStackSize(const ELFObjectFile *Obj, + RelocationRef Reloc, + SectionRef FunctionSec, + const StringRef &StackSizeSectionName, + const RelocationResolver &Resolver, + DataExtractor Data) { + 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 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. We will report + // its stack size anyway. + FunctionSec = **SectionOrErr; + } + + Expected 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->printStackSize(Obj, SymValue, FunctionSec, StackSizeSectionName, Data, + &Offset); +} + +template +SectionRef toSectionRef(const ObjectFile *Obj, const typename ELFT::Shdr *Sec) { + DataRefImpl DRI; + DRI.p = reinterpret_cast(Sec); + return SectionRef(DRI, Obj); +} + +template +void DumpStyle::printNonRelocatableStackSizes( + const ELFObjectFile *Obj, std::function PrintHeader) { + const ELFFile *EF = Obj->getELFFile(); + StringRef FileStr = Obj->getFileName(); + if (!Obj->isRelocatableObject()) { + for (const SectionRef &Sec : Obj->sections()) { + const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl()); + Expected NameOrErr = EF->getSectionName(ElfSec); + // Ignore sections whose name we cannot find. + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + continue; + } + StringRef SectionName = *NameOrErr; + if (!SectionName.startswith(".stack_sizes")) + continue; + PrintHeader(); + ArrayRef Contents = + unwrapOrError(EF->getSectionContents(ElfSec)); + DataExtractor Data( + StringRef(reinterpret_cast(Contents.data()), + Contents.size()), + true, 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)); + printStackSize(Obj, SymValue, toSectionRef(Obj, FunctionELFSec), + SectionName, Data, &Offset); + } + } + return; + } +} + +template +void DumpStyle::printRelocatableStackSizes( + const ELFObjectFile *Obj, std::function PrintHeader) { + const ELFFile *EF = Obj->getELFFile(); + StringRef FileStr = Obj->getFileName(); + // For relocatable objects we build a map between stack size sections and + // their corresponding relocation sections. + llvm::MapVector 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; + + // We are ignoring potentially erroneous input in this context, + // unless it is directly related to stack size reporting. + SectionRef Contents = *Sec.getRelocatedSection(); + const Elf_Shdr *ContentsSec = Obj->getSection(Contents.getRawDataRefImpl()); + Expected 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(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( + Obj, unwrapOrError(EF->getSection(StackSizesELFSec->sh_link))); + + StringRef RelocSectionName; + RelocSec.getName(RelocSectionName); + + bool (*IsSupportedFn)(uint64_t); + RelocationResolver Resolver; + std::tie(IsSupportedFn, Resolver) = getRelocationResolver(*Obj); + auto Contents = unwrapOrError(StackSizesSec.getContents()); + DataExtractor Data( + StringRef(reinterpret_cast(Contents.data()), + Contents.size()), + true, sizeof(Elf_Addr)); + for (const RelocationRef &Reloc : RelocSec.relocations()) { + if (!IsSupportedFn(Reloc.getType())) { + 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 +void GNUStyle::printStackSizes(const ELFObjectFile *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 void GNUStyle::printMipsGOT(const MipsGOTParser &Parser) { size_t Bias = ELFT::Is64Bits ? 8 : 0; @@ -5138,6 +5461,17 @@ } } +template +void LLVMStyle::printStackSizes(const ELFObjectFile *Obj) { + W.printString( + "Dumping of stack sizes in LLVM style is not implemented yet\n"); +} + +template +void LLVMStyle::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + // FIXME: Implement this function for LLVM-style dumping. +} + template void LLVMStyle::printMipsGOT(const MipsGOTParser &Parser) { auto PrintEntry = [&](const Elf_Addr *E) { Index: llvm/tools/llvm-readobj/ObjDumper.h =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.h +++ llvm/tools/llvm-readobj/ObjDumper.h @@ -68,6 +68,7 @@ virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} + virtual void printStackSizes() {} // Only implemented for ARM ELF at this time. virtual void printAttributes() { } Index: llvm/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -55,7 +55,7 @@ cl::desc("Equivalent to setting: --file-headers, --program-headers, " "--section-headers, --symbols, --relocations, " "--dynamic-table, --notes, --version-info, --unwind, " - "--section-groups and --elf-hash-histogram.")); + "--stack-sizes, --section-groups and --elf-hash-histogram.")); cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All)); // --headers -e @@ -324,6 +324,11 @@ PrintStackMap("stackmap", cl::desc("Display contents of stackmap section")); + // --stack-sizes + cl::opt + PrintStackSizes("stack-sizes", + cl::desc("Display contents of all stack sizes sections")); + // --version-info, -V cl::opt VersionInfo("version-info", @@ -583,6 +588,8 @@ } if (opts::PrintStackMap) Dumper->printStackMap(); + if (opts::PrintStackSizes) + Dumper->printStackSizes(); } /// Dumps each object file in \a Arc; @@ -727,6 +734,10 @@ opts::UnwindInfo = true; opts::SectionGroups = true; opts::HashHistogram = true; + // FIXME: As soon as we implement LLVM-style printing of the .stack_size + // section, we will enable it unconditionally with --all. + if (opts::Output == opts::GNU) + opts::PrintStackSizes = true; } if (opts::Headers) {