[llvm] [llvm-readobj] Dump callgraph section info for ELF (PR #157499)

Prabhu Rajasekaran via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 31 13:30:27 PDT 2025


================
@@ -8094,6 +8413,82 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
   }
 }
 
+template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
+  if (!this->processCallGraphSection() || this->FuncCGInfos.empty())
+    return;
+
+  std::vector<Relocation<ELFT>> Relocations;
+  const Elf_Shdr *RelocSymTab = nullptr;
+  this->getCallGraphRelocations(Relocations, RelocSymTab);
+
+  auto PrintFunc = [](uint64_t FuncEntryPC, std::string FuncSymName,
+                      ScopedPrinter &W) {
+    if (!FuncSymName.empty())
+      W.printString("Name", FuncSymName);
+    W.printHex("Address", FuncEntryPC);
+  };
+
+  auto PrintReloc =
+      [&](uint64_t FuncEntryPC,
+          typename std::vector<Relocation<ELFT>>::iterator &Reloc) {
+        if (Reloc == Relocations.end()) {
+          W.printHex("Offset", FuncEntryPC);
+          return;
+        }
+        Expected<RelSymbol<ELFT>> RelSym =
+            this->getRelocationTarget(*Reloc, RelocSymTab);
+        if (!RelSym) {
+          this->reportUniqueWarning(RelSym.takeError());
+          W.printHex("Offset", FuncEntryPC);
+          return;
+        }
+        DictScope DCs(W, "Entry");
+        SmallString<32> RelocName;
+        this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
+        W.printString("Relocation", RelocName);
+        W.printString("Symbol", RelSym->Name);
+        if (Reloc->Addend)
+          W.printHex("Addend", (uintX_t)*Reloc->Addend);
+      };
+
+  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
+    if (this->Obj.getHeader().e_type != ELF::ET_REL) {
+      std::string FuncSymName = this->getFunctionNames(FuncEntryPC);
+      PrintFunc(FuncEntryPC, FuncSymName, W);
+      return;
+    }
+    auto Reloc = llvm::lower_bound(
+        Relocations, FuncEntryPC,
+        [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+    PrintReloc(FuncEntryPC, Reloc);
+  };
+
+  ListScope CGI(W, "callgraph_info");
+  for (const auto &CGInfo : this->FuncCGInfos) {
+    DictScope D(W, "Function");
+    PrintFunctionInfo(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 (auto CalleePC : CGInfo.DirectCallees) {
+        DictScope D(W, "Entry");
----------------
Prabhuk wrote:

Each "Entry" here has a name field and an offset/address field.

```
DirectCallees [
  Entry {
    Name: foo
    Address: 0x1790
  }
  Entry {
    Name: bar
    Address: 0x17A0
  }
  Entry {
    Name: baz
    Address: 0x17B0
  }
]
```
Without "Entry" DictScope, it will become hard to parse this as a list of objects for any scripting that will consume this info:

```
DirectCallees [
      Name: foo
      Address: 0x1790
      Name: bar
      Address: 0x17A0
      Name: baz
      Address: 0x17B0
]
```


https://github.com/llvm/llvm-project/pull/157499


More information about the llvm-commits mailing list