[llvm-branch-commits] [llvm] release/23.x: [llvm-readobj] Print all callgraph sections (#198929) (PR #211118)

Douglas Yung via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jul 22 07:03:33 PDT 2026


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 <prabhukr at google.com>
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));
   }
 }
 



More information about the llvm-branch-commits mailing list