From a224c5199b327ed0efcdcd87b6dbf950cf4d9ee1 Mon Sep 17 00:00:00 2001 From: Alexander Yermolovich Date: Thu, 24 Jun 2021 08:02:45 -0700 Subject: [PATCH] [LLD][LLVM] CG Graph profile using relocations Currently when .llvm.call-graph-profile is created by llvm it explicitly encodes the symbol indices. This section is basically a black box for post processing tools. For example, if we run strip -s on the object files the symbol table changes, but indices in that section do not. In non-visible behavior indices point to wrong symbols. The visible behavior indices point outside of Symbol table: "invalid symbol index". This patch changes the format by using R_*_NONE relocations to indicate the from/to symbols. The Frequency (Weight) will still be in the .llvm.call-graph-profile, but symbol information will be in relocation section. In LLD information from both sections is used to reconstruct call graph profile. Relocations themselves will never be applied. With this approach post processing tools that handle relocations correctly work for this section also. Tools can add/remove symbols and as long as they handle relocation sections with this approach information stays correct. Doing a quick experiment with clang-13. The size went up from 107KB to 322KB, aggregate of all the input sections. Size of clang-13 binary is ~118MB. For users of -fprofile-use/-fprofile-sample-use the size of object files will go up slightly, it will not impact final binary size. Reviewed By: jhenderson, MaskRay Differential Revision: https://reviews.llvm.org/D104080 --- lld/ELF/Driver.cpp | 19 +- lld/ELF/InputFiles.cpp | 13 +- lld/ELF/InputFiles.h | 4 +- llvm/include/llvm/BinaryFormat/ELF.h | 10 +- llvm/include/llvm/MC/MCELFStreamer.h | 2 +- llvm/include/llvm/Object/ELFTypes.h | 2 - llvm/include/llvm/ObjectYAML/ELFYAML.h | 14 +- llvm/lib/MC/ELFObjectWriter.cpp | 25 +-- llvm/lib/MC/MCELFStreamer.cpp | 38 ++-- llvm/lib/ObjectYAML/ELFEmitter.cpp | 10 +- llvm/lib/ObjectYAML/ELFYAML.cpp | 6 +- llvm/test/MC/ELF/cgprofile.s | 42 +++- llvm/test/Object/multiple-sections.yaml | 11 +- .../llvm-readobj/ELF/call-graph-profile.test | 193 ++++++++++++++++-- .../test/tools/llvm-readobj/ELF/demangle.test | 13 +- .../ELF/call-graph-profile-section.yaml | 100 +++------ .../ELF/call-graph-profile-section.yaml | 146 ++----------- .../yaml2obj/ELF/section-headers-exclude.yaml | 2 +- llvm/tools/llvm-readobj/ELFDumper.cpp | 136 +++++++----- llvm/tools/obj2yaml/elf2yaml.cpp | 26 +-- 20 files changed, 444 insertions(+), 368 deletions(-) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index b654bb86d680..a6aee7a52a53 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -856,12 +856,23 @@ static void readCallGraph(MemoryBufferRef mb) { } template static void readCallGraphsFromObjectFiles() { + auto getIndex = [&](ObjFile *obj, uint32_t index) { + const Elf_Rel_Impl &rel = obj->cgProfileRela[index]; + return rel.getSymbol(config->isMips64EL); + }; + for (auto file : objectFiles) { auto *obj = cast>(file); - - for (const Elf_CGProfile_Impl &cgpe : obj->cgProfile) { - auto *fromSym = dyn_cast(&obj->getSymbol(cgpe.cgp_from)); - auto *toSym = dyn_cast(&obj->getSymbol(cgpe.cgp_to)); + if (obj->cgProfileRela.empty()) + continue; + if (obj->cgProfileRela.size() != obj->cgProfile.size() * 2) + fatal("number of relocations doesn't match Weights"); + for (uint32_t i = 0, size = obj->cgProfile.size(); i < size; ++i) { + const Elf_CGProfile_Impl &cgpe = obj->cgProfile[i]; + uint32_t fromIndex = getIndex(obj, i * 2); + uint32_t toIndex = getIndex(obj, i * 2 + 1); + auto *fromSym = dyn_cast(&obj->getSymbol(fromIndex)); + auto *toSym = dyn_cast(&obj->getSymbol(toIndex)); if (!fromSym || !toSym) continue; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 3e0c1d77823f..77636af6e1fb 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -571,15 +571,19 @@ void ObjFile::initializeSections(bool ignoreComdats) { CHECK(obj.getSectionStringTable(objSections), this); std::vector> selectedGroups; + // SHT_LLVM_CALL_GRAPH_PROFILE Section Index. + size_t cgProfileSectionIndex = 0; for (size_t i = 0, e = objSections.size(); i < e; ++i) { if (this->sections[i] == &InputSection::discarded) continue; const Elf_Shdr &sec = objSections[i]; - if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) + if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) { cgProfile = check(obj.template getSectionContentsAsArray(sec)); + cgProfileSectionIndex = i; + } // SHF_EXCLUDE'ed sections are discarded by the linker. However, // if -r is given, we'll let the final link discard such sections. @@ -665,6 +669,13 @@ void ObjFile::initializeSections(bool ignoreComdats) { continue; const Elf_Shdr &sec = objSections[i]; + if (cgProfileSectionIndex && sec.sh_info == cgProfileSectionIndex) { + if (sec.sh_type == SHT_RELA) + cgProfileRela = CHECK(getObj().relas(sec), this); + else + warn(toString(this) + ": unsupported call graph section type"); + } + if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA) this->sections[i] = createInputSection(sec); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index f66d7a25d954..31bffb91d275 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -249,8 +249,10 @@ public: // Pointer to this input file's .llvm_addrsig section, if it has one. const Elf_Shdr *addrsigSec = nullptr; - // SHT_LLVM_CALL_GRAPH_PROFILE table + // SHT_LLVM_CALL_GRAPH_PROFILE table. ArrayRef cgProfile; + // SHT_LLVM_CALL_GRAPH_PROFILE relocations. + ArrayRef cgProfileRela; // Get cached DWARF information. DWARFCache *getDwarf(); diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 255fa1414191..99ad345bbcc4 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -926,17 +926,17 @@ enum : unsigned { // https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#37 SHT_ANDROID_REL = 0x60000001, SHT_ANDROID_RELA = 0x60000002, - SHT_LLVM_ODRTAB = 0x6fff4c00, // LLVM ODR table. - SHT_LLVM_LINKER_OPTIONS = 0x6fff4c01, // LLVM Linker Options. - SHT_LLVM_CALL_GRAPH_PROFILE = 0x6fff4c02, // LLVM Call Graph Profile. - SHT_LLVM_ADDRSIG = 0x6fff4c03, // List of address-significant symbols - // for safe ICF. + SHT_LLVM_ODRTAB = 0x6fff4c00, // LLVM ODR table. + SHT_LLVM_LINKER_OPTIONS = 0x6fff4c01, // LLVM Linker Options. + SHT_LLVM_ADDRSIG = 0x6fff4c03, // List of address-significant symbols + // for safe ICF. SHT_LLVM_DEPENDENT_LIBRARIES = 0x6fff4c04, // LLVM Dependent Library Specifiers. SHT_LLVM_SYMPART = 0x6fff4c05, // Symbol partition specification. SHT_LLVM_PART_EHDR = 0x6fff4c06, // ELF header for loadable partition. SHT_LLVM_PART_PHDR = 0x6fff4c07, // Phdrs for loadable partition. SHT_LLVM_BB_ADDR_MAP = 0x6fff4c08, // LLVM Basic Block Address Map. + SHT_LLVM_CALL_GRAPH_PROFILE = 0x6fff4c09, // LLVM Call Graph Profile. // Android's experimental support for SHT_RELR sections. // https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512 SHT_ANDROID_RELR = 0x6fffff00, // Relocation entries; only offsets. diff --git a/llvm/include/llvm/MC/MCELFStreamer.h b/llvm/include/llvm/MC/MCELFStreamer.h index 280eb7ec92cf..45179378e71c 100644 --- a/llvm/include/llvm/MC/MCELFStreamer.h +++ b/llvm/include/llvm/MC/MCELFStreamer.h @@ -85,7 +85,7 @@ private: void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override; void fixSymbolsInTLSFixups(const MCExpr *expr); - void finalizeCGProfileEntry(const MCSymbolRefExpr *&S); + void finalizeCGProfileEntry(const MCSymbolRefExpr *&S, uint64_t Offset); void finalizeCGProfile(); /// Merge the content of the fragment \p EF into the fragment \p DF. diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 6cd93efa41a9..54ebd751d8d2 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -737,8 +737,6 @@ public: template struct Elf_CGProfile_Impl { LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) - Elf_Word cgp_from; - Elf_Word cgp_to; Elf_Xword cgp_weight; }; diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index 521bdee8b49c..92a9f78ce7bf 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -516,17 +516,13 @@ struct DependentLibrariesSection : Section { }; // Represents the call graph profile section entry. -struct CallGraphEntry { - // The symbol of the source of the edge. - StringRef From; - // The symbol index of the destination of the edge. - StringRef To; +struct CallGraphEntryWeight { // The weight of the edge. uint64_t Weight; }; struct CallGraphProfileSection : Section { - Optional> Entries; + Optional> Entries; CallGraphProfileSection() : Section(ChunkKind::CallGraphProfile) {} @@ -738,7 +734,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::LinkerOption) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::CallGraphEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::CallGraphEntryWeight) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::NoteEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionHeader) @@ -932,8 +928,8 @@ template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::LinkerOption &Sym); }; -template <> struct MappingTraits { - static void mapping(IO &IO, ELFYAML::CallGraphEntry &E); +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::CallGraphEntryWeight &E); }; template <> struct MappingTraits { diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index 534c263d735c..fec77d27eb9a 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -1127,14 +1127,6 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { OWriter.TargetObjectWriter->addTargetSectionFlags(Ctx, Section); } - MCSectionELF *CGProfileSection = nullptr; - if (!Asm.CGProfile.empty()) { - CGProfileSection = Ctx.getELFSection(".llvm.call-graph-profile", - ELF::SHT_LLVM_CALL_GRAPH_PROFILE, - ELF::SHF_EXCLUDE, 16); - SectionIndexMap[CGProfileSection] = addToSectionTable(CGProfileSection); - } - for (MCSectionELF *Group : Groups) { // Remember the offset into the file for this section. const uint64_t SecStart = align(Group->getAlignment()); @@ -1186,17 +1178,6 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { } } - if (CGProfileSection) { - uint64_t SecStart = W.OS.tell(); - for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) { - W.write(CGPE.From->getSymbol().getIndex()); - W.write(CGPE.To->getSymbol().getIndex()); - W.write(CGPE.Count); - } - uint64_t SecEnd = W.OS.tell(); - SectionOffsets[CGProfileSection] = std::make_pair(SecStart, SecEnd); - } - { uint64_t SecStart = W.OS.tell(); StrTabBuilder.write(W.OS); @@ -1471,7 +1452,11 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm, return; unsigned Type = TargetObjectWriter->getRelocType(Ctx, Target, Fixup, IsPCRel); - bool RelocateWithSymbol = shouldRelocateWithSymbol(Asm, RefA, SymA, C, Type); + const auto *Parent = cast(Fragment->getParent()); + // Emiting relocation with sybmol for CG Profile to help with --cg-profile. + bool RelocateWithSymbol = + shouldRelocateWithSymbol(Asm, RefA, SymA, C, Type) || + (Parent->getType() == ELF::SHT_LLVM_CALL_GRAPH_PROFILE); uint64_t Addend = 0; FixedValue = !RelocateWithSymbol && SymA && !SymA->isUndefined() diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 5a6c042333dc..76e69a65996e 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -477,7 +477,8 @@ void MCELFStreamer::fixSymbolsInTLSFixups(const MCExpr *expr) { } } -void MCELFStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE) { +void MCELFStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE, + uint64_t Offset) { const MCSymbol *S = &SRE->getSymbol(); if (S->isTemporary()) { if (!S->isInSection()) { @@ -488,22 +489,35 @@ void MCELFStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE) { } S = S->getSection().getBeginSymbol(); S->setUsedInReloc(); - SRE = - MCSymbolRefExpr::create(S, SRE->getKind(), getContext(), SRE->getLoc()); - return; + SRE = MCSymbolRefExpr::create(S, MCSymbolRefExpr::VK_None, getContext(), + SRE->getLoc()); } - // Not a temporary, referece it as a weak undefined. - bool Created; - getAssembler().registerSymbol(*S, &Created); - if (Created) - cast(S)->setBinding(ELF::STB_WEAK); + const MCConstantExpr *MCOffset = MCConstantExpr::create(Offset, getContext()); + MCObjectStreamer::visitUsedExpr(*SRE); + if (Optional> Err = + MCObjectStreamer::emitRelocDirective( + *MCOffset, "BFD_RELOC_NONE", SRE, SRE->getLoc(), + *getContext().getSubtargetInfo())) + report_fatal_error("Relocation for CG Profile could not be created: " + + Err->second); } void MCELFStreamer::finalizeCGProfile() { - for (MCAssembler::CGProfileEntry &E : getAssembler().CGProfile) { - finalizeCGProfileEntry(E.From); - finalizeCGProfileEntry(E.To); + MCAssembler &Asm = getAssembler(); + if (Asm.CGProfile.empty()) + return; + MCSection *CGProfile = getAssembler().getContext().getELFSection( + ".llvm.call-graph-profile", ELF::SHT_LLVM_CALL_GRAPH_PROFILE, + ELF::SHF_EXCLUDE, /*sizeof(Elf_CGProfile_Impl<>)=*/8); + PushSection(); + SwitchSection(CGProfile); + uint64_t Offset = 0; + for (MCAssembler::CGProfileEntry &E : Asm.CGProfile) { + finalizeCGProfileEntry(E.From, Offset++); + finalizeCGProfileEntry(E.To, Offset++); + emitIntValue(E.Count, sizeof(uint64_t)); } + PopSection(); } void MCELFStreamer::emitInstToFragment(const MCInst &Inst, diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index 7890884711a8..f8f2f0c12020 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -18,6 +18,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" #include "llvm/ObjectYAML/DWARFEmitter.h" #include "llvm/ObjectYAML/DWARFYAML.h" #include "llvm/ObjectYAML/ELFYAML.h" @@ -1474,14 +1475,9 @@ void ELFState::writeSectionContent( if (!Section.Entries) return; - for (const ELFYAML::CallGraphEntry &E : *Section.Entries) { - unsigned From = toSymbolIndex(E.From, Section.Name, /*IsDynamic=*/false); - unsigned To = toSymbolIndex(E.To, Section.Name, /*IsDynamic=*/false); - - CBA.write(From, ELFT::TargetEndianness); - CBA.write(To, ELFT::TargetEndianness); + for (const ELFYAML::CallGraphEntryWeight &E : *Section.Entries) { CBA.write(E.Weight, ELFT::TargetEndianness); - SHeader.sh_size += 16; + SHeader.sh_size += sizeof(object::Elf_CGProfile_Impl); } } diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 31bae6bf1f33..af6247e9339a 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -1834,11 +1834,9 @@ void MappingTraits::mapping(IO &IO, IO.mapRequired("Value", Opt.Value); } -void MappingTraits::mapping( - IO &IO, ELFYAML::CallGraphEntry &E) { +void MappingTraits::mapping( + IO &IO, ELFYAML::CallGraphEntryWeight &E) { assert(IO.getContext() && "The IO context is not initialized"); - IO.mapRequired("From", E.From); - IO.mapRequired("To", E.To); IO.mapRequired("Weight", E.Weight); } diff --git a/llvm/test/MC/ELF/cgprofile.s b/llvm/test/MC/ELF/cgprofile.s index 7938c2db8ebb..11c7464976db 100644 --- a/llvm/test/MC/ELF/cgprofile.s +++ b/llvm/test/MC/ELF/cgprofile.s @@ -15,22 +15,46 @@ late3: .L.local: # CHECK: Name: .llvm.call-graph-profile -# CHECK-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE (0x6FFF4C02) +# CHECK-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE (0x6FFF4C09) # CHECK-NEXT: Flags [ (0x80000000) # CHECK-NEXT: SHF_EXCLUDE (0x80000000) # CHECK-NEXT: ] # CHECK-NEXT: Address: # CHECK-NEXT: Offset: -# CHECK-NEXT: Size: 64 -# CHECK-NEXT: Link: 6 +# CHECK-NEXT: Size: 32 +# CHECK-NEXT: Link: 7 # CHECK-NEXT: Info: 0 # CHECK-NEXT: AddressAlignment: 1 -# CHECK-NEXT: EntrySize: 16 +# CHECK-NEXT: EntrySize: 8 # CHECK-NEXT: SectionData ( -# CHECK-NEXT: 0000: 02000000 05000000 20000000 00000000 -# CHECK-NEXT: 0010: 07000000 02000000 0B000000 00000000 -# CHECK-NEXT: 0020: 06000000 03000000 14000000 00000000 -# CHECK-NEXT: 0030: 01000000 05000000 2A000000 00000000 +# CHECK-NEXT: 0000: 20000000 00000000 0B000000 00000000 +# CHECK-NEXT: 0010: 14000000 00000000 2A000000 00000000 +# CHECK-NEXT: ) + +# CHECK: Name: .rela.llvm.call-graph-profile (28) +# CHECK-NEXT: Type: SHT_RELA (0x4) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Offset: 0x140 +# CHECK-NEXT: Size: 192 +# CHECK-NEXT: Link: 7 +# CHECK-NEXT: Info: 5 +# CHECK-NEXT: AddressAlignment: 8 +# CHECK-NEXT: EntrySize: 24 +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 00000000 00000000 00000000 02000000 +# CHECK-NEXT: 0010: 00000000 00000000 01000000 00000000 +# CHECK-NEXT: 0020: 00000000 05000000 00000000 00000000 +# CHECK-NEXT: 0030: 02000000 00000000 00000000 07000000 +# CHECK-NEXT: 0040: 00000000 00000000 03000000 00000000 +# CHECK-NEXT: 0050: 00000000 02000000 00000000 00000000 +# CHECK-NEXT: 0060: 04000000 00000000 00000000 06000000 +# CHECK-NEXT: 0070: 00000000 00000000 05000000 00000000 +# CHECK-NEXT: 0080: 00000000 03000000 00000000 00000000 +# CHECK-NEXT: 0090: 06000000 00000000 00000000 01000000 +# CHECK-NEXT: 00A0: 00000000 00000000 07000000 00000000 +# CHECK-NEXT: 00B0: 00000000 05000000 00000000 00000000 # CHECK-NEXT: ) # CHECK: Symbols [ @@ -72,7 +96,7 @@ late3: # CHECK: Name: freq # CHECK-NEXT: Value: # CHECK-NEXT: Size: -# CHECK-NEXT: Binding: Weak +# CHECK-NEXT: Binding: Global # CHECK-NEXT: Type: # CHECK-NEXT: Other: # CHECK-NEXT: Section: Undefined diff --git a/llvm/test/Object/multiple-sections.yaml b/llvm/test/Object/multiple-sections.yaml index 07af9252e26a..a0f8df7cfeb5 100644 --- a/llvm/test/Object/multiple-sections.yaml +++ b/llvm/test/Object/multiple-sections.yaml @@ -8,15 +8,16 @@ # CHECK: VersionDefinitions [ # CHECK: VersionRequirements [ # CHECK: CGProfile [ +# CHECK: CGProfile [ # CHECK: Addrsig [ --- !ELF -FileHeader: +FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB Type: ET_REL Machine: EM_X86_64 -Sections: +Sections: - Name: .symtab2 Type: SHT_SYMTAB Link: .strtab @@ -49,18 +50,18 @@ Sections: - Name: .llvm.call-graph-profile Type: SHT_LLVM_CALL_GRAPH_PROFILE Content: '' - EntSize: 16 + EntSize: 8 - Name: .llvm.call-graph-profile2 Type: SHT_LLVM_CALL_GRAPH_PROFILE Content: '' - EntSize: 16 + EntSize: 8 - Name: .llvm_addrsig Type: SHT_LLVM_ADDRSIG Content: '' - Name: .llvm_addrsig2 Type: SHT_LLVM_ADDRSIG Content: '' -Symbols: +Symbols: - Name: f DynamicSymbols: [] ... diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test index 92539a4601d2..d0115b4c6c11 100644 --- a/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test @@ -26,17 +26,29 @@ FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB Type: ET_DYN + Machine: EM_X86_64 Sections: - Name: .llvm.call-graph-profile Type: SHT_LLVM_CALL_GRAPH_PROFILE Entries: - - From: foo - To: bar - Weight: 89 - - From: bar - To: foo - Weight: 98 + - Weight: 89 + - Weight: 98 EntSize: [[ENTSIZE=]] + - Name: .rela.llvm.call-graph-profile + Type: SHT_RELA + Info: .llvm.call-graph-profile + Relocations: + - Symbol: foo + Type: R_X86_64_NONE + - Offset: 0x1 + Symbol: bar + Type: R_X86_64_NONE + - Offset: 0x2 + Symbol: bar + Type: R_X86_64_NONE + - Offset: 0x3 + Symbol: foo + Type: R_X86_64_NONE Symbols: - Name: foo - Name: bar @@ -46,9 +58,7 @@ Symbols: # RUN: llvm-readobj %t2.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t2.o --check-prefix=LLVM-ERR # RUN: llvm-readelf %t2.o --cg-profile | FileCheck %s --check-prefix=GNU -# LLVM-ERR: CGProfile [ -# LLVM-ERR-NEXT: warning: '[[FILE]]': unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: section [index 1] has invalid sh_entsize: expected 16, but got 15 -# LLVM-ERR-NEXT: ] +# LLVM-ERR: warning: '[[FILE]]': unable to load the SHT_LLVM_CALL_GRAPH_PROFILE section: section [index 1] has invalid sh_entsize: expected 8, but got 15 ## Check we report a warning when unable to dump a name of a symbol. # RUN: yaml2obj %s --docnum=2 -o %t3.o @@ -69,7 +79,7 @@ Symbols: # LLVM-BROKEN-SYM-NEXT: } # LLVM-BROKEN-SYM-NEXT: CGProfileEntry { # LLVM-BROKEN-SYM-NEXT: From: (0) -# LLVM-BROKEN-SYM-NEXT: warning: '[[FILE]]': unable to read the name of symbol with index 4: unable to get symbol from section [index 3]: invalid symbol index (4) +# LLVM-BROKEN-SYM-NEXT: warning: '[[FILE]]': unable to read the name of symbol with index 4: unable to get symbol from section [index 4]: invalid symbol index (4) # LLVM-BROKEN-SYM-NEXT: To: (4) # LLVM-BROKEN-SYM-NEXT: Weight: 20 # LLVM-BROKEN-SYM-NEXT: } @@ -80,19 +90,35 @@ FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB Type: ET_DYN + Machine: EM_X86_64 Sections: - Name: .llvm.call-graph-profile Type: SHT_LLVM_CALL_GRAPH_PROFILE Entries: - - From: 1 - To: 2 - Weight: 10 - - From: 2 - To: 3 - Weight: 20 - - From: 0x0 ## Null symbol. - To: 0x4 ## This index goes past the end of the symbol table. - Weight: 20 + - Weight: 10 + - Weight: 20 + - Weight: 20 + - Name: .rela.llvm.call-graph-profile + Type: SHT_RELA + Info: .llvm.call-graph-profile + Relocations: + - Symbol: 1 + Type: R_X86_64_NONE + - Offset: 0x1 + Symbol: 2 + Type: R_X86_64_NONE + - Offset: 0x2 + Symbol: 2 + Type: R_X86_64_NONE + - Offset: 0x3 + Symbol: 3 + Type: R_X86_64_NONE + - Offset: 0x4 + Symbol: 0x0 ## Null symbol. + Type: R_X86_64_NONE + - Offset: 0x5 + Symbol: 0x4 ## This index goes past the end of the symbol table. + Type: R_X86_64_NONE - Name: .strtab Type: SHT_STRTAB Content: "0041004200" ## '\0', 'A', '\0', 'B', '\0' @@ -100,3 +126,132 @@ Symbols: - StName: 1 ## 'A' - StName: 0xFF ## An arbitrary currupted index in the string table. - StName: 3 ## 'B' + +## Check we report a warning when a relocation section is not present. +# RUN: yaml2obj %s --docnum=3 -o %t4.o +# RUN: llvm-readobj %t4.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t4.o --check-prefix=LLVM-NO-RELOC +# RUN: llvm-readobj %t4.o --elf-cg-profile 2>&1 | FileCheck %s -DFILE=%t4.o --check-prefix=LLVM-NO-RELOC + +# LLVM-NO-RELOC: warning: '[[FILE]]': relocation section for a call graph section doesn't exist +# LLVM-NO-RELOC-NEXT: CGProfile [ +# LLVM-NO-RELOC-NEXT: CGProfileEntry { +# LLVM-NO-RELOC-NEXT: Weight: 89 +# LLVM-NO-RELOC-NEXT: } +# LLVM-NO-RELOC-NEXT: CGProfileEntry { +# LLVM-NO-RELOC-NEXT: Weight: 98 +# LLVM-NO-RELOC-NEXT: } +# LLVM-NO-RELOC-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .llvm.call-graph-profile + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Entries: + - Weight: 89 + - Weight: 98 + EntSize: [[ENTSIZE=]] +Symbols: + - Name: foo + - Name: bar + +## Check we report a warning when the number of relocation section entries does not match the number of call graph entries. +# RUN: yaml2obj %s --docnum=4 -o %t5.o +# RUN: llvm-readobj %t5.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t5.o --check-prefix=LLVM-RELOC-GRAPH-NOT-MATCH +# RUN: llvm-readobj %t5.o --elf-cg-profile 2>&1 | FileCheck %s -DFILE=%t5.o --check-prefix=LLVM-RELOC-GRAPH-NOT-MATCH + +# LLVM-RELOC-GRAPH-NOT-MATCH: warning: '[[FILE]]': number of from/to pairs does not match number of frequencies +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: CGProfile [ +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: CGProfileEntry { +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: Weight: 89 +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: } +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: CGProfileEntry { +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: Weight: 98 +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: } +# LLVM-RELOC-GRAPH-NOT-MATCH-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .llvm.call-graph-profile + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Entries: + - Weight: 89 + - Weight: 98 + EntSize: [[ENTSIZE=]] + - Name: .rela.llvm.call-graph-profile + Type: SHT_RELA + Info: .llvm.call-graph-profile + Relocations: + - Symbol: foo + Type: R_X86_64_NONE + - Offset: 0x1 + Symbol: bar + Type: R_X86_64_NONE + - Offset: 0x2 + Symbol: bar + Type: R_X86_64_NONE + - Offset: 0x3 + Symbol: foo + Type: R_X86_64_NONE + - Offset: 0x4 + Symbol: foo + Type: R_X86_64_NONE +Symbols: + - Name: foo + - Name: bar + +## Check we report a warning when a relocation section cant't be loaded. +# RUN: yaml2obj %s --docnum=5 -o %t6.o +# RUN: llvm-readobj %t6.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t6.o --check-prefix=LLVM-RELOC-WRONG-SIZE +# RUN: llvm-readobj %t6.o --elf-cg-profile 2>&1 | FileCheck %s -DFILE=%t6.o --check-prefix=LLVM-RELOC-WRONG-SIZE + +# LLVM-RELOC-WRONG-SIZE: warning: '[[FILE]]': unable to load relocations for SHT_LLVM_CALL_GRAPH_PROFILE section: section [index 2] has invalid sh_entsize: expected 24, but got 32 +# LLVM-RELOC-WRONG-SIZE-NEXT: CGProfile [ +# LLVM-RELOC-WRONG-SIZE-NEXT: CGProfileEntry { +# LLVM-RELOC-WRONG-SIZE-NEXT: Weight: 89 +# LLVM-RELOC-WRONG-SIZE-NEXT: } +# LLVM-RELOC-WRONG-SIZE-NEXT: CGProfileEntry { +# LLVM-RELOC-WRONG-SIZE-NEXT: Weight: 98 +# LLVM-RELOC-WRONG-SIZE-NEXT: } +# LLVM-RELOC-WRONG-SIZE-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .llvm.call-graph-profile + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Entries: + - Weight: 89 + - Weight: 98 + EntSize: [[ENTSIZE=]] + - Name: .rela.llvm.call-graph-profile + Type: SHT_RELA + Info: .llvm.call-graph-profile + Relocations: + - Symbol: foo + Type: R_X86_64_NONE + - Offset: 0x1 + Symbol: bar + Type: R_X86_64_NONE + - Offset: 0x2 + Symbol: bar + Type: R_X86_64_NONE + - Offset: 0x3 + Symbol: foo + Type: R_X86_64_NONE + EntSize: 32 +Symbols: + - Name: foo + - Name: bar diff --git a/llvm/test/tools/llvm-readobj/ELF/demangle.test b/llvm/test/tools/llvm-readobj/ELF/demangle.test index 4369a9e94812..25be40a72b94 100644 --- a/llvm/test/tools/llvm-readobj/ELF/demangle.test +++ b/llvm/test/tools/llvm-readobj/ELF/demangle.test @@ -197,8 +197,17 @@ Sections: - Name: .llvm.call-graph-profile Type: SHT_LLVM_CALL_GRAPH_PROFILE Link: .symtab - EntSize: 16 - Content: "01000000020000002000000000000000" + EntSize: 8 + Content: "2000000000000000" + - Name: .rela.llvm.call-graph-profile + Type: SHT_RELA + Info: .llvm.call-graph-profile + Relocations: + - Symbol: 1 + Type: R_X86_64_NONE + - Offset: 0x1 + Symbol: 2 + Type: R_X86_64_NONE - Name: .llvm_addrsig Type: SHT_LLVM_ADDRSIG Link: .symtab diff --git a/llvm/test/tools/obj2yaml/ELF/call-graph-profile-section.yaml b/llvm/test/tools/obj2yaml/ELF/call-graph-profile-section.yaml index 88ea98af4bdd..a1ac4d9f58f5 100644 --- a/llvm/test/tools/obj2yaml/ELF/call-graph-profile-section.yaml +++ b/llvm/test/tools/obj2yaml/ELF/call-graph-profile-section.yaml @@ -16,12 +16,8 @@ # BASIC-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # BASIC-NEXT: Link: .symtab # BASIC-NEXT: Entries: -# BASIC-NEXT: - From: foo -# BASIC-NEXT: To: bar -# BASIC-NEXT: Weight: 89 -# BASIC-NEXT: - From: bar -# BASIC-NEXT: To: foo -# BASIC-NEXT: Weight: 98 +# BASIC-NEXT: - Weight: 89 +# BASIC-NEXT: - Weight: 98 # BASIC-NEXT: Symbols: --- !ELF @@ -33,12 +29,8 @@ Sections: - Name: .llvm.call-graph-profile Type: SHT_LLVM_CALL_GRAPH_PROFILE Entries: - - From: 1 - To: 2 - Weight: 89 - - From: 2 - To: 1 - Weight: 98 + - Weight: 89 + - Weight: 98 Symbols: - Name: foo - Name: bar @@ -57,59 +49,45 @@ Symbols: # INVALID-NEXT: - Name: .empty # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab -# INVALID-NEXT: - Name: .multiple.16.valid +# INVALID-NEXT: - Name: .multiple.8.valid # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab # INVALID-NEXT: Entries: -# INVALID-NEXT: - From: foo -# INVALID-NEXT: To: bar # INVALID-NEXT: Weight: 3 -# INVALID-NEXT: - Name: .non.multiple.16 +# INVALID-NEXT: - Name: .non.multiple.8 # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab -# INVALID-NEXT: Content: '0000000100000002000000000000000300' -# INVALID-NEXT: - Name: .multiple.16.invalid +# INVALID-NEXT: Content: '000000000000000300' +# INVALID-NEXT: - Name: .multiple.8.invalid # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab -# INVALID-NEXT: Content: 00112233445566778899AABBCCDDEEFF -# INVALID-NEXT: - Name: .unknown.symbol.1 -# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE -# INVALID-NEXT: Link: .symtab -# INVALID-NEXT: Content: 000000FF000000020000000000000003 -# INVALID-NEXT: - Name: .unknown.symbol.2 -# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE -# INVALID-NEXT: Link: .symtab -# INVALID-NEXT: Content: 00000001000000FF0000000000000003 +# INVALID-NEXT: Content: 008899AABBCCDDEEFF # INVALID-NEXT: - Name: .link.to.symtable # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab # INVALID-NEXT: Entries: -# INVALID-NEXT: - From: foo -# INVALID-NEXT: To: bar -# INVALID-NEXT: Weight: 0 +# INVALID-NEXT: - Weight: 0 # INVALID-NEXT: - Name: .link.to.non.symtable.1 # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE -# INVALID-NEXT: Content: '00000001000000020000000000000000' +# INVALID-NEXT: Entries: +# INVALID-NEXT: - Weight: 0 # INVALID-NEXT: - Name: .link.to.non.symtable.2 # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .empty -# INVALID-NEXT: Content: '00000001000000020000000000000000' +# INVALID-NEXT: Entries: +# INVALID-NEXT: - Weight: 0 # INVALID-NEXT: - Name: .zero.entry.size # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab # INVALID-NEXT: EntSize: 0x0 # INVALID-NEXT: Entries: -# INVALID-NEXT: - From: foo -# INVALID-NEXT: To: bar -# INVALID-NEXT: Weight: 0 +# INVALID-NEXT: - Weight: 0 # INVALID-NEXT: - Name: .invalid.entry.size # INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE # INVALID-NEXT: Link: .symtab # INVALID-NEXT: EntSize: 0x1 # INVALID-NEXT: Entries: -# INVALID-NEXT: - From: foo -# INVALID-NEXT: To: bar -# INVALID-NEXT: Weight: 0 +# INVALID-NEXT: - Weight: 0 # INVALID-NEXT: Symbols: # INVALID-NEXT: - Name: foo # INVALID-NEXT: - Name: bar @@ -129,67 +107,43 @@ Sections: ## using the "Content" property otherwise. ## TODO: Teach yaml2obj to accept 'Size' key for SHT_LLVM_CALL_GRAPH_PROFILE ## sections and use Entries for cases below. - - Name: .multiple.16.valid + - Name: .multiple.8.valid Type: SHT_LLVM_CALL_GRAPH_PROFILE - Content: "00000001000000020000000000000003" - - Name: .non.multiple.16 + Content: "0000000000000003" + - Name: .non.multiple.8 Type: SHT_LLVM_CALL_GRAPH_PROFILE - Content: "0000000100000002000000000000000300" - - Name: .multiple.16.invalid + Content: "000000000000000300" + - Name: .multiple.8.invalid Type: SHT_LLVM_CALL_GRAPH_PROFILE - Content: "00112233445566778899AABBCCDDEEFF" -## Case 3: Check we use the "Content" property when unable to match a -## symbol index to a symbol. - - Name: .unknown.symbol.1 - Type: SHT_LLVM_CALL_GRAPH_PROFILE - Entries: - - From: 0xff - To: 2 - Weight: 3 - - Name: .unknown.symbol.2 - Type: SHT_LLVM_CALL_GRAPH_PROFILE - Entries: - - From: 1 - To: 0xff - Weight: 3 + Content: "008899AABBCCDDEEFF" ## Case 4: Check we use the "Content" property when a linked section ## is not a symbol table. - Name: .link.to.symtable Type: SHT_LLVM_CALL_GRAPH_PROFILE Entries: - - From: 1 - To: 2 - Weight: 0 + - Weight: 0 - Name: .link.to.non.symtable.1 Type: SHT_LLVM_CALL_GRAPH_PROFILE Link: 0 Entries: - - From: 1 - To: 2 - Weight: 0 + - Weight: 0 - Name: .link.to.non.symtable.2 Type: SHT_LLVM_CALL_GRAPH_PROFILE Link: 1 Entries: - - From: 1 - To: 2 - Weight: 0 + - Weight: 0 ## Case 5: Check we can dump a section that has a sh_entsize that is not a multiple of 16. ## Check that in these cases we print the "EntSize" key. - Name: .zero.entry.size Type: SHT_LLVM_CALL_GRAPH_PROFILE EntSize: 0 Entries: - - From: 1 - To: 2 - Weight: 0 + - Weight: 0 - Name: .invalid.entry.size Type: SHT_LLVM_CALL_GRAPH_PROFILE EntSize: 1 Entries: - - From: 1 - To: 2 - Weight: 0 + - Weight: 0 Symbols: - Name: foo - Name: bar diff --git a/llvm/test/tools/yaml2obj/ELF/call-graph-profile-section.yaml b/llvm/test/tools/yaml2obj/ELF/call-graph-profile-section.yaml index c52add3a0217..121b1e950c39 100644 --- a/llvm/test/tools/yaml2obj/ELF/call-graph-profile-section.yaml +++ b/llvm/test/tools/yaml2obj/ELF/call-graph-profile-section.yaml @@ -4,11 +4,11 @@ ## for 32/64-bit little/big endian targets is correct. # RUN: yaml2obj --docnum=1 -D BITS=64 -D ENCODE=LSB %s -o %t.le64 # RUN: llvm-readobj --cg-profile --sections --section-data %t.le64 | FileCheck %s --check-prefixes=BASIC,BASIC-LE -# RUN: yaml2obj --docnum=1 -D BITS=64 -D ENCODE=MSB %s -o %t.be64 +# RUN: yaml2obj --docnum=1 -D BITS=64 -D ENCODE=MSB %s -o %t.be64 # RUN: llvm-readobj --cg-profile --sections --section-data %t.be64 | FileCheck %s --check-prefixes=BASIC,BASIC-BE -# RUN: yaml2obj --docnum=1 -D BITS=32 -D ENCODE=LSB %s -o %t.le32 +# RUN: yaml2obj --docnum=1 -D BITS=32 -D ENCODE=LSB %s -o %t.le32 # RUN: llvm-readobj --cg-profile --sections --section-data %t.le32 | FileCheck %s --check-prefixes=BASIC,BASIC-LE -# RUN: yaml2obj --docnum=1 -D BITS=32 -D ENCODE=MSB %s -o %t.be32 +# RUN: yaml2obj --docnum=1 -D BITS=32 -D ENCODE=MSB %s -o %t.be32 # RUN: llvm-readobj --cg-profile --sections --section-data %t.be32 | FileCheck %s --check-prefixes=BASIC,BASIC-BE # BASIC: Name: .llvm.call-graph-profile @@ -17,33 +17,27 @@ # BASIC-NEXT: ] # BASIC-NEXT: Address: 0x0 # BASIC-NEXT: Offset: -# BASIC-NEXT: Size: 32 +# BASIC-NEXT: Size: 16 ## Check that we link SHT_LLVM_CALL_GRAPH_PROFILE section with .symtab by default. # BASIC-NEXT: Link: [[SYMTABNDX:.*]] # BASIC-NEXT: Info: 0 # BASIC-NEXT: AddressAlignment: 0 ## Check that the entry size is set to 16 by default. -# BASIC-NEXT: EntrySize: 16 +# BASIC-NEXT: EntrySize: 8 # BASIC-NEXT: SectionData ( -# BASIC-LE-NEXT: 0000: 01000000 02000000 59000000 00000000 -# BASIC-LE-NEXT: 0010: 02000000 01000000 62000000 00000000 -# BASIC-BE-NEXT: 0000: 00000001 00000002 00000000 00000059 -# BASIC-BE-NEXT: 0010: 00000002 00000001 00000000 00000062 +# BASIC-LE-NEXT: 0000: 59000000 00000000 62000000 00000000 +# BASIC-BE-NEXT: 0000: 00000000 00000059 00000000 00000062 # BASIC-NEXT: ) # BASIC-NEXT: } # BASIC-NEXT: Section { -# BASIC-NEXT: Index: [[SYMTABNDX]] -# BASIC-NEXT: Name: .symtab +# BASIC-NEXT: Index: [[SYMTABNDX]] +# BASIC-NEXT: Name: .symtab # BASIC: CGProfile [ # BASIC-NEXT: CGProfileEntry { -# BASIC-NEXT: From: foo (1) -# BASIC-NEXT: To: bar (2) # BASIC-NEXT: Weight: 89 # BASIC-NEXT: } # BASIC-NEXT: CGProfileEntry { -# BASIC-NEXT: From: bar (2) -# BASIC-NEXT: To: foo (1) # BASIC-NEXT: Weight: 98 # BASIC-NEXT: } # BASIC-NEXT: ] @@ -57,12 +51,8 @@ Sections: - Name: .llvm.call-graph-profile Type: SHT_LLVM_CALL_GRAPH_PROFILE Entries: - - From: 1 - To: 2 - Weight: 89 - - From: 2 - To: 1 - Weight: 98 + - Weight: 89 + - Weight: 98 Symbols: - Name: foo - Name: bar @@ -91,47 +81,8 @@ Sections: Link: 0xFF EntSize: 0xFF -## Check we can refer to symbols by name. -# RUN: yaml2obj --docnum=3 %s -o %t.sym -# RUN: llvm-readobj --cg-profile %t.sym | FileCheck %s --check-prefix=SYMBOL-NAMES - -# SYMBOL-NAMES: CGProfile [ -# SYMBOL-NAMES-NEXT: CGProfileEntry { -# SYMBOL-NAMES-NEXT: From: foo (1) -# SYMBOL-NAMES-NEXT: To: bar (2) -# SYMBOL-NAMES-NEXT: Weight: 10 -# SYMBOL-NAMES-NEXT: } -# SYMBOL-NAMES-NEXT: CGProfileEntry { -# SYMBOL-NAMES-NEXT: From: foo (1) -# SYMBOL-NAMES-NEXT: To: foo (3) -# SYMBOL-NAMES-NEXT: Weight: 30 -# SYMBOL-NAMES-NEXT: } -# SYMBOL-NAMES-NEXT: ] - ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_DYN -Sections: - - Name: .llvm.call-graph-profile - Type: SHT_LLVM_CALL_GRAPH_PROFILE - Entries: -## Case 1: Test we can use symbol names to describe an entry. - - From: foo - To: bar - Weight: 10 -## Case 2: Test we can refer to symbols with suffixes. - - From: foo - To: 'foo (1)' - Weight: 30 -Symbols: - - Name: foo - - Name: bar - - Name: 'foo (1)' - ## Check we can describe SHT_LLVM_CALL_GRAPH_PROFILE sections using the "Content" tag. -# RUN: yaml2obj --docnum=4 %s -o %t.content +# RUN: yaml2obj --docnum=3 %s -o %t.content # RUN: llvm-readobj --sections --section-data %t.content | FileCheck %s --check-prefix=CONTENT # CONTENT: Name: .llvm.call-graph-profile @@ -149,69 +100,10 @@ Sections: Type: SHT_LLVM_CALL_GRAPH_PROFILE Content: "11223344" -## Check we can't reference unknown symbols by name. -# RUN: not yaml2obj --docnum=5 %s 2>&1 | FileCheck %s --check-prefix=UNKNOWN-NAME -# RUN: not yaml2obj --docnum=6 %s 2>&1 | FileCheck %s --check-prefix=UNKNOWN-NAME -# UNKNOWN-NAME: error: unknown symbol referenced: 'bar' by YAML section '.llvm.call-graph-profile' - ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_DYN -Sections: - - Name: .llvm.call-graph-profile - Type: SHT_LLVM_CALL_GRAPH_PROFILE -## The first symbol is valid, but the second is unknown. - Entries: - - From: foo - To: bar - Weight: 10 -Symbols: - - Name: foo - ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_DYN -Sections: - - Name: .llvm.call-graph-profile - Type: SHT_LLVM_CALL_GRAPH_PROFILE -## The first symbol is unknown, but the second is valid. - Entries: - - From: bar - To: foo - Weight: 10 -Symbols: - - Name: foo - -## Check we can specify arbitrary symbol indexes for an SHT_LLVM_CALL_GRAPH_PROFILE section entry. -# RUN: yaml2obj --docnum=7 %s -o %t.unk -# RUN: llvm-readobj --sections --section-data %t.unk | FileCheck %s --check-prefix=UNKNOWN-INDEX - -# UNKNOWN-INDEX: Name: .llvm.call-graph-profile -# UNKNOWN-INDEX: SectionData ( -# UNKNOWN-INDEX-NEXT: 0000: 01000000 02000000 03000000 00000000 | -# UNKNOWN-INDEX-NEXT: ) - ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_DYN -Sections: - - Name: .llvm.call-graph-profile - Type: SHT_LLVM_CALL_GRAPH_PROFILE - Entries: - - From: 1 - To: 2 - Weight: 3 - ## Check we can use the "Content" key with the "Size" key when the size is greater ## than or equal to the content size. -# RUN: not yaml2obj --docnum=8 -DSIZE=1 -DCONTENT="'0011'" %s 2>&1 | \ +# RUN: not yaml2obj --docnum=4 -DSIZE=1 -DCONTENT="'0011'" %s 2>&1 | \ # RUN: FileCheck %s --check-prefix=CONTENT-SIZE-ERR # CONTENT-SIZE-ERR: error: Section size must be greater than or equal to the content size @@ -230,11 +122,11 @@ Sections: Content: [[CONTENT=]] Entries: [[ENTRIES=]] -# RUN: yaml2obj --docnum=8 -DSIZE=2 -DCONTENT="'0011'" %s -o %t.cont.size.eq.o +# RUN: yaml2obj --docnum=4 -DSIZE=2 -DCONTENT="'0011'" %s -o %t.cont.size.eq.o # RUN: llvm-readobj --sections --section-data %t.cont.size.eq.o | \ # RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="0011" -# RUN: yaml2obj --docnum=8 -DSIZE=3 -DCONTENT="'0011'" %s -o %t.cont.size.gr.o +# RUN: yaml2obj --docnum=4 -DSIZE=3 -DCONTENT="'0011'" %s -o %t.cont.size.gr.o # RUN: llvm-readobj --sections --section-data %t.cont.size.gr.o | \ # RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="001100" @@ -255,21 +147,21 @@ Sections: ## Check we can use the "Size" key alone to create the section. -# RUN: yaml2obj --docnum=8 -DSIZE=3 %s -o %t.size.o +# RUN: yaml2obj --docnum=4 -DSIZE=3 %s -o %t.size.o # RUN: llvm-readobj --sections --section-data %t.size.o | \ # RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="000000" ## Check we can use the "Content" key alone to create the section. -# RUN: yaml2obj --docnum=8 -DCONTENT="'112233'" %s -o %t.content.o +# RUN: yaml2obj --docnum=4 -DCONTENT="'112233'" %s -o %t.content.o # RUN: llvm-readobj --sections --section-data %t.content.o | \ # RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="112233" ## Check we can't use the "Entries" key together with the "Content" or "Size" keys. -# RUN: not yaml2obj --docnum=8 -DSIZE=0 -DENTRIES="[]" %s 2>&1 | \ +# RUN: not yaml2obj --docnum=4 -DSIZE=0 -DENTRIES="[]" %s 2>&1 | \ # RUN: FileCheck %s --check-prefix=ENTRIES-ERR -# RUN: not yaml2obj --docnum=8 -DCONTENT="'00'" -DENTRIES="[]" %s 2>&1 | \ +# RUN: not yaml2obj --docnum=4 -DCONTENT="'00'" -DENTRIES="[]" %s 2>&1 | \ # RUN: FileCheck %s --check-prefix=ENTRIES-ERR # ENTRIES-ERR: error: "Entries" cannot be used with "Content" or "Size" diff --git a/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml index ce19696a35a9..016af7e031a5 100644 --- a/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml +++ b/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml @@ -367,7 +367,7 @@ Sections: # RUN: llvm-readelf %t7 --section-headers | FileCheck %s --check-prefix=LINK-IMPLICIT # LINK-IMPLICIT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al -# LINK-IMPLICIT: [ 1] .cgp LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 10 0 0 0 +# LINK-IMPLICIT: [ 1] .cgp LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 08 0 0 0 # LINK-IMPLICIT-NEXT: [ 2] .llvm_addrsig LLVM_ADDRSIG 0000000000000000 000040 000000 00 0 0 0 # LINK-IMPLICIT-NEXT: [ 3] .group GROUP 0000000000000000 000040 000000 04 0 0 0 # LINK-IMPLICIT-NEXT: [ 4] .rela RELA 0000000000000000 000040 000000 18 0 0 0 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 1a6fa0d6e2dd..bf2b642c6955 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -318,6 +318,12 @@ protected: 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; @@ -356,7 +362,6 @@ protected: const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; const Elf_Shdr *DotDynsymSec = nullptr; - const Elf_Shdr *DotCGProfileSec = nullptr; const Elf_Shdr *DotAddrsigSec = nullptr; DenseMap> ShndxTables; Optional SONameOffset; @@ -1838,10 +1843,6 @@ ELFDumper::ELFDumper(const object::ELFObjectFile &O, if (!SymbolVersionNeedSection) SymbolVersionNeedSection = &Sec; break; - case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: - if (!DotCGProfileSec) - DotCGProfileSec = &Sec; - break; case ELF::SHT_LLVM_ADDRSIG: if (!DotAddrsigSec) DotAddrsigSec = &Sec; @@ -5898,28 +5899,15 @@ void ELFDumper::printNonRelocatableStackSizes( } template -void ELFDumper::printRelocatableStackSizes( - std::function PrintHeader) { - // Build a map between stack size sections and their corresponding relocation - // sections. - llvm::MapVector StackSizeRelocMap; +void ELFDumper::getSectionAndRelocations( + std::function IsMatch, + llvm::MapVector &SecToRelocMap) { for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { - StringRef SectionName; - if (Expected NameOrErr = Obj.getSectionName(Sec)) - SectionName = *NameOrErr; - else - consumeError(NameOrErr.takeError()); - - // 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 == ".stack_sizes") - if (StackSizeRelocMap - .insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) + if (IsMatch(Sec)) + if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) .second) continue; - // Check relocation sections if they are relocating contents of a - // stack sizes section. if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL) continue; @@ -5930,14 +5918,28 @@ void ELFDumper::printRelocatableStackSizes( toString(RelSecOrErr.takeError())); continue; } - const Elf_Shdr *ContentsSec = *RelSecOrErr; - if (this->getPrintableSectionName(**RelSecOrErr) != ".stack_sizes") - continue; - - // Insert a mapping from the stack sizes section to its relocation section. - StackSizeRelocMap[ContentsSec] = &Sec; + 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)) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + return SectionName == ".stack_sizes"; + }; + getSectionAndRelocations(IsMatch, StackSizeRelocMap); for (const auto &StackSizeMapEntry : StackSizeRelocMap) { PrintHeader(); @@ -6699,27 +6701,65 @@ template void LLVMELFDumper::printHashHistograms() { } template void LLVMELFDumper::printCGProfile() { - ListScope L(W, "CGProfile"); - if (!this->DotCGProfileSec) - return; + llvm::MapVector SecToRelocMap; - Expected> CGProfileOrErr = - this->Obj.template getSectionContentsAsArray( - *this->DotCGProfileSec); - if (!CGProfileOrErr) { - this->reportUniqueWarning( - "unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " + - toString(CGProfileOrErr.takeError())); - return; - } + auto IsMatch = [](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE; + }; + this->getSectionAndRelocations(IsMatch, SecToRelocMap); - for (const Elf_CGProfile &CGPE : *CGProfileOrErr) { - DictScope D(W, "CGProfileEntry"); - W.printNumber("From", this->getStaticSymbolName(CGPE.cgp_from), - CGPE.cgp_from); - W.printNumber("To", this->getStaticSymbolName(CGPE.cgp_to), - CGPE.cgp_to); - W.printNumber("Weight", CGPE.cgp_weight); + for (const auto &CGMapEntry : SecToRelocMap) { + const Elf_Shdr *CGSection = CGMapEntry.first; + const Elf_Shdr *CGRelSection = CGMapEntry.second; + + Expected> CGProfileOrErr = + this->Obj.template getSectionContentsAsArray(*CGSection); + if (!CGProfileOrErr) { + this->reportUniqueWarning( + "unable to load the SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileOrErr.takeError())); + return; + } + + Elf_Rela_Range CGProfileRela; + bool UseReloc = (CGRelSection != nullptr); + if (UseReloc) { + Expected CGProfileRelaOrError = + this->Obj.relas(*CGRelSection); + if (!CGProfileRelaOrError) { + this->reportUniqueWarning("unable to load relocations for " + "SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileRelaOrError.takeError())); + UseReloc = false; + } else + CGProfileRela = *CGProfileRelaOrError; + + if (UseReloc && CGProfileRela.size() != (CGProfileOrErr->size() * 2)) { + this->reportUniqueWarning( + "number of from/to pairs does not match number of frequencies"); + UseReloc = false; + } + } else + this->reportUniqueWarning( + "relocation section for a call graph section doesn't exist"); + + auto GetIndex = [&](uint32_t Index) { + const Elf_Rel_Impl &Rel = CGProfileRela[Index]; + return Rel.getSymbol(this->Obj.isMips64EL()); + }; + + ListScope L(W, "CGProfile"); + for (uint32_t I = 0, Size = CGProfileOrErr->size(); I != Size; ++I) { + const Elf_CGProfile &CGPE = (*CGProfileOrErr)[I]; + DictScope D(W, "CGProfileEntry"); + if (UseReloc) { + uint32_t From = GetIndex(I * 2); + uint32_t To = GetIndex(I * 2 + 1); + W.printNumber("From", this->getStaticSymbolName(From), From); + W.printNumber("To", this->getStaticSymbolName(To), To); + } + W.printNumber("Weight", CGPE.cgp_weight); + } } } diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index a72020fb752f..028758d68d95 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -1022,41 +1022,31 @@ ELFDumper::dumpCallGraphProfileSection(const Elf_Shdr *Shdr) { if (!ContentOrErr) return ContentOrErr.takeError(); ArrayRef Content = *ContentOrErr; - + const uint32_t SizeOfEntry = ELFYAML::getDefaultShEntSize( + Obj.getHeader().e_machine, S->Type, S->Name); // Dump the section by using the Content key when it is truncated. // There is no need to create either "Content" or "Entries" fields when the // section is empty. - if (Content.empty() || Content.size() % 16 != 0) { + if (Content.empty() || Content.size() % SizeOfEntry != 0) { if (!Content.empty()) S->Content = yaml::BinaryRef(Content); return S.release(); } - std::vector Entries(Content.size() / 16); + std::vector Entries(Content.size() / + SizeOfEntry); DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0); DataExtractor::Cursor Cur(0); - auto ReadEntry = [&](ELFYAML::CallGraphEntry &E) { - uint32_t FromSymIndex = Data.getU32(Cur); - uint32_t ToSymIndex = Data.getU32(Cur); + auto ReadEntry = [&](ELFYAML::CallGraphEntryWeight &E) { E.Weight = Data.getU64(Cur); if (!Cur) { consumeError(Cur.takeError()); return false; } - - Expected From = getSymbolName(Shdr->sh_link, FromSymIndex); - Expected To = getSymbolName(Shdr->sh_link, ToSymIndex); - if (From && To) { - E.From = *From; - E.To = *To; - return true; - } - consumeError(From.takeError()); - consumeError(To.takeError()); - return false; + return true; }; - for (ELFYAML::CallGraphEntry &E : Entries) { + for (ELFYAML::CallGraphEntryWeight &E : Entries) { if (ReadEntry(E)) continue; S->Content = yaml::BinaryRef(Content);