https://github.com/dyung updated https://github.com/llvm/llvm-project/pull/211118
>From 6f033fece5f5ae501698b9f49f3bca3f1fa19b38 Mon Sep 17 00:00:00 2001 From: Prabhu Rajasekaran <[email protected]> Date: Mon, 20 Jul 2026 10:31:43 -0700 Subject: [PATCH] [llvm-readobj] Print all callgraph sections (#198929) Ensure that `llvm-readobj` / `llvm-readelf` processes and dumps all SHT_LLVM_CALL_GRAPH sections present in an object file, rather than just the first one. **Motivation:** Consider this `foo.s` file ``` .pushsection .text, "axG", %progbits, foo .globl foo foo: ret .pushsection .llvm.callgraph, "?", %llvm_call_graph .byte 0, 0 .dc.a foo .quad 0 .popsection .popsection .pushsection .text, "axG", %progbits, bar .globl foo bar: ret .pushsection .llvm.callgraph, "?", %llvm_call_graph .byte 0, 0 .dc.a bar .quad 0 .popsection .popsection ``` `clang -c foo.s && llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info foo.o` on that shows that there is only one record reported because the current implementation just looked at the first SHT_LLVM_CALL_GRAPH section and then ignored all others. This patch will address this gap by iterating over all the SHT_LLVM_CALL_GRAPH sections. (cherry picked from commit eb7677457c1611c02a6362d9eae043987c02ea25) --- .../ELF/call-graph-info-warn-malformed.test | 83 +------ .../llvm-readobj/ELF/call-graph-info.test | 215 ++++++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 215 +++++++++--------- 3 files changed, 327 insertions(+), 186 deletions(-) diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-malformed.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-malformed.test index 345c97c79ec29..b22f29e5aeeb9 100644 --- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-malformed.test +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-malformed.test @@ -271,87 +271,8 @@ Symbols: - Name: baz ... -## Check that the first call graph section is processed and the second call graph section is ignored. -## Note that we do not expect an object file to have more than one call graph section as we merge the -## call graph sections when there are more than one. -# RUN: yaml2obj --docnum=7 %s -o %t12 -# RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t12 2>&1 | \ -# RUN: FileCheck %s -DFILE=%t12 --check-prefix=LLVM-FIRST-CGSECTION --match-full-lines -# RUN: llvm-readobj --elf-output-style=JSON --pretty-print --call-graph-info %t12 2>&1 | \ -# RUN: FileCheck %s -DFILE=%t12 --check-prefix=JSON-FIRST-CGSECTION --match-full-lines - -# LLVM-FIRST-CGSECTION: CallGraph [ -# LLVM-FIRST-CGSECTION-NEXT: Function { -# LLVM-FIRST-CGSECTION-NEXT: Names: [foo] -# LLVM-FIRST-CGSECTION-NEXT: Address: 0x1790 -# LLVM-FIRST-CGSECTION-NEXT: Version: 0 -# LLVM-FIRST-CGSECTION-NEXT: IsIndirectTarget: Yes -# LLVM-FIRST-CGSECTION-NEXT: TypeID: 0x3ECBEEF531F74424 -# LLVM-FIRST-CGSECTION-NEXT: NumDirectCallees: 0 -# LLVM-FIRST-CGSECTION-NEXT: DirectCallees [ -# LLVM-FIRST-CGSECTION-NEXT: ] -# LLVM-FIRST-CGSECTION-NEXT: NumIndirectTargetTypeIDs: 0 -# LLVM-FIRST-CGSECTION-NEXT: IndirectTypeIDs: [] -# LLVM-FIRST-CGSECTION-NEXT: } -# LLVM-FIRST-CGSECTION-NEXT: ] -# LLVM-FIRST-CGSECTION-NOT: {{.}} - -# JSON-FIRST-CGSECTION: "CallGraph": [ -# JSON-FIRST-CGSECTION-NEXT: { -# JSON-FIRST-CGSECTION-NEXT: "Function": { -# JSON-FIRST-CGSECTION-NEXT: "Names": [ -# JSON-FIRST-CGSECTION-NEXT: "foo" -# JSON-FIRST-CGSECTION-NEXT: ], -# JSON-FIRST-CGSECTION-NEXT: "Address": 6032, -# JSON-FIRST-CGSECTION-NEXT: "Version": 0, -# JSON-FIRST-CGSECTION-NEXT: "IsIndirectTarget": true, -# JSON-FIRST-CGSECTION-NEXT: "TypeID": 4524972987496481828, -# JSON-FIRST-CGSECTION-NEXT: "NumDirectCallees": 0, -# JSON-FIRST-CGSECTION-NEXT: "DirectCallees": [], -# JSON-FIRST-CGSECTION-NEXT: "NumIndirectTargetTypeIDs": 0, -# JSON-FIRST-CGSECTION-NEXT: "IndirectTypeIDs": [] -# JSON-FIRST-CGSECTION-NEXT: } -# JSON-FIRST-CGSECTION-NEXT: } -# JSON-FIRST-CGSECTION-NEXT: ] -# JSON-FIRST-CGSECTION-NEXT: } -# JSON-FIRST-CGSECTION-NEXT: ] -# JSON-FIRST-CGSECTION-NOT: {{.}} - ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_DYN -Sections: - - Name: .text - Type: SHT_PROGBITS - - Type: SHT_LLVM_CALL_GRAPH - Link: .text - ContentArray: [ - ## --- foo --- - 0x00, ## Version - 0x01, ## Flags - 0x90, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ## FunctionEntryPC: 0x1790 - 0x24, 0x44, 0xF7, 0x31, 0xF5, 0xEE, 0xCB, 0x3E, ## FunctionTypeID: 0x3ECBEEF531F74424 - ] - ## This following second call graph section will be completely ignored. - - Type: SHT_LLVM_CALL_GRAPH - Link: .text - ContentArray: [ - ## --- bar --- - 0x00, ## Version - 0x01, ## Flags - 0xA0, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ## FunctionEntryPC: 0x17A0 - 0x24, 0x44, 0xF7, 0x31, 0xF5, 0xEE, 0xCB, 0x3E, ## FunctionTypeID: 0x3ECBEEF531F74424 - ] -Symbols: - - Name: foo - Type: STT_FUNC - Section: .text - Value: 0x1790 - ## Check warning when the relocation section linked to SHT_LLVM_CALL_GRAPH has an invalid sh_link. -# RUN: yaml2obj --docnum=8 -DLINK=255 -DINFO=.llvm_call_graph %s -o %t13 +# RUN: yaml2obj --docnum=7 -DLINK=255 -DINFO=.llvm_call_graph %s -o %t13 # RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t13 2>&1 | \ # RUN: FileCheck %s -DFILE=%t13 --check-prefix=INVALID-RELA-LINK # RUN: llvm-readobj --elf-output-style=JSON --call-graph-info %t13 2>&1 | \ @@ -360,7 +281,7 @@ Symbols: # INVALID-RELA-LINK: warning: '[[FILE]]': invalid section linked to SHT_RELA section with index 2: invalid section index: 255 ## Check warning when getSectionAndRelocations fails to read sections. -# RUN: yaml2obj --docnum=8 -DLINK=0 -DINFO=255 %s -o %t14 +# RUN: yaml2obj --docnum=7 -DLINK=0 -DINFO=255 %s -o %t14 # RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t14 2>&1 | \ # RUN: FileCheck %s -DFILE=%t14 --check-prefix=ERR-READ-SECTION # RUN: llvm-readobj --elf-output-style=JSON --call-graph-info %t14 2>&1 | \ diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test index 4d333a26d6de5..68e258e3c828f 100644 --- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test @@ -441,6 +441,7 @@ Symbols: ## Check ARM 32-bit Thumb address handling. # RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: llvm-readobj --elf-output-style=GNU --call-graph-info %t3 2>&1 | count 0 # RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t3 2>&1 | \ # RUN: FileCheck %s --match-full-lines --check-prefix=LLVM-ARM-THUMB -DFILE=%t3 # RUN: llvm-readobj --elf-output-style=JSON --pretty-print --call-graph-info %t3 2>&1 | \ @@ -509,6 +510,7 @@ Symbols: ## Check multiple function names handling. # RUN: yaml2obj --docnum=4 %s -o %t4 +# RUN: llvm-readobj --elf-output-style=GNU --call-graph-info %t4 2>&1 | count 0 # RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t4 2>&1 | \ # RUN: FileCheck %s --match-full-lines --check-prefix=LLVM-MULTI-NAMES -DFILE=%t4 # RUN: llvm-readobj --elf-output-style=JSON --pretty-print --call-graph-info %t4 2>&1 | \ @@ -574,3 +576,216 @@ Symbols: Type: STT_FUNC Section: .text Value: 0x1000 + +## Check handling of multiple call graph sections in a relocatable object file. +# RUN: yaml2obj --docnum=5 %s -o %t5 +# RUN: llvm-readobj --elf-output-style=GNU --call-graph-info %t5 2>&1 | count 0 +# RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t5 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --check-prefix=LLVM-MULTI-RELOC -DFILE=%t5 +# RUN: llvm-readobj --elf-output-style=JSON --pretty-print --call-graph-info %t5 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --check-prefix=JSON-MULTI-RELOC -DFILE=%t5 + +# LLVM-MULTI-RELOC: CallGraph [ +# LLVM-MULTI-RELOC-NEXT: Function { +# LLVM-MULTI-RELOC-NEXT: Name: foo +# LLVM-MULTI-RELOC-NEXT: Version: 0 +# LLVM-MULTI-RELOC-NEXT: IsIndirectTarget: Yes +# LLVM-MULTI-RELOC-NEXT: TypeID: 0xF85C699BB8EF20A2 +# LLVM-MULTI-RELOC-NEXT: NumDirectCallees: 0 +# LLVM-MULTI-RELOC-NEXT: DirectCallees [ +# LLVM-MULTI-RELOC-NEXT: ] +# LLVM-MULTI-RELOC-NEXT: NumIndirectTargetTypeIDs: 0 +# LLVM-MULTI-RELOC-NEXT: IndirectTypeIDs: [] +# LLVM-MULTI-RELOC-NEXT: } +# LLVM-MULTI-RELOC-NEXT: Function { +# LLVM-MULTI-RELOC-NEXT: Name: bar +# LLVM-MULTI-RELOC-NEXT: Version: 0 +# LLVM-MULTI-RELOC-NEXT: IsIndirectTarget: Yes +# LLVM-MULTI-RELOC-NEXT: TypeID: 0xF85C699BB8EF20A2 +# LLVM-MULTI-RELOC-NEXT: NumDirectCallees: 0 +# LLVM-MULTI-RELOC-NEXT: DirectCallees [ +# LLVM-MULTI-RELOC-NEXT: ] +# LLVM-MULTI-RELOC-NEXT: NumIndirectTargetTypeIDs: 0 +# LLVM-MULTI-RELOC-NEXT: IndirectTypeIDs: [] +# LLVM-MULTI-RELOC-NEXT: } +# LLVM-MULTI-RELOC-NEXT: ] + +# JSON-MULTI-RELOC: "CallGraph": [ +# JSON-MULTI-RELOC-NEXT: { +# JSON-MULTI-RELOC-NEXT: "Function": { +# JSON-MULTI-RELOC-NEXT: "Name": "foo", +# JSON-MULTI-RELOC-NEXT: "Version": 0, +# JSON-MULTI-RELOC-NEXT: "IsIndirectTarget": true, +# JSON-MULTI-RELOC-NEXT: "TypeID": 17896295136807035042, +# JSON-MULTI-RELOC-NEXT: "NumDirectCallees": 0, +# JSON-MULTI-RELOC-NEXT: "DirectCallees": [], +# JSON-MULTI-RELOC-NEXT: "NumIndirectTargetTypeIDs": 0, +# JSON-MULTI-RELOC-NEXT: "IndirectTypeIDs": [] +# JSON-MULTI-RELOC-NEXT: } +# JSON-MULTI-RELOC-NEXT: }, +# JSON-MULTI-RELOC-NEXT: { +# JSON-MULTI-RELOC-NEXT: "Function": { +# JSON-MULTI-RELOC-NEXT: "Name": "bar", +# JSON-MULTI-RELOC-NEXT: "Version": 0, +# JSON-MULTI-RELOC-NEXT: "IsIndirectTarget": true, +# JSON-MULTI-RELOC-NEXT: "TypeID": 17896295136807035042, +# JSON-MULTI-RELOC-NEXT: "NumDirectCallees": 0, +# JSON-MULTI-RELOC-NEXT: "DirectCallees": [], +# JSON-MULTI-RELOC-NEXT: "NumIndirectTargetTypeIDs": 0, +# JSON-MULTI-RELOC-NEXT: "IndirectTypeIDs": [] +# JSON-MULTI-RELOC-NEXT: } +# JSON-MULTI-RELOC-NEXT: } +# JSON-MULTI-RELOC-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .llvm.callgraph.1 + Type: SHT_LLVM_CALL_GRAPH + Link: .text + ContentArray: [ + ## --- foo --- + 0x00, ## Format Version + 0x01, ## Flags + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ## Address of foo + 0xA2, 0x20, 0xEF, 0xB8, 0x9B, 0x69, 0x5C, 0xF8 ## TypeID of foo + ] + - Name: .rela.llvm.callgraph.1 + Type: SHT_RELA + Info: .llvm.callgraph.1 + Relocations: + - Offset: 0x2 + Symbol: foo + Type: R_X86_64_64 + - Name: .llvm.callgraph.2 + Type: SHT_LLVM_CALL_GRAPH + Link: .text + ContentArray: [ + ## --- bar --- + 0x00, ## Format Version + 0x01, ## Flags + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ## Address of bar + 0xA2, 0x20, 0xEF, 0xB8, 0x9B, 0x69, 0x5C, 0xF8 ## TypeID of bar + ] + - Name: .rela.llvm.callgraph.2 + Type: SHT_RELA + Info: .llvm.callgraph.2 + Relocations: + - Offset: 0x2 + Symbol: bar + Type: R_X86_64_64 +Symbols: + - Name: foo + - Name: bar +... + +## Check handling of multiple call graph sections in a non-relocatable object file. +# RUN: yaml2obj --docnum=6 %s -o %t6 +# RUN: llvm-readobj --elf-output-style=GNU --call-graph-info %t6 2>&1 | count 0 +# RUN: llvm-readobj --elf-output-style=LLVM --call-graph-info %t6 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --check-prefix=LLVM-MULTI-NONRELOC -DFILE=%t6 +# RUN: llvm-readobj --elf-output-style=JSON --pretty-print --call-graph-info %t6 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --check-prefix=JSON-MULTI-NONRELOC -DFILE=%t6 + +# LLVM-MULTI-NONRELOC: CallGraph [ +# LLVM-MULTI-NONRELOC-NEXT: Function { +# LLVM-MULTI-NONRELOC-NEXT: Names: [foo] +# LLVM-MULTI-NONRELOC-NEXT: Address: 0x1790 +# LLVM-MULTI-NONRELOC-NEXT: Version: 0 +# LLVM-MULTI-NONRELOC-NEXT: IsIndirectTarget: Yes +# LLVM-MULTI-NONRELOC-NEXT: TypeID: 0x3ECBEEF531F74424 +# LLVM-MULTI-NONRELOC-NEXT: NumDirectCallees: 0 +# LLVM-MULTI-NONRELOC-NEXT: DirectCallees [ +# LLVM-MULTI-NONRELOC-NEXT: ] +# LLVM-MULTI-NONRELOC-NEXT: NumIndirectTargetTypeIDs: 0 +# LLVM-MULTI-NONRELOC-NEXT: IndirectTypeIDs: [] +# LLVM-MULTI-NONRELOC-NEXT: } +# LLVM-MULTI-NONRELOC-NEXT: Function { +# LLVM-MULTI-NONRELOC-NEXT: Names: [bar] +# LLVM-MULTI-NONRELOC-NEXT: Address: 0x17A0 +# LLVM-MULTI-NONRELOC-NEXT: Version: 0 +# LLVM-MULTI-NONRELOC-NEXT: IsIndirectTarget: Yes +# LLVM-MULTI-NONRELOC-NEXT: TypeID: 0x3ECBEEF531F74424 +# LLVM-MULTI-NONRELOC-NEXT: NumDirectCallees: 0 +# LLVM-MULTI-NONRELOC-NEXT: DirectCallees [ +# LLVM-MULTI-NONRELOC-NEXT: ] +# LLVM-MULTI-NONRELOC-NEXT: NumIndirectTargetTypeIDs: 0 +# LLVM-MULTI-NONRELOC-NEXT: IndirectTypeIDs: [] +# LLVM-MULTI-NONRELOC-NEXT: } +# LLVM-MULTI-NONRELOC-NEXT: ] + +# JSON-MULTI-NONRELOC: "CallGraph": [ +# JSON-MULTI-NONRELOC-NEXT: { +# JSON-MULTI-NONRELOC-NEXT: "Function": { +# JSON-MULTI-NONRELOC-NEXT: "Names": [ +# JSON-MULTI-NONRELOC-NEXT: "foo" +# JSON-MULTI-NONRELOC-NEXT: ], +# JSON-MULTI-NONRELOC-NEXT: "Address": 6032, +# JSON-MULTI-NONRELOC-NEXT: "Version": 0, +# JSON-MULTI-NONRELOC-NEXT: "IsIndirectTarget": true, +# JSON-MULTI-NONRELOC-NEXT: "TypeID": 4524972987496481828, +# JSON-MULTI-NONRELOC-NEXT: "NumDirectCallees": 0, +# JSON-MULTI-NONRELOC-NEXT: "DirectCallees": [], +# JSON-MULTI-NONRELOC-NEXT: "NumIndirectTargetTypeIDs": 0, +# JSON-MULTI-NONRELOC-NEXT: "IndirectTypeIDs": [] +# JSON-MULTI-NONRELOC-NEXT: } +# JSON-MULTI-NONRELOC-NEXT: }, +# JSON-MULTI-NONRELOC-NEXT: { +# JSON-MULTI-NONRELOC-NEXT: "Function": { +# JSON-MULTI-NONRELOC-NEXT: "Names": [ +# JSON-MULTI-NONRELOC-NEXT: "bar" +# JSON-MULTI-NONRELOC-NEXT: ], +# JSON-MULTI-NONRELOC-NEXT: "Address": 6048, +# JSON-MULTI-NONRELOC-NEXT: "Version": 0, +# JSON-MULTI-NONRELOC-NEXT: "IsIndirectTarget": true, +# JSON-MULTI-NONRELOC-NEXT: "TypeID": 4524972987496481828, +# JSON-MULTI-NONRELOC-NEXT: "NumDirectCallees": 0, +# JSON-MULTI-NONRELOC-NEXT: "DirectCallees": [], +# JSON-MULTI-NONRELOC-NEXT: "NumIndirectTargetTypeIDs": 0, +# JSON-MULTI-NONRELOC-NEXT: "IndirectTypeIDs": [] +# JSON-MULTI-NONRELOC-NEXT: } +# JSON-MULTI-NONRELOC-NEXT: } +# JSON-MULTI-NONRELOC-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .text + Type: SHT_PROGBITS + - Type: SHT_LLVM_CALL_GRAPH + Link: .text + ContentArray: [ + ## --- foo --- + 0x00, ## Version + 0x01, ## Flags + 0x90, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ## FunctionEntryPC: 0x1790 + 0x24, 0x44, 0xF7, 0x31, 0xF5, 0xEE, 0xCB, 0x3E, ## FunctionTypeID: 0x3ECBEEF531F74424 + ] + - Type: SHT_LLVM_CALL_GRAPH + Link: .text + ContentArray: [ + ## --- bar --- + 0x00, ## Version + 0x01, ## Flags + 0xA0, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ## FunctionEntryPC: 0x17A0 + 0x24, 0x44, 0xF7, 0x31, 0xF5, 0xEE, 0xCB, 0x3E, ## FunctionTypeID: 0x3ECBEEF531F74424 + ] +Symbols: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x1790 + - Name: bar + Type: STT_FUNC + Section: .text + Value: 0x17A0 +... diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 1ceef6f38dc38..bdc3062f3a6b6 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -450,15 +450,12 @@ template <typename ELFT> class ELFDumper : public ObjDumper { const SFrameParser<ELFT::Endianness> &Parser, const typename SFrameParser<ELFT::Endianness>::FDERange::iterator FDE, ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab); - // Callgraph - Main data structure to maintain per function callgraph - // information. - SmallVector<FunctionCallgraphInfo, 16> FuncCGInfos; - - // Read the SHT_LLVM_CALL_GRAPH type section and process its contents to + // Read the SHT_LLVM_CALL_GRAPH type sections and process their contents to // populate call graph related data structures which will be used to dump call - // graph info. Returns false if there is no SHT_LLVM_CALL_GRAPH type section - // in the input file. - bool processCallGraphSection(const Elf_Shdr *CGSection); + // graph info. Returns an empty vector if there are no such sections or if + // parsing fails. + SmallVector<FunctionCallgraphInfo, 16> + processCallGraphSection(const Elf_Shdr *CGSection); std::string getProgramHeadersNumString(); @@ -5337,7 +5334,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() { } template <class ELFT> -bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { +SmallVector<FunctionCallgraphInfo, 16> +ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { + SmallVector<FunctionCallgraphInfo, 16> FuncCGInfos; ArrayRef<uint8_t> Contents = cantFail(Obj.getSectionContents(*CGSection)); DataExtractor Data(Contents, Obj.isLE()); DataExtractor::Cursor C(0); @@ -5351,7 +5350,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { std::to_string(FormatVersionNumber) + "] in SHT_LLVM_CALL_GRAPH type section"), FileName); - return false; + return {}; } uint8_t FlagsVal = Data.getU8(C); @@ -5360,7 +5359,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { createError("failed while reading call graph info's Flags: " + toString(C.takeError())), FileName); - return false; + return {}; } callgraph::Flags CGFlags = static_cast<callgraph::Flags>(FlagsVal); constexpr callgraph::Flags ValidFlags = callgraph::IsIndirectTarget | @@ -5371,7 +5370,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { reportWarning(createError("unsupported Flags value [" + std::to_string(FlagsVal) + "] "), FileName); - return false; + return {}; } uint64_t FuncAddrOffset = C.tell(); @@ -5383,7 +5382,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { "failed while reading call graph info function entry PC: " + toString(C.takeError())), FileName); - return false; + return {}; } bool IsETREL = this->Obj.getHeader().e_type == ELF::ET_REL; @@ -5399,7 +5398,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { reportWarning(createError("failed while reading function type ID: " + toString(C.takeError())), FileName); - return false; + return {}; } CGInfo.FunctionTypeID = TypeID; if (IsIndirectTarget && TypeID == 0) @@ -5413,7 +5412,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { createError("failed while reading number of direct callees: " + toString(C.takeError())), FileName); - return false; + return {}; } // Read unique direct callees and populate FuncCGInfos. for (uint64_t I = 0; I < NumDirectCallees; ++I) { @@ -5424,7 +5423,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { reportWarning(createError("failed while reading direct callee: " + toString(C.takeError())), FileName); - return false; + return {}; } CGInfo.DirectCallees.insert((IsETREL ? CalleeOffset : Callee)); } @@ -5438,7 +5437,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { "failed while reading number of indirect target type IDs: " + toString(C.takeError())), FileName); - return false; + return {}; } // Read unique indirect target type IDs and populate FuncCGInfos. for (uint64_t I = 0; I < NumIndirectTargetTypeIDs; ++I) { @@ -5448,7 +5447,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { createError("failed while reading indirect target type ID: " + toString(C.takeError())), FileName); - return false; + return {}; } CGInfo.IndirectTypeIDs.insert(TargetType); } @@ -5460,7 +5459,7 @@ bool ELFDumper<ELFT>::processCallGraphSection(const Elf_Shdr *CGSection) { reportUniqueWarning( "SHT_LLVM_CALL_GRAPH type section has unknown type ID for " + Twine(UnknownCount) + " indirect targets"); - return true; + return FuncCGInfos; } template <class ELFT> @@ -8338,101 +8337,107 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() { return; } - // Process and print the first SHT_LLVM_CALL_GRAPH type section found. - if (!this->processCallGraphSection(MapOrErr->begin()->first) || - this->FuncCGInfos.empty()) - return; + std::unique_ptr<ListScope> CGI; + for (const auto &CGMapEntry : *MapOrErr) { + const Elf_Shdr *CGSection = CGMapEntry.first; + const Elf_Shdr *CGRelSection = CGMapEntry.second; - std::vector<Relocation<ELFT>> Relocations; - const Elf_Shdr *RelocSymTab = nullptr; - if (this->Obj.getHeader().e_type == ELF::ET_REL) { - const Elf_Shdr *CGRelSection = MapOrErr->front().second; - if (CGRelSection) { - Expected<const typename ELFT::Shdr *> SymtabOrErr = - this->Obj.getSection(CGRelSection->sh_link); - if (!SymtabOrErr) { - reportWarning(createError("invalid section linked to " + - this->describe(*CGRelSection) + ": " + - toString(SymtabOrErr.takeError())), - this->FileName); - return; + SmallVector<FunctionCallgraphInfo, 16> FuncCGInfos = + this->processCallGraphSection(CGSection); + if (FuncCGInfos.empty()) + continue; + + std::vector<Relocation<ELFT>> Relocations; + const Elf_Shdr *RelocSymTab = nullptr; + if (this->Obj.getHeader().e_type == ELF::ET_REL) { + if (CGRelSection) { + Expected<const typename ELFT::Shdr *> SymtabOrErr = + this->Obj.getSection(CGRelSection->sh_link); + if (!SymtabOrErr) { + reportWarning(createError("invalid section linked to " + + this->describe(*CGRelSection) + ": " + + toString(SymtabOrErr.takeError())), + this->FileName); + return; + } + RelocSymTab = *SymtabOrErr; + this->forEachRelocationDo(*CGRelSection, [&](const auto &R, ...) { + Relocations.push_back(R); + }); + llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) { + return LHS.Offset < RHS.Offset; + }); } - RelocSymTab = *SymtabOrErr; - this->forEachRelocationDo( - *CGRelSection, [&](const auto &R, ...) { Relocations.push_back(R); }); - llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) { - return LHS.Offset < RHS.Offset; - }); } - } - - auto GetFunctionNames = [&](uint64_t FuncAddr) { - SmallVector<uint32_t> FuncSymIndexes = - this->getSymbolIndexesForFunctionAddress(FuncAddr, std::nullopt); - SmallVector<std::string> FuncSymNames; - FuncSymNames.reserve(FuncSymIndexes.size()); - for (uint32_t Index : FuncSymIndexes) - FuncSymNames.push_back(this->getStaticSymbolName(Index)); - return FuncSymNames; - }; - auto PrintNonRelocatableFuncSymbol = [&](uint64_t FuncEntryPC) { - SmallVector<std::string> FuncSymNames = GetFunctionNames(FuncEntryPC); - if (!FuncSymNames.empty()) - W.printList("Names", FuncSymNames); - W.printHex("Address", FuncEntryPC); - }; + auto GetFunctionNames = [&](uint64_t FuncAddr) { + SmallVector<uint32_t> FuncSymIndexes = + this->getSymbolIndexesForFunctionAddress(FuncAddr, std::nullopt); + SmallVector<std::string> FuncSymNames; + FuncSymNames.reserve(FuncSymIndexes.size()); + for (uint32_t Index : FuncSymIndexes) + FuncSymNames.push_back(this->getStaticSymbolName(Index)); + return FuncSymNames; + }; - auto PrintRelocatableFuncSymbol = [&](uint64_t RelocOffset) { - auto R = llvm::find_if(Relocations, [&](const Relocation<ELFT> &R) { - return R.Offset == RelocOffset; - }); - if (R == Relocations.end()) { - this->reportUniqueWarning("missing relocation for symbol at offset " + - Twine(RelocOffset)); - return; - } - Expected<RelSymbol<ELFT>> RelSymOrErr = - this->getRelocationTarget(*R, RelocSymTab); - if (!RelSymOrErr) { - this->reportUniqueWarning(RelSymOrErr.takeError()); - return; - } - W.printString("Name", RelSymOrErr->Name); - }; + auto PrintNonRelocatableFuncSymbol = [&](uint64_t FuncEntryPC) { + SmallVector<std::string> FuncSymNames = GetFunctionNames(FuncEntryPC); + if (!FuncSymNames.empty()) + W.printList("Names", FuncSymNames); + W.printHex("Address", FuncEntryPC); + }; - auto PrintFunc = [&](uint64_t FuncPC) { - uint64_t FuncEntryPC = FuncPC; - // In ARM thumb mode the LSB of the function pointer is set to 1. Since this - // detail is unncessary in call graph reconstruction, we are clearing this - // bit to facilate tooling. - if (this->Obj.getHeader().e_machine == ELF::EM_ARM) - FuncEntryPC = FuncPC & ~1; - if (this->Obj.getHeader().e_type == ELF::ET_REL) - PrintRelocatableFuncSymbol(FuncEntryPC); - else - PrintNonRelocatableFuncSymbol(FuncEntryPC); - }; + auto PrintRelocatableFuncSymbol = [&](uint64_t RelocOffset) { + auto R = llvm::find_if(Relocations, [&](const Relocation<ELFT> &R) { + return R.Offset == RelocOffset; + }); + if (R == Relocations.end()) { + this->reportUniqueWarning("missing relocation for symbol at offset " + + Twine(RelocOffset)); + return; + } + Expected<RelSymbol<ELFT>> RelSymOrErr = + this->getRelocationTarget(*R, RelocSymTab); + if (!RelSymOrErr) { + this->reportUniqueWarning(RelSymOrErr.takeError()); + return; + } + W.printString("Name", RelSymOrErr->Name); + }; - ListScope CGI(W, "CallGraph"); - for (const FunctionCallgraphInfo &CGInfo : this->FuncCGInfos) { - DictScope D(W, "Function"); - PrintFunc(CGInfo.FunctionAddress); - W.printNumber("Version", CGInfo.FormatVersionNumber); - W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget); - W.printHex("TypeID", CGInfo.FunctionTypeID); - W.printNumber("NumDirectCallees", CGInfo.DirectCallees.size()); - { - ListScope DCs(W, "DirectCallees"); - for (uint64_t CalleePC : CGInfo.DirectCallees) { - DictScope D(W); - PrintFunc(CalleePC); + auto PrintFunc = [&](uint64_t FuncPC) { + uint64_t FuncEntryPC = FuncPC; + // In ARM thumb mode the LSB of the function pointer is set to 1. Since + // this detail is unncessary in call graph reconstruction, we are clearing + // this bit to facilate tooling. + if (this->Obj.getHeader().e_machine == ELF::EM_ARM) + FuncEntryPC = FuncPC & ~1; + if (this->Obj.getHeader().e_type == ELF::ET_REL) + PrintRelocatableFuncSymbol(FuncEntryPC); + else + PrintNonRelocatableFuncSymbol(FuncEntryPC); + }; + if (!CGI) + CGI = std::make_unique<ListScope>(W, "CallGraph"); + for (const FunctionCallgraphInfo &CGInfo : FuncCGInfos) { + DictScope D(W, "Function"); + PrintFunc(CGInfo.FunctionAddress); + W.printNumber("Version", CGInfo.FormatVersionNumber); + W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget); + W.printHex("TypeID", CGInfo.FunctionTypeID); + W.printNumber("NumDirectCallees", CGInfo.DirectCallees.size()); + { + ListScope DCs(W, "DirectCallees"); + for (uint64_t CalleePC : CGInfo.DirectCallees) { + DictScope D(W); + PrintFunc(CalleePC); + } } + W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size()); + SmallVector<uint64_t, 4> IndirectTypeIDsList( + CGInfo.IndirectTypeIDs.begin(), CGInfo.IndirectTypeIDs.end()); + W.printHexList("IndirectTypeIDs", ArrayRef(IndirectTypeIDsList)); } - W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size()); - SmallVector<uint64_t, 4> IndirectTypeIDsList(CGInfo.IndirectTypeIDs.begin(), - CGInfo.IndirectTypeIDs.end()); - W.printHexList("IndirectTypeIDs", ArrayRef(IndirectTypeIDsList)); } } _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
