[llvm-branch-commits] [llvm] 6f033fe - [llvm-readobj] Print all callgraph sections (#198929)
Douglas Yung via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jul 22 07:03:33 PDT 2026
Author: Prabhu Rajasekaran
Date: 2026-07-22T14:03:18Z
New Revision: 6f033fece5f5ae501698b9f49f3bca3f1fa19b38
URL: https://github.com/llvm/llvm-project/commit/6f033fece5f5ae501698b9f49f3bca3f1fa19b38
DIFF: https://github.com/llvm/llvm-project/commit/6f033fece5f5ae501698b9f49f3bca3f1fa19b38.diff
LOG: [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)
Added:
Modified:
llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-malformed.test
llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
llvm/tools/llvm-readobj/ELFDumper.cpp
Removed:
################################################################################
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));
}
}
More information about the llvm-branch-commits
mailing list