diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -14,6 +14,7 @@ #define LLVM_OBJECT_ELF_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -393,6 +394,14 @@ Expected> getSegmentContents(const Elf_Phdr &Phdr) const; Expected> decodeBBAddrMap(const Elf_Shdr &Sec) const; + /// Returns a map from every section matching \p IsMatch to its relocation + /// section, or \p nullptr if it has no relocation section. This function + /// returns an error if any of the \p IsMatch calls fail or if it fails to + /// retrieve the content section of any relocation section. + Expected> + getSectionAndRelocations( + std::function(const Elf_Shdr &)> IsMatch) const; + void createFakeSections(); }; diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -706,6 +706,50 @@ return FunctionEntries; } +template +Expected< + MapVector> +ELFFile::getSectionAndRelocations( + std::function(const Elf_Shdr &)> IsMatch) const { + MapVector SecToRelocMap; + Error Errors = Error::success(); + for (const Elf_Shdr &Sec : cantFail(this->sections())) { + Expected DoesSectionMatch = IsMatch(Sec); + if (!DoesSectionMatch) { + Errors = joinErrors(std::move(Errors), DoesSectionMatch.takeError()); + continue; + } + if (*DoesSectionMatch) { + if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) + .second) + continue; + } + + if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL) + continue; + + Expected RelSecOrErr = this->getSection(Sec.sh_info); + if (!RelSecOrErr) { + Errors = joinErrors(std::move(Errors), + createError(describe(*this, Sec) + + ": failed to get a relocated section: " + + toString(RelSecOrErr.takeError()))); + continue; + } + const Elf_Shdr *ContentsSec = *RelSecOrErr; + Expected DoesRelTargetMatch = IsMatch(*ContentsSec); + if (!DoesRelTargetMatch) { + Errors = joinErrors(std::move(Errors), DoesRelTargetMatch.takeError()); + continue; + } + if (*DoesRelTargetMatch) + SecToRelocMap[ContentsSec] = &Sec; + } + if(Errors) + return Errors; + return SecToRelocMap; +} + template class llvm::object::ELFFile; template class llvm::object::ELFFile; template class llvm::object::ELFFile; diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test --- a/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test @@ -350,3 +350,23 @@ Symbols: - Name: foo - Name: bar + +## Check that we report a warning when we fail to get a section associated with +## a relocation section. + +# RUN: yaml2obj %s --docnum=8 -o %t9.o +# RUN: llvm-readobj %t9.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t9.o --check-prefix=LLVM-RELOC-NO-SECTIONS +# RUN: llvm-readobj %t9.o --elf-cg-profile 2>&1 | FileCheck %s -DFILE=%t9.o --check-prefix=LLVM-RELOC-NO-SECTIONS + +# LLVM-RELOC-NO-SECTIONS: warning: '[[FILE]]': unable to get CG Profile section(s): SHT_RELA section with index 1: failed to get a relocated section: invalid section index: 255 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .rela.llvm.call-graph-profile + Type: SHT_RELA + Info: 0xFF diff --git a/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test b/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test --- a/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test +++ b/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test @@ -870,8 +870,8 @@ # RUN: llvm-readobj --stack-sizes %t18 2>&1 | \ # RUN: FileCheck %s --implicit-check-not="warning:" -DFILE=%t18 --check-prefix=INVALID-TARGET -# INVALID-TARGET: warning: '[[FILE]]': SHT_RELA section with index 1: failed to get a relocated section: invalid section index: 255 -# INVALID-TARGET: warning: '[[FILE]]': SHT_RELA section with index 2: failed to get a relocated section: invalid section index: 255 +# INVALID-TARGET: warning: '[[FILE]]': unable to get stack size map section(s): SHT_RELA section with index 1: failed to get a relocated section: invalid section index: 255 +# INVALID-TARGET: SHT_RELA section with index 2: failed to get a relocated section: invalid section index: 255 --- !ELF FileHeader: 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 @@ -328,12 +328,6 @@ void printRelocatableStackSizes(std::function PrintHeader); void printNonRelocatableStackSizes(std::function PrintHeader); - /// Retrieves sections with corresponding relocation sections based on - /// IsMatch. - void getSectionAndRelocations( - std::function IsMatch, - llvm::MapVector &SecToRelocMap); - const object::ELFObjectFile &ObjF; const ELFFile &Obj; StringRef FileName; @@ -6281,38 +6275,11 @@ } } -template -void ELFDumper::getSectionAndRelocations( - std::function IsMatch, - llvm::MapVector &SecToRelocMap) { - for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { - if (IsMatch(Sec)) - if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) - .second) - continue; - - if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL) - continue; - - Expected RelSecOrErr = Obj.getSection(Sec.sh_info); - if (!RelSecOrErr) { - reportUniqueWarning(describe(Sec) + - ": failed to get a relocated section: " + - toString(RelSecOrErr.takeError())); - continue; - } - const Elf_Shdr *ContentsSec = *RelSecOrErr; - if (IsMatch(*ContentsSec)) - SecToRelocMap[ContentsSec] = &Sec; - } -} - template void ELFDumper::printRelocatableStackSizes( std::function PrintHeader) { // Build a map between stack size sections and their corresponding relocation // sections. - llvm::MapVector StackSizeRelocMap; auto IsMatch = [&](const Elf_Shdr &Sec) -> bool { StringRef SectionName; if (Expected NameOrErr = Obj.getSectionName(Sec)) @@ -6322,9 +6289,16 @@ return SectionName == ".stack_sizes"; }; - getSectionAndRelocations(IsMatch, StackSizeRelocMap); - for (const auto &StackSizeMapEntry : StackSizeRelocMap) { + Expected> + StackSizeRelocMapOrErr = Obj.getSectionAndRelocations(IsMatch); + if (!StackSizeRelocMapOrErr) { + reportUniqueWarning("unable to get stack size map section(s): " + + toString(StackSizeRelocMapOrErr.takeError())); + return; + } + + for (const auto &StackSizeMapEntry : *StackSizeRelocMapOrErr) { PrintHeader(); const Elf_Shdr *StackSizesELFSec = StackSizeMapEntry.first; const Elf_Shdr *RelocSec = StackSizeMapEntry.second; @@ -7148,14 +7122,19 @@ } template void LLVMELFDumper::printCGProfile() { - llvm::MapVector SecToRelocMap; - auto IsMatch = [](const Elf_Shdr &Sec) -> bool { return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE; }; - this->getSectionAndRelocations(IsMatch, SecToRelocMap); - for (const auto &CGMapEntry : SecToRelocMap) { + Expected> SecToRelocMapOrErr = + this->Obj.getSectionAndRelocations(IsMatch); + if (!SecToRelocMapOrErr) { + this->reportUniqueWarning("unable to get CG Profile section(s): " + + toString(SecToRelocMapOrErr.takeError())); + return; + } + + for (const auto &CGMapEntry : *SecToRelocMapOrErr) { const Elf_Shdr *CGSection = CGMapEntry.first; const Elf_Shdr *CGRelSection = CGMapEntry.second; diff --git a/llvm/unittests/Object/ELFObjectFileTest.cpp b/llvm/unittests/Object/ELFObjectFileTest.cpp --- a/llvm/unittests/Object/ELFObjectFileTest.cpp +++ b/llvm/unittests/Object/ELFObjectFileTest.cpp @@ -808,3 +808,104 @@ RelocatableFileYamlString += ContentsString; DoCheck(RelocatableFileYamlString); } + +TEST(ELFObjectFileTest, GetSectionAndRelocations) { + StringRef HeaderString(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +)"); + + using Elf_Shdr = Elf_Shdr_Impl; + + auto DoCheckSucceeds = [&](StringRef ContentsString, + std::function(const Elf_Shdr &)> + Matcher) { + SmallString<0> Storage; + SmallString<128> FullYamlString(HeaderString); + FullYamlString += ContentsString; + Expected> ElfOrErr = + toBinary(Storage, FullYamlString); + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + + Expected> SecToRelocMapOrErr = + ElfOrErr->getELFFile().getSectionAndRelocations(Matcher); + ASSERT_THAT_EXPECTED(SecToRelocMapOrErr, Succeeded()); + + // Basic verification to make sure we have the correct section types. + for (auto const &[Sec, RelaSec] : *SecToRelocMapOrErr) { + ASSERT_EQ(Sec->sh_type, ELF::SHT_PROGBITS); + ASSERT_EQ(RelaSec->sh_type, ELF::SHT_RELA); + } + }; + + auto DoCheckFails = [&](StringRef ContentsString, + std::function(const Elf_Shdr &)> + Matcher, + const char *ErrorMessage) { + SmallString<0> Storage; + SmallString<128> FullYamlString(HeaderString); + FullYamlString += ContentsString; + Expected> ElfOrErr = + toBinary(Storage, FullYamlString); + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + + Expected> SecToRelocMapOrErr = + ElfOrErr->getELFFile().getSectionAndRelocations(Matcher); + ASSERT_THAT_ERROR(SecToRelocMapOrErr.takeError(), + FailedWithMessage(ErrorMessage)); + }; + + auto DefaultMatcher = [](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_PROGBITS; + }; + + StringRef TwoTextSections = R"( +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + - Name: .rela.text + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Info: .text + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + - Name: .rela.text2 + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Info: .text2 +)"; + DoCheckSucceeds(TwoTextSections, DefaultMatcher); + + StringRef OneTextSection = R"( +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] +)"; + + auto ErroringMatcher = [](const Elf_Shdr &Sec) -> Expected { + if(Sec.sh_type == ELF::SHT_PROGBITS) + return createError("This was supposed to fail."); + return false; + }; + + DoCheckFails(OneTextSection, ErroringMatcher, + "This was supposed to fail."); + + StringRef MissingRelocatableContent = R"( +Sections: + - Name: .rela.text + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Info: 0xFF +)"; + + DoCheckFails(MissingRelocatableContent, DefaultMatcher, + "SHT_RELA section with index 1: failed to get a " + "relocated section: invalid section index: 255"); +}