Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -153,6 +153,7 @@ Fill, LinkerOptions, DependentLibraries, + CallGraphProfile }; ChunkKind Kind; @@ -385,6 +386,27 @@ } }; +// 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; + // The weight of the edge. + uint64_t Weight; +}; + +struct CallGraphProfileSection : Section { + Optional> Entries; + Optional Content; + + CallGraphProfileSection() : Section(ChunkKind::CallGraphProfile) {} + + static bool classof(const Chunk *S) { + return S->Kind == ChunkKind::CallGraphProfile; + } +}; + struct SymverSection : Section { std::vector Entries; @@ -514,6 +536,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry) 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::NoteEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) @@ -685,6 +708,10 @@ 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::Relocation &Rel); }; Index: llvm/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -210,6 +210,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::DependentLibrariesSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::CallGraphProfileSection &Section, + ContiguousBlobAccumulator &CBA); void writeFill(ELFYAML::Fill &Fill, ContiguousBlobAccumulator &CBA); @@ -492,6 +495,8 @@ writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); } else { llvm_unreachable("Unknown section type"); } @@ -982,6 +987,41 @@ } } +template +void ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::CallGraphProfileSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + if (Section.EntSize) + SHeader.sh_entsize = *Section.EntSize; + else + SHeader.sh_entsize = 16; + + unsigned Link = 0; + if (Section.Link.empty() && SN2I.lookup(".symtab", Link)) + SHeader.sh_link = Link; + + if (Section.Content) { + SHeader.sh_size = writeContent(OS, Section.Content, None); + return; + } + + 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); + + support::endian::write(OS, From, ELFT::TargetEndianness); + support::endian::write(OS, To, ELFT::TargetEndianness); + support::endian::write(OS, E.Weight, ELFT::TargetEndianness); + SHeader.sh_size += 16; + } +} + template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::HashSection &Section, Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -1149,6 +1149,12 @@ IO.mapOptional("Content", Section.Content); } +static void sectionMapping(IO &IO, ELFYAML::CallGraphProfileSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Entries", Section.Entries); + IO.mapOptional("Content", Section.Content); +} + void MappingTraits::mapping( IO &IO, ELFYAML::SectionOrType §ionOrType) { IO.mapRequired("SectionOrType", sectionOrType.sectionNameOrType); @@ -1282,6 +1288,11 @@ sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: + if (!IO.outputting()) + Section.reset(new ELFYAML::CallGraphProfileSection()); + sectionMapping(IO, *cast(Section.get())); + break; default: if (!IO.outputting()) { StringRef Name; @@ -1463,6 +1474,12 @@ return {}; } + if (const auto *CGP = dyn_cast(C.get())) { + if (CGP->Entries && CGP->Content) + return "\"Entries\" and \"Content\" can't be used together"; + return {}; + } + return {}; } @@ -1600,6 +1617,14 @@ IO.mapRequired("Value", Opt.Value); } +void MappingTraits::mapping( + IO &IO, ELFYAML::CallGraphEntry &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); +} + LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_AFL_REG) LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_ABI_FP) LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_EXT) Index: llvm/test/tools/obj2yaml/call-graph-profile-section.yaml =================================================================== --- /dev/null +++ llvm/test/tools/obj2yaml/call-graph-profile-section.yaml @@ -0,0 +1,269 @@ +## Test how we dump SHT_LLVM_CALL_GRAPH_PROFILE sections for 32 and 64-bit targets. + +## Test we use the "Entries" property when it is possible to dump values correctly. + +# RUN: yaml2obj --docnum=1 %s -o %t.le64 +# RUN: obj2yaml %t.le64 | FileCheck %s --check-prefix=BASIC +# RUN: yaml2obj --docnum=2 %s -o %t.be64 +# RUN: obj2yaml %t.be64 | FileCheck %s --check-prefix=BASIC +# RUN: yaml2obj --docnum=3 %s -o %t.le32 +# RUN: obj2yaml %t.le32 | FileCheck %s --check-prefix=BASIC +# RUN: yaml2obj --docnum=4 %s -o %t.be32 +# RUN: obj2yaml %t.be32 | FileCheck %s --check-prefix=BASIC + +# BASIC: Sections: +# BASIC-NEXT: - Name: .llvm.call-graph-profile +# BASIC-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# BASIC-NEXT: Link: .symtab +# BASIC-NEXT: EntSize: 0x0000000000000010 +# 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: Symbols: + +## TODO: we should really improve yaml2obj somehow to be able to collapse +## the following four YAML descriptions into a single one. + +--- !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: + - From: 1 + To: 2 + Weight: 89 + - From: 2 + To: 1 + Weight: 98 +Symbols: + - Name: foo + - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2MSB + 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: 89 + - From: 2 + To: 1 + Weight: 98 +Symbols: + - Name: foo + - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +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 +Symbols: + - Name: foo + - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2MSB + Type: ET_DYN + Machine: EM_386 +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 +Symbols: + - Name: foo + - Name: bar + +## Check how we handle broken cases. + +# RUN: yaml2obj --docnum=5 %s -o %t.invalid +# RUN: obj2yaml %t.invalid | FileCheck %s --check-prefix=INVALID + +# INVALID: --- !ELF +# INVALID-NEXT: FileHeader: +# INVALID-NEXT: Class: ELFCLASS32 +# INVALID-NEXT: Data: ELFDATA2MSB +# INVALID-NEXT: Type: ET_DYN +# INVALID-NEXT: Machine: EM_386 +# INVALID-NEXT: Sections: +# INVALID-NEXT: - Name: .empty +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: - Name: .multiple.16.valid +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Entries: +# INVALID-NEXT: - From: foo +# INVALID-NEXT: To: bar +# INVALID-NEXT: Weight: 3 +# INVALID-NEXT: - Name: .non.multiple.16 +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Content: '0000000100000002000000000000000300' +# INVALID-NEXT: - Name: .multiple.16.invalid +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Content: 00112233445566778899AABBCCDDEEFF +# INVALID-NEXT: - Name: .unknown.symbol.1 +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Content: 000000FF000000020000000000000003 +# INVALID-NEXT: - Name: .unknown.symbol.2 +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Content: 00000001000000FF0000000000000003 +# INVALID-NEXT: - Name: .link.to.symtable +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Entries: +# INVALID-NEXT: - From: foo +# INVALID-NEXT: To: bar +# INVALID-NEXT: Weight: 0 +# INVALID-NEXT: - Name: .link.to.non.symtable.1 +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Content: '00000001000000020000000000000000' +# INVALID-NEXT: - Name: .link.to.non.symtable.2 +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .empty +# INVALID-NEXT: EntSize: 0x0000000000000010 +# INVALID-NEXT: Content: '00000001000000020000000000000000' +# INVALID-NEXT: - Name: .zero.entry.size +# 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: - Name: .invalid.entry.size +# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# INVALID-NEXT: Link: .symtab +# INVALID-NEXT: EntSize: 0x0000000000000001 +# INVALID-NEXT: Entries: +# INVALID-NEXT: - From: foo +# INVALID-NEXT: To: bar +# INVALID-NEXT: Weight: 0 +# INVALID-NEXT: Symbols: +# INVALID-NEXT: - Name: foo +# INVALID-NEXT: - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2MSB + Type: ET_DYN + Machine: EM_386 +Sections: +## Case 1: Content is empty. + - Name: .empty + Type: SHT_LLVM_CALL_GRAPH_PROFILE +## Case 2: Check that we use the "Entries" property to dump the data when it +## has a size that is a multiple of 16 and is valid (it is possible to match +## symbol indexes to symbols), but fallback to dumping the whole section +## 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 + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Content: "00000001000000020000000000000003" + - Name: .non.multiple.16 + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Content: "0000000100000002000000000000000300" + - Name: .multiple.16.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 +## 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 + - Name: .link.to.non.symtable.1 + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Link: 0 + Entries: + - From: 1 + To: 2 + Weight: 0 + - Name: .link.to.non.symtable.2 + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Link: 1 + Entries: + - From: 1 + To: 2 + Weight: 0 +## Case 5: Check we can dump a section that has a sh_entsize that is not a multiple of 16. + - Name: .zero.entry.size + Type: SHT_LLVM_CALL_GRAPH_PROFILE + EntSize: 0 + Entries: + - From: 1 + To: 2 + Weight: 0 + - Name: .invalid.entry.size + Type: SHT_LLVM_CALL_GRAPH_PROFILE + EntSize: 1 + Entries: + - From: 1 + To: 2 + Weight: 0 +Symbols: + - Name: foo + - Name: bar Index: llvm/test/tools/yaml2obj/ELF/call-graph-profile-section.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/ELF/call-graph-profile-section.yaml @@ -0,0 +1,295 @@ +## Test how we create SHT_LLVM_CALL_GRAPH_PROFILE sections. + +## Test that the content of SHT_LLVM_CALL_GRAPH_PROFILE sections +## for 32/64-bit little/big endian targets is correct. +# RUN: yaml2obj --docnum=1 %s -o %t.le64 +# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.le64 | FileCheck %s --check-prefixes=BASIC,BASIC-LE +# RUN: yaml2obj --docnum=2 %s -o %t.be64 +# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.be64 | FileCheck %s --check-prefixes=BASIC,BASIC-BE +# RUN: yaml2obj --docnum=3 %s -o %t.le32 +# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.le32 | FileCheck %s --check-prefixes=BASIC,BASIC-LE +# RUN: yaml2obj --docnum=4 %s -o %t.be32 +# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.be32 | FileCheck %s --check-prefixes=BASIC,BASIC-BE + +# BASIC: Name: .llvm.call-graph-profile +# BASIC-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE +# BASIC-NEXT: Flags [ +# BASIC-NEXT: ] +# BASIC-NEXT: Address: 0x0 +# BASIC-NEXT: Offset: +# BASIC-NEXT: Size: 32 +## 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: 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-NEXT: ) +# BASIC-NEXT: } +# BASIC-NEXT: Section { +# 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: ] + +## TODO: we should really improve yaml2obj somehow to be able to collapse +## the following four YAML descriptions into a single one. + +--- !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: + - From: 1 + To: 2 + Weight: 89 + - From: 2 + To: 1 + Weight: 98 +Symbols: + - Name: foo + - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2MSB + 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: 89 + - From: 2 + To: 1 + Weight: 98 +Symbols: + - Name: foo + - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +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 +Symbols: + - Name: foo + - Name: bar + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2MSB + Type: ET_DYN + Machine: EM_386 +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 +Symbols: + - Name: foo + - Name: bar + +## Check we can set arbitrary sh_link and sh_entsize values. +## Check we can specify neither "Content" nor "Entries" tags. +# RUN: yaml2obj --docnum=5 %s -o %t.link +# RUN: llvm-readelf --sections %t.link | FileCheck %s --check-prefix=LINK + +# LINK: [Nr] Name Type Address Off Size ES Flg Lk +# LINK: [ 1] .llvm.foo LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 00 0 +# LINK: [ 2] .llvm.bar LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 ff 255 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .llvm.foo + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Link: 0x0 + EntSize: 0 + - Name: .llvm.bar + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Link: 0xFF + EntSize: 0xFF + +## Check we can't specify both "Content" and "Entries" tags. +# RUN: not yaml2obj --docnum=6 %s 2>&1 | FileCheck %s --check-prefix=BOTH +# BOTH: error: "Entries" and "Content" can't be used together + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .llvm.foo + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Content: "" + Entries: [] + +## Check we can refer to symbols by name. +# RUN: yaml2obj --docnum=7 %s -o %t.sym +# RUN: llvm-readobj --elf-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 + Machine: EM_X86_64 +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=8 %s -o %t.content +# RUN: llvm-readobj --sections --section-data %t.content | FileCheck %s --check-prefix=CONTENT + +# CONTENT: Name: .llvm.call-graph-profile +# CONTENT: SectionData ( +# CONTENT-NEXT: 0000: 11223344 | +# CONTENT-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 + Content: "11223344" + +## Check we can't reference unknown symbols by name. +# RUN: not yaml2obj --docnum=9 %s 2>&1 | FileCheck %s --check-prefix=UNKNOWN-NAME +# RUN: not yaml2obj --docnum=10 %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 + Machine: EM_X86_64 +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 + Machine: EM_X86_64 +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=11 %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 + Machine: EM_X86_64 +Sections: + - Name: .llvm.call-graph-profile + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Entries: + - From: 1 + To: 2 + Weight: 3 Index: llvm/tools/obj2yaml/elf2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/elf2yaml.cpp +++ llvm/tools/obj2yaml/elf2yaml.cpp @@ -65,6 +65,8 @@ dumpLinkerOptionsSection(const Elf_Shdr *Shdr); Expected dumpDependentLibrariesSection(const Elf_Shdr *Shdr); + Expected + dumpCallGraphProfileSection(const Elf_Shdr *Shdr); Expected dumpDynamicSection(const Elf_Shdr *Shdr); Expected dumpRelocSection(const Elf_Shdr *Shdr); Expected dumpRelrSection(const Elf_Shdr *Shdr); @@ -346,6 +348,14 @@ Y->Chunks.emplace_back(*SecOrErr); break; } + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: { + Expected SecOrErr = + dumpCallGraphProfileSection(&Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + Y->Chunks.emplace_back(*SecOrErr); + break; + } case ELF::SHT_NULL: { // We only dump the SHT_NULL section at index 0 when it // has at least one non-null field, because yaml2obj @@ -677,6 +687,62 @@ return DL.release(); } +template +Expected +ELFDumper::dumpCallGraphProfileSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + Expected> ContentOrErr = Obj.getSectionContents(Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + ArrayRef Content = *ContentOrErr; + + // Dump the section by using the Content key when it is truncated. + // There is no need to create neither "Content" nor "Entries" fields when the + // section is empty. + if (Content.empty() || Content.size() % 16 != 0) { + if (!Content.empty()) + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + std::vector Entries(Content.size() / 16); + 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); + 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; + }; + + for (ELFYAML::CallGraphEntry &E : Entries) { + if (ReadEntry(E)) + continue; + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + S->Entries = std::move(Entries); + return S.release(); +} + template Expected ELFDumper::dumpDynamicSection(const Elf_Shdr *Shdr) {