Index: test/tools/llvm-readobj/stack-sizes.test =================================================================== --- test/tools/llvm-readobj/stack-sizes.test +++ test/tools/llvm-readobj/stack-sizes.test @@ -92,16 +92,24 @@ Binding: STB_GLOBAL ## Check that we correctly report the stack sizes in an executable (non-relocatable) -## object file. +## object file. Furthermore, check that we report stack sizes for functions that +## share a stack size entry with other functions (e.g. because several functions +## have been collapsed into a single function body by the linker). Also, check +## that if we have duplicate stack size entries for a function, we only print one +## entry. Lastly, check that we issue a warning should the stack sizes mismatch in +## duplicate entries. # RUN: yaml2obj --docnum=2 %s > %t02 -# RUN: llvm-readelf --stack-sizes %t02 \ +# RUN: llvm-readelf --stack-sizes %t02 2> %t02-gnu.err \ # RUN: | FileCheck %s --check-prefix=EXEC-GNU --strict-whitespace --match-full-lines -# RUN: llvm-readobj --stack-sizes %t02 | FileCheck %s --check-prefix=EXEC-LLVM +# RUN: llvm-readobj --stack-sizes %t02 2> %t02-llvm.err | FileCheck %s --check-prefix=EXEC-LLVM +# RUN: FileCheck %s < %t02-gnu.err --check-prefix=EXEC-ERR -DFILE=%t02 +# RUN: FileCheck %s < %t02-llvm.err --check-prefix=EXEC-ERR -DFILE=%t02 # EXEC-GNU: Size Function # EXEC-GNU-NEXT: 16 foo # EXEC-GNU-NEXT: 32 bar +# EXEC-GNU-NEXT: 32 baz # EXEC-GNU-NOT:{{.}} # EXEC-LLVM: StackSizes [ @@ -113,8 +121,14 @@ # EXEC-LLVM-NEXT: Function: bar # EXEC-LLVM-NEXT: Size: 0x20 # EXEC-LLVM-NEXT: } +# EXEC-LLVM-NEXT: Entry { +# EXEC-LLVM-NEXT: Function: baz +# EXEC-LLVM-NEXT: Size: 0x20 +# EXEC-LLVM-NEXT: } # EXEC-LLVM-NEXT: ] +# EXEC-ERR: warning: '[[FILE]]': mismatching sizes in stack size entries for the same address + --- !ELF FileHeader: Class: ELFCLASS64 @@ -133,6 +147,11 @@ Size: 0x10 - Address: 0x20 Size: 0x20 +# 2 duplicate entries for address 0x20, one with mismatching size. + - Address: 0x20 + Size: 0x20 + - Address: 0x20 + Size: 0x30 Link: .text Symbols: - Name: foo @@ -145,6 +164,16 @@ Value: 0x20 Type: STT_FUNC Binding: STB_GLOBAL + - Name: baz + Section: .text + Value: 0x20 + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: myobj + Section: .text + Value: 0x20 + Type: STT_OBJECT + Binding: STB_GLOBAL ## Check that we report an error when we find relocations whose offsets point outside ## of the .stack_sizes section. @@ -449,6 +478,10 @@ # MULTIPLE-LLVM-NEXT: Function: bar # MULTIPLE-LLVM-NEXT: Size: 0x20 # MULTIPLE-LLVM-NEXT: } +# MULTIPLE-LLVM-NEXT: Entry { +# MULTIPLE-LLVM-NEXT: Function: baz +# MULTIPLE-LLVM-NEXT: Size: 0x20 +# MULTIPLE-LLVM-NEXT: } # MULTIPLE-LLVM-NEXT: ] ## Check that we do not consider symbols that are not function symbols, even though @@ -641,3 +674,64 @@ Relocations: - Offset: 0 Type: R_X86_64_64 + +## Check that duplicate stack size entries in relocatable files are not displayed +## and that they elicit a warning when their sizes don't match. + +# RUN: yaml2obj --docnum=13 %s > %t18 +# RUN: llvm-readelf --stack-sizes %t18 2> %t18-gnu.err \ +# RUN: | FileCheck %s -DFILE=%t18 --check-prefix=DUPLICATE-GNU +# RUN: llvm-readobj --stack-sizes %t18 2> %t18-llvm.err \ +# RUN: | FileCheck %s -DFILE=%t18 --check-prefix=DUPLICATE-LLVM +# RUN: FileCheck %s < %t18-gnu.err --check-prefix=DUPLICATE-ERR -DFILE=%t18 +# RUN: FileCheck %s < %t18-llvm.err --check-prefix=DUPLICATE-ERR -DFILE=%t18 + +# DUPLICATE-GNU: Size Function +# DUPLICATE-GNU-NEXT: 16 foo +# DUPLICATE-GNU-NOT:{{.}} + +# DUPLICATE-LLVM: StackSizes [ +# DUPLICATE-LLVM-NEXT: Entry { +# DUPLICATE-LLVM-NEXT: Function: foo +# DUPLICATE-LLVM-NEXT: Size: 0x10 +# DUPLICATE-LLVM-NEXT: } +# DUPLICATE-LLVM-NEXT: ] + +# DUPLICATE-ERR: warning: '[[FILE]]': mismatching sizes in stack size entries for the same address + +--- !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 + Entries: + - Size: 0x10 + - Size: 0x20 + Link: .text + - Name: .rela.stack_sizes + Type: SHT_RELA + Info: .stack_sizes + Relocations: +## Both relocations resolve to the same symbol. + - Offset: 0 + Symbol: .text + Type: R_X86_64_64 + - Offset: 9 + Symbol: foo + Type: R_X86_64_64 +Symbols: + - Name: foo + Section: .text + Value: 0 + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: .text + Section: .text + Type: STT_SECTION Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -370,6 +370,7 @@ using Elf_Shdr = typename ELFT::Shdr; using Elf_Sym = typename ELFT::Sym; using Elf_Addr = typename ELFT::Addr; + using StackSizeEntryMap = std::map; DumpStyle(ELFDumper *Dumper) : Dumper(Dumper) { FileName = this->Dumper->getElfObject()->getFileName(); @@ -421,11 +422,13 @@ void printFunctionStackSize(const ELFObjectFile *Obj, uint64_t SymValue, SectionRef FunctionSec, const StringRef SectionName, DataExtractor Data, + StackSizeEntryMap &StackSizeEntries, uint64_t *Offset); void printStackSize(const ELFObjectFile *Obj, RelocationRef Rel, SectionRef FunctionSec, const StringRef &StackSizeSectionName, - const RelocationResolver &Resolver, DataExtractor Data); + const RelocationResolver &Resolver, + StackSizeEntryMap &StackSizeEntries, 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; @@ -4711,34 +4714,31 @@ template void DumpStyle::printFunctionStackSize( const ELFObjectFile *Obj, uint64_t SymValue, SectionRef FunctionSec, - const StringRef SectionName, DataExtractor Data, uint64_t *Offset) { + const StringRef SectionName, DataExtractor Data, + StackSizeEntryMap &StackSizeEntries, uint64_t *Offset) { // This function ignores potentially erroneous input, unless it is directly // related to stack size reporting. - SymbolRef FuncSym; + + auto reportUnknownFunctionSymbol = [](const ELFObjectFile *Obj) { + reportWarning( + createError("could not identify function symbol for stack size entry"), + Obj->getFileName()); + }; + + // Find all the function symbols that match SymValue and are in FunctionSec. + std::vector FuncSyms; for (const ELFSymbolRef &Symbol : Obj->symbols()) { Expected SymAddrOrErr = Symbol.getAddress(); if (!SymAddrOrErr) { consumeError(SymAddrOrErr.takeError()); continue; } - if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) { + if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) // Check if the symbol is in the right section. - if (FunctionSec.containsSymbol(Symbol)) { - FuncSym = Symbol; - break; - } - } + if (FunctionSec.containsSymbol(Symbol)) + FuncSyms.push_back(Symbol); } - std::string FuncName = "?"; - // A valid SymbolRef has a non-null object file pointer. - if (FuncSym.BasicSymbolRef::getObject()) - FuncName = getSymbolName(FuncSym); - else - reportWarning( - createError("could not identify function symbol for stack size entry"), - Obj->getFileName()); - // Extract the size. The expectation is that Offset is pointing to the right // place, i.e. past the function address. uint64_t PrevOffset = *Offset; @@ -4752,7 +4752,35 @@ SectionName.data()), Obj->getFileName()); - printStackSizeEntry(StackSize, FuncName); + // If we have already printed a stack size entry with this SymValue, do not + // print it again, but report a warning if the sizes don't match. + auto PrevSizeIt = StackSizeEntries.find(SymValue); + if (PrevSizeIt != StackSizeEntries.end()) { + if (PrevSizeIt->second != StackSize) + reportWarning( + createError( + "mismatching sizes in stack size entries for the same address"), + Obj->getFileName()); + return; + } + StackSizeEntries.emplace(SymValue, StackSize); + + // If we didn't find any matching symbols, print one entry with an unknown + // function symbol name and issue a warning. + if (FuncSyms.empty()) { + reportUnknownFunctionSymbol(Obj); + printStackSizeEntry(StackSize, "?"); + } + + for (const SymbolRef &FuncSym : FuncSyms) { + std::string FuncName = "?"; + // A valid SymbolRef has a non-null object file pointer. + if (FuncSym.BasicSymbolRef::getObject()) + FuncName = getSymbolName(FuncSym); + else + reportUnknownFunctionSymbol(Obj); + printStackSizeEntry(StackSize, FuncName); + } } template @@ -4764,12 +4792,10 @@ } template -void DumpStyle::printStackSize(const ELFObjectFile *Obj, - RelocationRef Reloc, - SectionRef FunctionSec, - const StringRef &StackSizeSectionName, - const RelocationResolver &Resolver, - DataExtractor Data) { +void DumpStyle::printStackSize( + const ELFObjectFile *Obj, RelocationRef Reloc, SectionRef FunctionSec, + const StringRef &StackSizeSectionName, const RelocationResolver &Resolver, + StackSizeEntryMap &StackSizeEntries, DataExtractor Data) { // This function ignores potentially erroneous input, unless it is directly // related to stack size reporting. object::symbol_iterator RelocSym = Reloc.getSymbol(); @@ -4815,7 +4841,7 @@ uint64_t Addend = Data.getAddress(&Offset); uint64_t SymValue = Resolver(Reloc, RelocSymValue, Addend); this->printFunctionStackSize(Obj, SymValue, FunctionSec, StackSizeSectionName, - Data, &Offset); + Data, StackSizeEntries, &Offset); } template @@ -4840,6 +4866,7 @@ const Elf_Shdr *FunctionELFSec = unwrapOrError(this->FileName, EF->getSection(ElfSec->sh_link)); uint64_t Offset = 0; + StackSizeEntryMap StackSizeEntries; 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. @@ -4853,7 +4880,7 @@ } uint64_t SymValue = Data.getAddress(&Offset); printFunctionStackSize(Obj, SymValue, Obj->toSectionRef(FunctionELFSec), - SectionName, Data, &Offset); + SectionName, Data, StackSizeEntries, &Offset); } } } @@ -4932,6 +4959,7 @@ std::tie(IsSupportedFn, Resolver) = getRelocationResolver(*Obj); auto Contents = unwrapOrError(this->FileName, StackSizesSec.getContents()); DataExtractor Data(Contents, Obj->isLittleEndian(), sizeof(Elf_Addr)); + StackSizeEntryMap StackSizeEntries; for (const RelocationRef &Reloc : RelocSec.relocations()) { if (!IsSupportedFn || !IsSupportedFn(Reloc.getType())) reportError(createStringError( @@ -4941,7 +4969,7 @@ EF->getRelocationTypeName(Reloc.getType()).data()), Obj->getFileName()); this->printStackSize(Obj, Reloc, FunctionSec, StackSizeSectionName, - Resolver, Data); + Resolver, StackSizeEntries, Data); } } }