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

Prabhu Rajasekaran via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 4 11:46:41 PST 2025


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

>From ac4b7bcadfdbb7eea2ac8b64cc5941e2fae10da2 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 5 Sep 2025 17:12:01 -0700
Subject: [PATCH 01/44] [llvm-readobj] Dump callgraph section info for ELF

Introduce a new flag `--call-graph-info` which outputs callgraph ELF
section information to the console as a text output or as JSON output.
---
 llvm/tools/llvm-readobj/ELFDumper.cpp    | 130 +++++++++++++++++++++++
 llvm/tools/llvm-readobj/ObjDumper.h      |   1 +
 llvm/tools/llvm-readobj/Opts.td          |   1 +
 llvm/tools/llvm-readobj/llvm-readobj.cpp |   4 +
 4 files changed, 136 insertions(+)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 30102abdc5c5b..a4fa60fd4cb2f 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -619,6 +619,7 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
   void printVersionDefinitionSection(const Elf_Shdr *Sec) override;
   void printVersionDependencySection(const Elf_Shdr *Sec) override;
   void printCGProfile() override;
+  void printCallGraphInfo() override;
   void printBBAddrMaps(bool PrettyPGOAnalysis) override;
   void printAddrsig() override;
   void printNotes() override;
@@ -730,6 +731,7 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
   void printVersionDefinitionSection(const Elf_Shdr *Sec) override;
   void printVersionDependencySection(const Elf_Shdr *Sec) override;
   void printCGProfile() override;
+  void printCallGraphInfo() override;
   void printBBAddrMaps(bool PrettyPGOAnalysis) override;
   void printAddrsig() override;
   void printNotes() override;
@@ -5260,6 +5262,11 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
   OS << "GNUStyle::printCGProfile not implemented\n";
 }
 
+template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
+  OS << "GNUStyle::printCallGraphInfo not implemented\n";
+}
+
+
 template <class ELFT>
 void GNUELFDumper<ELFT>::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) {
   OS << "GNUStyle::printBBAddrMaps not implemented\n";
@@ -8091,6 +8098,129 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
   }
 }
 
+enum class FunctionKind : uint64_t {
+  // Function cannot be target to indirect calls.
+  NOT_INDIRECT_TARGET = 0,
+  // Function may be target to indirect calls but its type id is unknown.
+  INDIRECT_TARGET_UNKNOWN_TID = 1,
+  // Function may be target to indirect calls and its type id is known.
+  INDIRECT_TARGET_KNOWN_TID = 2,
+
+  // Available in the binary but not listed in the call graph section.
+  NOT_LISTED = 3,
+};
+
+struct FunctionInfo {
+  FunctionKind Kind;
+  struct DirectCallSite {
+    uint64_t CallSite;
+    uint64_t Callee;
+    DirectCallSite(uint64_t CallSite, uint64_t Callee)
+        : CallSite(CallSite), Callee(Callee) {}
+  };
+  SmallVector<DirectCallSite> DirectCallSites;
+  SmallVector<uint64_t> IndirectCallSites;
+};
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
+  // Get the .callgraph section.
+  StringRef CallGraphSectionName(".callgraph");
+  std::optional<object::SectionRef> CallGraphSection;
+  for (auto Sec : this->ObjF.sections()) {
+    StringRef Name;
+    if (Expected<StringRef> NameOrErr = Sec.getName())
+      Name = *NameOrErr;
+    else
+      consumeError(NameOrErr.takeError());
+
+    if (Name == CallGraphSectionName) {
+      CallGraphSection = Sec;
+      break;
+    }
+  }
+  if (!CallGraphSection) {
+    this->reportUniqueWarning("there is no .callgraph section");
+    return;
+  }
+
+  // Map type id to indirect call sites.
+  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirCallSites;
+  // Map type id to indirect targets.
+  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
+  // Map function entry pc to function info.
+  MapVector<uint64_t, FunctionInfo> FuncInfo;
+  // Instructions that are not indirect calls but have a type id are ignored.
+  // uint64_t IgnoredICallIdCount = 0;
+  // Number of valid indirect calls with type ids.
+  // uint64_t ICallWithTypeIdCount = 0;
+  if (CallGraphSection) {
+    StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
+    // TODO: some entries are written in pointer size. are they always 64-bit?
+    if (CGSecContents.size() % sizeof(uint64_t))
+      this->reportUniqueWarning(
+                  "Malformed .callgraph section. Unexpected size.");
+
+    size_t Size = CGSecContents.size() / sizeof(uint64_t);
+    auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
+    const auto *const End = It + Size;
+
+    auto CGHasNext = [&]() { return It < End; };
+    auto CGNext = [&]() -> uint64_t {
+      if (!CGHasNext())
+        this->reportUniqueWarning(
+                    "Malformed .callgraph section. Parsing error.");
+      return *It++;
+    };
+
+    // Parse the content
+    while (CGHasNext()) {
+      // Format version number.
+      uint64_t FormatVersionNumber = CGNext();
+      if (FormatVersionNumber != 0)
+        this->reportUniqueWarning(
+                    "Unknown format version in .callgraph section.");
+      // Function entry pc.
+      uint64_t FuncEntryPc = CGNext();
+      // Function kind.
+      uint64_t Kind = CGNext();
+      switch (Kind) {
+      case 0: // not an indirect target
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
+        break;
+      case 1: // indirect target with unknown type id
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+        break;
+      case 2: // indirect target with known type id
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
+        TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
+        break;
+      default:
+        this->reportUniqueWarning(
+                    "Unknown function kind in .callgraph section.");
+      }
+
+      // Read indirect call sites info.
+      uint64_t IndirectCallSiteCount = CGNext();
+      for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
+        uint64_t TypeId = CGNext();
+        uint64_t CallSitePc = CGNext();
+        TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
+        FuncInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
+      }
+
+      // Read direct call sites info.
+      uint64_t DirectCallSiteCount = CGNext();
+      for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
+        uint64_t CallSitePc = CGNext();
+        uint64_t CalleePc = CGNext();
+        FuncInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
+                                                           CalleePc);
+      }
+    }
+  }
+}
+
+
 template <class ELFT>
 void LLVMELFDumper<ELFT>::printBBAddrMaps(bool PrettyPGOAnalysis) {
   bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL;
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index a654078a770ff..c704a24290b30 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -129,6 +129,7 @@ class ObjDumper {
   virtual void printGroupSections() {}
   virtual void printHashHistograms() {}
   virtual void printCGProfile() {}
+  virtual void printCallGraphInfo() {}
   // If PrettyPGOAnalysis is true, prints BFI as relative frequency and BPI as
   // percentage. Otherwise raw values are displayed.
   virtual void printBBAddrMaps(bool PrettyPGOAnalysis) {}
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index 711522c4acb14..209881d67eb0c 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -20,6 +20,7 @@ def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --
 def arch_specific : FF<"arch-specific", "Display architecture-specific information">;
 def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">;
 def pretty_pgo_analysis_map : FF<"pretty-pgo-analysis-map", "Display PGO analysis values with formatting rather than raw numbers">;
+def call_graph_info : FF<"call-graph-info", "Display call graph information">;
 def cg_profile : FF<"cg-profile", "Display call graph profile section">;
 def decompress : FF<"decompress", "Dump decompressed section content when used with -x or -p">;
 defm demangle : BB<"demangle", "Demangle symbol names", "Do not demangle symbol names (default)">;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 2b34761b2cc6c..cb22f95eedbac 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -97,6 +97,7 @@ static bool Addrsig;
 static bool All;
 static bool ArchSpecificInfo;
 static bool BBAddrMap;
+static bool CallGraphInfo;
 static bool PrettyPGOAnalysisMap;
 bool ExpandRelocs;
 static bool CGProfile;
@@ -216,6 +217,7 @@ static void parseOptions(const opt::InputArgList &Args) {
   opts::All = Args.hasArg(OPT_all);
   opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific);
   opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map);
+  opts::CallGraphInfo = Args.hasArg(OPT_call_graph_info);
   opts::PrettyPGOAnalysisMap = Args.hasArg(OPT_pretty_pgo_analysis_map);
   if (opts::PrettyPGOAnalysisMap && !opts::BBAddrMap)
     WithColor::warning(errs(), ToolName)
@@ -474,6 +476,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
       Dumper->printHashHistograms();
     if (opts::CGProfile)
       Dumper->printCGProfile();
+    if (opts::CallGraphInfo)
+      Dumper->printCallGraphInfo();
     if (opts::BBAddrMap)
       Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap);
     if (opts::Addrsig)

>From 31c4d2bbcbc036b3ef19810999c8ea8253bb1084 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 8 Sep 2025 09:05:21 -0700
Subject: [PATCH 02/44] Format code

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index a4fa60fd4cb2f..a1b1733b10989 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5266,7 +5266,6 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   OS << "GNUStyle::printCallGraphInfo not implemented\n";
 }
 
-
 template <class ELFT>
 void GNUELFDumper<ELFT>::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) {
   OS << "GNUStyle::printBBAddrMaps not implemented\n";
@@ -8158,7 +8157,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     // TODO: some entries are written in pointer size. are they always 64-bit?
     if (CGSecContents.size() % sizeof(uint64_t))
       this->reportUniqueWarning(
-                  "Malformed .callgraph section. Unexpected size.");
+          "Malformed .callgraph section. Unexpected size.");
 
     size_t Size = CGSecContents.size() / sizeof(uint64_t);
     auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
@@ -8168,7 +8167,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     auto CGNext = [&]() -> uint64_t {
       if (!CGHasNext())
         this->reportUniqueWarning(
-                    "Malformed .callgraph section. Parsing error.");
+            "Malformed .callgraph section. Parsing error.");
       return *It++;
     };
 
@@ -8178,7 +8177,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
       uint64_t FormatVersionNumber = CGNext();
       if (FormatVersionNumber != 0)
         this->reportUniqueWarning(
-                    "Unknown format version in .callgraph section.");
+            "Unknown format version in .callgraph section.");
       // Function entry pc.
       uint64_t FuncEntryPc = CGNext();
       // Function kind.
@@ -8196,7 +8195,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
         break;
       default:
         this->reportUniqueWarning(
-                    "Unknown function kind in .callgraph section.");
+            "Unknown function kind in .callgraph section.");
       }
 
       // Read indirect call sites info.
@@ -8220,7 +8219,6 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   }
 }
 
-
 template <class ELFT>
 void LLVMELFDumper<ELFT>::printBBAddrMaps(bool PrettyPGOAnalysis) {
   bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL;

>From 4d1a7c72c7782316aa77e7f3612c348c22b824b3 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 8 Sep 2025 09:16:45 -0700
Subject: [PATCH 03/44] Implement JSON output for printCallGraph()

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 418 ++++++++++++++++++--------
 1 file changed, 298 insertions(+), 120 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index a1b1733b10989..66f84c333470b 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -183,6 +183,31 @@ struct GroupSection {
   std::vector<GroupMember> Members;
 };
 
+// Callgraph section handling implementation.
+enum class FunctionKind : uint64_t {
+  // Function cannot be target to indirect calls.
+  NOT_INDIRECT_TARGET = 0,
+  // Function may be target to indirect calls but its type id is unknown.
+  INDIRECT_TARGET_UNKNOWN_TID = 1,
+  // Function may be target to indirect calls and its type id is known.
+  INDIRECT_TARGET_KNOWN_TID = 2,
+
+  // Available in the binary but not listed in the call graph section.
+  NOT_LISTED = 3,
+};
+
+struct FunctionInfo {
+  FunctionKind Kind;
+  struct DirectCallSite {
+    uint64_t CallSite;
+    uint64_t Callee;
+    DirectCallSite(uint64_t CallSite, uint64_t Callee)
+        : CallSite(CallSite), Callee(Callee) {}
+  };
+  SmallVector<DirectCallSite> DirectCallSites;
+  SmallVector<uint64_t> IndirectCallSites;
+};
+
 namespace {
 
 struct NoteType {
@@ -433,6 +458,14 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
       const typename SFrameParser<ELFT::Endianness>::FDERange::iterator FDE,
       ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab);
 
+  void callGraphInfoHelper();
+  // Map type id to indirect call sites.
+  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirCallSites;
+  // Map type id to indirect targets.
+  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
+  // Map function entry pc to function info.
+  MapVector<uint64_t, FunctionInfo> FuncInfo;
+
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
 };
@@ -812,6 +845,8 @@ template <typename ELFT> class JSONELFDumper : public LLVMELFDumper<ELFT> {
 
   void printDynamicTable() override;
 
+  void printCallGraphInfo() override;
+
 private:
   void printAuxillaryDynamicTableEntryInfo(const Elf_Dyn &Entry);
 
@@ -5262,8 +5297,189 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
   OS << "GNUStyle::printCGProfile not implemented\n";
 }
 
+template <class ELFT> void ELFDumper<ELFT>::callGraphInfoHelper() {
+  // Get the .callgraph section.
+  StringRef CallGraphSectionName(".callgraph");
+  std::optional<object::SectionRef> CallGraphSection;
+  for (auto Sec : this->ObjF.sections()) {
+    StringRef Name;
+    if (Expected<StringRef> NameOrErr = Sec.getName())
+      Name = *NameOrErr;
+    else
+      consumeError(NameOrErr.takeError());
+
+    if (Name == CallGraphSectionName) {
+      CallGraphSection = Sec;
+      break;
+    }
+  }
+  if (!CallGraphSection) {
+    this->reportUniqueWarning("there is no .callgraph section");
+    return;
+  }
+
+  // Instructions that are not indirect calls but have a type id are ignored.
+  // uint64_t IgnoredICallIdCount = 0;
+  // Number of valid indirect calls with type ids.
+  // uint64_t ICallWithTypeIdCount = 0;
+  if (CallGraphSection) {
+    StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
+    // TODO: some entries are written in pointer size. are they always 64-bit?
+    if (CGSecContents.size() % sizeof(uint64_t))
+      this->reportUniqueWarning(
+          "Malformed .callgraph section. Unexpected size.");
+
+    size_t Size = CGSecContents.size() / sizeof(uint64_t);
+    auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
+    const auto *const End = It + Size;
+
+    auto CGHasNext = [&]() { return It < End; };
+    auto CGNext = [&]() -> uint64_t {
+      if (!CGHasNext())
+        this->reportUniqueWarning(
+            "Malformed .callgraph section. Parsing error.");
+      return *It++;
+    };
+
+    // Parse the content
+    while (CGHasNext()) {
+      // Format version number.
+      uint64_t FormatVersionNumber = CGNext();
+      if (FormatVersionNumber != 0)
+        this->reportUniqueWarning(
+            "Unknown format version in .callgraph section.");
+      // Function entry pc.
+      uint64_t FuncEntryPc = CGNext();
+      // Function kind.
+      uint64_t Kind = CGNext();
+      switch (Kind) {
+      case 0: // not an indirect target
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
+        break;
+      case 1: // indirect target with unknown type id
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+        break;
+      case 2: // indirect target with known type id
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
+        TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
+        break;
+      default:
+        this->reportUniqueWarning(
+            "Unknown function kind in .callgraph section.");
+      }
+
+      // Read indirect call sites info.
+      uint64_t IndirectCallSiteCount = CGNext();
+      for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
+        uint64_t TypeId = CGNext();
+        uint64_t CallSitePc = CGNext();
+        TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
+        FuncInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
+      }
+
+      // Read direct call sites info.
+      uint64_t DirectCallSiteCount = CGNext();
+      for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
+        uint64_t CallSitePc = CGNext();
+        uint64_t CalleePc = CGNext();
+        FuncInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
+                                                           CalleePc);
+      }
+    }
+  }
+  llvm::sort(FuncInfo,
+             [](const auto &A, const auto &B) { return A.first < B.first; });
+}
+
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
-  OS << "GNUStyle::printCallGraphInfo not implemented\n";
+  this->callGraphInfoHelper();
+  uint64_t NotListedCount = 0;
+  uint64_t UnknownCount = 0;
+
+  for (const auto &El : this->FuncInfo) {
+    NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
+    UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+  }
+  if (NotListedCount) // TODO(prabhuk): include object file name to the warning
+    this->reportUniqueWarning(
+        "callgraph section does not have information for " +
+        std::to_string(NotListedCount) + " functions");
+  if (UnknownCount) // TODO(prabhuk): include object file name to the warning
+    this->reportUniqueWarning("callgraph section has unknown type id for " +
+                              std::to_string(UnknownCount) +
+                              " indirect targets");
+
+  // Print indirect targets
+  OS << "\nINDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])";
+
+  // Print indirect targets with unknown type.
+  // For completeness, functions for which the call graph section does not
+  // provide information are included.
+  if (NotListedCount || UnknownCount) {
+    OS << "\nUNKNOWN";
+    for (const auto &El : this->FuncInfo) {
+      uint64_t FuncEntryPc = El.first;
+      FunctionKind FuncKind = El.second.Kind;
+      if (FuncKind == FunctionKind::NOT_LISTED ||
+          FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
+        OS << " " << format("%lx", FuncEntryPc);
+    }
+  }
+
+  // Print indirect targets to type id mapping.
+  for (const auto &El : this->TypeIdToIndirTargets) {
+    uint64_t TypeId = El.first;
+    OS << "\n" << format("%lx", TypeId);
+    for (uint64_t IndirTargetPc : El.second)
+      OS << " " << format("%lx", IndirTargetPc);
+  }
+
+  // Print indirect calls to type id mapping. Any indirect call without a
+  // type id can be deduced by comparing this list to indirect call sites
+  // list.
+  OS << "\n\nINDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])";
+  for (const auto &El : this->TypeIdToIndirCallSites) {
+    uint64_t TypeId = El.first;
+    OS << "\n" << format("%lx", TypeId);
+    for (uint64_t IndirCallSitePc : El.second)
+      OS << " " << format("%lx", IndirCallSitePc);
+  }
+
+  // Print function entry to indirect call site addresses mapping from disasm.
+  OS << "\n\nINDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])";
+  for (const auto &El : this->FuncInfo) {
+    auto CallerPc = El.first;
+    auto FuncIndirCallSites = El.second.IndirectCallSites;
+    if (!FuncIndirCallSites.empty()) {
+      OS << "\n" << format("%lx", CallerPc);
+      for (auto IndirCallSitePc : FuncIndirCallSites)
+        OS << " " << format("%lx", IndirCallSitePc);
+    }
+  }
+
+  // Print function entry to direct call site and target function entry
+  // addresses mapping from disasm.
+  OS << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])";
+  for (const auto &El : this->FuncInfo) {
+    auto CallerPc = El.first;
+    auto FuncDirCallSites = El.second.DirectCallSites;
+    if (!FuncDirCallSites.empty()) {
+      OS << "\n" << format("%lx", CallerPc);
+      for (const FunctionInfo::DirectCallSite &DCS : FuncDirCallSites) {
+        OS << " " << format("%lx", DCS.CallSite) << " "
+           << format("%lx", DCS.Callee);
+      }
+    }
+  }
+
+  // Print function entry pc to function name mapping.
+  // OS << "\n\nFUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)";
+  // for (const auto &El : FuncInfo) {
+  //   uint64_t EntryPc = El.first;
+  //   const auto &Name = El.second.Name;
+  //   OS << "\n" << format("%lx", EntryPc) << " " << Name;
+  // }
+  OS << "\n";
 }
 
 template <class ELFT>
@@ -7843,6 +8059,86 @@ template <class ELFT> void JSONELFDumper<ELFT>::printDynamicTable() {
   }
 }
 
+template <class ELFT> void JSONELFDumper<ELFT>::printCallGraphInfo() {
+  this->callGraphInfoHelper();
+  uint64_t NotListedCount = 0;
+  uint64_t UnknownCount = 0;
+
+  for (const auto &El : this->FuncInfo) {
+    NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
+    UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+  }
+  if (NotListedCount) // TODO(prabhuk): include object file name to the warning
+    this->reportUniqueWarning(
+        "callgraph section does not have information for " +
+        std::to_string(NotListedCount) + " functions");
+  if (UnknownCount) // TODO(prabhuk): include object file name to the warning
+    this->reportUniqueWarning("callgraph section has unknown type id for " +
+                              std::to_string(UnknownCount) +
+                              " indirect targets");
+
+  DictScope D(this->W, "callgraph_info");
+
+  // Use llvm-symbolizer to get function name and address instead.
+  // {
+  //   ListScope A(this->W, "functions");
+  //   for (auto const &F : this->FuncInfo) {
+  //     DictScope D(this->W);
+  //     this->W.printHex("function_address", F.first);
+  //   }
+  // }
+
+  {
+    ListScope A(this->W, "direct_call_sites");
+    for (auto const &F : this->FuncInfo) {
+      if (F.second.DirectCallSites.empty())
+        continue;
+      DictScope D(this->W);
+      this->W.printHex("caller", F.first);
+      ListScope A(this->W, "call_sites");
+      for (auto const &CS : F.second.DirectCallSites) {
+        DictScope D(this->W);
+        this->W.printHex("call_site", CS.CallSite);
+        this->W.printHex("callee", CS.Callee);
+      }
+    }
+  }
+
+  {
+    ListScope A(this->W, "indirect_call_sites");
+    for (auto const &F : this->FuncInfo) {
+      if (F.second.IndirectCallSites.empty())
+        continue;
+      DictScope D(this->W);
+      this->W.printHex("caller", F.first);
+      // ListScope A(this->W, "call_sites");
+      this->W.printHexList("call_sites", F.second.IndirectCallSites);
+    }
+  }
+
+  {
+    ListScope A(this->W, "indirect_call_types");
+    for (auto const &T : this->TypeIdToIndirCallSites) {
+      for (auto const &CS : T.second) {
+        DictScope D(this->W);
+        this->W.printHex("call_site", CS);
+        this->W.printHex("type_id", T.first);
+      }
+    }
+  }
+
+  {
+    ListScope A(this->W, "indirect_target_types");
+    for (auto const &T : this->TypeIdToIndirTargets) {
+      for (auto const &Target : T.second) {
+        DictScope D(this->W);
+        this->W.printHex("function_address", Target);
+        this->W.printHex("type_id", T.first);
+      }
+    }
+  }
+}
+
 template <class ELFT> void LLVMELFDumper<ELFT>::printDynamicRelocations() {
   W.startLine() << "Dynamic Relocations {\n";
   W.indent();
@@ -8097,126 +8393,8 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
   }
 }
 
-enum class FunctionKind : uint64_t {
-  // Function cannot be target to indirect calls.
-  NOT_INDIRECT_TARGET = 0,
-  // Function may be target to indirect calls but its type id is unknown.
-  INDIRECT_TARGET_UNKNOWN_TID = 1,
-  // Function may be target to indirect calls and its type id is known.
-  INDIRECT_TARGET_KNOWN_TID = 2,
-
-  // Available in the binary but not listed in the call graph section.
-  NOT_LISTED = 3,
-};
-
-struct FunctionInfo {
-  FunctionKind Kind;
-  struct DirectCallSite {
-    uint64_t CallSite;
-    uint64_t Callee;
-    DirectCallSite(uint64_t CallSite, uint64_t Callee)
-        : CallSite(CallSite), Callee(Callee) {}
-  };
-  SmallVector<DirectCallSite> DirectCallSites;
-  SmallVector<uint64_t> IndirectCallSites;
-};
-
 template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
-  // Get the .callgraph section.
-  StringRef CallGraphSectionName(".callgraph");
-  std::optional<object::SectionRef> CallGraphSection;
-  for (auto Sec : this->ObjF.sections()) {
-    StringRef Name;
-    if (Expected<StringRef> NameOrErr = Sec.getName())
-      Name = *NameOrErr;
-    else
-      consumeError(NameOrErr.takeError());
-
-    if (Name == CallGraphSectionName) {
-      CallGraphSection = Sec;
-      break;
-    }
-  }
-  if (!CallGraphSection) {
-    this->reportUniqueWarning("there is no .callgraph section");
-    return;
-  }
-
-  // Map type id to indirect call sites.
-  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirCallSites;
-  // Map type id to indirect targets.
-  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
-  // Map function entry pc to function info.
-  MapVector<uint64_t, FunctionInfo> FuncInfo;
-  // Instructions that are not indirect calls but have a type id are ignored.
-  // uint64_t IgnoredICallIdCount = 0;
-  // Number of valid indirect calls with type ids.
-  // uint64_t ICallWithTypeIdCount = 0;
-  if (CallGraphSection) {
-    StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
-    // TODO: some entries are written in pointer size. are they always 64-bit?
-    if (CGSecContents.size() % sizeof(uint64_t))
-      this->reportUniqueWarning(
-          "Malformed .callgraph section. Unexpected size.");
-
-    size_t Size = CGSecContents.size() / sizeof(uint64_t);
-    auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
-    const auto *const End = It + Size;
-
-    auto CGHasNext = [&]() { return It < End; };
-    auto CGNext = [&]() -> uint64_t {
-      if (!CGHasNext())
-        this->reportUniqueWarning(
-            "Malformed .callgraph section. Parsing error.");
-      return *It++;
-    };
-
-    // Parse the content
-    while (CGHasNext()) {
-      // Format version number.
-      uint64_t FormatVersionNumber = CGNext();
-      if (FormatVersionNumber != 0)
-        this->reportUniqueWarning(
-            "Unknown format version in .callgraph section.");
-      // Function entry pc.
-      uint64_t FuncEntryPc = CGNext();
-      // Function kind.
-      uint64_t Kind = CGNext();
-      switch (Kind) {
-      case 0: // not an indirect target
-        FuncInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
-        break;
-      case 1: // indirect target with unknown type id
-        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
-        break;
-      case 2: // indirect target with known type id
-        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
-        TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
-        break;
-      default:
-        this->reportUniqueWarning(
-            "Unknown function kind in .callgraph section.");
-      }
-
-      // Read indirect call sites info.
-      uint64_t IndirectCallSiteCount = CGNext();
-      for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
-        uint64_t TypeId = CGNext();
-        uint64_t CallSitePc = CGNext();
-        TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
-        FuncInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
-      }
-
-      // Read direct call sites info.
-      uint64_t DirectCallSiteCount = CGNext();
-      for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
-        uint64_t CallSitePc = CGNext();
-        uint64_t CalleePc = CGNext();
-        FuncInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
-                                                           CalleePc);
-      }
-    }
-  }
+  this->callGraphInfoHelper();
 }
 
 template <class ELFT>

>From f0810b268b3dd1f603fe90b1d2d419262bea6827 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 8 Sep 2025 16:11:33 -0700
Subject: [PATCH 04/44] ELFDumper cleanups

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 395 +++++++++++++-------------
 1 file changed, 194 insertions(+), 201 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 66f84c333470b..314f6484f49e2 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -183,20 +183,19 @@ struct GroupSection {
   std::vector<GroupMember> Members;
 };
 
-// Callgraph section handling implementation.
-enum class FunctionKind : uint64_t {
-  // Function cannot be target to indirect calls.
-  NOT_INDIRECT_TARGET = 0,
-  // Function may be target to indirect calls but its type id is unknown.
-  INDIRECT_TARGET_UNKNOWN_TID = 1,
-  // Function may be target to indirect calls and its type id is known.
-  INDIRECT_TARGET_KNOWN_TID = 2,
-
-  // Available in the binary but not listed in the call graph section.
-  NOT_LISTED = 3,
-};
-
-struct FunctionInfo {
+// Per-function Callgraph information.
+struct FunctionCallgraphInfo {
+  enum class FunctionKind : uint64_t {
+    // Function cannot be target to indirect calls.
+    NOT_INDIRECT_TARGET = 0,
+    // Function may be target to indirect calls but its type id is unknown.
+    INDIRECT_TARGET_UNKNOWN_TID = 1,
+    // Function may be target to indirect calls and its type id is known.
+    INDIRECT_TARGET_KNOWN_TID = 2,
+
+    // Available in the binary but not listed in the call graph section.
+    NOT_LISTED = 3,
+  };
   FunctionKind Kind;
   struct DirectCallSite {
     uint64_t CallSite;
@@ -207,6 +206,7 @@ struct FunctionInfo {
   SmallVector<DirectCallSite> DirectCallSites;
   SmallVector<uint64_t> IndirectCallSites;
 };
+typedef FunctionCallgraphInfo::FunctionKind FunctionKind;
 
 namespace {
 
@@ -458,13 +458,19 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
       const typename SFrameParser<ELFT::Endianness>::FDERange::iterator FDE,
       ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab);
 
-  void callGraphInfoHelper();
-  // Map type id to indirect call sites.
+  // Callgraph - Main data structure to maintain per function callgraph
+  // information.
+  MapVector<uint64_t, FunctionCallgraphInfo> FuncCGInfo;
+  // Callgraph - 64 bit type id mapped to indirect callsites whose potential
+  // callee(s) should be of given type id.
   MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirCallSites;
-  // Map type id to indirect targets.
+  // Callgraph - 64 bit type id mapped to entry PC addresses of functions which
+  // are of the given type id.
   MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
-  // Map function entry pc to function info.
-  MapVector<uint64_t, FunctionInfo> FuncInfo;
+  // Callgraph - Read callgraph section and process its contents to populate
+  // Callgraph related data structures which will be used to dump callgraph
+  // info.
+  void processCallGraphSection();
 
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
@@ -845,8 +851,6 @@ template <typename ELFT> class JSONELFDumper : public LLVMELFDumper<ELFT> {
 
   void printDynamicTable() override;
 
-  void printCallGraphInfo() override;
-
 private:
   void printAuxillaryDynamicTableEntryInfo(const Elf_Dyn &Entry);
 
@@ -5297,11 +5301,13 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
   OS << "GNUStyle::printCGProfile not implemented\n";
 }
 
-template <class ELFT> void ELFDumper<ELFT>::callGraphInfoHelper() {
+template <class ELFT>
+static std::optional<object::SectionRef>
+getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
   // Get the .callgraph section.
   StringRef CallGraphSectionName(".callgraph");
   std::optional<object::SectionRef> CallGraphSection;
-  for (auto Sec : this->ObjF.sections()) {
+  for (auto Sec : ObjF.sections()) {
     StringRef Name;
     if (Expected<StringRef> NameOrErr = Sec.getName())
       Name = *NameOrErr;
@@ -5313,101 +5319,106 @@ template <class ELFT> void ELFDumper<ELFT>::callGraphInfoHelper() {
       break;
     }
   }
+  return CallGraphSection;
+}
+
+template <class ELFT> void ELFDumper<ELFT>::processCallGraphSection() {
+  std::optional<object::SectionRef> CallGraphSection =
+      getCallGraphSection(ObjF);
   if (!CallGraphSection) {
-    this->reportUniqueWarning("there is no .callgraph section");
+    reportUniqueWarning("there is no .callgraph section in " +
+                        getElfObject().getFileName());
     return;
   }
 
-  // Instructions that are not indirect calls but have a type id are ignored.
-  // uint64_t IgnoredICallIdCount = 0;
-  // Number of valid indirect calls with type ids.
-  // uint64_t ICallWithTypeIdCount = 0;
-  if (CallGraphSection) {
-    StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
-    // TODO: some entries are written in pointer size. are they always 64-bit?
-    if (CGSecContents.size() % sizeof(uint64_t))
-      this->reportUniqueWarning(
-          "Malformed .callgraph section. Unexpected size.");
-
-    size_t Size = CGSecContents.size() / sizeof(uint64_t);
-    auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
-    const auto *const End = It + Size;
-
-    auto CGHasNext = [&]() { return It < End; };
-    auto CGNext = [&]() -> uint64_t {
-      if (!CGHasNext())
-        this->reportUniqueWarning(
-            "Malformed .callgraph section. Parsing error.");
-      return *It++;
-    };
+  StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
+  // TODO: some entries are written in pointer size. are they always 64-bit?
+  if (CGSecContents.size() % sizeof(uint64_t))
+    reportUniqueWarning("Malformed .callgraph section. Unexpected size in " +
+                        getElfObject().getFileName());
 
-    // Parse the content
-    while (CGHasNext()) {
-      // Format version number.
-      uint64_t FormatVersionNumber = CGNext();
-      if (FormatVersionNumber != 0)
-        this->reportUniqueWarning(
-            "Unknown format version in .callgraph section.");
-      // Function entry pc.
-      uint64_t FuncEntryPc = CGNext();
-      // Function kind.
-      uint64_t Kind = CGNext();
-      switch (Kind) {
-      case 0: // not an indirect target
-        FuncInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
-        break;
-      case 1: // indirect target with unknown type id
-        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
-        break;
-      case 2: // indirect target with known type id
-        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
-        TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
-        break;
-      default:
-        this->reportUniqueWarning(
-            "Unknown function kind in .callgraph section.");
-      }
+  size_t Size = CGSecContents.size() / sizeof(uint64_t);
+  auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
+  const auto *const End = It + Size;
 
-      // Read indirect call sites info.
-      uint64_t IndirectCallSiteCount = CGNext();
-      for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
-        uint64_t TypeId = CGNext();
-        uint64_t CallSitePc = CGNext();
-        TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
-        FuncInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
-      }
+  auto CGHasNext = [&]() { return It < End; };
+  auto CGNext = [&]() -> uint64_t {
+    if (!CGHasNext())
+      reportUniqueWarning("Malformed .callgraph section. Parsing error in " +
+                          getElfObject().getFileName());
+    return *It++;
+  };
 
-      // Read direct call sites info.
-      uint64_t DirectCallSiteCount = CGNext();
-      for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
-        uint64_t CallSitePc = CGNext();
-        uint64_t CalleePc = CGNext();
-        FuncInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
+  while (CGHasNext()) {
+    // Format version number.
+    uint64_t FormatVersionNumber = CGNext();
+    if (FormatVersionNumber != 0)
+      reportUniqueWarning("Unknown format version in .callgraph section in " +
+                          getElfObject().getFileName());
+    // Function entry pc.
+    uint64_t FuncEntryPc = CGNext();
+    // Function kind.
+    uint64_t Kind = CGNext();
+    switch (Kind) {
+    case 0: // not an indirect target
+      FuncCGInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
+      break;
+    case 1: // indirect target with unknown type id
+      FuncCGInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+      break;
+    case 2: // indirect target with known type id
+      FuncCGInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
+      TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
+      break;
+    default:
+      this->reportUniqueWarning(
+          "Unknown function kind in .callgraph section in " +
+          getElfObject().getFileName());
+    }
+    // Read indirect call sites info.
+    uint64_t IndirectCallSiteCount = CGNext();
+    for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
+      uint64_t TypeId = CGNext();
+      uint64_t CallSitePc = CGNext();
+      TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
+      FuncCGInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
+    }
+    // Read direct call sites info.
+    uint64_t DirectCallSiteCount = CGNext();
+    for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
+      uint64_t CallSitePc = CGNext();
+      uint64_t CalleePc = CGNext();
+      FuncCGInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
                                                            CalleePc);
-      }
     }
   }
-  llvm::sort(FuncInfo,
+
+  // Sort function info by function PC.
+  llvm::sort(FuncCGInfo,
              [](const auto &A, const auto &B) { return A.first < B.first; });
-}
 
-template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
-  this->callGraphInfoHelper();
   uint64_t NotListedCount = 0;
   uint64_t UnknownCount = 0;
-
-  for (const auto &El : this->FuncInfo) {
+  for (const auto &El : FuncCGInfo) {
     NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
     UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
   }
-  if (NotListedCount) // TODO(prabhuk): include object file name to the warning
-    this->reportUniqueWarning(
-        "callgraph section does not have information for " +
-        std::to_string(NotListedCount) + " functions");
-  if (UnknownCount) // TODO(prabhuk): include object file name to the warning
-    this->reportUniqueWarning("callgraph section has unknown type id for " +
-                              std::to_string(UnknownCount) +
-                              " indirect targets");
+  for (const auto &El : FuncCGInfo) {
+    NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
+    UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+  }
+  if (NotListedCount)
+    reportUniqueWarning("callgraph section does not have information for " +
+                        std::to_string(NotListedCount) + " functions in " +
+                        getElfObject().getFileName());
+  if (UnknownCount)
+    reportUniqueWarning("callgraph section has unknown type id for " +
+                        std::to_string(UnknownCount) + " indirect targets in " +
+                        getElfObject().getFileName());
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
+  this->processCallGraphSection();
 
   // Print indirect targets
   OS << "\nINDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])";
@@ -5415,17 +5426,14 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   // Print indirect targets with unknown type.
   // For completeness, functions for which the call graph section does not
   // provide information are included.
-  if (NotListedCount || UnknownCount) {
-    OS << "\nUNKNOWN";
-    for (const auto &El : this->FuncInfo) {
+  for (const auto &El : this->FuncCGInfo) {
+    FunctionKind FuncKind = El.second.Kind;
+    if (FuncKind == FunctionKind::NOT_LISTED ||
+        FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) {
       uint64_t FuncEntryPc = El.first;
-      FunctionKind FuncKind = El.second.Kind;
-      if (FuncKind == FunctionKind::NOT_LISTED ||
-          FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
-        OS << " " << format("%lx", FuncEntryPc);
+      OS << " " << format("%lx", FuncEntryPc);
     }
   }
-
   // Print indirect targets to type id mapping.
   for (const auto &El : this->TypeIdToIndirTargets) {
     uint64_t TypeId = El.first;
@@ -5447,7 +5455,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
 
   // Print function entry to indirect call site addresses mapping from disasm.
   OS << "\n\nINDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])";
-  for (const auto &El : this->FuncInfo) {
+  for (const auto &El : this->FuncCGInfo) {
     auto CallerPc = El.first;
     auto FuncIndirCallSites = El.second.IndirectCallSites;
     if (!FuncIndirCallSites.empty()) {
@@ -5460,25 +5468,18 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   // Print function entry to direct call site and target function entry
   // addresses mapping from disasm.
   OS << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])";
-  for (const auto &El : this->FuncInfo) {
+  for (const auto &El : this->FuncCGInfo) {
     auto CallerPc = El.first;
     auto FuncDirCallSites = El.second.DirectCallSites;
     if (!FuncDirCallSites.empty()) {
       OS << "\n" << format("%lx", CallerPc);
-      for (const FunctionInfo::DirectCallSite &DCS : FuncDirCallSites) {
+      for (const FunctionCallgraphInfo::DirectCallSite &DCS :
+           FuncDirCallSites) {
         OS << " " << format("%lx", DCS.CallSite) << " "
            << format("%lx", DCS.Callee);
       }
     }
   }
-
-  // Print function entry pc to function name mapping.
-  // OS << "\n\nFUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)";
-  // for (const auto &El : FuncInfo) {
-  //   uint64_t EntryPc = El.first;
-  //   const auto &Name = El.second.Name;
-  //   OS << "\n" << format("%lx", EntryPc) << " " << Name;
-  // }
   OS << "\n";
 }
 
@@ -8059,86 +8060,6 @@ template <class ELFT> void JSONELFDumper<ELFT>::printDynamicTable() {
   }
 }
 
-template <class ELFT> void JSONELFDumper<ELFT>::printCallGraphInfo() {
-  this->callGraphInfoHelper();
-  uint64_t NotListedCount = 0;
-  uint64_t UnknownCount = 0;
-
-  for (const auto &El : this->FuncInfo) {
-    NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
-    UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
-  }
-  if (NotListedCount) // TODO(prabhuk): include object file name to the warning
-    this->reportUniqueWarning(
-        "callgraph section does not have information for " +
-        std::to_string(NotListedCount) + " functions");
-  if (UnknownCount) // TODO(prabhuk): include object file name to the warning
-    this->reportUniqueWarning("callgraph section has unknown type id for " +
-                              std::to_string(UnknownCount) +
-                              " indirect targets");
-
-  DictScope D(this->W, "callgraph_info");
-
-  // Use llvm-symbolizer to get function name and address instead.
-  // {
-  //   ListScope A(this->W, "functions");
-  //   for (auto const &F : this->FuncInfo) {
-  //     DictScope D(this->W);
-  //     this->W.printHex("function_address", F.first);
-  //   }
-  // }
-
-  {
-    ListScope A(this->W, "direct_call_sites");
-    for (auto const &F : this->FuncInfo) {
-      if (F.second.DirectCallSites.empty())
-        continue;
-      DictScope D(this->W);
-      this->W.printHex("caller", F.first);
-      ListScope A(this->W, "call_sites");
-      for (auto const &CS : F.second.DirectCallSites) {
-        DictScope D(this->W);
-        this->W.printHex("call_site", CS.CallSite);
-        this->W.printHex("callee", CS.Callee);
-      }
-    }
-  }
-
-  {
-    ListScope A(this->W, "indirect_call_sites");
-    for (auto const &F : this->FuncInfo) {
-      if (F.second.IndirectCallSites.empty())
-        continue;
-      DictScope D(this->W);
-      this->W.printHex("caller", F.first);
-      // ListScope A(this->W, "call_sites");
-      this->W.printHexList("call_sites", F.second.IndirectCallSites);
-    }
-  }
-
-  {
-    ListScope A(this->W, "indirect_call_types");
-    for (auto const &T : this->TypeIdToIndirCallSites) {
-      for (auto const &CS : T.second) {
-        DictScope D(this->W);
-        this->W.printHex("call_site", CS);
-        this->W.printHex("type_id", T.first);
-      }
-    }
-  }
-
-  {
-    ListScope A(this->W, "indirect_target_types");
-    for (auto const &T : this->TypeIdToIndirTargets) {
-      for (auto const &Target : T.second) {
-        DictScope D(this->W);
-        this->W.printHex("function_address", Target);
-        this->W.printHex("type_id", T.first);
-      }
-    }
-  }
-}
-
 template <class ELFT> void LLVMELFDumper<ELFT>::printDynamicRelocations() {
   W.startLine() << "Dynamic Relocations {\n";
   W.indent();
@@ -8394,7 +8315,79 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
 }
 
 template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
-  this->callGraphInfoHelper();
+  this->processCallGraphSection();
+
+  DictScope D(this->W, "callgraph_info");
+  // Use llvm-symbolizer to get function name and address instead.
+  // stack-sizes embeds function mangled name directly in JSON.
+  // {
+  //   ListScope A(this->W, "functions");
+  //   for (auto const &F : this->FuncCGInfo) {
+  //     DictScope D(this->W);
+  //     this->W.printHex("function_address", F.first);
+  //   }
+  // }
+
+  {
+    ListScope A(this->W, "indirect_target_types");
+    SmallVector<uint64_t, 4> Unknowns;
+    for (const auto &El : this->FuncCGInfo) {
+      FunctionKind FuncKind = El.second.Kind;
+      if (FuncKind == FunctionKind::NOT_LISTED ||
+          FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) {
+        uint64_t FuncEntryPc = El.first;
+        Unknowns.emplace_back(FuncEntryPc);
+      }
+    }
+    if (!Unknowns.empty())
+      this->W.printHexList("unknown", Unknowns);
+    for (auto const &T : this->TypeIdToIndirTargets) {
+      for (auto const &Target : T.second) {
+        DictScope D(this->W);
+        this->W.printHex("function_address", Target);
+        this->W.printHex("type_id", T.first);
+      }
+    }
+  }
+
+  {
+    ListScope A(this->W, "direct_call_sites");
+    for (auto const &F : this->FuncCGInfo) {
+      if (F.second.DirectCallSites.empty())
+        continue;
+      DictScope D(this->W);
+      this->W.printHex("caller", F.first);
+      ListScope A(this->W, "call_sites");
+      for (auto const &CS : F.second.DirectCallSites) {
+        DictScope D(this->W);
+        this->W.printHex("call_site", CS.CallSite);
+        this->W.printHex("callee", CS.Callee);
+      }
+    }
+  }
+
+  {
+    ListScope A(this->W, "indirect_call_sites");
+    for (auto const &F : this->FuncCGInfo) {
+      if (F.second.IndirectCallSites.empty())
+        continue;
+      DictScope D(this->W);
+      this->W.printHex("caller", F.first);
+      // ListScope A(this->W, "call_sites");
+      this->W.printHexList("call_sites", F.second.IndirectCallSites);
+    }
+  }
+
+  {
+    ListScope A(this->W, "indirect_call_types");
+    for (auto const &T : this->TypeIdToIndirCallSites) {
+      for (auto const &CS : T.second) {
+        DictScope D(this->W);
+        this->W.printHex("call_site", CS);
+        this->W.printHex("type_id", T.first);
+      }
+    }
+  }
 }
 
 template <class ELFT>

>From 5f20b7b1fa65b8140174c78af21f28337bdda3cb Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 8 Sep 2025 18:48:06 -0700
Subject: [PATCH 05/44] Add readelf callgraph info tests.

---
 .../call-graph-info-callgraph-section.test    | 77 +++++++++++++++++++
 ...call-graph-info-err-invalid-func-kind.test | 19 +++++
 ...-info-err-malformed-callgraph-section.test | 21 +++++
 ...info-err-malformed-callgraph-section2.test | 21 +++++
 ...info-err-malformed-callgraph-section3.test | 23 ++++++
 ...all-graph-info-invalid-format-version.test | 17 ++++
 ...-graph-info-warn-no-callgraph-section.test | 15 ++++
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 67 ++++++++--------
 8 files changed, 227 insertions(+), 33 deletions(-)
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
new file mode 100644
index 0000000000000..60d1c8323f8e8
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -0,0 +1,77 @@
+## Tests --call-graph-info prints information from call graph section.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+
+# CHECK: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
+# CHECK-NEXT: UNKNOWN 6
+# CHECK-NEXT: 20 a
+# CHECK-EMPTY:
+# CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
+# CHECK-NEXT: 10 9
+# CHECK-EMPTY:
+# CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
+# CHECK-NEXT: 6 9
+# CHECK-EMPTY:
+# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
+# CHECK-NEXT: 0 5 5
+#
+#FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)
+# 0 foo
+# 6 bar
+# a baz
+# b qux
+
+.text
+
+.globl foo
+.type foo, at function
+foo:                  #< foo is at 0.
+.Lfoo_begin:
+ callq foo            #< direct call is at 5. target is foo (5).
+ retq
+
+.globl bar
+.type bar, at function
+bar:                  #< bar is at 6.
+ callq	*-40(%rbp)    #< indirect call is at 9.
+ retq
+
+.globl baz
+.type baz, at function
+baz:                  #< baz is at 10 (a).
+ retq
+
+.globl qux
+.type qux, at function
+qux:                  #< qux is at 11 (b).
+ retq
+
+.section	.callgraph,"o", at progbits,.text
+.quad	0       #< Format version number.
+.quad	0       #< foo()'s entry address.
+.quad	0       #< Function kind: not an indirect target.
+.quad	0       #< Count of indirect call sites that follow: 0.
+.quad   1       #< Count of direct call sites that follow: 1>
+.quad   5       #< Direct callsite call to foo>
+.quad   5       #< Direct callee foo's address>
+
+.quad	0       #< Format version number.
+.quad	6       #< bar()'s entry address.
+.quad	1       #< Function kind: indirect target with unknown type id.
+.quad	1       #< Count of indirect call sites that follow: 1.
+.quad 16      #< Indirect call type id.
+.quad 9       #< Indirect call site.
+.quad   0       #< Count of direct call sites that follow: 0>
+
+.quad	0       #< Format version number.
+.quad	10      #< baz()'s entry address.
+.quad	2       #< Function kind: indirect target with known type id.
+.quad 32      #< Indirect target type id.
+.quad	0       #< Count of indirect call sites that follow: 0>.
+.quad   0       #< Count of direct call sites that follow: 0>
+
+# No call graph section entry for qux. 
+# Technically its "UNKNOWN" type id but will not be printed as such by llvm-readelf.  
+
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
new file mode 100644
index 0000000000000..b55717b25fee0
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
@@ -0,0 +1,19 @@
+## Tests that --call-graph-info fails if .callgraph section has invalid
+## function kind value.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+
+# ERR: llvm-readelf: warning: '[[FILE]]': Unknown function kind in .callgraph section.
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
+
+.section	.callgraph,"o", at progbits,.text
+.quad	0   #< Format version number.
+.quad 0   #< Function entry address.
+.quad 3   #< Invalid function kind: must be one of 0, 1, 2.
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
new file mode 100644
index 0000000000000..459d002d35352
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -0,0 +1,21 @@
+## Tests that --call-graph-info fails if .callgraph section has invalid size.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+
+# ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
+
+.section	.callgraph,"o", at progbits,.text
+# Each unit in .callgraph section must have 64bit size. Therefore, byte size
+# must be divisible by 64.
+.quad 0
+.quad 0
+.quad 0
+.byte 0
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
new file mode 100644
index 0000000000000..2c84469526552
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
@@ -0,0 +1,21 @@
+## Tests that --call-graph-info fails if .callgraph section does not have
+## an expected value, e.g., not as much call sites as the given count.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+
+# ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
+
+.section	.callgraph,"o", at progbits,.text
+.quad 0  #< Format version number.
+.quad 0  #< Function entry address.
+.quad 0  #< Function kind.
+.quad 1  #< Indirect call site count that follows.
+         #< Missing indirect call entries here.
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
new file mode 100644
index 0000000000000..5bfbd709c78e2
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -0,0 +1,23 @@
+## Tests that --call-graph-info fails if .callgraph section does not have
+## an expected value, e.g., not as much call sites as the given count.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+
+# ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov
+ retq
+
+.section	.callgraph,"o", at progbits,.text
+.quad 0  #< Format version number.
+.quad 0  #< Function entry address.
+.quad 0  #< Function kind.
+.quad 0  #< Indirect call site count.
+.quad 1  #< Direct call site count that follows.
+         #< Missing direct call entries here.
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
new file mode 100644
index 0000000000000..4f9d7dbeb844a
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
@@ -0,0 +1,17 @@
+## Tests that --call-graph-info fails if .callgraph section has unknown format
+## version number.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+
+# ERR: llvm-readelf: warning: '[[FILE]]': Unknown format version in .callgraph section.
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
+
+.section	.callgraph,"o", at progbits,.text
+.quad	1  #< Invalid format version number: the only supported version is 0.
+.text
\ No newline at end of file
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
new file mode 100644
index 0000000000000..cc904dbed2e67
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
@@ -0,0 +1,15 @@
+## Tests that --call-graph-info warns if there is no .callgraph section.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+
+# CHECK: llvm-readelf: warning: '[[FILE]]': No .callgraph section found.
+## Make sure callgraph info header is not printed when there is no .callgraph section
+# CHECK-NOT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
+# CHECK-NOT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 314f6484f49e2..84db935b0c4d2 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -469,8 +469,8 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
   // Callgraph - Read callgraph section and process its contents to populate
   // Callgraph related data structures which will be used to dump callgraph
-  // info.
-  void processCallGraphSection();
+  // info. Returns false if there is no .callgraph section in the input file.
+  bool processCallGraphSection();
 
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
@@ -5314,28 +5314,26 @@ getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
     else
       consumeError(NameOrErr.takeError());
 
-    if (Name == CallGraphSectionName) {
-      CallGraphSection = Sec;
-      break;
-    }
+    if (Name == CallGraphSectionName)
+      return Sec;
   }
   return CallGraphSection;
 }
 
-template <class ELFT> void ELFDumper<ELFT>::processCallGraphSection() {
+template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   std::optional<object::SectionRef> CallGraphSection =
       getCallGraphSection(ObjF);
-  if (!CallGraphSection) {
-    reportUniqueWarning("there is no .callgraph section in " +
-                        getElfObject().getFileName());
-    return;
+  if (!CallGraphSection.has_value()) {
+    reportUniqueWarning("No .callgraph section found.");
+    return false;
   }
 
   StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
   // TODO: some entries are written in pointer size. are they always 64-bit?
-  if (CGSecContents.size() % sizeof(uint64_t))
-    reportUniqueWarning("Malformed .callgraph section. Unexpected size in " +
-                        getElfObject().getFileName());
+  if (CGSecContents.size() % sizeof(uint64_t)) {
+    reportUniqueWarning("Malformed .callgraph section. Unexpected size.");
+    exit(1);
+  }
 
   size_t Size = CGSecContents.size() / sizeof(uint64_t);
   auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
@@ -5343,18 +5341,20 @@ template <class ELFT> void ELFDumper<ELFT>::processCallGraphSection() {
 
   auto CGHasNext = [&]() { return It < End; };
   auto CGNext = [&]() -> uint64_t {
-    if (!CGHasNext())
-      reportUniqueWarning("Malformed .callgraph section. Parsing error in " +
-                          getElfObject().getFileName());
+    if (!CGHasNext()) {
+      reportUniqueWarning("Malformed .callgraph section. Parsing error.");
+      exit(1);
+    }
     return *It++;
   };
 
   while (CGHasNext()) {
     // Format version number.
     uint64_t FormatVersionNumber = CGNext();
-    if (FormatVersionNumber != 0)
-      reportUniqueWarning("Unknown format version in .callgraph section in " +
-                          getElfObject().getFileName());
+    if (FormatVersionNumber != 0) {
+      reportUniqueWarning("Unknown format version in .callgraph section.");
+      exit(1);
+    }
     // Function entry pc.
     uint64_t FuncEntryPc = CGNext();
     // Function kind.
@@ -5371,9 +5371,8 @@ template <class ELFT> void ELFDumper<ELFT>::processCallGraphSection() {
       TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
       break;
     default:
-      this->reportUniqueWarning(
-          "Unknown function kind in .callgraph section in " +
-          getElfObject().getFileName());
+      this->reportUniqueWarning("Unknown function kind in .callgraph section.");
+      exit(1);
     }
     // Read indirect call sites info.
     uint64_t IndirectCallSiteCount = CGNext();
@@ -5403,22 +5402,18 @@ template <class ELFT> void ELFDumper<ELFT>::processCallGraphSection() {
     NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
     UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
   }
-  for (const auto &El : FuncCGInfo) {
-    NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
-    UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
-  }
   if (NotListedCount)
     reportUniqueWarning("callgraph section does not have information for " +
-                        std::to_string(NotListedCount) + " functions in " +
-                        getElfObject().getFileName());
+                        std::to_string(NotListedCount) + " functions.");
   if (UnknownCount)
     reportUniqueWarning("callgraph section has unknown type id for " +
-                        std::to_string(UnknownCount) + " indirect targets in " +
-                        getElfObject().getFileName());
+                        std::to_string(UnknownCount) + " indirect targets.");
+  return true;
 }
 
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
-  this->processCallGraphSection();
+  if (!this->processCallGraphSection())
+    return;
 
   // Print indirect targets
   OS << "\nINDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])";
@@ -5426,10 +5421,15 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   // Print indirect targets with unknown type.
   // For completeness, functions for which the call graph section does not
   // provide information are included.
+  bool printedHeader = false;
   for (const auto &El : this->FuncCGInfo) {
     FunctionKind FuncKind = El.second.Kind;
     if (FuncKind == FunctionKind::NOT_LISTED ||
         FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) {
+      if (!printedHeader) {
+        OS << "\nUNKNOWN";
+        printedHeader = true;
+      }
       uint64_t FuncEntryPc = El.first;
       OS << " " << format("%lx", FuncEntryPc);
     }
@@ -8315,7 +8315,8 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
 }
 
 template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
-  this->processCallGraphSection();
+  if (!this->processCallGraphSection())
+    return;
 
   DictScope D(this->W, "callgraph_info");
   // Use llvm-symbolizer to get function name and address instead.

>From d9911942831daa0027166d624e9d8f0761bb2dc8 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 9 Sep 2025 10:37:42 -0700
Subject: [PATCH 06/44] more tests

---
 ...all-graph-info-invalid-format-version.test |   2 +-
 .../llvm-readobj/ELF/call-graph-info.test     | 496 ++++++++++++++++++
 2 files changed, 497 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info.test

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
index 4f9d7dbeb844a..a1213db081951 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
@@ -14,4 +14,4 @@ _Z3foov:
 
 .section	.callgraph,"o", at progbits,.text
 .quad	1  #< Invalid format version number: the only supported version is 0.
-.text
\ No newline at end of file
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
new file mode 100644
index 0000000000000..547b608cc5950
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -0,0 +1,496 @@
+## Tests how --call-graph-info prints the call graph information.
+# RUN: yaml2obj --docnum=1 %s -o %t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+
+# Yaml input is obtained by compiling the below source to object with:
+#   clang -fexperimental-call-graph-section test.c -o test.o
+# then to yaml with:
+#   obj2yaml test.o > test.yaml
+# Remove ProgramHeaders if obj2yaml fails.
+
+# The content of the .callgraph section is fixed with this yaml in raw format.
+
+# Source:
+#   void foo() {}
+#
+#   void bar() {}
+#
+#   int baz(char a) {
+#     return 0;
+#   }
+#
+#   int main() {
+#     // Indirect calls.
+#     void (*fp_foo)() = foo;
+#     fp_foo();
+#
+#     void (*fp_bar)() = bar;
+#     fp_bar();
+#
+#     char a;
+#     int (*fp_baz)(char) = baz;
+#     fp_baz(a);
+#
+#     // Direct calls.
+#     foo();
+#     bar();
+#     baz(a);
+#
+#     return 0;
+#   }
+
+# CHECK: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
+# CHECK-NEXT: f85c699bb8ef20a2 17a0 17b0
+# CHECK-NEXT: 308e4b8159bc8654 17c0
+# CHECK-NEXT: a9494def81a01dc 17d0
+# CHECK-EMPTY:
+# CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
+# CHECK-NEXT: f85c699bb8ef20a2 17ed 17fb
+# CHECK-NEXT: 308e4b8159bc8654 1810
+# CHECK-EMPTY:
+# CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
+# CHECK-NEXT: 17d0 17ed 17fb 1810
+# CHECK-EMPTY:
+# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
+# CHECK-NEXT: 17d0 1815 17a0 181a 17b0 1823 17c0
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_X86_64
+  Entry:           0x16E0
+Sections:
+  - Name:            .interp
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x2A8
+    AddressAlign:    0x1
+    Content:         2F6C696236342F6C642D6C696E75782D7838362D36342E736F2E3200
+  - Name:            .note.ABI-tag
+    Type:            SHT_NOTE
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x2C4
+    AddressAlign:    0x4
+    Notes:
+      - Name:            GNU
+        Desc:            '00000000030000000200000000000000'
+        Type:            NT_VERSION
+  - Name:            .note.gnu.build-id
+    Type:            SHT_NOTE
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x2E4
+    AddressAlign:    0x4
+    Notes:
+      - Name:            GNU
+        Desc:            3E3D197EC968B0AD7D4A3809E7355CA3BE816929
+        Type:            NT_PRPSINFO
+  - Name:            .dynsym
+    Type:            SHT_DYNSYM
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x308
+    Link:            .dynstr
+    AddressAlign:    0x8
+  - Name:            .gnu.version
+    Type:            SHT_GNU_versym
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x398
+    Link:            .dynsym
+    AddressAlign:    0x2
+    Entries:         [ 0, 2, 1, 1, 3, 1 ]
+  - Name:            .gnu.version_r
+    Type:            SHT_GNU_verneed
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x3A4
+    Link:            .dynstr
+    AddressAlign:    0x4
+    Dependencies:
+      - Version:         1
+        File:            libc.so.6
+        Entries:
+          - Name:            GLIBC_2.2.5
+            Hash:            157882997
+            Flags:           0
+            Other:           3
+          - Name:            GLIBC_2.34
+            Hash:            110530996
+            Flags:           0
+            Other:           2
+  - Name:            .gnu.hash
+    Type:            SHT_GNU_HASH
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x3D8
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Header:
+      SymNdx:          0x6
+      Shift2:          0x1A
+    BloomFilter:     [ 0x0 ]
+    HashBuckets:     [ 0x0 ]
+    HashValues:      [  ]
+  - Name:            .dynstr
+    Type:            SHT_STRTAB
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x3F4
+    AddressAlign:    0x1
+  - Name:            .rela.dyn
+    Type:            SHT_RELA
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x480
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Relocations:
+      - Offset:          0x2890
+        Type:            R_X86_64_RELATIVE
+        Addend:          5904
+      - Offset:          0x2898
+        Type:            R_X86_64_RELATIVE
+        Addend:          5968
+      - Offset:          0x3A80
+        Type:            R_X86_64_RELATIVE
+        Addend:          14976
+      - Offset:          0x2A50
+        Symbol:          __libc_start_main
+        Type:            R_X86_64_GLOB_DAT
+      - Offset:          0x2A58
+        Symbol:          __gmon_start__
+        Type:            R_X86_64_GLOB_DAT
+      - Offset:          0x2A60
+        Symbol:          __register_frame_info
+        Type:            R_X86_64_GLOB_DAT
+      - Offset:          0x2A68
+        Symbol:          __cxa_finalize
+        Type:            R_X86_64_GLOB_DAT
+      - Offset:          0x2A70
+        Symbol:          __deregister_frame_info
+        Type:            R_X86_64_GLOB_DAT
+  - Name:            .rela.plt
+    Type:            SHT_RELA
+    Flags:           [ SHF_ALLOC, SHF_INFO_LINK ]
+    Address:         0x540
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Info:            .got.plt
+    Relocations:
+      - Offset:          0x3AA0
+        Symbol:          __register_frame_info
+        Type:            R_X86_64_JUMP_SLOT
+      - Offset:          0x3AA8
+        Symbol:          __cxa_finalize
+        Type:            R_X86_64_JUMP_SLOT
+      - Offset:          0x3AB0
+        Symbol:          __deregister_frame_info
+        Type:            R_X86_64_JUMP_SLOT
+  - Name:            .rodata
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_MERGE ]
+    Address:         0x588
+    AddressAlign:    0x4
+    EntSize:         0x4
+    Content:         '01000200'
+  - Name:            .eh_frame_hdr
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x58C
+    AddressAlign:    0x4
+    Content:         011B033B4000000007000000541100005C0000008411000088000000C4110000A800000014120000C800000024120000E800000034120000080100004412000028010000
+  - Name:            .eh_frame
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x5D0
+    AddressAlign:    0x8
+    Content:         1400000000000000017A5200017810011B0C070890010710100000001C000000F010000022000000000000001400000000000000017A5200017810011B0C0708900100001C0000001C000000F41000003800000000410E108602430D06730C07080000001C0000003C000000141100004A00000000410E108602430D0602450C070800001C0000005C000000441100000600000000410E108602430D06410C07080000001C0000007C000000341100000600000000410E108602430D06410C07080000001C0000009C000000241100000E00000000410E108602430D06490C07080000001C000000BC000000141100005B00000000410E108602430D0602560C0708000000000000
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x16E0
+    AddressAlign:    0x10
+    Content:         31ED4989D15E4889E24883E4F050544531C031C9488D3DD5000000FF154F130000F4CCCCCCCCCCCCCCCCCCCCCCCCCCCC554889E5F6059D230000017402EB27C6059223000001488B05331300004883F8007413488D3DC2EEFFFF488D357F230000E81A0100005DC30F1F840000000000554889E5F605A5230000017402EB39C6059A23000001488B05FB1200004883F800740C488B3D06230000E8F1000000488B05EA1200004883F800740C488D3D69EEFFFFE8E80000005DC3CCCCCCCCCCCC554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC30C745FC00000000488D05BAFFFFFF488945F0FF55F0488D05BCFFFFFF488945E8FF55E8488D05BEFFFFFF488945D8488B45D80FBE7DE7FFD0E88BFFFFFFE896FFFFFF0FBE7DE7E89DFFFFFF31C04883C4305DC3CC
+  - Name:            .init
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x182C
+    AddressAlign:    0x4
+    Content:         4883EC08488B05211200004885C07402FFD04883C408C3
+  - Name:            .fini
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1844
+    AddressAlign:    0x4
+    Content:         4883EC084883C408C3
+  - Name:            .plt
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1850
+    AddressAlign:    0x10
+    Content:         FF353A220000FF253C2200000F1F4000FF253A2200006800000000E9E0FFFFFFFF25322200006801000000E9D0FFFFFFFF252A2200006802000000E9C0FFFFFF
+  - Name:            .init_array
+    Type:            SHT_INIT_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2890
+    AddressAlign:    0x8
+    Content:         '0000000000000000'
+  - Name:            .fini_array
+    Type:            SHT_FINI_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2898
+    AddressAlign:    0x8
+    Content:         '0000000000000000'
+  - Name:            .dynamic
+    Type:            SHT_DYNAMIC
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x28A0
+    Link:            .dynstr
+    AddressAlign:    0x8
+    Entries:
+      - Tag:             DT_NEEDED
+        Value:           0x80
+      - Tag:             DT_NEEDED
+        Value:           0x5F
+      - Tag:             DT_FLAGS_1
+        Value:           0x8000000
+      - Tag:             DT_DEBUG
+        Value:           0x0
+      - Tag:             DT_RELA
+        Value:           0x480
+      - Tag:             DT_RELASZ
+        Value:           0xC0
+      - Tag:             DT_RELAENT
+        Value:           0x18
+      - Tag:             DT_RELACOUNT
+        Value:           0x3
+      - Tag:             DT_JMPREL
+        Value:           0x540
+      - Tag:             DT_PLTRELSZ
+        Value:           0x48
+      - Tag:             DT_PLTGOT
+        Value:           0x3A88
+      - Tag:             DT_PLTREL
+        Value:           0x7
+      - Tag:             DT_SYMTAB
+        Value:           0x308
+      - Tag:             DT_SYMENT
+        Value:           0x18
+      - Tag:             DT_STRTAB
+        Value:           0x3F4
+      - Tag:             DT_STRSZ
+        Value:           0x8A
+      - Tag:             DT_GNU_HASH
+        Value:           0x3D8
+      - Tag:             DT_INIT_ARRAY
+        Value:           0x2890
+      - Tag:             DT_INIT_ARRAYSZ
+        Value:           0x8
+      - Tag:             DT_FINI_ARRAY
+        Value:           0x2898
+      - Tag:             DT_FINI_ARRAYSZ
+        Value:           0x8
+      - Tag:             DT_INIT
+        Value:           0x182C
+      - Tag:             DT_FINI
+        Value:           0x1844
+      - Tag:             DT_VERSYM
+        Value:           0x398
+      - Tag:             DT_VERNEED
+        Value:           0x3A4
+      - Tag:             DT_VERNEEDNUM
+        Value:           0x1
+      - Tag:             DT_NULL
+        Value:           0x0
+  - Name:            .got
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2A50
+    AddressAlign:    0x8
+    Content:         '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
+  - Name:            .relro_padding
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x2A78
+    AddressAlign:    0x1
+    Size:            0x588
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x3A78
+    AddressAlign:    0x8
+    Content:         '00000000000000000000000000000000'
+  - Name:            .got.plt
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x3A88
+    AddressAlign:    0x8
+    Content:         A02800000000000000000000000000000000000000000000661800000000000076180000000000008618000000000000
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x3AB8
+    AddressAlign:    0x8
+    Size:            0x49
+  - Name:            .comment
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_MERGE, SHF_STRINGS ]
+    AddressAlign:    0x1
+    EntSize:         0x1
+    Content:         004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A6C6C766D2F6C6C766D2D70726F6A6563742E676974206135343965373363616436303333366438653963303632326165376164383661613635656634636529004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203239343432666166656337373437633632356135653466393937303861633532323139373662663729004675636873696120636C616E672076657273696F6E2032322E302E3067697420282F7573722F6C6F63616C2F676F6F676C652F686F6D652F7072616268756B722F6C6C766D2F6C6C766D2D70726F6A6563742F636C616E67203335383463366161636163363239633531633464316538653038323538633063323466336131363529004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420323934343266616665633737343763363235613565346639393730386163353232313937366266372900
+  - Name:            .callgraph
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_LINK_ORDER ]
+    Link:            .text
+    AddressAlign:    0x1
+    Content:         0000000000000000A0170000000000000200000000000000A220EFB89B695CF8000000000000000000000000000000000000000000000000B0170000000000000200000000000000A220EFB89B695CF8000000000000000000000000000000000000000000000000C01700000000000002000000000000005486BC59814B8E30000000000000000000000000000000000000000000000000D0170000000000000200000000000000DC011AF8DE94940A0300000000000000A220EFB89B695CF8ED17000000000000A220EFB89B695CF8FB170000000000005486BC59814B8E30101800000000000003000000000000001518000000000000A0170000000000001A18000000000000B0170000000000002318000000000000C017000000000000
+Symbols:
+  - Name:            __abi_tag
+    Type:            STT_OBJECT
+    Section:         .note.ABI-tag
+    Value:           0x2C4
+    Size:            0x20
+  - Name:            crtbegin.c
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            __do_init
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x1710
+    Size:            0x38
+  - Name:            __do_init.__initialized
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x3AB8
+    Size:            0x1
+  - Name:            __EH_FRAME_LIST__
+    Type:            STT_OBJECT
+    Section:         .eh_frame
+    Value:           0x5FC
+  - Name:            __do_init.__object
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x3AC0
+    Size:            0x40
+  - Name:            __do_fini
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x1750
+    Size:            0x4A
+  - Name:            __do_fini.__finalized
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x3B00
+    Size:            0x1
+  - Name:            __init
+    Type:            STT_OBJECT
+    Section:         .init_array
+    Value:           0x2890
+    Size:            0x8
+  - Name:            __fini
+    Type:            STT_OBJECT
+    Section:         .fini_array
+    Value:           0x2898
+    Size:            0x8
+  - Name:            __dso_handle
+    Type:            STT_OBJECT
+    Section:         .data
+    Value:           0x3A80
+    Size:            0x8
+    Other:           [ STV_HIDDEN ]
+  - Name:            call_graph_info.cpp
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            crtend.c
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            __EH_FRAME_LIST_END__
+    Type:            STT_OBJECT
+    Section:         .eh_frame
+    Value:           0x5D0
+    Size:            0x4
+    Other:           [ STV_HIDDEN ]
+  - Name:            _GLOBAL_OFFSET_TABLE_
+    Section:         .got.plt
+    Value:           0x3A88
+    Other:           [ STV_HIDDEN ]
+  - Name:            _DYNAMIC
+    Section:         .dynamic
+    Value:           0x28A0
+    Other:           [ STV_HIDDEN ]
+  - Name:            _init
+    Type:            STT_FUNC
+    Section:         .init
+    Value:           0x182C
+    Other:           [ STV_HIDDEN ]
+  - Name:            _fini
+    Type:            STT_FUNC
+    Section:         .fini
+    Value:           0x1844
+    Other:           [ STV_HIDDEN ]
+  - Name:            _start
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x16E0
+    Size:            0x22
+  - Name:            main
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x17D0
+    Size:            0x5B
+  - Name:            data_start
+    Section:         .data
+    Binding:         STB_WEAK
+    Value:           0x3A78
+  - Name:            _IO_stdin_used
+    Type:            STT_OBJECT
+    Section:         .rodata
+    Binding:         STB_GLOBAL
+    Value:           0x588
+    Size:            0x4
+  - Name:            __libc_start_main
+    Type:            STT_FUNC
+    Binding:         STB_GLOBAL
+  - Name:            __data_start
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x3A78
+  - Name:            __gmon_start__
+    Binding:         STB_WEAK
+  - Name:            __register_frame_info
+    Binding:         STB_WEAK
+  - Name:            __cxa_finalize
+    Type:            STT_FUNC
+    Binding:         STB_WEAK
+  - Name:            __deregister_frame_info
+    Binding:         STB_WEAK
+  - Name:            _Z3foov
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x17A0
+    Size:            0x6
+  - Name:            _Z3barv
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x17B0
+    Size:            0x6
+  - Name:            _Z3bazc
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x17C0
+    Size:            0xE
+DynamicSymbols:
+  - Name:            __libc_start_main
+    Type:            STT_FUNC
+    Binding:         STB_GLOBAL
+  - Name:            __gmon_start__
+    Binding:         STB_WEAK
+  - Name:            __register_frame_info
+    Binding:         STB_WEAK
+  - Name:            __cxa_finalize
+    Type:            STT_FUNC
+    Binding:         STB_WEAK
+  - Name:            __deregister_frame_info
+    Binding:         STB_WEAK
+...

>From c2d8d2297742491a9c15eff87ea4da1396a58a67 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 9 Sep 2025 11:27:29 -0700
Subject: [PATCH 07/44] Extend tests to json

---
 .../call-graph-info-callgraph-section.test    | 108 +++++++++++++++---
 llvm/tools/llvm-readobj/ELFDumper.cpp         |  16 +--
 2 files changed, 98 insertions(+), 26 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 60d1c8323f8e8..85a8dce015a62 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -1,26 +1,96 @@
 ## Tests --call-graph-info prints information from call graph section.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=GNU
+# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
+# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
-# CHECK: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
-# CHECK-NEXT: UNKNOWN 6
-# CHECK-NEXT: 20 a
-# CHECK-EMPTY:
-# CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
-# CHECK-NEXT: 10 9
-# CHECK-EMPTY:
-# CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
-# CHECK-NEXT: 6 9
-# CHECK-EMPTY:
-# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
-# CHECK-NEXT: 0 5 5
-#
-#FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)
-# 0 foo
-# 6 bar
-# a baz
-# b qux
+# GNU: llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# GNU-EMPTY:
+# GNU-NEXT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
+# GNU-NEXT: UNKNOWN 6
+# GNU-NEXT: 20 a
+# GNU-EMPTY:
+# GNU-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
+# GNU-NEXT: 10 9
+# GNU-EMPTY:
+# GNU-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
+# GNU-NEXT: 6 9
+# GNU-EMPTY:
+# GNU-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
+# GNU-NEXT: 0 5 5
+
+# LLVM: {{.*}}llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# LLVM: callgraph_info {
+# LLVM-NEXT:  unknown_target_types: [0x6]
+# LLVM-NEXT:  indirect_target_types [
+# LLVM-NEXT:    {
+# LLVM-NEXT:     function_address: 0xA
+# LLVM-NEXT:      type_id: 0x20
+# LLVM-NEXT:    }
+# LLVM-NEXT:  ]
+# LLVM-NEXT:  direct_call_sites [
+# LLVM-NEXT:    {
+# LLVM-NEXT:      caller: 0x0
+# LLVM-NEXT:      call_sites [
+# LLVM-NEXT:        {
+# LLVM-NEXT:          call_site: 0x5
+# LLVM-NEXT:          callee: 0x5
+# LLVM-NEXT:        }
+# LLVM-NEXT:      ]
+# LLVM-NEXT:    }
+# LLVM-NEXT:  ]
+# LLVM-NEXT:  indirect_call_sites [
+# LLVM-NEXT:    {
+# LLVM-NEXT:      caller: 0x6
+# LLVM-NEXT:      call_sites: [0x9]
+# LLVM-NEXT:    }
+# LLVM-NEXT:  ]
+# LLVM-NEXT:  indirect_call_types [
+# LLVM-NEXT:    {
+# LLVM-NEXT:      call_site: 0x9
+# LLVM-NEXT:      type_id: 0x10
+# LLVM-NEXT:    }
+# LLVM-NEXT:  ]
+# LLVM-NEXT: }
+
+# JSON: {{.*}}llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# JSON: "callgraph_info": {
+# JSON-NEXT:      "unknown_target_types": [
+# JSON-NEXT:        6
+# JSON-NEXT:      ],
+# JSON-NEXT:      "indirect_target_types": [
+# JSON-NEXT:        {
+# JSON-NEXT:          "function_address": 10,
+# JSON-NEXT:          "type_id": 32
+# JSON-NEXT:        }
+# JSON-NEXT:      ],
+# JSON-NEXT:      "direct_call_sites": [
+# JSON-NEXT:        {
+# JSON-NEXT:          "caller": 0,
+# JSON-NEXT:          "call_sites": [
+# JSON-NEXT:            {
+# JSON-NEXT:              "call_site": 5,
+# JSON-NEXT:              "callee": 5
+# JSON-NEXT:            }
+# JSON-NEXT:          ]
+# JSON-NEXT:        }
+# JSON-NEXT:      ],
+# JSON-NEXT:      "indirect_call_sites": [
+# JSON-NEXT:        {
+# JSON-NEXT:          "caller": 6,
+# JSON-NEXT:          "call_sites": [
+# JSON-NEXT:            9
+# JSON-NEXT:          ]
+# JSON-NEXT:        }
+# JSON-NEXT:      ],
+# JSON-NEXT:      "indirect_call_types": [
+# JSON-NEXT:        {
+# JSON-NEXT:          "call_site": 9,
+# JSON-NEXT:          "type_id": 16
+# JSON-NEXT:        }
+# JSON-NEXT:      ]
+# JSON-NEXT:    }
 
 .text
 
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 84db935b0c4d2..e1e094637bc0a 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -8330,7 +8330,6 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   // }
 
   {
-    ListScope A(this->W, "indirect_target_types");
     SmallVector<uint64_t, 4> Unknowns;
     for (const auto &El : this->FuncCGInfo) {
       FunctionKind FuncKind = El.second.Kind;
@@ -8341,7 +8340,11 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
       }
     }
     if (!Unknowns.empty())
-      this->W.printHexList("unknown", Unknowns);
+      this->W.printHexList("unknown_target_types", Unknowns);
+  }
+
+  {
+    ListScope ITT(this->W, "indirect_target_types");
     for (auto const &T : this->TypeIdToIndirTargets) {
       for (auto const &Target : T.second) {
         DictScope D(this->W);
@@ -8352,13 +8355,13 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   }
 
   {
-    ListScope A(this->W, "direct_call_sites");
+    ListScope DCT(this->W, "direct_call_sites");
     for (auto const &F : this->FuncCGInfo) {
       if (F.second.DirectCallSites.empty())
         continue;
       DictScope D(this->W);
       this->W.printHex("caller", F.first);
-      ListScope A(this->W, "call_sites");
+      ListScope CT(this->W, "call_sites");
       for (auto const &CS : F.second.DirectCallSites) {
         DictScope D(this->W);
         this->W.printHex("call_site", CS.CallSite);
@@ -8368,19 +8371,18 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   }
 
   {
-    ListScope A(this->W, "indirect_call_sites");
+    ListScope ICT(this->W, "indirect_call_sites");
     for (auto const &F : this->FuncCGInfo) {
       if (F.second.IndirectCallSites.empty())
         continue;
       DictScope D(this->W);
       this->W.printHex("caller", F.first);
-      // ListScope A(this->W, "call_sites");
       this->W.printHexList("call_sites", F.second.IndirectCallSites);
     }
   }
 
   {
-    ListScope A(this->W, "indirect_call_types");
+    ListScope ICT(this->W, "indirect_call_types");
     for (auto const &T : this->TypeIdToIndirCallSites) {
       for (auto const &CS : T.second) {
         DictScope D(this->W);

>From 2874918bc7352627710acd6ff00541556546b6d8 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 9 Sep 2025 15:10:53 -0700
Subject: [PATCH 08/44] Expand all tests to include JSON and LLVM formats.

---
 ...call-graph-info-err-invalid-func-kind.test |   2 +
 ...-info-err-malformed-callgraph-section.test |   4 +-
 ...info-err-malformed-callgraph-section2.test |   2 +
 ...info-err-malformed-callgraph-section3.test |   2 +
 ...all-graph-info-invalid-format-version.test |   2 +
 ...-graph-info-warn-no-callgraph-section.test |   5 +-
 .../llvm-readobj/ELF/call-graph-info.test     | 129 +++++++++++++++++-
 7 files changed, 141 insertions(+), 5 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
index b55717b25fee0..3bea2d057b4d7 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
@@ -3,6 +3,8 @@
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
 # ERR: llvm-readelf: warning: '[[FILE]]': Unknown function kind in .callgraph section.
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
index 459d002d35352..b9219cd64e019 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -2,7 +2,9 @@
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+ 
 # ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
 
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
index 2c84469526552..83e7825de0abb 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
@@ -3,6 +3,8 @@
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
 # ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
index 5bfbd709c78e2..7488d15ca94a7 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -3,6 +3,8 @@
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
 # ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
index a1213db081951..3bac1f1850bef 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
@@ -3,6 +3,8 @@
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
 # ERR: llvm-readelf: warning: '[[FILE]]': Unknown format version in .callgraph section.
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
index cc904dbed2e67..fe0428f6635f6 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
@@ -2,11 +2,10 @@
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
 
 # CHECK: llvm-readelf: warning: '[[FILE]]': No .callgraph section found.
-## Make sure callgraph info header is not printed when there is no .callgraph section
-# CHECK-NOT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
-# CHECK-NOT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
 
 .text
 .globl _Z3foov
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 547b608cc5950..778813197f70b 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -1,6 +1,8 @@
 ## Tests how --call-graph-info prints the call graph information.
 # RUN: yaml2obj --docnum=1 %s -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=LLVM -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=JSON -DFILE=%t
 
 # Yaml input is obtained by compiling the below source to object with:
 #   clang -fexperimental-call-graph-section test.c -o test.o
@@ -54,6 +56,131 @@
 # CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
 # CHECK-NEXT: 17d0 1815 17a0 181a 17b0 1823 17c0
 
+# LLVM: callgraph_info {
+# LLVM-NEXT:   indirect_target_types [
+# LLVM-NEXT:     {
+# LLVM-NEXT:       function_address: 0x17A0
+# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:     }
+# LLVM-NEXT:     {
+# LLVM-NEXT:       function_address: 0x17B0
+# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:     }
+# LLVM-NEXT:     {
+# LLVM-NEXT:       function_address: 0x17C0
+# LLVM-NEXT:       type_id: 0x308E4B8159BC8654
+# LLVM-NEXT:     }
+# LLVM-NEXT:     {
+# LLVM-NEXT:       function_address: 0x17D0
+# LLVM-NEXT:       type_id: 0xA9494DEF81A01DC
+# LLVM-NEXT:     }
+# LLVM-NEXT:   ]
+# LLVM-NEXT:   direct_call_sites [
+# LLVM-NEXT:     {
+# LLVM-NEXT:       caller: 0x17D0
+# LLVM-NEXT:       call_sites [
+# LLVM-NEXT:         {
+# LLVM-NEXT:           call_site: 0x1815
+# LLVM-NEXT:           callee: 0x17A0
+# LLVM-NEXT:         }
+# LLVM-NEXT:         {
+# LLVM-NEXT:           call_site: 0x181A
+# LLVM-NEXT:           callee: 0x17B0
+# LLVM-NEXT:         }
+# LLVM-NEXT:         {
+# LLVM-NEXT:           call_site: 0x1823
+# LLVM-NEXT:           callee: 0x17C0
+# LLVM-NEXT:         }
+# LLVM-NEXT:       ]
+# LLVM-NEXT:     }
+# LLVM-NEXT:   ]
+# LLVM-NEXT:   indirect_call_sites [
+# LLVM-NEXT:     {
+# LLVM-NEXT:       caller: 0x17D0
+# LLVM-NEXT:       call_sites: [0x17ED, 0x17FB, 0x1810]
+# LLVM-NEXT:     }
+# LLVM-NEXT:   ]
+# LLVM-NEXT:   indirect_call_types [
+# LLVM-NEXT:     {
+# LLVM-NEXT:       call_site: 0x17ED
+# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:     }
+# LLVM-NEXT:     {
+# LLVM-NEXT:       call_site: 0x17FB
+# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:     }
+# LLVM-NEXT:     {
+# LLVM-NEXT:       call_site: 0x1810
+# LLVM-NEXT:       type_id: 0x308E4B8159BC8654
+# LLVM-NEXT:     }
+# LLVM-NEXT:   ]
+# LLVM-NEXT: }
+
+#JSON:  "callgraph_info": {
+#JSON-NEXT:      "indirect_target_types": [
+#JSON-NEXT:        {
+#JSON-NEXT:          "function_address": 6048,
+#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:        },
+#JSON-NEXT:        {
+#JSON-NEXT:          "function_address": 6064,
+#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:        },
+#JSON-NEXT:        {
+#JSON-NEXT:          "function_address": 6080,
+#JSON-NEXT:          "type_id": 3498816979441845844
+#JSON-NEXT:        },
+#JSON-NEXT:        {
+#JSON-NEXT:          "function_address": 6096,
+#JSON-NEXT:          "type_id": 762397922298560988
+#JSON-NEXT:        }
+#JSON-NEXT:      ],
+#JSON-NEXT:      "direct_call_sites": [
+#JSON-NEXT:        {
+#JSON-NEXT:          "caller": 6096,
+#JSON-NEXT:          "call_sites": [
+#JSON-NEXT:            {
+#JSON-NEXT:              "call_site": 6165,
+#JSON-NEXT:              "callee": 6048
+#JSON-NEXT:            },
+#JSON-NEXT:            {
+#JSON-NEXT:              "call_site": 6170,
+#JSON-NEXT:              "callee": 6064
+#JSON-NEXT:            },
+#JSON-NEXT:            {
+#JSON-NEXT:              "call_site": 6179,
+#JSON-NEXT:              "callee": 6080
+#JSON-NEXT:            }
+#JSON-NEXT:          ]
+#JSON-NEXT:        }
+#JSON-NEXT:      ],
+#JSON-NEXT:      "indirect_call_sites": [
+#JSON-NEXT:        {
+#JSON-NEXT:          "caller": 6096,
+#JSON-NEXT:          "call_sites": [
+#JSON-NEXT:            6125,
+#JSON-NEXT:            6139,
+#JSON-NEXT:            6160
+#JSON-NEXT:          ]
+#JSON-NEXT:        }
+#JSON-NEXT:      ],
+#JSON-NEXT:      "indirect_call_types": [
+#JSON-NEXT:        {
+#JSON-NEXT:          "call_site": 6125,
+#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:        },
+#JSON-NEXT:        {
+#JSON-NEXT:          "call_site": 6139,
+#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:        },
+#JSON-NEXT:        {
+#JSON-NEXT:          "call_site": 6160,
+#JSON-NEXT:          "type_id": 3498816979441845844
+#JSON-NEXT:        }
+#JSON-NEXT:      ]
+#JSON-NEXT:    }
+#JSON-NEXT:  }
+
 --- !ELF
 FileHeader:
   Class:           ELFCLASS64

>From 9803c62ba1549d02fee2da7e5d7731e747f529cc Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 9 Sep 2025 15:32:18 -0700
Subject: [PATCH 09/44] Improve tests. Match full lines.

---
 .../ELF/call-graph-info-callgraph-section.test            | 4 ++--
 .../ELF/call-graph-info-err-invalid-func-kind.test        | 8 ++++----
 .../call-graph-info-err-malformed-callgraph-section.test  | 8 ++++----
 .../call-graph-info-err-malformed-callgraph-section2.test | 8 ++++----
 .../call-graph-info-err-malformed-callgraph-section3.test | 8 ++++----
 .../ELF/call-graph-info-invalid-format-version.test       | 8 ++++----
 .../ELF/call-graph-info-warn-no-callgraph-section.test    | 8 ++++----
 7 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 85a8dce015a62..fd957e983760d 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -1,11 +1,11 @@
 ## Tests --call-graph-info prints information from call graph section.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=GNU
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines --check-prefix=GNU
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
-# GNU: llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# GNU: {{.*}}llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
 # GNU-EMPTY:
 # GNU-NEXT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
 # GNU-NEXT: UNKNOWN 6
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
index 3bea2d057b4d7..ea1152ddb45e1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
@@ -2,11 +2,11 @@
 ## function kind value.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: llvm-readelf: warning: '[[FILE]]': Unknown function kind in .callgraph section.
+# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Unknown function kind in .callgraph section.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
index b9219cd64e019..c1f7585e2795c 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -1,11 +1,11 @@
 ## Tests that --call-graph-info fails if .callgraph section has invalid size.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
  
-# ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
+# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section. Unexpected size.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
index 83e7825de0abb..4a0a82b5ca99e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
@@ -2,11 +2,11 @@
 ## an expected value, e.g., not as much call sites as the given count.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
+# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section. Parsing error.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
index 7488d15ca94a7..825bafb2e0e93 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -2,11 +2,11 @@
 ## an expected value, e.g., not as much call sites as the given count.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section.
+# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section. Parsing error.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
index 3bac1f1850bef..f7edebc0931b5 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
@@ -2,11 +2,11 @@
 ## version number.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: llvm-readelf: warning: '[[FILE]]': Unknown format version in .callgraph section.
+# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Unknown format version in .callgraph section.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
index fe0428f6635f6..f9e82d0b62bc9 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
@@ -1,11 +1,11 @@
 ## Tests that --call-graph-info warns if there is no .callgraph section.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
-# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
-# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 
-# CHECK: llvm-readelf: warning: '[[FILE]]': No .callgraph section found.
+# CHECK: {{.*}}llvm-readelf: warning: '[[FILE]]': No .callgraph section found.
 
 .text
 .globl _Z3foov

>From 7285210391130dc53639dec4ecf4c2d326d78992 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 10:07:26 -0700
Subject: [PATCH 10/44] Make call graph processing agnostic of target
 architecture pointer width size.

---
 .../call-graph-info-callgraph-section.test    |   6 +-
 ...raph-info-err-invalid-format-version.test} |   2 +-
 ...call-graph-info-err-invalid-func-kind.test |   4 +-
 ...-info-err-malformed-callgraph-section.test |   2 +-
 ...info-err-malformed-callgraph-section2.test |   2 +-
 ...info-err-malformed-callgraph-section3.test |   2 +-
 ...l-graph-info-err-no-callgraph-section.test |  14 +
 ...-graph-info-warn-no-callgraph-section.test |  14 -
 .../llvm-readobj/ELF/call-graph-info.test     |   2 +-
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 272 ++++++++++++------
 10 files changed, 204 insertions(+), 116 deletions(-)
 rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-invalid-format-version.test => call-graph-info-err-invalid-format-version.test} (87%)
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
 delete mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index fd957e983760d..57410588a2a7e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -5,7 +5,7 @@
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
-# GNU: {{.*}}llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# GNU: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # GNU-EMPTY:
 # GNU-NEXT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
 # GNU-NEXT: UNKNOWN 6
@@ -20,7 +20,7 @@
 # GNU-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
 # GNU-NEXT: 0 5 5
 
-# LLVM: {{.*}}llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# LLVM: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # LLVM: callgraph_info {
 # LLVM-NEXT:  unknown_target_types: [0x6]
 # LLVM-NEXT:  indirect_target_types [
@@ -54,7 +54,7 @@
 # LLVM-NEXT:  ]
 # LLVM-NEXT: }
 
-# JSON: {{.*}}llvm-readelf: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets.
+# JSON: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # JSON: "callgraph_info": {
 # JSON-NEXT:      "unknown_target_types": [
 # JSON-NEXT:        6
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
similarity index 87%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
index f7edebc0931b5..233d4bb8f63d4 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
@@ -6,7 +6,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Unknown format version in .callgraph section.
+# ERR: {{.*}}llvm-readelf: error: 'Unknown value': Unknown format version value [1] in .callgraph section.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
index ea1152ddb45e1..eef4eefc7da4c 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
@@ -6,7 +6,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Unknown function kind in .callgraph section.
+# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [FunctionKind] for function at [0x0]': Unknown value [4].
 
 .text
 .globl _Z3foov
@@ -17,5 +17,5 @@ _Z3foov:
 .section	.callgraph,"o", at progbits,.text
 .quad	0   #< Format version number.
 .quad 0   #< Function entry address.
-.quad 3   #< Invalid function kind: must be one of 0, 1, 2.
+.quad 4   #< Invalid function kind: must be one of 0, 1, 2, 3.
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
index c1f7585e2795c..7872b5d749ef9 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -5,7 +5,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
  
-# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section. Unexpected size.
+# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [number of indirect callsites] for function at [0x0]': unexpected end of data at offset 0x19 while reading [0x18, 0x20)
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
index 4a0a82b5ca99e..38dc0eca939a7 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
@@ -6,7 +6,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section. Parsing error.
+# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [indirect target type id] for function at [0x0]': unexpected end of data at offset 0x20 while reading [0x20, 0x28)
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
index 825bafb2e0e93..88204bd28f494 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -6,7 +6,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: warning: '[[FILE]]': Malformed .callgraph section. Parsing error.
+# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [direct callsite PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
new file mode 100644
index 0000000000000..cece28f776b5f
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
@@ -0,0 +1,14 @@
+## Tests that --call-graph-info warns if there is no .callgraph section.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+
+# CHECK: {{.*}}llvm-readelf: error: 'Missing section': No .callgraph section found.
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
deleted file mode 100644
index f9e82d0b62bc9..0000000000000
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-warn-no-callgraph-section.test
+++ /dev/null
@@ -1,14 +0,0 @@
-## Tests that --call-graph-info warns if there is no .callgraph section.
-
-# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
-# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
-# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
-
-# CHECK: {{.*}}llvm-readelf: warning: '[[FILE]]': No .callgraph section found.
-
-.text
-.globl _Z3foov
-.type _Z3foov, at function
-_Z3foov:
- callq _Z3foov at PLT
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 778813197f70b..26ae628135d22 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -1,6 +1,6 @@
 ## Tests how --call-graph-info prints the call graph information.
 # RUN: yaml2obj --docnum=1 %s -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=LLVM -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=JSON -DFILE=%t
 
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index e1e094637bc0a..75cb2c2b55843 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -47,7 +47,10 @@
 #include "llvm/Support/ARMBuildAttributes.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -74,6 +77,7 @@
 #include <optional>
 #include <string>
 #include <system_error>
+#include <utility>
 #include <vector>
 
 using namespace llvm;
@@ -183,30 +187,31 @@ struct GroupSection {
   std::vector<GroupMember> Members;
 };
 
-// Per-function Callgraph information.
-struct FunctionCallgraphInfo {
-  enum class FunctionKind : uint64_t {
-    // Function cannot be target to indirect calls.
-    NOT_INDIRECT_TARGET = 0,
-    // Function may be target to indirect calls but its type id is unknown.
-    INDIRECT_TARGET_UNKNOWN_TID = 1,
-    // Function may be target to indirect calls and its type id is known.
-    INDIRECT_TARGET_KNOWN_TID = 2,
-
-    // Available in the binary but not listed in the call graph section.
-    NOT_LISTED = 3,
-  };
+// Call graph function kind.
+enum class FunctionKind : uint64_t {
+  // Function cannot be target to indirect calls.
+  NOT_INDIRECT_TARGET = 0,
+  // Function may be target to indirect calls but its type id is unknown.
+  INDIRECT_TARGET_UNKNOWN_TID = 1,
+  // Function may be target to indirect calls and its type id is known.
+  INDIRECT_TARGET_KNOWN_TID = 2,
+
+  // Available in the binary but not listed in the call graph section.
+  NOT_LISTED = 3,
+};
+
+// Per-function call graph information.
+template <typename AddrType> struct FunctionCallgraphInfoImpl {
   FunctionKind Kind;
   struct DirectCallSite {
-    uint64_t CallSite;
-    uint64_t Callee;
-    DirectCallSite(uint64_t CallSite, uint64_t Callee)
+    AddrType CallSite;
+    AddrType Callee;
+    DirectCallSite(AddrType CallSite, AddrType Callee)
         : CallSite(CallSite), Callee(Callee) {}
   };
   SmallVector<DirectCallSite> DirectCallSites;
-  SmallVector<uint64_t> IndirectCallSites;
+  SmallVector<AddrType> IndirectCallSites;
 };
-typedef FunctionCallgraphInfo::FunctionKind FunctionKind;
 
 namespace {
 
@@ -458,15 +463,18 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
       const typename SFrameParser<ELFT::Endianness>::FDERange::iterator FDE,
       ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab);
 
+  using FunctionCallgraphInfo =
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
+
   // Callgraph - Main data structure to maintain per function callgraph
   // information.
-  MapVector<uint64_t, FunctionCallgraphInfo> FuncCGInfo;
+  MapVector<typename ELFT::uint, FunctionCallgraphInfo> FuncCGInfo;
   // Callgraph - 64 bit type id mapped to indirect callsites whose potential
   // callee(s) should be of given type id.
-  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirCallSites;
+  MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirCallSites;
   // Callgraph - 64 bit type id mapped to entry PC addresses of functions which
   // are of the given type id.
-  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
+  MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirTargets;
   // Callgraph - Read callgraph section and process its contents to populate
   // Callgraph related data structures which will be used to dump callgraph
   // info. Returns false if there is no .callgraph section in the input file.
@@ -5321,93 +5329,170 @@ getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
 }
 
 template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
-  std::optional<object::SectionRef> CallGraphSection =
-      getCallGraphSection(ObjF);
-  if (!CallGraphSection.has_value()) {
-    reportUniqueWarning("No .callgraph section found.");
+  const Elf_Shdr *CGSection = findSectionByName(".callgraph");
+  if (!CGSection) {
+    Error NoSectionErr = createError("No .callgraph section found.");    
+    reportError(std::move(NoSectionErr), "Missing section");
     return false;
   }
 
-  StringRef CGSecContents = cantFail(CallGraphSection.value().getContents());
-  // TODO: some entries are written in pointer size. are they always 64-bit?
-  if (CGSecContents.size() % sizeof(uint64_t)) {
-    reportUniqueWarning("Malformed .callgraph section. Unexpected size.");
-    exit(1);
+  Expected<ArrayRef<uint8_t>> SectionBytesOrErr =
+      Obj.getSectionContents(*CGSection);
+  if (!SectionBytesOrErr) {
+    Error SectionReadErr = SectionBytesOrErr.takeError();
+    reportError(std::move(SectionReadErr), "Unable to read the .callgraph section");
+    return false;
   }
 
-  size_t Size = CGSecContents.size() / sizeof(uint64_t);
-  auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
-  const auto *const End = It + Size;
-
-  auto CGHasNext = [&]() { return It < End; };
-  auto CGNext = [&]() -> uint64_t {
-    if (!CGHasNext()) {
-      reportUniqueWarning("Malformed .callgraph section. Parsing error.");
-      exit(1);
-    }
-    return *It++;
+  auto PrintMalformedError = [&](Error &E, Twine FuncPC, StringRef Component) {
+    // auto Msg = llvm::Twine("Malformed callgraph section while reading [") +
+    // Component + llvm::Twine("] .\n");
+    std::string Msg =
+        (StringRef("While reading call graph info's [") + Component +
+         StringRef("] for function at [0x") + StringRef(FuncPC.str()) + "]")
+            .str();
+    reportError(std::move(E), StringRef(Msg));
   };
 
-  while (CGHasNext()) {
+  DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(),
+                     ObjF.getBytesInAddress());
+
+  uint64_t NotListedCount = 0;
+  uint64_t UnknownCount = 0;
+
+  uint64_t Offset = 0;
+  while (Offset < CGSection->sh_size) {
+    Error CGSectionErr = Error::success();
     // Format version number.
-    uint64_t FormatVersionNumber = CGNext();
-    if (FormatVersionNumber != 0) {
-      reportUniqueWarning("Unknown format version in .callgraph section.");
-      exit(1);
-    }
-    // Function entry pc.
-    uint64_t FuncEntryPc = CGNext();
-    // Function kind.
-    uint64_t Kind = CGNext();
-    switch (Kind) {
-    case 0: // not an indirect target
-      FuncCGInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
-      break;
-    case 1: // indirect target with unknown type id
-      FuncCGInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
-      break;
-    case 2: // indirect target with known type id
-      FuncCGInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
-      TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
-      break;
-    default:
-      this->reportUniqueWarning("Unknown function kind in .callgraph section.");
-      exit(1);
-    }
-    // Read indirect call sites info.
-    uint64_t IndirectCallSiteCount = CGNext();
-    for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
-      uint64_t TypeId = CGNext();
-      uint64_t CallSitePc = CGNext();
-      TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
-      FuncCGInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
+    uint64_t FormatVersionNumber = Data.getU64(&Offset, &CGSectionErr);
+
+    if (CGSectionErr) {
+      reportError(std::move(CGSectionErr),
+                  "While reading call graph info FormatVersionNumber");
+      return false;
     }
-    // Read direct call sites info.
-    uint64_t DirectCallSiteCount = CGNext();
-    for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
-      uint64_t CallSitePc = CGNext();
-      uint64_t CalleePc = CGNext();
-      FuncCGInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
-                                                           CalleePc);
+
+    if (FormatVersionNumber != 0) {      
+      Error FormatErr = createError("Unknown format version value [" + std::to_string(FormatVersionNumber) + "] in .callgraph section.");
+      reportError(std::move(FormatErr), "Unknown value");
+      return false;
     }
-  }
 
-  // Sort function info by function PC.
-  llvm::sort(FuncCGInfo,
-             [](const auto &A, const auto &B) { return A.first < B.first; });
+    // Read function address.
+    typename ELFT::uint FuncAddr =
+        Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr);
+    if (CGSectionErr) {
+      reportError(std::move(CGSectionErr),
+                  "While reading call graph info function entry PC");
+      return false;
+    }
 
-  uint64_t NotListedCount = 0;
-  uint64_t UnknownCount = 0;
-  for (const auto &El : FuncCGInfo) {
-    NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
-    UnknownCount += El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+    if (FuncCGInfo.find(FuncAddr) != FuncCGInfo.end()) {
+      Error DuplicatePcErr = createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
+      reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");      
+      return false;
+    }
+
+    // Read function kind.
+    uint64_t KindVal = Data.getU64(&Offset, &CGSectionErr);
+    if (CGSectionErr) {
+      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), "Kind");
+      return false;
+    }
+
+    if (KindVal > 3) {
+      Error KindErr = createError("Unknown value [" + std::to_string(KindVal) + "].");
+      PrintMalformedError(KindErr, Twine::utohexstr(FuncAddr), "FunctionKind");
+      return false;
+    }
+
+    FunctionKind Kind = static_cast<FunctionKind>(KindVal);
+    if (Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) {
+      // Read type id if this function is an indirect call target.
+      uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
+      if (CGSectionErr) {
+        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                            "indirect type id");
+        return false;
+      }
+      TypeIdToIndirTargets[TypeId].push_back(FuncAddr);
+    }
+    if (Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
+      UnknownCount++;
+    if (Kind == FunctionKind::NOT_LISTED)
+      NotListedCount++;
+
+    using FunctionCallgraphInfo =
+        ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
+
+    // Create a new entry for this function.
+    FunctionCallgraphInfo CGInfo;
+    CGInfo.Kind = Kind;
+
+    // Read number of indirect call sites for this function.
+    uint64_t NumIndirectCallSites = Data.getU64(&Offset, &CGSectionErr);
+    if (CGSectionErr) {
+      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                          "number of indirect callsites");
+      return false;
+    }
+
+    for (unsigned long I = 0; I < NumIndirectCallSites; I++) {
+      uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
+      if (CGSectionErr) {
+        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                            "indirect target type id");
+        return false;
+      }
+      typename ELFT::uint CallSitePc =
+          Data.getUnsigned(&Offset, sizeof(CallSitePc), &CGSectionErr);
+      if (CGSectionErr) {
+        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                            "indirect callsite PC");
+        return false;
+      }
+      TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
+      CGInfo.IndirectCallSites.push_back(CallSitePc);
+    }
+
+    // Read number of direct call sites for this function.
+    uint64_t NumDirectCallSites = Data.getU64(&Offset, &CGSectionErr);
+    if (CGSectionErr) {
+      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                          "number of direct callsites");
+      return false;
+    }
+    // Read direct call sites and populate FuncCGInfo.
+    for (uint64_t I = 0; I < NumDirectCallSites; ++I) {
+      typename ELFT::uint CallSite =
+          Data.getUnsigned(&Offset, sizeof(CallSite), &CGSectionErr);
+      if (CGSectionErr) {
+        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                            "direct callsite PC");
+        return false;
+      }
+      typename ELFT::uint Callee =
+          Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
+      if (CGSectionErr) {
+        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                            "indirect callee PC");
+        return false;
+      }
+      CGInfo.DirectCallSites.emplace_back(CallSite, Callee);
+    }
+    FuncCGInfo[FuncAddr] = CGInfo;
   }
+
   if (NotListedCount)
-    reportUniqueWarning("callgraph section does not have information for " +
+    reportUniqueWarning(".callgraph section does not have information for " +
                         std::to_string(NotListedCount) + " functions.");
   if (UnknownCount)
-    reportUniqueWarning("callgraph section has unknown type id for " +
+    reportUniqueWarning(".callgraph section has unknown type id for " +
                         std::to_string(UnknownCount) + " indirect targets.");
+
+  // Sort function info by function PC.
+  llvm::sort(FuncCGInfo,
+             [](const auto &A, const auto &B) { return A.first < B.first; });
   return true;
 }
 
@@ -5465,6 +5550,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     }
   }
 
+  using FunctionCallgraphInfo =
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
+
   // Print function entry to direct call site and target function entry
   // addresses mapping from disasm.
   OS << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])";
@@ -5473,7 +5561,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     auto FuncDirCallSites = El.second.DirectCallSites;
     if (!FuncDirCallSites.empty()) {
       OS << "\n" << format("%lx", CallerPc);
-      for (const FunctionCallgraphInfo::DirectCallSite &DCS :
+      for (typename FunctionCallgraphInfo::DirectCallSite &DCS :
            FuncDirCallSites) {
         OS << " " << format("%lx", DCS.CallSite) << " "
            << format("%lx", DCS.Callee);

>From 8236798676b23483707386fa9701113932310835 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 10:46:54 -0700
Subject: [PATCH 11/44] Remove unnecessary function returns after reportError
 calls which bottoms out to llvm_unreachable.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 52 ++++++++-------------------
 1 file changed, 14 insertions(+), 38 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 75cb2c2b55843..3da54edd27d28 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5331,17 +5331,16 @@ getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
 template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   const Elf_Shdr *CGSection = findSectionByName(".callgraph");
   if (!CGSection) {
-    Error NoSectionErr = createError("No .callgraph section found.");    
+    Error NoSectionErr = createError("No .callgraph section found.");
     reportError(std::move(NoSectionErr), "Missing section");
-    return false;
   }
 
   Expected<ArrayRef<uint8_t>> SectionBytesOrErr =
       Obj.getSectionContents(*CGSection);
   if (!SectionBytesOrErr) {
     Error SectionReadErr = SectionBytesOrErr.takeError();
-    reportError(std::move(SectionReadErr), "Unable to read the .callgraph section");
-    return false;
+    reportError(std::move(SectionReadErr),
+                "Unable to read the .callgraph section");
   }
 
   auto PrintMalformedError = [&](Error &E, Twine FuncPC, StringRef Component) {
@@ -5366,55 +5365,44 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     // Format version number.
     uint64_t FormatVersionNumber = Data.getU64(&Offset, &CGSectionErr);
 
-    if (CGSectionErr) {
+    if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info FormatVersionNumber");
-      return false;
-    }
 
     if (FormatVersionNumber != 0) {      
       Error FormatErr = createError("Unknown format version value [" + std::to_string(FormatVersionNumber) + "] in .callgraph section.");
       reportError(std::move(FormatErr), "Unknown value");
-      return false;
     }
 
     // Read function address.
     typename ELFT::uint FuncAddr =
         Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr);
-    if (CGSectionErr) {
+    if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info function entry PC");
-      return false;
-    }
 
     if (FuncCGInfo.find(FuncAddr) != FuncCGInfo.end()) {
       Error DuplicatePcErr = createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
-      reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");      
-      return false;
+      reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
     }
 
     // Read function kind.
     uint64_t KindVal = Data.getU64(&Offset, &CGSectionErr);
-    if (CGSectionErr) {
+    if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), "Kind");
-      return false;
-    }
 
     if (KindVal > 3) {
       Error KindErr = createError("Unknown value [" + std::to_string(KindVal) + "].");
       PrintMalformedError(KindErr, Twine::utohexstr(FuncAddr), "FunctionKind");
-      return false;
     }
 
     FunctionKind Kind = static_cast<FunctionKind>(KindVal);
     if (Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) {
       // Read type id if this function is an indirect call target.
       uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
-      if (CGSectionErr) {
+      if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "indirect type id");
-        return false;
-      }
       TypeIdToIndirTargets[TypeId].push_back(FuncAddr);
     }
     if (Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
@@ -5431,53 +5419,41 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
 
     // Read number of indirect call sites for this function.
     uint64_t NumIndirectCallSites = Data.getU64(&Offset, &CGSectionErr);
-    if (CGSectionErr) {
+    if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of indirect callsites");
-      return false;
-    }
 
     for (unsigned long I = 0; I < NumIndirectCallSites; I++) {
       uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
-      if (CGSectionErr) {
+      if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "indirect target type id");
-        return false;
-      }
       typename ELFT::uint CallSitePc =
           Data.getUnsigned(&Offset, sizeof(CallSitePc), &CGSectionErr);
-      if (CGSectionErr) {
+      if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "indirect callsite PC");
-        return false;
-      }
       TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
       CGInfo.IndirectCallSites.push_back(CallSitePc);
     }
 
     // Read number of direct call sites for this function.
     uint64_t NumDirectCallSites = Data.getU64(&Offset, &CGSectionErr);
-    if (CGSectionErr) {
+    if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of direct callsites");
-      return false;
-    }
     // Read direct call sites and populate FuncCGInfo.
     for (uint64_t I = 0; I < NumDirectCallSites; ++I) {
       typename ELFT::uint CallSite =
           Data.getUnsigned(&Offset, sizeof(CallSite), &CGSectionErr);
-      if (CGSectionErr) {
+      if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "direct callsite PC");
-        return false;
-      }
       typename ELFT::uint Callee =
           Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
-      if (CGSectionErr) {
+      if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "indirect callee PC");
-        return false;
-      }
       CGInfo.DirectCallSites.emplace_back(CallSite, Callee);
     }
     FuncCGInfo[FuncAddr] = CGInfo;

>From abe189ecc5f6d7e8a3f26b59e8f7e7928d9e08e7 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 12:06:01 -0700
Subject: [PATCH 12/44] Formatting fixes.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 3da54edd27d28..1d1d87f4baee3 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5369,8 +5369,10 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       reportError(std::move(CGSectionErr),
                   "While reading call graph info FormatVersionNumber");
 
-    if (FormatVersionNumber != 0) {      
-      Error FormatErr = createError("Unknown format version value [" + std::to_string(FormatVersionNumber) + "] in .callgraph section.");
+    if (FormatVersionNumber != 0) {
+      Error FormatErr = createError("Unknown format version value [" +
+                                    std::to_string(FormatVersionNumber) +
+                                    "] in .callgraph section.");
       reportError(std::move(FormatErr), "Unknown value");
     }
 
@@ -5382,7 +5384,8 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                   "While reading call graph info function entry PC");
 
     if (FuncCGInfo.find(FuncAddr) != FuncCGInfo.end()) {
-      Error DuplicatePcErr = createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
+      Error DuplicatePcErr =
+          createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
       reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
     }
 
@@ -5392,7 +5395,8 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), "Kind");
 
     if (KindVal > 3) {
-      Error KindErr = createError("Unknown value [" + std::to_string(KindVal) + "].");
+      Error KindErr =
+          createError("Unknown value [" + std::to_string(KindVal) + "].");
       PrintMalformedError(KindErr, Twine::utohexstr(FuncAddr), "FunctionKind");
     }
 

>From 0b9c46d2c8d8ce9aa39722752e15353ec5cb290e Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 20:07:50 -0700
Subject: [PATCH 13/44] Remove callsites from direct call data.

---
 .../call-graph-info-callgraph-section.test    |   7 +-
 ...info-err-malformed-callgraph-section3.test |   2 +-
 .../llvm-readobj/ELF/call-graph-info.test     | 300 +++++++++++-------
 llvm/tools/llvm-readobj/ELFDumper.cpp         |  53 ++--
 4 files changed, 203 insertions(+), 159 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 57410588a2a7e..f945bbaa0bf02 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -18,7 +18,7 @@
 # GNU-NEXT: 6 9
 # GNU-EMPTY:
 # GNU-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
-# GNU-NEXT: 0 5 5
+# GNU-NEXT: 0 5
 
 # LLVM: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # LLVM: callgraph_info {
@@ -34,7 +34,6 @@
 # LLVM-NEXT:      caller: 0x0
 # LLVM-NEXT:      call_sites [
 # LLVM-NEXT:        {
-# LLVM-NEXT:          call_site: 0x5
 # LLVM-NEXT:          callee: 0x5
 # LLVM-NEXT:        }
 # LLVM-NEXT:      ]
@@ -70,7 +69,6 @@
 # JSON-NEXT:          "caller": 0,
 # JSON-NEXT:          "call_sites": [
 # JSON-NEXT:            {
-# JSON-NEXT:              "call_site": 5,
 # JSON-NEXT:              "callee": 5
 # JSON-NEXT:            }
 # JSON-NEXT:          ]
@@ -122,8 +120,7 @@ qux:                  #< qux is at 11 (b).
 .quad	0       #< foo()'s entry address.
 .quad	0       #< Function kind: not an indirect target.
 .quad	0       #< Count of indirect call sites that follow: 0.
-.quad   1       #< Count of direct call sites that follow: 1>
-.quad   5       #< Direct callsite call to foo>
+.quad   1       #< Count of direct callees that follow: 1>
 .quad   5       #< Direct callee foo's address>
 
 .quad	0       #< Format version number.
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
index 88204bd28f494..4ec11f2e65638 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -6,7 +6,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [direct callsite PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)
+# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [direct callee PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)
 
 .text
 .globl _Z3foov
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 26ae628135d22..0e67b80a95615 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -1,6 +1,6 @@
 ## Tests how --call-graph-info prints the call graph information.
 # RUN: yaml2obj --docnum=1 %s -o %t
-# llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=LLVM -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=JSON -DFILE=%t
 
@@ -42,75 +42,72 @@
 #   }
 
 # CHECK: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
-# CHECK-NEXT: f85c699bb8ef20a2 17a0 17b0
-# CHECK-NEXT: 308e4b8159bc8654 17c0
-# CHECK-NEXT: a9494def81a01dc 17d0
+# CHECK-NEXT: 3ecbeef531f74424 1790 17a0
+# CHECK-NEXT: 308e4b8159bc8654 17b0
+# CHECK-NEXT: fa6809609a76afca 17c0
 # CHECK-EMPTY:
 # CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
-# CHECK-NEXT: f85c699bb8ef20a2 17ed 17fb
-# CHECK-NEXT: 308e4b8159bc8654 1810
+# CHECK-NEXT: 3ecbeef531f74424 17df 17ef
+# CHECK-NEXT: 308e4b8159bc8654 1804
 # CHECK-EMPTY:
 # CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
-# CHECK-NEXT: 17d0 17ed 17fb 1810
+# CHECK-NEXT: 17c0 17df 17ef 1804
 # CHECK-EMPTY:
 # CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
-# CHECK-NEXT: 17d0 1815 17a0 181a 17b0 1823 17c0
+# CHECK-NEXT: 17c0 1790 17a0 17b0
 
 # LLVM: callgraph_info {
 # LLVM-NEXT:   indirect_target_types [
 # LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17A0
-# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:       function_address: 0x1790
+# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
 # LLVM-NEXT:     }
 # LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17B0
-# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:       function_address: 0x17A0
+# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
 # LLVM-NEXT:     }
 # LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17C0
+# LLVM-NEXT:       function_address: 0x17B0
 # LLVM-NEXT:       type_id: 0x308E4B8159BC8654
 # LLVM-NEXT:     }
 # LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17D0
-# LLVM-NEXT:       type_id: 0xA9494DEF81A01DC
+# LLVM-NEXT:       function_address: 0x17C0
+# LLVM-NEXT:       type_id: 0xFA6809609A76AFCA
 # LLVM-NEXT:     }
 # LLVM-NEXT:   ]
 # LLVM-NEXT:   direct_call_sites [
 # LLVM-NEXT:     {
-# LLVM-NEXT:       caller: 0x17D0
+# LLVM-NEXT:       caller: 0x17C0
 # LLVM-NEXT:       call_sites [
 # LLVM-NEXT:         {
-# LLVM-NEXT:           call_site: 0x1815
-# LLVM-NEXT:           callee: 0x17A0
+# LLVM-NEXT:           callee: 0x1790
 # LLVM-NEXT:         }
 # LLVM-NEXT:         {
-# LLVM-NEXT:           call_site: 0x181A
-# LLVM-NEXT:           callee: 0x17B0
+# LLVM-NEXT:           callee: 0x17A0
 # LLVM-NEXT:         }
 # LLVM-NEXT:         {
-# LLVM-NEXT:           call_site: 0x1823
-# LLVM-NEXT:           callee: 0x17C0
+# LLVM-NEXT:           callee: 0x17B0
 # LLVM-NEXT:         }
 # LLVM-NEXT:       ]
 # LLVM-NEXT:     }
 # LLVM-NEXT:   ]
 # LLVM-NEXT:   indirect_call_sites [
 # LLVM-NEXT:     {
-# LLVM-NEXT:       caller: 0x17D0
-# LLVM-NEXT:       call_sites: [0x17ED, 0x17FB, 0x1810]
+# LLVM-NEXT:       caller: 0x17C0
+# LLVM-NEXT:       call_sites: [0x17DF, 0x17EF, 0x1804]
 # LLVM-NEXT:     }
 # LLVM-NEXT:   ]
 # LLVM-NEXT:   indirect_call_types [
 # LLVM-NEXT:     {
-# LLVM-NEXT:       call_site: 0x17ED
-# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:       call_site: 0x17DF
+# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
 # LLVM-NEXT:     }
 # LLVM-NEXT:     {
-# LLVM-NEXT:       call_site: 0x17FB
-# LLVM-NEXT:       type_id: 0xF85C699BB8EF20A2
+# LLVM-NEXT:       call_site: 0x17EF
+# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
 # LLVM-NEXT:     }
 # LLVM-NEXT:     {
-# LLVM-NEXT:       call_site: 0x1810
+# LLVM-NEXT:       call_site: 0x1804
 # LLVM-NEXT:       type_id: 0x308E4B8159BC8654
 # LLVM-NEXT:     }
 # LLVM-NEXT:   ]
@@ -119,62 +116,59 @@
 #JSON:  "callgraph_info": {
 #JSON-NEXT:      "indirect_target_types": [
 #JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6048,
-#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:          "function_address": 6032,
+#JSON-NEXT:          "type_id": 4524972987496481828
 #JSON-NEXT:        },
 #JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6064,
-#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:          "function_address": 6048,
+#JSON-NEXT:          "type_id": 4524972987496481828
 #JSON-NEXT:        },
 #JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6080,
+#JSON-NEXT:          "function_address": 6064,
 #JSON-NEXT:          "type_id": 3498816979441845844
 #JSON-NEXT:        },
 #JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6096,
-#JSON-NEXT:          "type_id": 762397922298560988
+#JSON-NEXT:          "function_address": 6080,
+#JSON-NEXT:          "type_id": 18043682217572872138
 #JSON-NEXT:        }
 #JSON-NEXT:      ],
 #JSON-NEXT:      "direct_call_sites": [
 #JSON-NEXT:        {
-#JSON-NEXT:          "caller": 6096,
+#JSON-NEXT:          "caller": 6080,
 #JSON-NEXT:          "call_sites": [
 #JSON-NEXT:            {
-#JSON-NEXT:              "call_site": 6165,
-#JSON-NEXT:              "callee": 6048
+#JSON-NEXT:              "callee": 6032
 #JSON-NEXT:            },
 #JSON-NEXT:            {
-#JSON-NEXT:              "call_site": 6170,
-#JSON-NEXT:              "callee": 6064
+#JSON-NEXT:              "callee": 6048
 #JSON-NEXT:            },
 #JSON-NEXT:            {
-#JSON-NEXT:              "call_site": 6179,
-#JSON-NEXT:              "callee": 6080
+#JSON-NEXT:              "callee": 6064
 #JSON-NEXT:            }
 #JSON-NEXT:          ]
 #JSON-NEXT:        }
 #JSON-NEXT:      ],
 #JSON-NEXT:      "indirect_call_sites": [
 #JSON-NEXT:        {
-#JSON-NEXT:          "caller": 6096,
+#JSON-NEXT:          "caller": 6080,
 #JSON-NEXT:          "call_sites": [
-#JSON-NEXT:            6125,
-#JSON-NEXT:            6139,
-#JSON-NEXT:            6160
+#JSON-NEXT:            6111,
+#JSON-NEXT:            6127,
+#JSON-NEXT:            6148
 #JSON-NEXT:          ]
 #JSON-NEXT:        }
 #JSON-NEXT:      ],
 #JSON-NEXT:      "indirect_call_types": [
 #JSON-NEXT:        {
-#JSON-NEXT:          "call_site": 6125,
-#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:          "call_site": 6111,
+#JSON-NEXT:          "type_id": 4524972987496481828
 #JSON-NEXT:        },
 #JSON-NEXT:        {
-#JSON-NEXT:          "call_site": 6139,
-#JSON-NEXT:          "type_id": 17896295136807035042
+#JSON-NEXT:          "call_site": 6127,
+#JSON-NEXT:          "type_id": 4524972987496481828
 #JSON-NEXT:        },
 #JSON-NEXT:        {
-#JSON-NEXT:          "call_site": 6160,
+#JSON-NEXT:          "call_site": 6148,
 #JSON-NEXT:          "type_id": 3498816979441845844
 #JSON-NEXT:        }
 #JSON-NEXT:      ]
@@ -187,7 +181,77 @@ FileHeader:
   Data:            ELFDATA2LSB
   Type:            ET_DYN
   Machine:         EM_X86_64
-  Entry:           0x16E0
+  Entry:           0x16D0
+ProgramHeaders:
+  - Type:            PT_PHDR
+    Flags:           [ PF_R ]
+    VAddr:           0x40
+    Align:           0x8
+    Offset:          0x40
+  - Type:            PT_INTERP
+    Flags:           [ PF_R ]
+    FirstSec:        .interp
+    LastSec:         .interp
+    VAddr:           0x2A8
+    Offset:          0x2A8
+  - Type:            PT_LOAD
+    Flags:           [ PF_R ]
+    FirstSec:        .interp
+    LastSec:         .eh_frame
+    Align:           0x1000
+    Offset:          0x0
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    FirstSec:        .text
+    LastSec:         .plt
+    VAddr:           0x16D0
+    Align:           0x1000
+    Offset:          0x6D0
+  - Type:            PT_LOAD
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .init_array
+    LastSec:         .relro_padding
+    VAddr:           0x2890
+    Align:           0x1000
+    Offset:          0x890
+  - Type:            PT_LOAD
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .data
+    LastSec:         .bss
+    VAddr:           0x3A68
+    Align:           0x1000
+    Offset:          0xA68
+  - Type:            PT_DYNAMIC
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .dynamic
+    LastSec:         .dynamic
+    VAddr:           0x28A0
+    Align:           0x8
+    Offset:          0x8A0
+  - Type:            PT_GNU_RELRO
+    Flags:           [ PF_R ]
+    FirstSec:        .init_array
+    LastSec:         .relro_padding
+    VAddr:           0x2890
+    Offset:          0x890
+  - Type:            PT_GNU_EH_FRAME
+    Flags:           [ PF_R ]
+    FirstSec:        .eh_frame_hdr
+    LastSec:         .eh_frame_hdr
+    VAddr:           0x584
+    Align:           0x4
+    Offset:          0x584
+  - Type:            PT_GNU_STACK
+    Flags:           [ PF_W, PF_R ]
+    Align:           0x0
+    Offset:          0x0
+  - Type:            PT_NOTE
+    Flags:           [ PF_R ]
+    FirstSec:        .note.ABI-tag
+    LastSec:         .note.gnu.build-id
+    VAddr:           0x2C4
+    Align:           0x4
+    Offset:          0x2C4
 Sections:
   - Name:            .interp
     Type:            SHT_PROGBITS
@@ -211,7 +275,7 @@ Sections:
     AddressAlign:    0x4
     Notes:
       - Name:            GNU
-        Desc:            3E3D197EC968B0AD7D4A3809E7355CA3BE816929
+        Desc:            EB389D8FA0BAAB684147C8107EEA8143CBF335AC
         Type:            NT_PRPSINFO
   - Name:            .dynsym
     Type:            SHT_DYNSYM
@@ -264,86 +328,86 @@ Sections:
   - Name:            .rela.dyn
     Type:            SHT_RELA
     Flags:           [ SHF_ALLOC ]
-    Address:         0x480
+    Address:         0x478
     Link:            .dynsym
     AddressAlign:    0x8
     Relocations:
       - Offset:          0x2890
         Type:            R_X86_64_RELATIVE
-        Addend:          5904
+        Addend:          5888
       - Offset:          0x2898
         Type:            R_X86_64_RELATIVE
-        Addend:          5968
-      - Offset:          0x3A80
+        Addend:          5952
+      - Offset:          0x3A70
         Type:            R_X86_64_RELATIVE
-        Addend:          14976
-      - Offset:          0x2A50
+        Addend:          14960
+      - Offset:          0x2A40
         Symbol:          __libc_start_main
         Type:            R_X86_64_GLOB_DAT
-      - Offset:          0x2A58
+      - Offset:          0x2A48
         Symbol:          __gmon_start__
         Type:            R_X86_64_GLOB_DAT
-      - Offset:          0x2A60
+      - Offset:          0x2A50
         Symbol:          __register_frame_info
         Type:            R_X86_64_GLOB_DAT
-      - Offset:          0x2A68
+      - Offset:          0x2A58
         Symbol:          __cxa_finalize
         Type:            R_X86_64_GLOB_DAT
-      - Offset:          0x2A70
+      - Offset:          0x2A60
         Symbol:          __deregister_frame_info
         Type:            R_X86_64_GLOB_DAT
   - Name:            .rela.plt
     Type:            SHT_RELA
     Flags:           [ SHF_ALLOC, SHF_INFO_LINK ]
-    Address:         0x540
+    Address:         0x538
     Link:            .dynsym
     AddressAlign:    0x8
     Info:            .got.plt
     Relocations:
-      - Offset:          0x3AA0
+      - Offset:          0x3A90
         Symbol:          __register_frame_info
         Type:            R_X86_64_JUMP_SLOT
-      - Offset:          0x3AA8
+      - Offset:          0x3A98
         Symbol:          __cxa_finalize
         Type:            R_X86_64_JUMP_SLOT
-      - Offset:          0x3AB0
+      - Offset:          0x3AA0
         Symbol:          __deregister_frame_info
         Type:            R_X86_64_JUMP_SLOT
   - Name:            .rodata
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC, SHF_MERGE ]
-    Address:         0x588
+    Address:         0x580
     AddressAlign:    0x4
     EntSize:         0x4
     Content:         '01000200'
   - Name:            .eh_frame_hdr
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC ]
-    Address:         0x58C
+    Address:         0x584
     AddressAlign:    0x4
-    Content:         011B033B4000000007000000541100005C0000008411000088000000C4110000A800000014120000C800000024120000E800000034120000080100004412000028010000
+    Content:         011B033B40000000070000004C1100005C0000007C11000088000000BC110000A80000000C120000C80000001C120000E80000002C120000080100003C12000028010000
   - Name:            .eh_frame
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC ]
-    Address:         0x5D0
+    Address:         0x5C8
     AddressAlign:    0x8
-    Content:         1400000000000000017A5200017810011B0C070890010710100000001C000000F010000022000000000000001400000000000000017A5200017810011B0C0708900100001C0000001C000000F41000003800000000410E108602430D06730C07080000001C0000003C000000141100004A00000000410E108602430D0602450C070800001C0000005C000000441100000600000000410E108602430D06410C07080000001C0000007C000000341100000600000000410E108602430D06410C07080000001C0000009C000000241100000E00000000410E108602430D06490C07080000001C000000BC000000141100005B00000000410E108602430D0602560C0708000000000000
+    Content:         1400000000000000017A5200017810011B0C070890010710100000001C000000E810000022000000000000001400000000000000017A5200017810011B0C0708900100001C0000001C000000EC1000003800000000410E108602430D06730C07080000001C0000003C0000000C1100004A00000000410E108602430D0602450C070800001C0000005C0000003C1100000600000000410E108602430D06410C07080000001C0000007C0000002C1100000600000000410E108602430D06410C07080000001C0000009C0000001C1100000E00000000410E108602430D06490C07080000001C000000BC0000000C1100005F00000000410E108602430D06025A0C0708000000000000
   - Name:            .text
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x16E0
+    Address:         0x16D0
     AddressAlign:    0x10
-    Content:         31ED4989D15E4889E24883E4F050544531C031C9488D3DD5000000FF154F130000F4CCCCCCCCCCCCCCCCCCCCCCCCCCCC554889E5F6059D230000017402EB27C6059223000001488B05331300004883F8007413488D3DC2EEFFFF488D357F230000E81A0100005DC30F1F840000000000554889E5F605A5230000017402EB39C6059A23000001488B05FB1200004883F800740C488B3D06230000E8F1000000488B05EA1200004883F800740C488D3D69EEFFFFE8E80000005DC3CCCCCCCCCCCC554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC30C745FC00000000488D05BAFFFFFF488945F0FF55F0488D05BCFFFFFF488945E8FF55E8488D05BEFFFFFF488945D8488B45D80FBE7DE7FFD0E88BFFFFFFE896FFFFFF0FBE7DE7E89DFFFFFF31C04883C4305DC3CC
+    Content:         31ED4989D15E4889E24883E4F050544531C031C9488D3DD5000000FF154F130000F4CCCCCCCCCCCCCCCCCCCCCCCCCCCC554889E5F6059D230000017402EB27C6059223000001488B05331300004883F8007413488D3DCAEEFFFF488D357F230000E82A0100005DC30F1F840000000000554889E5F605A5230000017402EB39C6059A23000001488B05FB1200004883F800740C488B3D06230000E801010000488B05EA1200004883F800740C488D3D71EEFFFFE8F80000005DC3CCCCCCCCCCCC554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC30C745FC00000000488D05BAFFFFFF488945F0B000FF55F0488D05BAFFFFFF488945E8B000FF55E8488D05BAFFFFFF488945D8488B45D80FBE7DE7FFD0E887FFFFFFE892FFFFFF0FBE7DE7E899FFFFFF31C04883C4305DC3CC
   - Name:            .init
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x182C
+    Address:         0x1820
     AddressAlign:    0x4
-    Content:         4883EC08488B05211200004885C07402FFD04883C408C3
+    Content:         4883EC08488B051D1200004885C07402FFD04883C408C3
   - Name:            .fini
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x1844
+    Address:         0x1838
     AddressAlign:    0x4
     Content:         4883EC084883C408C3
   - Name:            .plt
@@ -351,7 +415,7 @@ Sections:
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
     Address:         0x1850
     AddressAlign:    0x10
-    Content:         FF353A220000FF253C2200000F1F4000FF253A2200006800000000E9E0FFFFFFFF25322200006801000000E9D0FFFFFFFF252A2200006802000000E9C0FFFFFF
+    Content:         FF352A220000FF252C2200000F1F4000FF252A2200006800000000E9E0FFFFFFFF25222200006801000000E9D0FFFFFFFF251A2200006802000000E9C0FFFFFF
   - Name:            .init_array
     Type:            SHT_INIT_ARRAY
     Flags:           [ SHF_WRITE, SHF_ALLOC ]
@@ -371,8 +435,6 @@ Sections:
     Link:            .dynstr
     AddressAlign:    0x8
     Entries:
-      - Tag:             DT_NEEDED
-        Value:           0x80
       - Tag:             DT_NEEDED
         Value:           0x5F
       - Tag:             DT_FLAGS_1
@@ -380,7 +442,7 @@ Sections:
       - Tag:             DT_DEBUG
         Value:           0x0
       - Tag:             DT_RELA
-        Value:           0x480
+        Value:           0x478
       - Tag:             DT_RELASZ
         Value:           0xC0
       - Tag:             DT_RELAENT
@@ -388,11 +450,11 @@ Sections:
       - Tag:             DT_RELACOUNT
         Value:           0x3
       - Tag:             DT_JMPREL
-        Value:           0x540
+        Value:           0x538
       - Tag:             DT_PLTRELSZ
         Value:           0x48
       - Tag:             DT_PLTGOT
-        Value:           0x3A88
+        Value:           0x3A78
       - Tag:             DT_PLTREL
         Value:           0x7
       - Tag:             DT_SYMTAB
@@ -402,7 +464,7 @@ Sections:
       - Tag:             DT_STRTAB
         Value:           0x3F4
       - Tag:             DT_STRSZ
-        Value:           0x8A
+        Value:           0x80
       - Tag:             DT_GNU_HASH
         Value:           0x3D8
       - Tag:             DT_INIT_ARRAY
@@ -414,9 +476,9 @@ Sections:
       - Tag:             DT_FINI_ARRAYSZ
         Value:           0x8
       - Tag:             DT_INIT
-        Value:           0x182C
+        Value:           0x1820
       - Tag:             DT_FINI
-        Value:           0x1844
+        Value:           0x1838
       - Tag:             DT_VERSYM
         Value:           0x398
       - Tag:             DT_VERNEED
@@ -428,31 +490,31 @@ Sections:
   - Name:            .got
     Type:            SHT_PROGBITS
     Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x2A50
+    Address:         0x2A40
     AddressAlign:    0x8
     Content:         '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
   - Name:            .relro_padding
     Type:            SHT_NOBITS
     Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x2A78
+    Address:         0x2A68
     AddressAlign:    0x1
-    Size:            0x588
+    Size:            0x598
   - Name:            .data
     Type:            SHT_PROGBITS
     Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x3A78
+    Address:         0x3A68
     AddressAlign:    0x8
     Content:         '00000000000000000000000000000000'
   - Name:            .got.plt
     Type:            SHT_PROGBITS
     Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x3A88
+    Address:         0x3A78
     AddressAlign:    0x8
     Content:         A02800000000000000000000000000000000000000000000661800000000000076180000000000008618000000000000
   - Name:            .bss
     Type:            SHT_NOBITS
     Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x3AB8
+    Address:         0x3AA8
     AddressAlign:    0x8
     Size:            0x49
   - Name:            .comment
@@ -460,13 +522,13 @@ Sections:
     Flags:           [ SHF_MERGE, SHF_STRINGS ]
     AddressAlign:    0x1
     EntSize:         0x1
-    Content:         004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A6C6C766D2F6C6C766D2D70726F6A6563742E676974206135343965373363616436303333366438653963303632326165376164383661613635656634636529004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203239343432666166656337373437633632356135653466393937303861633532323139373662663729004675636873696120636C616E672076657273696F6E2032322E302E3067697420282F7573722F6C6F63616C2F676F6F676C652F686F6D652F7072616268756B722F6C6C766D2F6C6C766D2D70726F6A6563742F636C616E67203335383463366161636163363239633531633464316538653038323538633063323466336131363529004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420323934343266616665633737343763363235613565346639393730386163353232313937366266372900
+    Content:         004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974206530383561356338333634363166323964373366373339333433343437656439643533386265633129004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420653038356135633833363436316632396437336637333933343334343765643964353338626563312900
   - Name:            .callgraph
     Type:            SHT_PROGBITS
     Flags:           [ SHF_LINK_ORDER ]
     Link:            .text
     AddressAlign:    0x1
-    Content:         0000000000000000A0170000000000000200000000000000A220EFB89B695CF8000000000000000000000000000000000000000000000000B0170000000000000200000000000000A220EFB89B695CF8000000000000000000000000000000000000000000000000C01700000000000002000000000000005486BC59814B8E30000000000000000000000000000000000000000000000000D0170000000000000200000000000000DC011AF8DE94940A0300000000000000A220EFB89B695CF8ED17000000000000A220EFB89B695CF8FB170000000000005486BC59814B8E30101800000000000003000000000000001518000000000000A0170000000000001A18000000000000B0170000000000002318000000000000C017000000000000
+    Content:         0000000000000000901700000000000002000000000000002444F731F5EECB3E000000000000000000000000000000000000000000000000A01700000000000002000000000000002444F731F5EECB3E000000000000000000000000000000000000000000000000B01700000000000002000000000000005486BC59814B8E30000000000000000000000000000000000000000000000000C0170000000000000200000000000000CAAF769A600968FA03000000000000002444F731F5EECB3EDF170000000000002444F731F5EECB3EEF170000000000005486BC59814B8E30041800000000000003000000000000009017000000000000A017000000000000B017000000000000
 Symbols:
   - Name:            __abi_tag
     Type:            STT_OBJECT
@@ -479,31 +541,31 @@ Symbols:
   - Name:            __do_init
     Type:            STT_FUNC
     Section:         .text
-    Value:           0x1710
+    Value:           0x1700
     Size:            0x38
   - Name:            __do_init.__initialized
     Type:            STT_OBJECT
     Section:         .bss
-    Value:           0x3AB8
+    Value:           0x3AA8
     Size:            0x1
   - Name:            __EH_FRAME_LIST__
     Type:            STT_OBJECT
     Section:         .eh_frame
-    Value:           0x5FC
+    Value:           0x5F4
   - Name:            __do_init.__object
     Type:            STT_OBJECT
     Section:         .bss
-    Value:           0x3AC0
+    Value:           0x3AB0
     Size:            0x40
   - Name:            __do_fini
     Type:            STT_FUNC
     Section:         .text
-    Value:           0x1750
+    Value:           0x1740
     Size:            0x4A
   - Name:            __do_fini.__finalized
     Type:            STT_OBJECT
     Section:         .bss
-    Value:           0x3B00
+    Value:           0x3AF0
     Size:            0x1
   - Name:            __init
     Type:            STT_OBJECT
@@ -518,10 +580,10 @@ Symbols:
   - Name:            __dso_handle
     Type:            STT_OBJECT
     Section:         .data
-    Value:           0x3A80
+    Value:           0x3A70
     Size:            0x8
     Other:           [ STV_HIDDEN ]
-  - Name:            call_graph_info.cpp
+  - Name:            test.c
     Type:            STT_FILE
     Index:           SHN_ABS
   - Name:            crtend.c
@@ -530,12 +592,12 @@ Symbols:
   - Name:            __EH_FRAME_LIST_END__
     Type:            STT_OBJECT
     Section:         .eh_frame
-    Value:           0x5D0
+    Value:           0x5C8
     Size:            0x4
     Other:           [ STV_HIDDEN ]
   - Name:            _GLOBAL_OFFSET_TABLE_
     Section:         .got.plt
-    Value:           0x3A88
+    Value:           0x3A78
     Other:           [ STV_HIDDEN ]
   - Name:            _DYNAMIC
     Section:         .dynamic
@@ -544,34 +606,34 @@ Symbols:
   - Name:            _init
     Type:            STT_FUNC
     Section:         .init
-    Value:           0x182C
+    Value:           0x1820
     Other:           [ STV_HIDDEN ]
   - Name:            _fini
     Type:            STT_FUNC
     Section:         .fini
-    Value:           0x1844
+    Value:           0x1838
     Other:           [ STV_HIDDEN ]
   - Name:            _start
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
-    Value:           0x16E0
+    Value:           0x16D0
     Size:            0x22
   - Name:            main
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
-    Value:           0x17D0
-    Size:            0x5B
+    Value:           0x17C0
+    Size:            0x5F
   - Name:            data_start
     Section:         .data
     Binding:         STB_WEAK
-    Value:           0x3A78
+    Value:           0x3A68
   - Name:            _IO_stdin_used
     Type:            STT_OBJECT
     Section:         .rodata
     Binding:         STB_GLOBAL
-    Value:           0x588
+    Value:           0x580
     Size:            0x4
   - Name:            __libc_start_main
     Type:            STT_FUNC
@@ -579,7 +641,7 @@ Symbols:
   - Name:            __data_start
     Section:         .data
     Binding:         STB_GLOBAL
-    Value:           0x3A78
+    Value:           0x3A68
   - Name:            __gmon_start__
     Binding:         STB_WEAK
   - Name:            __register_frame_info
@@ -589,23 +651,23 @@ Symbols:
     Binding:         STB_WEAK
   - Name:            __deregister_frame_info
     Binding:         STB_WEAK
-  - Name:            _Z3foov
+  - Name:            foo
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
-    Value:           0x17A0
+    Value:           0x1790
     Size:            0x6
-  - Name:            _Z3barv
+  - Name:            bar
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
-    Value:           0x17B0
+    Value:           0x17A0
     Size:            0x6
-  - Name:            _Z3bazc
+  - Name:            baz
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
-    Value:           0x17C0
+    Value:           0x17B0
     Size:            0xE
 DynamicSymbols:
   - Name:            __libc_start_main
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 1d1d87f4baee3..7f504ba00211b 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -23,6 +23,7 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -203,14 +204,8 @@ enum class FunctionKind : uint64_t {
 // Per-function call graph information.
 template <typename AddrType> struct FunctionCallgraphInfoImpl {
   FunctionKind Kind;
-  struct DirectCallSite {
-    AddrType CallSite;
-    AddrType Callee;
-    DirectCallSite(AddrType CallSite, AddrType Callee)
-        : CallSite(CallSite), Callee(Callee) {}
-  };
-  SmallVector<DirectCallSite> DirectCallSites;
-  SmallVector<AddrType> IndirectCallSites;
+  SmallSet<AddrType, 4> DirectCallees;
+  SmallVector<AddrType> IndirectCallsites;
 };
 
 namespace {
@@ -5422,12 +5417,12 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     CGInfo.Kind = Kind;
 
     // Read number of indirect call sites for this function.
-    uint64_t NumIndirectCallSites = Data.getU64(&Offset, &CGSectionErr);
+    uint64_t NumIndirectCallsites = Data.getU64(&Offset, &CGSectionErr);
     if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of indirect callsites");
 
-    for (unsigned long I = 0; I < NumIndirectCallSites; I++) {
+    for (unsigned long I = 0; I < NumIndirectCallsites; I++) {
       uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
@@ -5438,27 +5433,22 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "indirect callsite PC");
       TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
-      CGInfo.IndirectCallSites.push_back(CallSitePc);
+      CGInfo.IndirectCallsites.push_back(CallSitePc);
     }
 
     // Read number of direct call sites for this function.
-    uint64_t NumDirectCallSites = Data.getU64(&Offset, &CGSectionErr);
+    uint64_t NumDirectCallees = Data.getU64(&Offset, &CGSectionErr);
     if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of direct callsites");
     // Read direct call sites and populate FuncCGInfo.
-    for (uint64_t I = 0; I < NumDirectCallSites; ++I) {
-      typename ELFT::uint CallSite =
-          Data.getUnsigned(&Offset, sizeof(CallSite), &CGSectionErr);
-      if (CGSectionErr)
-        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "direct callsite PC");
+    for (uint64_t I = 0; I < NumDirectCallees; ++I) {      
       typename ELFT::uint Callee =
           Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect callee PC");
-      CGInfo.DirectCallSites.emplace_back(CallSite, Callee);
+                            "direct callee PC");
+      CGInfo.DirectCallees.insert(Callee);
     }
     FuncCGInfo[FuncAddr] = CGInfo;
   }
@@ -5522,7 +5512,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   OS << "\n\nINDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])";
   for (const auto &El : this->FuncCGInfo) {
     auto CallerPc = El.first;
-    auto FuncIndirCallSites = El.second.IndirectCallSites;
+    auto FuncIndirCallSites = El.second.IndirectCallsites;
     if (!FuncIndirCallSites.empty()) {
       OS << "\n" << format("%lx", CallerPc);
       for (auto IndirCallSitePc : FuncIndirCallSites)
@@ -5530,21 +5520,17 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     }
   }
 
-  using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-
   // Print function entry to direct call site and target function entry
   // addresses mapping from disasm.
   OS << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])";
   for (const auto &El : this->FuncCGInfo) {
     auto CallerPc = El.first;
-    auto FuncDirCallSites = El.second.DirectCallSites;
+    auto FuncDirCallSites = El.second.DirectCallees;
     if (!FuncDirCallSites.empty()) {
       OS << "\n" << format("%lx", CallerPc);
-      for (typename FunctionCallgraphInfo::DirectCallSite &DCS :
+      for (typename ELFT::uint Callee :
            FuncDirCallSites) {
-        OS << " " << format("%lx", DCS.CallSite) << " "
-           << format("%lx", DCS.Callee);
+        OS << " " << format("%lx", Callee);
       }
     }
   }
@@ -8425,15 +8411,14 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   {
     ListScope DCT(this->W, "direct_call_sites");
     for (auto const &F : this->FuncCGInfo) {
-      if (F.second.DirectCallSites.empty())
+      if (F.second.DirectCallees.empty())
         continue;
       DictScope D(this->W);
       this->W.printHex("caller", F.first);
       ListScope CT(this->W, "call_sites");
-      for (auto const &CS : F.second.DirectCallSites) {
+      for (auto const &CS : F.second.DirectCallees) {
         DictScope D(this->W);
-        this->W.printHex("call_site", CS.CallSite);
-        this->W.printHex("callee", CS.Callee);
+        this->W.printHex("callee", CS);
       }
     }
   }
@@ -8441,11 +8426,11 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   {
     ListScope ICT(this->W, "indirect_call_sites");
     for (auto const &F : this->FuncCGInfo) {
-      if (F.second.IndirectCallSites.empty())
+      if (F.second.IndirectCallsites.empty())
         continue;
       DictScope D(this->W);
       this->W.printHex("caller", F.first);
-      this->W.printHexList("call_sites", F.second.IndirectCallSites);
+      this->W.printHexList("call_sites", F.second.IndirectCallsites);
     }
   }
 

>From 5b504fc2fb99dd6c25144ebb0d5cd9317d9dcedd Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 20:10:46 -0700
Subject: [PATCH 14/44] Format

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 7f504ba00211b..2c25fe4e7d9dd 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -22,8 +22,8 @@
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -5442,7 +5442,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of direct callsites");
     // Read direct call sites and populate FuncCGInfo.
-    for (uint64_t I = 0; I < NumDirectCallees; ++I) {      
+    for (uint64_t I = 0; I < NumDirectCallees; ++I) {
       typename ELFT::uint Callee =
           Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
       if (CGSectionErr)
@@ -5528,8 +5528,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     auto FuncDirCallSites = El.second.DirectCallees;
     if (!FuncDirCallSites.empty()) {
       OS << "\n" << format("%lx", CallerPc);
-      for (typename ELFT::uint Callee :
-           FuncDirCallSites) {
+      for (typename ELFT::uint Callee : FuncDirCallSites) {
         OS << " " << format("%lx", Callee);
       }
     }

>From 5b6bb00892cbcff4268985ebdf9d52de078e8e18 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 21:16:36 -0700
Subject: [PATCH 15/44] Simplify readelf output.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 229 +++++++++-----------------
 1 file changed, 81 insertions(+), 148 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 2c25fe4e7d9dd..366e7fc19d3db 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -203,9 +203,11 @@ enum class FunctionKind : uint64_t {
 
 // Per-function call graph information.
 template <typename AddrType> struct FunctionCallgraphInfoImpl {
+  uint64_t FormatVersionNumber;  
   FunctionKind Kind;
+  uint64_t FunctionTypeId; // Only if Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID
+  DenseMap<AddrType, uint64_t> IndirectCallsites;
   SmallSet<AddrType, 4> DirectCallees;
-  SmallVector<AddrType> IndirectCallsites;
 };
 
 namespace {
@@ -464,12 +466,11 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   // Callgraph - Main data structure to maintain per function callgraph
   // information.
   MapVector<typename ELFT::uint, FunctionCallgraphInfo> FuncCGInfo;
-  // Callgraph - 64 bit type id mapped to indirect callsites whose potential
-  // callee(s) should be of given type id.
-  MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirCallSites;
-  // Callgraph - 64 bit type id mapped to entry PC addresses of functions which
-  // are of the given type id.
-  MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirTargets;
+  
+  // // Callgraph - 64 bit type id mapped to entry PC addresses of functions which
+  // // are of the given type id.
+  // MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirTargets;
+  
   // Callgraph - Read callgraph section and process its contents to populate
   // Callgraph related data structures which will be used to dump callgraph
   // info. Returns false if there is no .callgraph section in the input file.
@@ -5384,6 +5385,13 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
     }
 
+    using FunctionCallgraphInfo =
+        ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
+
+    // Create a new entry for this function.
+    FunctionCallgraphInfo CGInfo;
+    CGInfo.FormatVersionNumber = FormatVersionNumber;
+
     // Read function kind.
     uint64_t KindVal = Data.getU64(&Offset, &CGSectionErr);
     if (CGSectionErr)
@@ -5396,26 +5404,20 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     }
 
     FunctionKind Kind = static_cast<FunctionKind>(KindVal);
+    CGInfo.Kind = Kind;
     if (Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) {
       // Read type id if this function is an indirect call target.
       uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                             "indirect type id");
-      TypeIdToIndirTargets[TypeId].push_back(FuncAddr);
+      CGInfo.FunctionTypeId = TypeId;
     }
     if (Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
       UnknownCount++;
     if (Kind == FunctionKind::NOT_LISTED)
       NotListedCount++;
 
-    using FunctionCallgraphInfo =
-        ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-
-    // Create a new entry for this function.
-    FunctionCallgraphInfo CGInfo;
-    CGInfo.Kind = Kind;
-
     // Read number of indirect call sites for this function.
     uint64_t NumIndirectCallsites = Data.getU64(&Offset, &CGSectionErr);
     if (CGSectionErr)
@@ -5431,9 +5433,8 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
           Data.getUnsigned(&Offset, sizeof(CallSitePc), &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect callsite PC");
-      TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
-      CGInfo.IndirectCallsites.push_back(CallSitePc);
+                            "indirect callsite PC");      
+      CGInfo.IndirectCallsites.try_emplace(CallSitePc, TypeId);
     }
 
     // Read number of direct call sites for this function.
@@ -5466,74 +5467,52 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   return true;
 }
 
+static StringRef GetFuntionKindString(FunctionKind Kind) {
+  switch(Kind) {
+    case FunctionKind::NOT_INDIRECT_TARGET:
+      return "NOT_INDIRECT_TARGET";
+    case FunctionKind::INDIRECT_TARGET_UNKNOWN_TID:
+      return "INDIRECT_TARGET_UNKNOWN_TID";
+    case FunctionKind::INDIRECT_TARGET_KNOWN_TID:
+      return "INDIRECT_TARGET_KNOWN_TID";
+    case FunctionKind::NOT_LISTED:
+      return "NOT_LISTED";
+  }
+  llvm_unreachable("Unknown FunctionKind.");
+}
+
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
-
-  // Print indirect targets
-  OS << "\nINDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])";
-
-  // Print indirect targets with unknown type.
-  // For completeness, functions for which the call graph section does not
-  // provide information are included.
-  bool printedHeader = false;
+  using FunctionCallgraphInfo =
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;    
   for (const auto &El : this->FuncCGInfo) {
-    FunctionKind FuncKind = El.second.Kind;
-    if (FuncKind == FunctionKind::NOT_LISTED ||
-        FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) {
-      if (!printedHeader) {
-        OS << "\nUNKNOWN";
-        printedHeader = true;
+      typename ELFT::uint FuncEntryPc = El.first;
+      FunctionCallgraphInfo CGInfo = El.second;
+      OS << "Function PC:: " << format("%lx", FuncEntryPc); // TODO: Print function name
+      OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
+      OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
+      if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
+        OS << "\nFunction Type ID:: " << CGInfo.FunctionTypeId;
+      OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size();
+      if(CGInfo.IndirectCallsites.size() > 0) {
+        OS << "\n{";
+        for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
+            OS << "\ncallsite: " << format("%lx", IndirCallSitePc);
+            OS << "\ncalleeTypeId: " << format("%lx", TypeId);
+        }
+        OS << "\n}";
       }
-      uint64_t FuncEntryPc = El.first;
-      OS << " " << format("%lx", FuncEntryPc);
-    }
-  }
-  // Print indirect targets to type id mapping.
-  for (const auto &El : this->TypeIdToIndirTargets) {
-    uint64_t TypeId = El.first;
-    OS << "\n" << format("%lx", TypeId);
-    for (uint64_t IndirTargetPc : El.second)
-      OS << " " << format("%lx", IndirTargetPc);
-  }
-
-  // Print indirect calls to type id mapping. Any indirect call without a
-  // type id can be deduced by comparing this list to indirect call sites
-  // list.
-  OS << "\n\nINDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])";
-  for (const auto &El : this->TypeIdToIndirCallSites) {
-    uint64_t TypeId = El.first;
-    OS << "\n" << format("%lx", TypeId);
-    for (uint64_t IndirCallSitePc : El.second)
-      OS << " " << format("%lx", IndirCallSitePc);
-  }
-
-  // Print function entry to indirect call site addresses mapping from disasm.
-  OS << "\n\nINDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])";
-  for (const auto &El : this->FuncCGInfo) {
-    auto CallerPc = El.first;
-    auto FuncIndirCallSites = El.second.IndirectCallsites;
-    if (!FuncIndirCallSites.empty()) {
-      OS << "\n" << format("%lx", CallerPc);
-      for (auto IndirCallSitePc : FuncIndirCallSites)
-        OS << " " << format("%lx", IndirCallSitePc);
-    }
-  }
-
-  // Print function entry to direct call site and target function entry
-  // addresses mapping from disasm.
-  OS << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])";
-  for (const auto &El : this->FuncCGInfo) {
-    auto CallerPc = El.first;
-    auto FuncDirCallSites = El.second.DirectCallees;
-    if (!FuncDirCallSites.empty()) {
-      OS << "\n" << format("%lx", CallerPc);
-      for (typename ELFT::uint Callee : FuncDirCallSites) {
-        OS << " " << format("%lx", Callee);
+      OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
+      if(CGInfo.DirectCallees.size() > 0) {
+        OS << "\n{";
+        for (auto CalleePC : CGInfo.DirectCallees) {
+            OS << "\n" << format("%lx", CalleePC);            
+        }
+        OS << "\n}";
       }
-    }
-  }
-  OS << "\n";
+      OS << "\n";
+    }    
 }
 
 template <class ELFT>
@@ -8372,76 +8351,30 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     return;
 
   DictScope D(this->W, "callgraph_info");
-  // Use llvm-symbolizer to get function name and address instead.
-  // stack-sizes embeds function mangled name directly in JSON.
-  // {
-  //   ListScope A(this->W, "functions");
-  //   for (auto const &F : this->FuncCGInfo) {
-  //     DictScope D(this->W);
-  //     this->W.printHex("function_address", F.first);
-  //   }
-  // }
-
-  {
-    SmallVector<uint64_t, 4> Unknowns;
-    for (const auto &El : this->FuncCGInfo) {
-      FunctionKind FuncKind = El.second.Kind;
-      if (FuncKind == FunctionKind::NOT_LISTED ||
-          FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) {
-        uint64_t FuncEntryPc = El.first;
-        Unknowns.emplace_back(FuncEntryPc);
-      }
-    }
-    if (!Unknowns.empty())
-      this->W.printHexList("unknown_target_types", Unknowns);
-  }
-
-  {
-    ListScope ITT(this->W, "indirect_target_types");
-    for (auto const &T : this->TypeIdToIndirTargets) {
-      for (auto const &Target : T.second) {
-        DictScope D(this->W);
-        this->W.printHex("function_address", Target);
-        this->W.printHex("type_id", T.first);
-      }
-    }
-  }
 
-  {
-    ListScope DCT(this->W, "direct_call_sites");
-    for (auto const &F : this->FuncCGInfo) {
-      if (F.second.DirectCallees.empty())
-        continue;
-      DictScope D(this->W);
-      this->W.printHex("caller", F.first);
-      ListScope CT(this->W, "call_sites");
-      for (auto const &CS : F.second.DirectCallees) {
-        DictScope D(this->W);
-        this->W.printHex("callee", CS);
-      }
-    }
-  }
-
-  {
-    ListScope ICT(this->W, "indirect_call_sites");
-    for (auto const &F : this->FuncCGInfo) {
-      if (F.second.IndirectCallsites.empty())
-        continue;
-      DictScope D(this->W);
-      this->W.printHex("caller", F.first);
-      this->W.printHexList("call_sites", F.second.IndirectCallsites);
-    }
-  }
-
-  {
-    ListScope ICT(this->W, "indirect_call_types");
-    for (auto const &T : this->TypeIdToIndirCallSites) {
-      for (auto const &CS : T.second) {
-        DictScope D(this->W);
-        this->W.printHex("call_site", CS);
-        this->W.printHex("type_id", T.first);
+  using FunctionCallgraphInfo =
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;        
+  for (const auto &El : this->FuncCGInfo) {
+      typename ELFT::uint FuncEntryPc = El.first;
+      FunctionCallgraphInfo CGInfo = El.second;
+      std::string FuncPCStr = std::to_string(FuncEntryPc);
+      DictScope FuncScope(this->W, FuncPCStr); // TODO: Print function name
+      this->W.printNumber("FormatVersionNumber", CGInfo.FormatVersionNumber);
+      this->W.printNumber("Kind", (uint64_t)CGInfo.Kind); // TODO: Print Function Kind String GetFuntionKindString(El.second.Kind);
+      if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
+        this->W.printNumber("TypeId", CGInfo.FunctionTypeId);
+      this->W.printNumber("NumIndirectCallSites", CGInfo.IndirectCallsites.size());      
+      if(CGInfo.IndirectCallsites.size() > 0) {
+        ListScope ICT(this->W, "indirect_call_sites");
+        for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
+            DictScope IDC(this->W);
+            this->W.printHex("callsite", IndirCallSitePc);
+            this->W.printHex("type_id", TypeId);            
+        }
       }
-    }
+      this->W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
+      if(CGInfo.DirectCallees.size() > 0)
+        this->W.printHexList("direct_callees", CGInfo.DirectCallees);      
   }
 }
 

>From 937ff616bb6ba9538e8e330eb48c0893d8441023 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 17 Sep 2025 21:16:56 -0700
Subject: [PATCH 16/44] format

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 126 ++++++++++++++------------
 1 file changed, 66 insertions(+), 60 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 366e7fc19d3db..c26461dd2eb7d 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -203,9 +203,10 @@ enum class FunctionKind : uint64_t {
 
 // Per-function call graph information.
 template <typename AddrType> struct FunctionCallgraphInfoImpl {
-  uint64_t FormatVersionNumber;  
+  uint64_t FormatVersionNumber;
   FunctionKind Kind;
-  uint64_t FunctionTypeId; // Only if Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID
+  uint64_t
+      FunctionTypeId; // Only if Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID
   DenseMap<AddrType, uint64_t> IndirectCallsites;
   SmallSet<AddrType, 4> DirectCallees;
 };
@@ -466,11 +467,12 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   // Callgraph - Main data structure to maintain per function callgraph
   // information.
   MapVector<typename ELFT::uint, FunctionCallgraphInfo> FuncCGInfo;
-  
-  // // Callgraph - 64 bit type id mapped to entry PC addresses of functions which
+
+  // // Callgraph - 64 bit type id mapped to entry PC addresses of functions
+  // which
   // // are of the given type id.
   // MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirTargets;
-  
+
   // Callgraph - Read callgraph section and process its contents to populate
   // Callgraph related data structures which will be used to dump callgraph
   // info. Returns false if there is no .callgraph section in the input file.
@@ -5433,7 +5435,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
           Data.getUnsigned(&Offset, sizeof(CallSitePc), &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect callsite PC");      
+                            "indirect callsite PC");
       CGInfo.IndirectCallsites.try_emplace(CallSitePc, TypeId);
     }
 
@@ -5468,15 +5470,15 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
 }
 
 static StringRef GetFuntionKindString(FunctionKind Kind) {
-  switch(Kind) {
-    case FunctionKind::NOT_INDIRECT_TARGET:
-      return "NOT_INDIRECT_TARGET";
-    case FunctionKind::INDIRECT_TARGET_UNKNOWN_TID:
-      return "INDIRECT_TARGET_UNKNOWN_TID";
-    case FunctionKind::INDIRECT_TARGET_KNOWN_TID:
-      return "INDIRECT_TARGET_KNOWN_TID";
-    case FunctionKind::NOT_LISTED:
-      return "NOT_LISTED";
+  switch (Kind) {
+  case FunctionKind::NOT_INDIRECT_TARGET:
+    return "NOT_INDIRECT_TARGET";
+  case FunctionKind::INDIRECT_TARGET_UNKNOWN_TID:
+    return "INDIRECT_TARGET_UNKNOWN_TID";
+  case FunctionKind::INDIRECT_TARGET_KNOWN_TID:
+    return "INDIRECT_TARGET_KNOWN_TID";
+  case FunctionKind::NOT_LISTED:
+    return "NOT_LISTED";
   }
   llvm_unreachable("Unknown FunctionKind.");
 }
@@ -5485,34 +5487,35 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
   using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;    
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
   for (const auto &El : this->FuncCGInfo) {
-      typename ELFT::uint FuncEntryPc = El.first;
-      FunctionCallgraphInfo CGInfo = El.second;
-      OS << "Function PC:: " << format("%lx", FuncEntryPc); // TODO: Print function name
-      OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
-      OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
-      if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-        OS << "\nFunction Type ID:: " << CGInfo.FunctionTypeId;
-      OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size();
-      if(CGInfo.IndirectCallsites.size() > 0) {
-        OS << "\n{";
-        for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
-            OS << "\ncallsite: " << format("%lx", IndirCallSitePc);
-            OS << "\ncalleeTypeId: " << format("%lx", TypeId);
-        }
-        OS << "\n}";
+    typename ELFT::uint FuncEntryPc = El.first;
+    FunctionCallgraphInfo CGInfo = El.second;
+    OS << "Function PC:: "
+       << format("%lx", FuncEntryPc); // TODO: Print function name
+    OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
+    OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
+    if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
+      OS << "\nFunction Type ID:: " << CGInfo.FunctionTypeId;
+    OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size();
+    if (CGInfo.IndirectCallsites.size() > 0) {
+      OS << "\n{";
+      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
+        OS << "\ncallsite: " << format("%lx", IndirCallSitePc);
+        OS << "\ncalleeTypeId: " << format("%lx", TypeId);
       }
-      OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
-      if(CGInfo.DirectCallees.size() > 0) {
-        OS << "\n{";
-        for (auto CalleePC : CGInfo.DirectCallees) {
-            OS << "\n" << format("%lx", CalleePC);            
-        }
-        OS << "\n}";
+      OS << "\n}";
+    }
+    OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
+    if (CGInfo.DirectCallees.size() > 0) {
+      OS << "\n{";
+      for (auto CalleePC : CGInfo.DirectCallees) {
+        OS << "\n" << format("%lx", CalleePC);
       }
-      OS << "\n";
-    }    
+      OS << "\n}";
+    }
+    OS << "\n";
+  }
 }
 
 template <class ELFT>
@@ -8353,28 +8356,31 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   DictScope D(this->W, "callgraph_info");
 
   using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;        
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
   for (const auto &El : this->FuncCGInfo) {
-      typename ELFT::uint FuncEntryPc = El.first;
-      FunctionCallgraphInfo CGInfo = El.second;
-      std::string FuncPCStr = std::to_string(FuncEntryPc);
-      DictScope FuncScope(this->W, FuncPCStr); // TODO: Print function name
-      this->W.printNumber("FormatVersionNumber", CGInfo.FormatVersionNumber);
-      this->W.printNumber("Kind", (uint64_t)CGInfo.Kind); // TODO: Print Function Kind String GetFuntionKindString(El.second.Kind);
-      if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-        this->W.printNumber("TypeId", CGInfo.FunctionTypeId);
-      this->W.printNumber("NumIndirectCallSites", CGInfo.IndirectCallsites.size());      
-      if(CGInfo.IndirectCallsites.size() > 0) {
-        ListScope ICT(this->W, "indirect_call_sites");
-        for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
-            DictScope IDC(this->W);
-            this->W.printHex("callsite", IndirCallSitePc);
-            this->W.printHex("type_id", TypeId);            
-        }
+    typename ELFT::uint FuncEntryPc = El.first;
+    FunctionCallgraphInfo CGInfo = El.second;
+    std::string FuncPCStr = std::to_string(FuncEntryPc);
+    DictScope FuncScope(this->W, FuncPCStr); // TODO: Print function name
+    this->W.printNumber("FormatVersionNumber", CGInfo.FormatVersionNumber);
+    this->W.printNumber(
+        "Kind", (uint64_t)CGInfo.Kind); // TODO: Print Function Kind String
+                                        // GetFuntionKindString(El.second.Kind);
+    if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
+      this->W.printNumber("TypeId", CGInfo.FunctionTypeId);
+    this->W.printNumber("NumIndirectCallSites",
+                        CGInfo.IndirectCallsites.size());
+    if (CGInfo.IndirectCallsites.size() > 0) {
+      ListScope ICT(this->W, "indirect_call_sites");
+      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
+        DictScope IDC(this->W);
+        this->W.printHex("callsite", IndirCallSitePc);
+        this->W.printHex("type_id", TypeId);
       }
-      this->W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
-      if(CGInfo.DirectCallees.size() > 0)
-        this->W.printHexList("direct_callees", CGInfo.DirectCallees);      
+    }
+    this->W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
+    if (CGInfo.DirectCallees.size() > 0)
+      this->W.printHexList("direct_callees", CGInfo.DirectCallees);
   }
 }
 

>From 1b86857d5911e4023771d5e12a029b08fdecfd8a Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 14:03:05 -0700
Subject: [PATCH 17/44] Clean up callgraph output

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 39 ++++++++++++++++-----------
 1 file changed, 24 insertions(+), 15 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index c26461dd2eb7d..4765549c63f1d 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -466,7 +466,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
 
   // Callgraph - Main data structure to maintain per function callgraph
   // information.
-  MapVector<typename ELFT::uint, FunctionCallgraphInfo> FuncCGInfo;
+  MapVector<typename ELFT::uint, FunctionCallgraphInfo> FuncCGInfos;
 
   // // Callgraph - 64 bit type id mapped to entry PC addresses of functions
   // which
@@ -5381,7 +5381,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       reportError(std::move(CGSectionErr),
                   "While reading call graph info function entry PC");
 
-    if (FuncCGInfo.find(FuncAddr) != FuncCGInfo.end()) {
+    if (FuncCGInfos.find(FuncAddr) != FuncCGInfos.end()) {
       Error DuplicatePcErr =
           createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
       reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
@@ -5444,7 +5444,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of direct callsites");
-    // Read direct call sites and populate FuncCGInfo.
+    // Read direct call sites and populate FuncCGInfos.
     for (uint64_t I = 0; I < NumDirectCallees; ++I) {
       typename ELFT::uint Callee =
           Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
@@ -5453,7 +5453,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                             "direct callee PC");
       CGInfo.DirectCallees.insert(Callee);
     }
-    FuncCGInfo[FuncAddr] = CGInfo;
+    FuncCGInfos[FuncAddr] = CGInfo;
   }
 
   if (NotListedCount)
@@ -5464,7 +5464,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                         std::to_string(UnknownCount) + " indirect targets.");
 
   // Sort function info by function PC.
-  llvm::sort(FuncCGInfo,
+  llvm::sort(FuncCGInfos,
              [](const auto &A, const auto &B) { return A.first < B.first; });
   return true;
 }
@@ -5486,32 +5486,41 @@ static StringRef GetFuntionKindString(FunctionKind Kind) {
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
+  if (this->FuncCGInfos.size() == 0)
+    return;
   using FunctionCallgraphInfo =
       ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-  for (const auto &El : this->FuncCGInfo) {
+  OS << "Call graph information:: \n";
+  for (const auto &El : this->FuncCGInfos) {
     typename ELFT::uint FuncEntryPc = El.first;
     FunctionCallgraphInfo CGInfo = El.second;
-    OS << "Function PC:: "
+    OS << "\nFunction PC:: 0x"
        << format("%lx", FuncEntryPc); // TODO: Print function name
     OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
     OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-      OS << "\nFunction Type ID:: " << CGInfo.FunctionTypeId;
+      OS << "\nFunction Type ID:: 0x" << format("%lx", CGInfo.FunctionTypeId);
     OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size();
     if (CGInfo.IndirectCallsites.size() > 0) {
-      OS << "\n{";
+      OS << "\n{";      
       for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
-        OS << "\ncallsite: " << format("%lx", IndirCallSitePc);
-        OS << "\ncalleeTypeId: " << format("%lx", TypeId);
-      }
+        OS << "\n";
+        OS.PadToColumn(2);
+        OS << "callsite: 0x" << format("%lx", IndirCallSitePc);
+        OS << "\n";
+        OS.PadToColumn(2);
+        OS << "calleeTypeId: 0x" << format("%lx", TypeId);
+      }      
       OS << "\n}";
     }
     OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
     if (CGInfo.DirectCallees.size() > 0) {
       OS << "\n{";
       for (auto CalleePC : CGInfo.DirectCallees) {
-        OS << "\n" << format("%lx", CalleePC);
-      }
+        OS << "\n";
+        OS.PadToColumn(2);
+        OS << "0x" << format("%lx", CalleePC);
+      }      
       OS << "\n}";
     }
     OS << "\n";
@@ -8357,7 +8366,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
 
   using FunctionCallgraphInfo =
       ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-  for (const auto &El : this->FuncCGInfo) {
+  for (const auto &El : this->FuncCGInfos) {
     typename ELFT::uint FuncEntryPc = El.first;
     FunctionCallgraphInfo CGInfo = El.second;
     std::string FuncPCStr = std::to_string(FuncEntryPc);

>From 7280ff3ac51a63a99743e4c192d69fb6fe9545f8 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 14:03:34 -0700
Subject: [PATCH 18/44] Format

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 4765549c63f1d..48827a7805654 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5502,7 +5502,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
       OS << "\nFunction Type ID:: 0x" << format("%lx", CGInfo.FunctionTypeId);
     OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size();
     if (CGInfo.IndirectCallsites.size() > 0) {
-      OS << "\n{";      
+      OS << "\n{";
       for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
         OS << "\n";
         OS.PadToColumn(2);
@@ -5510,7 +5510,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
         OS << "\n";
         OS.PadToColumn(2);
         OS << "calleeTypeId: 0x" << format("%lx", TypeId);
-      }      
+      }
       OS << "\n}";
     }
     OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
@@ -5520,7 +5520,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
         OS << "\n";
         OS.PadToColumn(2);
         OS << "0x" << format("%lx", CalleePC);
-      }      
+      }
       OS << "\n}";
     }
     OS << "\n";

>From bdb8683a51c688c7998b894ee7d3988aaa0010b3 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 15:08:45 -0700
Subject: [PATCH 19/44] Show symbol names.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 77 +++++++++++++++++++++------
 1 file changed, 62 insertions(+), 15 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 48827a7805654..1e9393c7c0213 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5490,12 +5490,27 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     return;
   using FunctionCallgraphInfo =
       ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-  OS << "Call graph information:: \n";
+  
+  auto GetFunctionName = [&](typename ELFT::uint EntryPc){
+    SmallVector<uint32_t> FuncSymIndexes =
+        this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
+    if (FuncSymIndexes.empty())
+      return std::string("");
+    
+    SmallVector<std::string> FuncSymNames;
+    for (uint32_t Index : FuncSymIndexes)
+      FuncSymNames.push_back(this->getStaticSymbolName(Index));
+    return join(FuncSymNames, ", ");    
+  };
+  
+  OS << "Per-function call graph information:: \n";
   for (const auto &El : this->FuncCGInfos) {
     typename ELFT::uint FuncEntryPc = El.first;
     FunctionCallgraphInfo CGInfo = El.second;
-    OS << "\nFunction PC:: 0x"
-       << format("%lx", FuncEntryPc); // TODO: Print function name
+    std::string FuncSymNames = GetFunctionName(FuncEntryPc);    
+    if (!FuncSymNames.empty())
+      OS << "\nFunction:: " << FuncSymNames;
+    OS << "\nFunction PC:: " << format("0x%lx", FuncEntryPc);    
     OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
     OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
@@ -5517,9 +5532,15 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     if (CGInfo.DirectCallees.size() > 0) {
       OS << "\n{";
       for (auto CalleePC : CGInfo.DirectCallees) {
+        std::string FuncSymNames = GetFunctionName(CalleePC);    
+        if (!FuncSymNames.empty()) {
+          OS << "\n";
+          OS.PadToColumn(2);
+          OS << "Callee:: " << FuncSymNames;
+        }
         OS << "\n";
         OS.PadToColumn(2);
-        OS << "0x" << format("%lx", CalleePC);
+        OS << "CalleePC:: 0x" << format("%lx", CalleePC);        
       }
       OS << "\n}";
     }
@@ -8361,22 +8382,41 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
 template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
+  if (this->FuncCGInfos.size() == 0)
+    return;
+  using FunctionCallgraphInfo =
+      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
+  
+  auto GetFunctionName = [&](typename ELFT::uint EntryPc){
+    SmallVector<uint32_t> FuncSymIndexes =
+        this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
+    if (FuncSymIndexes.empty())
+      return std::string("");
+    
+    SmallVector<std::string> FuncSymNames;
+    for (uint32_t Index : FuncSymIndexes)
+      FuncSymNames.push_back(this->getStaticSymbolName(Index));
+    return join(FuncSymNames, ", ");    
+  };
 
   DictScope D(this->W, "callgraph_info");
 
-  using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
   for (const auto &El : this->FuncCGInfos) {
     typename ELFT::uint FuncEntryPc = El.first;
-    FunctionCallgraphInfo CGInfo = El.second;
-    std::string FuncPCStr = std::to_string(FuncEntryPc);
-    DictScope FuncScope(this->W, FuncPCStr); // TODO: Print function name
+    FunctionCallgraphInfo CGInfo = El.second; 
+    std::string FuncPCStr;
+    raw_string_ostream OS(FuncPCStr);
+    OS << format("0x%lx", FuncEntryPc);
+    DictScope FuncScope(this->W, OS.str());
+    std::string FuncSymName = GetFunctionName(FuncEntryPc);    
+    if (!FuncSymName.empty())
+      this->W.printString("Name", FuncSymName);  
+    
     this->W.printNumber("FormatVersionNumber", CGInfo.FormatVersionNumber);
-    this->W.printNumber(
-        "Kind", (uint64_t)CGInfo.Kind); // TODO: Print Function Kind String
-                                        // GetFuntionKindString(El.second.Kind);
+    this->W.printString("KindStr", GetFuntionKindString(CGInfo.Kind));
+    this->W.printNumber("Kind", (uint64_t)CGInfo.Kind);    
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-      this->W.printNumber("TypeId", CGInfo.FunctionTypeId);
+      this->W.printHex("TypeId", CGInfo.FunctionTypeId);
     this->W.printNumber("NumIndirectCallSites",
                         CGInfo.IndirectCallsites.size());
     if (CGInfo.IndirectCallsites.size() > 0) {
@@ -8388,8 +8428,15 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
       }
     }
     this->W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
-    if (CGInfo.DirectCallees.size() > 0)
-      this->W.printHexList("direct_callees", CGInfo.DirectCallees);
+    if (CGInfo.DirectCallees.size() > 0) {
+      ListScope ICT(this->W, "direct_callees");
+      for (auto CalleePC : CGInfo.DirectCallees) {
+        this->W.printHex("calleePC", CalleePC);
+        std::string CalleeSymName = GetFunctionName(CalleePC);
+        if (!CalleeSymName.empty())
+          this->W.printString("Name", CalleeSymName);  
+      }
+    }
   }
 }
 

>From 09d3a8791b62557d7924f2376735cd35bf04475e Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 15:08:58 -0700
Subject: [PATCH 20/44] Format

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 38 +++++++++++++--------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 1e9393c7c0213..1b77c10f8db9c 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5490,27 +5490,27 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     return;
   using FunctionCallgraphInfo =
       ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-  
-  auto GetFunctionName = [&](typename ELFT::uint EntryPc){
+
+  auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
     if (FuncSymIndexes.empty())
       return std::string("");
-    
+
     SmallVector<std::string> FuncSymNames;
     for (uint32_t Index : FuncSymIndexes)
       FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    return join(FuncSymNames, ", ");    
+    return join(FuncSymNames, ", ");
   };
-  
+
   OS << "Per-function call graph information:: \n";
   for (const auto &El : this->FuncCGInfos) {
     typename ELFT::uint FuncEntryPc = El.first;
     FunctionCallgraphInfo CGInfo = El.second;
-    std::string FuncSymNames = GetFunctionName(FuncEntryPc);    
+    std::string FuncSymNames = GetFunctionName(FuncEntryPc);
     if (!FuncSymNames.empty())
       OS << "\nFunction:: " << FuncSymNames;
-    OS << "\nFunction PC:: " << format("0x%lx", FuncEntryPc);    
+    OS << "\nFunction PC:: " << format("0x%lx", FuncEntryPc);
     OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
     OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
@@ -5532,7 +5532,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     if (CGInfo.DirectCallees.size() > 0) {
       OS << "\n{";
       for (auto CalleePC : CGInfo.DirectCallees) {
-        std::string FuncSymNames = GetFunctionName(CalleePC);    
+        std::string FuncSymNames = GetFunctionName(CalleePC);
         if (!FuncSymNames.empty()) {
           OS << "\n";
           OS.PadToColumn(2);
@@ -5540,7 +5540,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
         }
         OS << "\n";
         OS.PadToColumn(2);
-        OS << "CalleePC:: 0x" << format("%lx", CalleePC);        
+        OS << "CalleePC:: 0x" << format("%lx", CalleePC);
       }
       OS << "\n}";
     }
@@ -8386,35 +8386,35 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     return;
   using FunctionCallgraphInfo =
       ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-  
-  auto GetFunctionName = [&](typename ELFT::uint EntryPc){
+
+  auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
     if (FuncSymIndexes.empty())
       return std::string("");
-    
+
     SmallVector<std::string> FuncSymNames;
     for (uint32_t Index : FuncSymIndexes)
       FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    return join(FuncSymNames, ", ");    
+    return join(FuncSymNames, ", ");
   };
 
   DictScope D(this->W, "callgraph_info");
 
   for (const auto &El : this->FuncCGInfos) {
     typename ELFT::uint FuncEntryPc = El.first;
-    FunctionCallgraphInfo CGInfo = El.second; 
+    FunctionCallgraphInfo CGInfo = El.second;
     std::string FuncPCStr;
     raw_string_ostream OS(FuncPCStr);
     OS << format("0x%lx", FuncEntryPc);
     DictScope FuncScope(this->W, OS.str());
-    std::string FuncSymName = GetFunctionName(FuncEntryPc);    
+    std::string FuncSymName = GetFunctionName(FuncEntryPc);
     if (!FuncSymName.empty())
-      this->W.printString("Name", FuncSymName);  
-    
+      this->W.printString("Name", FuncSymName);
+
     this->W.printNumber("FormatVersionNumber", CGInfo.FormatVersionNumber);
     this->W.printString("KindStr", GetFuntionKindString(CGInfo.Kind));
-    this->W.printNumber("Kind", (uint64_t)CGInfo.Kind);    
+    this->W.printNumber("Kind", (uint64_t)CGInfo.Kind);
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
       this->W.printHex("TypeId", CGInfo.FunctionTypeId);
     this->W.printNumber("NumIndirectCallSites",
@@ -8434,7 +8434,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
         this->W.printHex("calleePC", CalleePC);
         std::string CalleeSymName = GetFunctionName(CalleePC);
         if (!CalleeSymName.empty())
-          this->W.printString("Name", CalleeSymName);  
+          this->W.printString("Name", CalleeSymName);
       }
     }
   }

>From b851b8d7e412451a8fc48d78d0b3c4031198fbc0 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 15:38:33 -0700
Subject: [PATCH 21/44] Make llvm output better.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 55 +++++++++++++--------------
 1 file changed, 27 insertions(+), 28 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 1b77c10f8db9c..9e47e82719233 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5472,13 +5472,13 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
 static StringRef GetFuntionKindString(FunctionKind Kind) {
   switch (Kind) {
   case FunctionKind::NOT_INDIRECT_TARGET:
-    return "NOT_INDIRECT_TARGET";
+    return "NOT_INDIRECT";
   case FunctionKind::INDIRECT_TARGET_UNKNOWN_TID:
-    return "INDIRECT_TARGET_UNKNOWN_TID";
+    return "UNKNOWN_TID";
   case FunctionKind::INDIRECT_TARGET_KNOWN_TID:
-    return "INDIRECT_TARGET_KNOWN_TID";
+    return "KNOWN_TID";
   case FunctionKind::NOT_LISTED:
-    return "NOT_LISTED";
+    return "NO_INFO";
   }
   llvm_unreachable("Unknown FunctionKind.");
 }
@@ -8399,44 +8399,43 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     return join(FuncSymNames, ", ");
   };
 
-  DictScope D(this->W, "callgraph_info");
+  ListScope CGI(W, "callgraph_info");
 
-  for (const auto &El : this->FuncCGInfos) {
+  for (const auto &El : this->FuncCGInfos) {    
+    DictScope D(W, "Function");
     typename ELFT::uint FuncEntryPc = El.first;
     FunctionCallgraphInfo CGInfo = El.second;
-    std::string FuncPCStr;
-    raw_string_ostream OS(FuncPCStr);
-    OS << format("0x%lx", FuncEntryPc);
-    DictScope FuncScope(this->W, OS.str());
     std::string FuncSymName = GetFunctionName(FuncEntryPc);
     if (!FuncSymName.empty())
-      this->W.printString("Name", FuncSymName);
+      W.printString("Name", FuncSymName);
 
-    this->W.printNumber("FormatVersionNumber", CGInfo.FormatVersionNumber);
-    this->W.printString("KindStr", GetFuntionKindString(CGInfo.Kind));
-    this->W.printNumber("Kind", (uint64_t)CGInfo.Kind);
+    W.printHex("Address", FuncEntryPc);
+    W.printNumber("Version", CGInfo.FormatVersionNumber);
+    W.printString("KindStr", GetFuntionKindString(CGInfo.Kind));
+    W.printNumber("Kind", (uint64_t)CGInfo.Kind);
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-      this->W.printHex("TypeId", CGInfo.FunctionTypeId);
-    this->W.printNumber("NumIndirectCallSites",
+      W.printHex("TypeId", CGInfo.FunctionTypeId);
+    W.printNumber("NumIndirectCallSites",
                         CGInfo.IndirectCallsites.size());
     if (CGInfo.IndirectCallsites.size() > 0) {
-      ListScope ICT(this->W, "indirect_call_sites");
-      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
-        DictScope IDC(this->W);
-        this->W.printHex("callsite", IndirCallSitePc);
-        this->W.printHex("type_id", TypeId);
+      ListScope ICSs(W, "IndirectCallsites");
+      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {        
+        DictScope ICS(W, "IndirectCallsite");
+        W.printHex("Address", IndirCallSitePc);
+        W.printHex("TypeId", TypeId);
       }
     }
-    this->W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
+    W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
     if (CGInfo.DirectCallees.size() > 0) {
-      ListScope ICT(this->W, "direct_callees");
-      for (auto CalleePC : CGInfo.DirectCallees) {
-        this->W.printHex("calleePC", CalleePC);
+      ListScope DCs(W, "DirectCallees");
+      for (auto CalleePC : CGInfo.DirectCallees) {        
+        DictScope DCs(W, "DirectCallee");        
+        W.printHex("Address", CalleePC);
         std::string CalleeSymName = GetFunctionName(CalleePC);
         if (!CalleeSymName.empty())
-          this->W.printString("Name", CalleeSymName);
-      }
-    }
+          W.printString("Name", CalleeSymName);
+      }      
+    }  
   }
 }
 

>From fd50ff0ae79bc081301afc2c18f78149c6b077e1 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 15:38:46 -0700
Subject: [PATCH 22/44] format source.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 9e47e82719233..36c8b8dfebb8e 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -8401,7 +8401,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
 
   ListScope CGI(W, "callgraph_info");
 
-  for (const auto &El : this->FuncCGInfos) {    
+  for (const auto &El : this->FuncCGInfos) {
     DictScope D(W, "Function");
     typename ELFT::uint FuncEntryPc = El.first;
     FunctionCallgraphInfo CGInfo = El.second;
@@ -8415,11 +8415,10 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     W.printNumber("Kind", (uint64_t)CGInfo.Kind);
     if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
       W.printHex("TypeId", CGInfo.FunctionTypeId);
-    W.printNumber("NumIndirectCallSites",
-                        CGInfo.IndirectCallsites.size());
+    W.printNumber("NumIndirectCallSites", CGInfo.IndirectCallsites.size());
     if (CGInfo.IndirectCallsites.size() > 0) {
       ListScope ICSs(W, "IndirectCallsites");
-      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {        
+      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
         DictScope ICS(W, "IndirectCallsite");
         W.printHex("Address", IndirCallSitePc);
         W.printHex("TypeId", TypeId);
@@ -8428,14 +8427,14 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
     if (CGInfo.DirectCallees.size() > 0) {
       ListScope DCs(W, "DirectCallees");
-      for (auto CalleePC : CGInfo.DirectCallees) {        
-        DictScope DCs(W, "DirectCallee");        
+      for (auto CalleePC : CGInfo.DirectCallees) {
+        DictScope DCs(W, "DirectCallee");
         W.printHex("Address", CalleePC);
         std::string CalleeSymName = GetFunctionName(CalleePC);
         if (!CalleeSymName.empty())
           W.printString("Name", CalleeSymName);
-      }      
-    }  
+      }
+    }
   }
 }
 

>From ee3a081eabd2fd149c116402ee61d3292cbf06b6 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 15:47:04 -0700
Subject: [PATCH 23/44] Minor improvements to LLVM output.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 36c8b8dfebb8e..13e750ae34070 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -8428,11 +8428,11 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     if (CGInfo.DirectCallees.size() > 0) {
       ListScope DCs(W, "DirectCallees");
       for (auto CalleePC : CGInfo.DirectCallees) {
-        DictScope DCs(W, "DirectCallee");
-        W.printHex("Address", CalleePC);
+        DictScope DCs(W, "Entry");
         std::string CalleeSymName = GetFunctionName(CalleePC);
         if (!CalleeSymName.empty())
           W.printString("Name", CalleeSymName);
+        W.printHex("Address", CalleePC);
       }
     }
   }

>From 3c94a4404fc6a95a5f87d506c0cb25fbc396be6a Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 16:03:07 -0700
Subject: [PATCH 24/44] Fix tests to match the new output.

---
 .../call-graph-info-callgraph-section.test    | 207 ++++++-----
 .../llvm-readobj/ELF/call-graph-info.test     | 342 +++++++++++-------
 2 files changed, 337 insertions(+), 212 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index f945bbaa0bf02..f325a3dbb111a 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -1,94 +1,139 @@
 ## Tests --call-graph-info prints information from call graph section.
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines --check-prefix=GNU
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
-# GNU: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
-# GNU-EMPTY:
-# GNU-NEXT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
-# GNU-NEXT: UNKNOWN 6
-# GNU-NEXT: 20 a
-# GNU-EMPTY:
-# GNU-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
-# GNU-NEXT: 10 9
-# GNU-EMPTY:
-# GNU-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
-# GNU-NEXT: 6 9
-# GNU-EMPTY:
-# GNU-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
-# GNU-NEXT: 0 5
+# CHECK: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
+# CHECK-NEXT: Per-function call graph information:: 
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: foo
+# CHECK-NEXT: Function PC:: 0x0
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: NOT_INDIRECT
+# CHECK-NEXT: Indirect callee count:: 0
+# CHECK-NEXT: Direct callee count:: 1
+# CHECK-NEXT: {
+# CHECK-NEXT:   CalleePC:: 0x5
+# CHECK-NEXT: }
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: bar
+# CHECK-NEXT: Function PC:: 0x6
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: UNKNOWN_TID
+# CHECK-NEXT: Indirect callee count:: 1
+# CHECK-NEXT: {
+# CHECK-NEXT:   callsite: 0x9
+# CHECK-NEXT:   calleeTypeId: 0x10
+# CHECK-NEXT: }
+# CHECK-NEXT: Direct callee count:: 0
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: baz
+# CHECK-NEXT: Function PC:: 0xa
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: KNOWN_TID
+# CHECK-NEXT: Function Type ID:: 0x20
+# CHECK-NEXT: Indirect callee count:: 0
+# CHECK-NEXT: Direct callee count:: 0
+
 
 # LLVM: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
-# LLVM: callgraph_info {
-# LLVM-NEXT:  unknown_target_types: [0x6]
-# LLVM-NEXT:  indirect_target_types [
-# LLVM-NEXT:    {
-# LLVM-NEXT:     function_address: 0xA
-# LLVM-NEXT:      type_id: 0x20
-# LLVM-NEXT:    }
-# LLVM-NEXT:  ]
-# LLVM-NEXT:  direct_call_sites [
-# LLVM-NEXT:    {
-# LLVM-NEXT:      caller: 0x0
-# LLVM-NEXT:      call_sites [
-# LLVM-NEXT:        {
-# LLVM-NEXT:          callee: 0x5
-# LLVM-NEXT:        }
-# LLVM-NEXT:      ]
-# LLVM-NEXT:    }
-# LLVM-NEXT:  ]
-# LLVM-NEXT:  indirect_call_sites [
-# LLVM-NEXT:    {
-# LLVM-NEXT:      caller: 0x6
-# LLVM-NEXT:      call_sites: [0x9]
-# LLVM-NEXT:    }
-# LLVM-NEXT:  ]
-# LLVM-NEXT:  indirect_call_types [
-# LLVM-NEXT:    {
-# LLVM-NEXT:      call_site: 0x9
-# LLVM-NEXT:      type_id: 0x10
-# LLVM-NEXT:    }
-# LLVM-NEXT:  ]
-# LLVM-NEXT: }
+# LLVM: callgraph_info [
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: foo
+# LLVM-NEXT:     Address: 0x0
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: NOT_INDIRECT
+# LLVM-NEXT:     Kind: 0
+# LLVM-NEXT:     NumIndirectCallSites: 0
+# LLVM-NEXT:     NumDirectCallSites: 1
+# LLVM-NEXT:     DirectCallees [
+# LLVM-NEXT:       Entry {
+# LLVM-NEXT:         Address: 0x5
+# LLVM-NEXT:       }
+# LLVM-NEXT:     ]
+# LLVM-NEXT:   }
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: bar
+# LLVM-NEXT:     Address: 0x6
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: UNKNOWN_TID
+# LLVM-NEXT:     Kind: 1
+# LLVM-NEXT:     NumIndirectCallSites: 1
+# LLVM-NEXT:     IndirectCallsites [
+# LLVM-NEXT:       IndirectCallsite {
+# LLVM-NEXT:         Address: 0x9
+# LLVM-NEXT:         TypeId: 0x10
+# LLVM-NEXT:       }
+# LLVM-NEXT:     ]
+# LLVM-NEXT:     NumDirectCallSites: 0
+# LLVM-NEXT:   }
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: baz
+# LLVM-NEXT:     Address: 0xA
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: KNOWN_TID
+# LLVM-NEXT:     Kind: 2
+# LLVM-NEXT:     TypeId: 0x20
+# LLVM-NEXT:     NumIndirectCallSites: 0
+# LLVM-NEXT:     NumDirectCallSites: 0
+# LLVM-NEXT:   }
+# LLVM-NEXT: ]
 
 # JSON: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
-# JSON: "callgraph_info": {
-# JSON-NEXT:      "unknown_target_types": [
-# JSON-NEXT:        6
-# JSON-NEXT:      ],
-# JSON-NEXT:      "indirect_target_types": [
-# JSON-NEXT:        {
-# JSON-NEXT:          "function_address": 10,
-# JSON-NEXT:          "type_id": 32
-# JSON-NEXT:        }
-# JSON-NEXT:      ],
-# JSON-NEXT:      "direct_call_sites": [
-# JSON-NEXT:        {
-# JSON-NEXT:          "caller": 0,
-# JSON-NEXT:          "call_sites": [
-# JSON-NEXT:            {
-# JSON-NEXT:              "callee": 5
-# JSON-NEXT:            }
-# JSON-NEXT:          ]
-# JSON-NEXT:        }
-# JSON-NEXT:      ],
-# JSON-NEXT:      "indirect_call_sites": [
-# JSON-NEXT:        {
-# JSON-NEXT:          "caller": 6,
-# JSON-NEXT:          "call_sites": [
-# JSON-NEXT:            9
-# JSON-NEXT:          ]
-# JSON-NEXT:        }
-# JSON-NEXT:      ],
-# JSON-NEXT:      "indirect_call_types": [
-# JSON-NEXT:        {
-# JSON-NEXT:          "call_site": 9,
-# JSON-NEXT:          "type_id": 16
-# JSON-NEXT:        }
-# JSON-NEXT:      ]
-# JSON-NEXT:    }
+# JSON:     "callgraph_info": [
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "foo",
+# JSON-NEXT:           "Address": 0,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "NOT_INDIRECT",
+# JSON-NEXT:           "Kind": 0,
+# JSON-NEXT:           "NumIndirectCallSites": 0,
+# JSON-NEXT:           "NumDirectCallSites": 1,
+# JSON-NEXT:           "DirectCallees": [
+# JSON-NEXT:             {
+# JSON-NEXT:               "Entry": {
+# JSON-NEXT:                 "Address": 5
+# JSON-NEXT:               }
+# JSON-NEXT:             }
+# JSON-NEXT:           ]
+# JSON-NEXT:         }
+# JSON-NEXT:       },
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "bar",
+# JSON-NEXT:           "Address": 6,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "UNKNOWN_TID",
+# JSON-NEXT:           "Kind": 1,
+# JSON-NEXT:           "NumIndirectCallSites": 1,
+# JSON-NEXT:           "IndirectCallsites": [
+# JSON-NEXT:             {
+# JSON-NEXT:               "IndirectCallsite": {
+# JSON-NEXT:                 "Address": 9,
+# JSON-NEXT:                 "TypeId": 16
+# JSON-NEXT:               }
+# JSON-NEXT:             }
+# JSON-NEXT:           ],
+# JSON-NEXT:           "NumDirectCallSites": 0
+# JSON-NEXT:         }
+# JSON-NEXT:       },
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "baz",
+# JSON-NEXT:           "Address": 10,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "KNOWN_TID",
+# JSON-NEXT:           "Kind": 2,
+# JSON-NEXT:           "TypeId": 32,
+# JSON-NEXT:           "NumIndirectCallSites": 0,
+# JSON-NEXT:           "NumDirectCallSites": 0
+# JSON-NEXT:         }
+# JSON-NEXT:       }
+# JSON-NEXT:     ]
+
 
 .text
 
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 0e67b80a95615..594a3b9650dfe 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -41,139 +41,219 @@
 #     return 0;
 #   }
 
-# CHECK: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])
-# CHECK-NEXT: 3ecbeef531f74424 1790 17a0
-# CHECK-NEXT: 308e4b8159bc8654 17b0
-# CHECK-NEXT: fa6809609a76afca 17c0
-# CHECK-EMPTY:
-# CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])
-# CHECK-NEXT: 3ecbeef531f74424 17df 17ef
-# CHECK-NEXT: 308e4b8159bc8654 1804
-# CHECK-EMPTY:
-# CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
-# CHECK-NEXT: 17c0 17df 17ef 1804
-# CHECK-EMPTY:
-# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
-# CHECK-NEXT: 17c0 1790 17a0 17b0
+# CHECK: Per-function call graph information:: 
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: foo
+# CHECK-NEXT: Function PC:: 0x1790
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: KNOWN_TID
+# CHECK-NEXT: Function Type ID:: 0x3ecbeef531f74424
+# CHECK-NEXT: Indirect callee count:: 0
+# CHECK-NEXT: Direct callee count:: 0
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: bar
+# CHECK-NEXT: Function PC:: 0x17a0
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: KNOWN_TID
+# CHECK-NEXT: Function Type ID:: 0x3ecbeef531f74424
+# CHECK-NEXT: Indirect callee count:: 0
+# CHECK-NEXT: Direct callee count:: 0
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: baz
+# CHECK-NEXT: Function PC:: 0x17b0
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: KNOWN_TID
+# CHECK-NEXT: Function Type ID:: 0x308e4b8159bc8654
+# CHECK-NEXT: Indirect callee count:: 0
+# CHECK-NEXT: Direct callee count:: 0
+# CHECK-EMPTY: 
+# CHECK-NEXT: Function:: main
+# CHECK-NEXT: Function PC:: 0x17c0
+# CHECK-NEXT: FormatVersionNumber:: 0
+# CHECK-NEXT: Function Kind:: KNOWN_TID
+# CHECK-NEXT: Function Type ID:: 0xfa6809609a76afca
+# CHECK-NEXT: Indirect callee count:: 3
+# CHECK-NEXT: {
+# CHECK-NEXT:   callsite: 0x17ef
+# CHECK-NEXT:   calleeTypeId: 0x3ecbeef531f74424
+# CHECK-NEXT:   callsite: 0x17df
+# CHECK-NEXT:   calleeTypeId: 0x3ecbeef531f74424
+# CHECK-NEXT:   callsite: 0x1804
+# CHECK-NEXT:   calleeTypeId: 0x308e4b8159bc8654
+# CHECK-NEXT: }
+# CHECK-NEXT: Direct callee count:: 3
+# CHECK-NEXT: {
+# CHECK-NEXT:   Callee:: foo
+# CHECK-NEXT:   CalleePC:: 0x1790
+# CHECK-NEXT:   Callee:: bar
+# CHECK-NEXT:   CalleePC:: 0x17a0
+# CHECK-NEXT:   Callee:: baz
+# CHECK-NEXT:   CalleePC:: 0x17b0
+# CHECK-NEXT: }
 
-# LLVM: callgraph_info {
-# LLVM-NEXT:   indirect_target_types [
-# LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x1790
-# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
-# LLVM-NEXT:     }
-# LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17A0
-# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
-# LLVM-NEXT:     }
-# LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17B0
-# LLVM-NEXT:       type_id: 0x308E4B8159BC8654
-# LLVM-NEXT:     }
-# LLVM-NEXT:     {
-# LLVM-NEXT:       function_address: 0x17C0
-# LLVM-NEXT:       type_id: 0xFA6809609A76AFCA
-# LLVM-NEXT:     }
-# LLVM-NEXT:   ]
-# LLVM-NEXT:   direct_call_sites [
-# LLVM-NEXT:     {
-# LLVM-NEXT:       caller: 0x17C0
-# LLVM-NEXT:       call_sites [
-# LLVM-NEXT:         {
-# LLVM-NEXT:           callee: 0x1790
-# LLVM-NEXT:         }
-# LLVM-NEXT:         {
-# LLVM-NEXT:           callee: 0x17A0
-# LLVM-NEXT:         }
-# LLVM-NEXT:         {
-# LLVM-NEXT:           callee: 0x17B0
-# LLVM-NEXT:         }
-# LLVM-NEXT:       ]
-# LLVM-NEXT:     }
-# LLVM-NEXT:   ]
-# LLVM-NEXT:   indirect_call_sites [
-# LLVM-NEXT:     {
-# LLVM-NEXT:       caller: 0x17C0
-# LLVM-NEXT:       call_sites: [0x17DF, 0x17EF, 0x1804]
-# LLVM-NEXT:     }
-# LLVM-NEXT:   ]
-# LLVM-NEXT:   indirect_call_types [
-# LLVM-NEXT:     {
-# LLVM-NEXT:       call_site: 0x17DF
-# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
-# LLVM-NEXT:     }
-# LLVM-NEXT:     {
-# LLVM-NEXT:       call_site: 0x17EF
-# LLVM-NEXT:       type_id: 0x3ECBEEF531F74424
-# LLVM-NEXT:     }
-# LLVM-NEXT:     {
-# LLVM-NEXT:       call_site: 0x1804
-# LLVM-NEXT:       type_id: 0x308E4B8159BC8654
-# LLVM-NEXT:     }
-# LLVM-NEXT:   ]
-# LLVM-NEXT: }
 
-#JSON:  "callgraph_info": {
-#JSON-NEXT:      "indirect_target_types": [
-#JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6032,
-#JSON-NEXT:          "type_id": 4524972987496481828
-#JSON-NEXT:        },
-#JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6048,
-#JSON-NEXT:          "type_id": 4524972987496481828
-#JSON-NEXT:        },
-#JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6064,
-#JSON-NEXT:          "type_id": 3498816979441845844
-#JSON-NEXT:        },
-#JSON-NEXT:        {
-#JSON-NEXT:          "function_address": 6080,
-#JSON-NEXT:          "type_id": 18043682217572872138
-#JSON-NEXT:        }
-#JSON-NEXT:      ],
-#JSON-NEXT:      "direct_call_sites": [
-#JSON-NEXT:        {
-#JSON-NEXT:          "caller": 6080,
-#JSON-NEXT:          "call_sites": [
-#JSON-NEXT:            {
-#JSON-NEXT:              "callee": 6032
-#JSON-NEXT:            },
-#JSON-NEXT:            {
-#JSON-NEXT:              "callee": 6048
-#JSON-NEXT:            },
-#JSON-NEXT:            {
-#JSON-NEXT:              "callee": 6064
-#JSON-NEXT:            }
-#JSON-NEXT:          ]
-#JSON-NEXT:        }
-#JSON-NEXT:      ],
-#JSON-NEXT:      "indirect_call_sites": [
-#JSON-NEXT:        {
-#JSON-NEXT:          "caller": 6080,
-#JSON-NEXT:          "call_sites": [
-#JSON-NEXT:            6111,
-#JSON-NEXT:            6127,
-#JSON-NEXT:            6148
-#JSON-NEXT:          ]
-#JSON-NEXT:        }
-#JSON-NEXT:      ],
-#JSON-NEXT:      "indirect_call_types": [
-#JSON-NEXT:        {
-#JSON-NEXT:          "call_site": 6111,
-#JSON-NEXT:          "type_id": 4524972987496481828
-#JSON-NEXT:        },
-#JSON-NEXT:        {
-#JSON-NEXT:          "call_site": 6127,
-#JSON-NEXT:          "type_id": 4524972987496481828
-#JSON-NEXT:        },
-#JSON-NEXT:        {
-#JSON-NEXT:          "call_site": 6148,
-#JSON-NEXT:          "type_id": 3498816979441845844
-#JSON-NEXT:        }
-#JSON-NEXT:      ]
-#JSON-NEXT:    }
-#JSON-NEXT:  }
+# LLVM: callgraph_info [
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: foo
+# LLVM-NEXT:     Address: 0x1790
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: KNOWN_TID
+# LLVM-NEXT:     Kind: 2
+# LLVM-NEXT:     TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:     NumIndirectCallSites: 0
+# LLVM-NEXT:     NumDirectCallSites: 0
+# LLVM-NEXT:   }
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: bar
+# LLVM-NEXT:     Address: 0x17A0
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: KNOWN_TID
+# LLVM-NEXT:     Kind: 2
+# LLVM-NEXT:     TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:     NumIndirectCallSites: 0
+# LLVM-NEXT:     NumDirectCallSites: 0
+# LLVM-NEXT:   }
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: baz
+# LLVM-NEXT:     Address: 0x17B0
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: KNOWN_TID
+# LLVM-NEXT:     Kind: 2
+# LLVM-NEXT:     TypeId: 0x308E4B8159BC8654
+# LLVM-NEXT:     NumIndirectCallSites: 0
+# LLVM-NEXT:     NumDirectCallSites: 0
+# LLVM-NEXT:   }
+# LLVM-NEXT:   Function {
+# LLVM-NEXT:     Name: main
+# LLVM-NEXT:     Address: 0x17C0
+# LLVM-NEXT:     Version: 0
+# LLVM-NEXT:     KindStr: KNOWN_TID
+# LLVM-NEXT:     Kind: 2
+# LLVM-NEXT:     TypeId: 0xFA6809609A76AFCA
+# LLVM-NEXT:     NumIndirectCallSites: 3
+# LLVM-NEXT:     IndirectCallsites [
+# LLVM-NEXT:       IndirectCallsite {
+# LLVM-NEXT:         Address: 0x17EF
+# LLVM-NEXT:         TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:       }
+# LLVM-NEXT:       IndirectCallsite {
+# LLVM-NEXT:         Address: 0x17DF
+# LLVM-NEXT:         TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:       }
+# LLVM-NEXT:       IndirectCallsite {
+# LLVM-NEXT:         Address: 0x1804
+# LLVM-NEXT:         TypeId: 0x308E4B8159BC8654
+# LLVM-NEXT:       }
+# LLVM-NEXT:     ]
+# LLVM-NEXT:     NumDirectCallSites: 3
+# LLVM-NEXT:     DirectCallees [
+# LLVM-NEXT:       Entry {
+# LLVM-NEXT:         Name: foo
+# LLVM-NEXT:         Address: 0x1790
+# LLVM-NEXT:       }
+# LLVM-NEXT:       Entry {
+# LLVM-NEXT:         Name: bar
+# LLVM-NEXT:         Address: 0x17A0
+# LLVM-NEXT:       }
+# LLVM-NEXT:       Entry {
+# LLVM-NEXT:         Name: baz
+# LLVM-NEXT:         Address: 0x17B0
+# LLVM-NEXT:       }
+# LLVM-NEXT:     ]
+# LLVM-NEXT:   }
+# LLVM-NEXT: ]
+
+
+# JSON:     "callgraph_info": [
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "foo",
+# JSON-NEXT:           "Address": 6032,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "KNOWN_TID",
+# JSON-NEXT:           "Kind": 2,
+# JSON-NEXT:           "TypeId": 4524972987496481828,
+# JSON-NEXT:           "NumIndirectCallSites": 0,
+# JSON-NEXT:           "NumDirectCallSites": 0
+# JSON-NEXT:         }
+# JSON-NEXT:       },
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "bar",
+# JSON-NEXT:           "Address": 6048,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "KNOWN_TID",
+# JSON-NEXT:           "Kind": 2,
+# JSON-NEXT:           "TypeId": 4524972987496481828,
+# JSON-NEXT:           "NumIndirectCallSites": 0,
+# JSON-NEXT:           "NumDirectCallSites": 0
+# JSON-NEXT:         }
+# JSON-NEXT:       },
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "baz",
+# JSON-NEXT:           "Address": 6064,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "KNOWN_TID",
+# JSON-NEXT:           "Kind": 2,
+# JSON-NEXT:           "TypeId": 3498816979441845844,
+# JSON-NEXT:           "NumIndirectCallSites": 0,
+# JSON-NEXT:           "NumDirectCallSites": 0
+# JSON-NEXT:         }
+# JSON-NEXT:       },
+# JSON-NEXT:       {
+# JSON-NEXT:         "Function": {
+# JSON-NEXT:           "Name": "main",
+# JSON-NEXT:           "Address": 6080,
+# JSON-NEXT:           "Version": 0,
+# JSON-NEXT:           "KindStr": "KNOWN_TID",
+# JSON-NEXT:           "Kind": 2,
+# JSON-NEXT:           "TypeId": 18043682217572872138,
+# JSON-NEXT:           "NumIndirectCallSites": 3,
+# JSON-NEXT:           "IndirectCallsites": [
+# JSON-NEXT:             {
+# JSON-NEXT:               "IndirectCallsite": {
+# JSON-NEXT:                 "Address": 6127,
+# JSON-NEXT:                 "TypeId": 4524972987496481828
+# JSON-NEXT:               }
+# JSON-NEXT:             },
+# JSON-NEXT:             {
+# JSON-NEXT:               "IndirectCallsite": {
+# JSON-NEXT:                 "Address": 6111,
+# JSON-NEXT:                 "TypeId": 4524972987496481828
+# JSON-NEXT:               }
+# JSON-NEXT:             },
+# JSON-NEXT:             {
+# JSON-NEXT:               "IndirectCallsite": {
+# JSON-NEXT:                 "Address": 6148,
+# JSON-NEXT:                 "TypeId": 3498816979441845844
+# JSON-NEXT:               }
+# JSON-NEXT:             }
+# JSON-NEXT:           ],
+# JSON-NEXT:           "NumDirectCallSites": 3,
+# JSON-NEXT:           "DirectCallees": [
+# JSON-NEXT:             {
+# JSON-NEXT:               "Entry": {
+# JSON-NEXT:                 "Name": "foo",
+# JSON-NEXT:                 "Address": 6032
+# JSON-NEXT:               }
+# JSON-NEXT:             },
+# JSON-NEXT:             {
+# JSON-NEXT:               "Entry": {
+# JSON-NEXT:                 "Name": "bar",
+# JSON-NEXT:                 "Address": 6048
+# JSON-NEXT:               }
+# JSON-NEXT:             },
+# JSON-NEXT:             {
+# JSON-NEXT:               "Entry": {
+# JSON-NEXT:                 "Name": "baz",
+# JSON-NEXT:                 "Address": 6064
+# JSON-NEXT:               }
+# JSON-NEXT:             }
+# JSON-NEXT:           ]
+# JSON-NEXT:         }
+# JSON-NEXT:       }
+# JSON-NEXT:     ]
 
 --- !ELF
 FileHeader:

>From 026b184126de4bada670a74b874e513d819fef1d Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 18 Sep 2025 17:04:26 -0700
Subject: [PATCH 25/44] Add REQUIRES: x86-registered-target to tests.

---
 .../llvm-readobj/ELF/call-graph-info-callgraph-section.test     | 2 ++
 .../ELF/call-graph-info-err-invalid-format-version.test         | 2 ++
 .../llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test | 2 ++
 .../ELF/call-graph-info-err-malformed-callgraph-section.test    | 2 ++
 .../ELF/call-graph-info-err-malformed-callgraph-section2.test   | 2 ++
 .../ELF/call-graph-info-err-malformed-callgraph-section3.test   | 2 ++
 .../ELF/call-graph-info-err-no-callgraph-section.test           | 2 ++
 7 files changed, 14 insertions(+)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index f325a3dbb111a..732c5499240b8 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -1,5 +1,7 @@
 ## Tests --call-graph-info prints information from call graph section.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
index 233d4bb8f63d4..15bdb79a0d914 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
@@ -1,6 +1,8 @@
 ## Tests that --call-graph-info fails if .callgraph section has unknown format
 ## version number.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
index eef4eefc7da4c..f792fec850e50 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
@@ -1,6 +1,8 @@
 ## Tests that --call-graph-info fails if .callgraph section has invalid
 ## function kind value.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
index 7872b5d749ef9..74ab50771efff 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -1,5 +1,7 @@
 ## Tests that --call-graph-info fails if .callgraph section has invalid size.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
index 38dc0eca939a7..418caa297b8d8 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
@@ -1,6 +1,8 @@
 ## Tests that --call-graph-info fails if .callgraph section does not have
 ## an expected value, e.g., not as much call sites as the given count.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
index 4ec11f2e65638..ec041b09644bb 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -1,6 +1,8 @@
 ## Tests that --call-graph-info fails if .callgraph section does not have
 ## an expected value, e.g., not as much call sites as the given count.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
index cece28f776b5f..0a092fa5b9c2c 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
@@ -1,5 +1,7 @@
 ## Tests that --call-graph-info warns if there is no .callgraph section.
 
+# REQUIRES: x86-registered-target
+
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
 # RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t

>From d1655c2597acb2aac868130058ad1fddf6905342 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 19 Sep 2025 10:50:03 -0700
Subject: [PATCH 26/44] Account for windows binary name extension.

---
 .../llvm-readobj/ELF/call-graph-info-callgraph-section.test | 6 +++---
 .../ELF/call-graph-info-err-invalid-format-version.test     | 2 +-
 .../ELF/call-graph-info-err-invalid-func-kind.test          | 2 +-
 .../call-graph-info-err-malformed-callgraph-section.test    | 2 +-
 .../call-graph-info-err-malformed-callgraph-section2.test   | 2 +-
 .../call-graph-info-err-malformed-callgraph-section3.test   | 2 +-
 .../ELF/call-graph-info-err-no-callgraph-section.test       | 2 +-
 7 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 732c5499240b8..0130c4f35791e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -7,7 +7,7 @@
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
-# CHECK: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
+# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # CHECK-NEXT: Per-function call graph information:: 
 # CHECK-EMPTY: 
 # CHECK-NEXT: Function:: foo
@@ -40,7 +40,7 @@
 # CHECK-NEXT: Direct callee count:: 0
 
 
-# LLVM: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
+# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # LLVM: callgraph_info [
 # LLVM-NEXT:   Function {
 # LLVM-NEXT:     Name: foo
@@ -83,7 +83,7 @@
 # LLVM-NEXT:   }
 # LLVM-NEXT: ]
 
-# JSON: {{.*}}llvm-readelf: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
+# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
 # JSON:     "callgraph_info": [
 # JSON-NEXT:       {
 # JSON-NEXT:         "Function": {
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
index 15bdb79a0d914..2956f5317d22b 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: error: 'Unknown value': Unknown format version value [1] in .callgraph section.
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'Unknown value': Unknown format version value [1] in .callgraph section.
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
index f792fec850e50..985e00f2aac60 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [FunctionKind] for function at [0x0]': Unknown value [4].
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [FunctionKind] for function at [0x0]': Unknown value [4].
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
index 74ab50771efff..0f2528da803e1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -7,7 +7,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
  
-# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [number of indirect callsites] for function at [0x0]': unexpected end of data at offset 0x19 while reading [0x18, 0x20)
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [number of indirect callsites] for function at [0x0]': unexpected end of data at offset 0x19 while reading [0x18, 0x20)
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
index 418caa297b8d8..105831ec9dd65 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [indirect target type id] for function at [0x0]': unexpected end of data at offset 0x20 while reading [0x20, 0x28)
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [indirect target type id] for function at [0x0]': unexpected end of data at offset 0x20 while reading [0x20, 0x28)
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
index ec041b09644bb..f2d682d23217a 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf: error: 'While reading call graph info's [direct callee PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [direct callee PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)
 
 .text
 .globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
index 0a092fa5b9c2c..28e23e8db6f6d 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
@@ -7,7 +7,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 
-# CHECK: {{.*}}llvm-readelf: error: 'Missing section': No .callgraph section found.
+# CHECK: {{.*}}llvm-readelf{{.*}}: error: 'Missing section': No .callgraph section found.
 
 .text
 .globl _Z3foov

>From b7c56f96ca6fec27c4a111cf51b4a9089ce1ff6f Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 20 Oct 2025 13:29:54 -0700
Subject: [PATCH 27/44] Update callgraph section handling to match the new
 format.

---
 .../call-graph-info-callgraph-section.test    |  32 +--
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 227 +++++++-----------
 2 files changed, 101 insertions(+), 158 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 0130c4f35791e..74bea550019ff 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -163,27 +163,29 @@ qux:                  #< qux is at 11 (b).
  retq
 
 .section	.callgraph,"o", at progbits,.text
-.quad	0       #< Format version number.
+.byte	0       #< Format version number.
+.byte	1       #< Flag IsIndirectTarget true
 .quad	0       #< foo()'s entry address.
-.quad	0       #< Function kind: not an indirect target.
-.quad	0       #< Count of indirect call sites that follow: 0.
-.quad   1       #< Count of direct callees that follow: 1>
+.quad	0       #< TypeID: unknown.
+.long   1       #< Count of direct callees.
+.long	0       #< Count of indirect target type IDs.
 .quad   5       #< Direct callee foo's address>
 
-.quad	0       #< Format version number.
+.byte	0       #< Format version number.
+.byte   1       #< Flag IsIndirectTarget true
 .quad	6       #< bar()'s entry address.
-.quad	1       #< Function kind: indirect target with unknown type id.
-.quad	1       #< Count of indirect call sites that follow: 1.
-.quad 16      #< Indirect call type id.
-.quad 9       #< Indirect call site.
-.quad   0       #< Count of direct call sites that follow: 0>
+.quad	0       #< TypeID: unknown.
+.long   0       #< Count of direct callees
+.long	1       #< Count of indirect target type IDs
+.quad   16      #< Indirect call type id.
 
-.quad	0       #< Format version number.
+
+.byte	0       #< Format version number.
+.byte   1       #< Flag IsIndirectTarget true
 .quad	10      #< baz()'s entry address.
-.quad	2       #< Function kind: indirect target with known type id.
-.quad 32      #< Indirect target type id.
-.quad	0       #< Count of indirect call sites that follow: 0>.
-.quad   0       #< Count of direct call sites that follow: 0>
+.quad   32      #< Indirect target type id.
+.long	0       #< Count of direct callees
+.long   0       #< Count of indirect call type ids
 
 # No call graph section entry for qux. 
 # Technically its "UNKNOWN" type id but will not be printed as such by llvm-readelf.  
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 73a85189112f3..8cfcecfc5b7cb 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -188,27 +188,14 @@ struct GroupSection {
   std::vector<GroupMember> Members;
 };
 
-// Call graph function kind.
-enum class FunctionKind : uint64_t {
-  // Function cannot be target to indirect calls.
-  NOT_INDIRECT_TARGET = 0,
-  // Function may be target to indirect calls but its type id is unknown.
-  INDIRECT_TARGET_UNKNOWN_TID = 1,
-  // Function may be target to indirect calls and its type id is known.
-  INDIRECT_TARGET_KNOWN_TID = 2,
-
-  // Available in the binary but not listed in the call graph section.
-  NOT_LISTED = 3,
-};
-
 // Per-function call graph information.
 template <typename AddrType> struct FunctionCallgraphInfoImpl {
-  uint64_t FormatVersionNumber;
-  FunctionKind Kind;
-  uint64_t
-      FunctionTypeId; // Only if Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID
-  DenseMap<AddrType, uint64_t> IndirectCallsites;
+  AddrType FunctionAddress;
+  uint8_t FormatVersionNumber;
+  bool IsIndirectTarget;
+  uint64_t FunctionTypeId;
   SmallSet<AddrType, 4> DirectCallees;
+  SmallSet<uint64_t, 4> IndirectTypeIDs;
 };
 
 namespace {
@@ -466,7 +453,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
 
   // Callgraph - Main data structure to maintain per function callgraph
   // information.
-  MapVector<typename ELFT::uint, FunctionCallgraphInfo> FuncCGInfos;
+  SmallVector<FunctionCallgraphInfo, 16> FuncCGInfos;
 
   // // Callgraph - 64 bit type id mapped to entry PC addresses of functions
   // which
@@ -475,7 +462,8 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
 
   // Callgraph - Read callgraph section and process its contents to populate
   // Callgraph related data structures which will be used to dump callgraph
-  // info. Returns false if there is no .callgraph section in the input file.
+  // info. Returns false if there is no .llvm.callgraph section in the input
+  // file.
   bool processCallGraphSection();
 
 private:
@@ -5313,8 +5301,8 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
 template <class ELFT>
 static std::optional<object::SectionRef>
 getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
-  // Get the .callgraph section.
-  StringRef CallGraphSectionName(".callgraph");
+  // Get the .llvm.callgraph section.
+  StringRef CallGraphSectionName(".llvm.callgraph");
   std::optional<object::SectionRef> CallGraphSection;
   for (auto Sec : ObjF.sections()) {
     StringRef Name;
@@ -5330,9 +5318,9 @@ getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
 }
 
 template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
-  const Elf_Shdr *CGSection = findSectionByName(".callgraph");
+  const Elf_Shdr *CGSection = findSectionByName(".llvm.callgraph");
   if (!CGSection) {
-    Error NoSectionErr = createError("No .callgraph section found.");
+    Error NoSectionErr = createError("No .llvm.callgraph section found.");
     reportError(std::move(NoSectionErr), "Missing section");
   }
 
@@ -5341,7 +5329,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   if (!SectionBytesOrErr) {
     Error SectionReadErr = SectionBytesOrErr.takeError();
     reportError(std::move(SectionReadErr),
-                "Unable to read the .callgraph section");
+                "Unable to read the .llvm.callgraph section");
   }
 
   auto PrintMalformedError = [&](Error &E, Twine FuncPC, StringRef Component) {
@@ -5357,15 +5345,12 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(),
                      ObjF.getBytesInAddress());
 
-  uint64_t NotListedCount = 0;
   uint64_t UnknownCount = 0;
-
   uint64_t Offset = 0;
   while (Offset < CGSection->sh_size) {
     Error CGSectionErr = Error::success();
-    // Format version number.
-    uint64_t FormatVersionNumber = Data.getU64(&Offset, &CGSectionErr);
 
+    uint8_t FormatVersionNumber = Data.getU8(&Offset, &CGSectionErr);
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info FormatVersionNumber");
@@ -5373,22 +5358,29 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     if (FormatVersionNumber != 0) {
       Error FormatErr = createError("Unknown format version value [" +
                                     std::to_string(FormatVersionNumber) +
-                                    "] in .callgraph section.");
+                                    "] in .llvm.callgraph section.");
       reportError(std::move(FormatErr), "Unknown value");
     }
 
-    // Read function address.
+    uint8_t Flags = Data.getU8(&Offset, &CGSectionErr);
+    if (CGSectionErr)
+      reportError(std::move(CGSectionErr),
+                  "While reading call graph info's Flags");
+
+    // TODO(prabhuk): CANT JUST USE THE RAW BYTES AS ADDRESS IN OBJECT FILES
+    // WHICH ARE NOT LINKED FULLY THE ADDRESSES ARE ALWAYS ZERO Read function
+    // address.
     typename ELFT::uint FuncAddr =
         Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr);
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info function entry PC");
 
-    if (FuncCGInfos.find(FuncAddr) != FuncCGInfos.end()) {
-      Error DuplicatePcErr =
-          createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
-      reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
-    }
+    // if (FuncCGInfos.find(FuncAddr) != FuncCGInfos.end()) {
+    //   Error DuplicatePcErr =
+    //       createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
+    //   reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
+    // }
 
     using FunctionCallgraphInfo =
         ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
@@ -5396,58 +5388,24 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     // Create a new entry for this function.
     FunctionCallgraphInfo CGInfo;
     CGInfo.FormatVersionNumber = FormatVersionNumber;
-
-    // Read function kind.
-    uint64_t KindVal = Data.getU64(&Offset, &CGSectionErr);
-    if (CGSectionErr)
-      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), "Kind");
-
-    if (KindVal > 3) {
-      Error KindErr =
-          createError("Unknown value [" + std::to_string(KindVal) + "].");
-      PrintMalformedError(KindErr, Twine::utohexstr(FuncAddr), "FunctionKind");
-    }
-
-    FunctionKind Kind = static_cast<FunctionKind>(KindVal);
-    CGInfo.Kind = Kind;
-    if (Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) {
-      // Read type id if this function is an indirect call target.
-      uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
-      if (CGSectionErr)
-        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect type id");
-      CGInfo.FunctionTypeId = TypeId;
-    }
-    if (Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
-      UnknownCount++;
-    if (Kind == FunctionKind::NOT_LISTED)
-      NotListedCount++;
-
-    // Read number of indirect call sites for this function.
-    uint64_t NumIndirectCallsites = Data.getU64(&Offset, &CGSectionErr);
+    bool IsIndirectTarget = Flags & 1; // LSB is set to 1 if indirect target.
+    CGInfo.IsIndirectTarget = IsIndirectTarget;
+    uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
     if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                          "number of indirect callsites");
+                          "indirect type id");
+    CGInfo.FunctionTypeId = TypeId;
 
-    for (unsigned long I = 0; I < NumIndirectCallsites; I++) {
-      uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
-      if (CGSectionErr)
-        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect target type id");
-      typename ELFT::uint CallSitePc =
-          Data.getUnsigned(&Offset, sizeof(CallSitePc), &CGSectionErr);
-      if (CGSectionErr)
-        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect callsite PC");
-      CGInfo.IndirectCallsites.try_emplace(CallSitePc, TypeId);
-    }
+    if (IsIndirectTarget && TypeId == 0)
+      UnknownCount++;
 
     // Read number of direct call sites for this function.
-    uint64_t NumDirectCallees = Data.getU64(&Offset, &CGSectionErr);
+    uint64_t NumDirectCallees = Data.getULEB128(&Offset, &CGSectionErr);
     if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "number of direct callsites");
-    // Read direct call sites and populate FuncCGInfos.
+
+    // Read uniqeu direct callees and populate FuncCGInfos.
     for (uint64_t I = 0; I < NumDirectCallees; ++I) {
       typename ELFT::uint Callee =
           Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
@@ -5456,43 +5414,37 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                             "direct callee PC");
       CGInfo.DirectCallees.insert(Callee);
     }
-    FuncCGInfos[FuncAddr] = CGInfo;
+    uint64_t NumIndirectTargetTypeIDs = Data.getULEB128(&Offset, &CGSectionErr);
+    if (CGSectionErr)
+      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                          "number of indirect target type IDs");
+
+    // Read unique indirect target type IDs  and populate FuncCGInfos.
+    for (uint64_t I = 0; I < NumIndirectTargetTypeIDs; ++I) {
+      uint64_t TargetType = Data.getU64(&Offset, &CGSectionErr);
+      if (CGSectionErr)
+        PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                            "indirect type ID");
+      CGInfo.IndirectTypeIDs.insert(TargetType);
+    }
+    FuncCGInfos.push_back(CGInfo);
   }
 
-  if (NotListedCount)
-    reportUniqueWarning(".callgraph section does not have information for " +
-                        std::to_string(NotListedCount) + " functions.");
   if (UnknownCount)
-    reportUniqueWarning(".callgraph section has unknown type id for " +
+    reportUniqueWarning(".llvm.callgraph section has unknown type id for " +
                         std::to_string(UnknownCount) + " indirect targets.");
 
-  // Sort function info by function PC.
-  llvm::sort(FuncCGInfos,
-             [](const auto &A, const auto &B) { return A.first < B.first; });
+  // // Sort function info by function PC.
+  // llvm::sort(FuncCGInfos,
+  //            [](const auto &A, const auto &B) { return A.first < B.first; });
   return true;
 }
 
-static StringRef GetFuntionKindString(FunctionKind Kind) {
-  switch (Kind) {
-  case FunctionKind::NOT_INDIRECT_TARGET:
-    return "NOT_INDIRECT";
-  case FunctionKind::INDIRECT_TARGET_UNKNOWN_TID:
-    return "UNKNOWN_TID";
-  case FunctionKind::INDIRECT_TARGET_KNOWN_TID:
-    return "KNOWN_TID";
-  case FunctionKind::NOT_LISTED:
-    return "NO_INFO";
-  }
-  llvm_unreachable("Unknown FunctionKind.");
-}
-
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
   if (this->FuncCGInfos.size() == 0)
     return;
-  using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
 
   auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
@@ -5507,30 +5459,16 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   };
 
   OS << "Per-function call graph information:: \n";
-  for (const auto &El : this->FuncCGInfos) {
-    typename ELFT::uint FuncEntryPc = El.first;
-    FunctionCallgraphInfo CGInfo = El.second;
+  for (const auto &CGInfo : this->FuncCGInfos) {
+    typename ELFT::uint FuncEntryPc = CGInfo.FunctionAddress;
     std::string FuncSymNames = GetFunctionName(FuncEntryPc);
     if (!FuncSymNames.empty())
       OS << "\nFunction:: " << FuncSymNames;
     OS << "\nFunction PC:: " << format("0x%lx", FuncEntryPc);
     OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
-    OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind);
-    if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-      OS << "\nFunction Type ID:: 0x" << format("%lx", CGInfo.FunctionTypeId);
-    OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size();
-    if (CGInfo.IndirectCallsites.size() > 0) {
-      OS << "\n{";
-      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
-        OS << "\n";
-        OS.PadToColumn(2);
-        OS << "callsite: 0x" << format("%lx", IndirCallSitePc);
-        OS << "\n";
-        OS.PadToColumn(2);
-        OS << "calleeTypeId: 0x" << format("%lx", TypeId);
-      }
-      OS << "\n}";
-    }
+    OS << "\nIsIndirectTarget:: "
+       << (CGInfo.IsIndirectTarget ? "true" : "false");
+    OS << "\nFunction Type ID:: 0x" << format("%lx", CGInfo.FunctionTypeId);
     OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
     if (CGInfo.DirectCallees.size() > 0) {
       OS << "\n{";
@@ -5547,6 +5485,16 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
       }
       OS << "\n}";
     }
+    OS << "\nIndirect target type ID count:: " << CGInfo.IndirectTypeIDs.size();
+    if (CGInfo.IndirectTypeIDs.size() > 0) {
+      OS << "\n{";
+      for (auto TypeId : CGInfo.IndirectTypeIDs) {
+        OS << "\n";
+        OS.PadToColumn(2);
+        OS << "calleeTypeId: 0x" << format("%lx", TypeId);
+      }
+      OS << "\n}";
+    }
     OS << "\n";
   }
 }
@@ -8387,8 +8335,6 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     return;
   if (this->FuncCGInfos.size() == 0)
     return;
-  using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
 
   auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
@@ -8404,30 +8350,18 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
 
   ListScope CGI(W, "callgraph_info");
 
-  for (const auto &El : this->FuncCGInfos) {
+  for (const auto &CGInfo : this->FuncCGInfos) {
     DictScope D(W, "Function");
-    typename ELFT::uint FuncEntryPc = El.first;
-    FunctionCallgraphInfo CGInfo = El.second;
+    typename ELFT::uint FuncEntryPc = CGInfo.FunctionAddress;
     std::string FuncSymName = GetFunctionName(FuncEntryPc);
     if (!FuncSymName.empty())
       W.printString("Name", FuncSymName);
-
     W.printHex("Address", FuncEntryPc);
     W.printNumber("Version", CGInfo.FormatVersionNumber);
-    W.printString("KindStr", GetFuntionKindString(CGInfo.Kind));
-    W.printNumber("Kind", (uint64_t)CGInfo.Kind);
-    if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID)
-      W.printHex("TypeId", CGInfo.FunctionTypeId);
-    W.printNumber("NumIndirectCallSites", CGInfo.IndirectCallsites.size());
-    if (CGInfo.IndirectCallsites.size() > 0) {
-      ListScope ICSs(W, "IndirectCallsites");
-      for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) {
-        DictScope ICS(W, "IndirectCallsite");
-        W.printHex("Address", IndirCallSitePc);
-        W.printHex("TypeId", TypeId);
-      }
-    }
-    W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size());
+    W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget);
+    W.printHex("TypeId", CGInfo.FunctionTypeId);
+    W.printNumber("NumDirectCallees", CGInfo.DirectCallees.size());
+    W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size());
     if (CGInfo.DirectCallees.size() > 0) {
       ListScope DCs(W, "DirectCallees");
       for (auto CalleePC : CGInfo.DirectCallees) {
@@ -8438,6 +8372,13 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
         W.printHex("Address", CalleePC);
       }
     }
+    if (CGInfo.IndirectTypeIDs.size() > 0) {
+      ListScope ICSs(W, "IndirectTypeIDs");
+      for (auto TypeId : CGInfo.IndirectTypeIDs) {
+        DictScope ICS(W, "Entry");
+        W.printHex("TypeId", TypeId);
+      }
+    }
   }
 }
 

>From e075175776804e9c020e23176b440d49f00a49e7 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 20 Oct 2025 16:00:02 -0700
Subject: [PATCH 28/44] Handle relocations in callgraph section.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 222 ++++++++++++++++++--------
 1 file changed, 157 insertions(+), 65 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 8cfcecfc5b7cb..2c4bd63c1cb50 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -189,12 +189,13 @@ struct GroupSection {
 };
 
 // Per-function call graph information.
-template <typename AddrType> struct FunctionCallgraphInfoImpl {
-  AddrType FunctionAddress;
+struct FunctionCallgraphInfo {
+  uint64_t FunctionAddress;
+  uint64_t FunctionAddressOffset;
   uint8_t FormatVersionNumber;
   bool IsIndirectTarget;
   uint64_t FunctionTypeId;
-  SmallSet<AddrType, 4> DirectCallees;
+  SmallSet<uint64_t, 4> DirectCallees;
   SmallSet<uint64_t, 4> IndirectTypeIDs;
 };
 
@@ -447,10 +448,6 @@ 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);
-
-  using FunctionCallgraphInfo =
-      ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-
   // Callgraph - Main data structure to maintain per function callgraph
   // information.
   SmallVector<FunctionCallgraphInfo, 16> FuncCGInfos;
@@ -5349,7 +5346,6 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   uint64_t Offset = 0;
   while (Offset < CGSection->sh_size) {
     Error CGSectionErr = Error::success();
-
     uint8_t FormatVersionNumber = Data.getU8(&Offset, &CGSectionErr);
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
@@ -5367,26 +5363,17 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       reportError(std::move(CGSectionErr),
                   "While reading call graph info's Flags");
 
-    // TODO(prabhuk): CANT JUST USE THE RAW BYTES AS ADDRESS IN OBJECT FILES
-    // WHICH ARE NOT LINKED FULLY THE ADDRESSES ARE ALWAYS ZERO Read function
-    // address.
+    uint64_t FuncAddrOffset = Offset;
     typename ELFT::uint FuncAddr =
         Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr);
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info function entry PC");
 
-    // if (FuncCGInfos.find(FuncAddr) != FuncCGInfos.end()) {
-    //   Error DuplicatePcErr =
-    //       createError("for function PC: 0x" + Twine::utohexstr(FuncAddr));
-    //   reportError(std::move(DuplicatePcErr), "Duplicate call graph entry");
-    // }
-
-    using FunctionCallgraphInfo =
-        ::FunctionCallgraphInfoImpl<typename ELFT::uint>;
-
+    bool IsETREL = this->Obj.getHeader().e_type == ELF::ET_REL;
     // Create a new entry for this function.
     FunctionCallgraphInfo CGInfo;
+    CGInfo.FunctionAddress = IsETREL ? FuncAddrOffset : FuncAddr;
     CGInfo.FormatVersionNumber = FormatVersionNumber;
     bool IsIndirectTarget = Flags & 1; // LSB is set to 1 if indirect target.
     CGInfo.IsIndirectTarget = IsIndirectTarget;
@@ -5399,33 +5386,44 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     if (IsIndirectTarget && TypeId == 0)
       UnknownCount++;
 
-    // Read number of direct call sites for this function.
-    uint64_t NumDirectCallees = Data.getULEB128(&Offset, &CGSectionErr);
-    if (CGSectionErr)
-      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                          "number of direct callsites");
-
-    // Read uniqeu direct callees and populate FuncCGInfos.
-    for (uint64_t I = 0; I < NumDirectCallees; ++I) {
-      typename ELFT::uint Callee =
-          Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
+    bool HasDirectCallees =
+        Flags & (1u << 1); // LSB 1 is set to 1 if direct callees present.
+    if (HasDirectCallees) {
+      // Read number of direct call sites for this function.
+      uint64_t NumDirectCallees = Data.getULEB128(&Offset, &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "direct callee PC");
-      CGInfo.DirectCallees.insert(Callee);
+                            "number of direct callsites");
+      // Read uniqeu direct callees and populate FuncCGInfos.
+      for (uint64_t I = 0; I < NumDirectCallees; ++I) {
+        uint64_t CalleeOffset = Offset;
+        typename ELFT::uint Callee =
+            Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
+        if (CGSectionErr)
+          PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                              "direct callee PC");
+        CGInfo.DirectCallees.insert((IsETREL ? CalleeOffset : Callee));
+      }
     }
-    uint64_t NumIndirectTargetTypeIDs = Data.getULEB128(&Offset, &CGSectionErr);
-    if (CGSectionErr)
-      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                          "number of indirect target type IDs");
 
-    // Read unique indirect target type IDs  and populate FuncCGInfos.
-    for (uint64_t I = 0; I < NumIndirectTargetTypeIDs; ++I) {
-      uint64_t TargetType = Data.getU64(&Offset, &CGSectionErr);
+    bool HasIndirectTypeIds =
+        Flags &
+        (1u << 2); // LSB 2 is set to 1 if indirect target type Ids present.
+    if (HasIndirectTypeIds) {
+      uint64_t NumIndirectTargetTypeIDs =
+          Data.getULEB128(&Offset, &CGSectionErr);
       if (CGSectionErr)
         PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
-                            "indirect type ID");
-      CGInfo.IndirectTypeIDs.insert(TargetType);
+                            "number of indirect target type IDs");
+
+      // Read unique indirect target type IDs  and populate FuncCGInfos.
+      for (uint64_t I = 0; I < NumIndirectTargetTypeIDs; ++I) {
+        uint64_t TargetType = Data.getU64(&Offset, &CGSectionErr);
+        if (CGSectionErr)
+          PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                              "indirect type ID");
+        CGInfo.IndirectTypeIDs.insert(TargetType);
+      }
     }
     FuncCGInfos.push_back(CGInfo);
   }
@@ -5446,7 +5444,33 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   if (this->FuncCGInfos.size() == 0)
     return;
 
-  auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
+  const Elf_Shdr *CGSection = this->findSectionByName(".llvm.callgraph");
+  const Elf_Shdr *CGRelSection = nullptr;
+  std::vector<Relocation<ELFT>> Relocations;
+  const Elf_Shdr *RelocSymTab = nullptr;
+
+  if (CGSection) {
+    auto IsMatch = [&](const Elf_Shdr &Sec) { return &Sec == CGSection; };
+    Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> MapOrErr =
+        this->Obj.getSectionAndRelocations(IsMatch);
+    if (MapOrErr && !MapOrErr->empty()) {
+      CGRelSection = MapOrErr->front().second;
+    }
+  }
+
+  if (CGRelSection) {
+    this->forEachRelocationDo(*CGRelSection,
+                              [&](const Relocation<ELFT> &R, unsigned Ndx,
+                                  const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
+                                RelocSymTab = SymTab;
+                                Relocations.push_back(R);
+                              });
+    llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) {
+      return LHS.Offset < RHS.Offset;
+    });
+  }
+
+  auto GetFunctionName = [&](uint64_t EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
     if (FuncSymIndexes.empty())
@@ -5458,13 +5482,43 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     return join(FuncSymNames, ", ");
   };
 
+  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
+    if (this->Obj.getHeader().e_type == ELF::ET_REL) {
+      auto Reloc = llvm::lower_bound(
+          Relocations, FuncEntryPC,
+          [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+      OS << "\nFunction:: ";
+      Expected<RelSymbol<ELFT>> RelSym =
+          this->getRelocationTarget(*Reloc, RelocSymTab);
+      if (!RelSym) {
+        this->reportUniqueWarning(RelSym.takeError());
+        OS << format("0x%lx", FuncEntryPC);
+      } else {
+        SmallString<32> RelocName;
+        this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
+        OS << RelSym->Name;
+        if (Reloc->Addend) {
+          if (*Reloc->Addend >= 0)
+            OS << " + " << *Reloc->Addend;
+          else
+            OS << " - " << -(*Reloc->Addend);
+        }
+        OS << " (" << RelocName << ")";
+      }
+
+    } else {
+      std::string FuncSymNames = GetFunctionName(FuncEntryPC);
+      if (!FuncSymNames.empty())
+        OS << "\nFunction:: " << FuncSymNames;
+      OS << "\nFunction PC:: ";
+      OS << format("0x%lx", FuncEntryPC);
+    }
+  };
+
   OS << "Per-function call graph information:: \n";
   for (const auto &CGInfo : this->FuncCGInfos) {
-    typename ELFT::uint FuncEntryPc = CGInfo.FunctionAddress;
-    std::string FuncSymNames = GetFunctionName(FuncEntryPc);
-    if (!FuncSymNames.empty())
-      OS << "\nFunction:: " << FuncSymNames;
-    OS << "\nFunction PC:: " << format("0x%lx", FuncEntryPc);
+    PrintFunctionInfo(CGInfo.FunctionAddress);
+
     OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
     OS << "\nIsIndirectTarget:: "
        << (CGInfo.IsIndirectTarget ? "true" : "false");
@@ -5473,15 +5527,8 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     if (CGInfo.DirectCallees.size() > 0) {
       OS << "\n{";
       for (auto CalleePC : CGInfo.DirectCallees) {
-        std::string FuncSymNames = GetFunctionName(CalleePC);
-        if (!FuncSymNames.empty()) {
-          OS << "\n";
-          OS.PadToColumn(2);
-          OS << "Callee:: " << FuncSymNames;
-        }
-        OS << "\n";
         OS.PadToColumn(2);
-        OS << "CalleePC:: 0x" << format("%lx", CalleePC);
+        PrintFunctionInfo(CalleePC);
       }
       OS << "\n}";
     }
@@ -8336,6 +8383,32 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   if (this->FuncCGInfos.size() == 0)
     return;
 
+  const Elf_Shdr *CGSection = this->findSectionByName(".llvm.callgraph");
+  const Elf_Shdr *CGRelSection = nullptr;
+  std::vector<Relocation<ELFT>> Relocations;
+  const Elf_Shdr *RelocSymTab = nullptr;
+
+  if (CGSection) {
+    auto IsMatch = [&](const Elf_Shdr &Sec) { return &Sec == CGSection; };
+    Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> MapOrErr =
+        this->Obj.getSectionAndRelocations(IsMatch);
+    if (MapOrErr && !MapOrErr->empty()) {
+      CGRelSection = MapOrErr->front().second;
+    }
+  }
+
+  if (CGRelSection) {
+    this->forEachRelocationDo(*CGRelSection,
+                              [&](const Relocation<ELFT> &R, unsigned Ndx,
+                                  const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
+                                RelocSymTab = SymTab;
+                                Relocations.push_back(R);
+                              });
+    llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) {
+      return LHS.Offset < RHS.Offset;
+    });
+  }
+
   auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
@@ -8348,15 +8421,38 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     return join(FuncSymNames, ", ");
   };
 
+  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
+    if (this->Obj.getHeader().e_type == ELF::ET_REL) {
+      auto Reloc = llvm::lower_bound(
+          Relocations, FuncEntryPC,
+          [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+      Expected<RelSymbol<ELFT>> RelSym =
+          this->getRelocationTarget(*Reloc, RelocSymTab);
+      if (!RelSym) {
+        this->reportUniqueWarning(RelSym.takeError());
+        W.printHex("Address", FuncEntryPC);
+      } else {
+        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);
+      }
+    } else {
+      std::string FuncSymName = GetFunctionName(FuncEntryPC);
+      if (!FuncSymName.empty())
+        W.printString("Name", FuncSymName);
+      W.printHex("Address", FuncEntryPC);
+    }
+  };
+
   ListScope CGI(W, "callgraph_info");
 
   for (const auto &CGInfo : this->FuncCGInfos) {
     DictScope D(W, "Function");
-    typename ELFT::uint FuncEntryPc = CGInfo.FunctionAddress;
-    std::string FuncSymName = GetFunctionName(FuncEntryPc);
-    if (!FuncSymName.empty())
-      W.printString("Name", FuncSymName);
-    W.printHex("Address", FuncEntryPc);
+    PrintFunctionInfo(CGInfo.FunctionAddress);
     W.printNumber("Version", CGInfo.FormatVersionNumber);
     W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget);
     W.printHex("TypeId", CGInfo.FunctionTypeId);
@@ -8365,11 +8461,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     if (CGInfo.DirectCallees.size() > 0) {
       ListScope DCs(W, "DirectCallees");
       for (auto CalleePC : CGInfo.DirectCallees) {
-        DictScope DCs(W, "Entry");
-        std::string CalleeSymName = GetFunctionName(CalleePC);
-        if (!CalleeSymName.empty())
-          W.printString("Name", CalleeSymName);
-        W.printHex("Address", CalleePC);
+        PrintFunctionInfo(CalleePC);
       }
     }
     if (CGInfo.IndirectTypeIDs.size() > 0) {

>From 876f7a61a15c9bb1a618b4685167216d3dd1d1d2 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 20 Oct 2025 17:36:48 -0700
Subject: [PATCH 29/44] New format for GNU printer.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 67 +++++++++++++--------------
 1 file changed, 31 insertions(+), 36 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 2c4bd63c1cb50..c1e121dadaf04 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5441,7 +5441,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
-  if (this->FuncCGInfos.size() == 0)
+  if (this->FuncCGInfos.empty())
     return;
 
   const Elf_Shdr *CGSection = this->findSectionByName(".llvm.callgraph");
@@ -5482,67 +5482,62 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
     return join(FuncSymNames, ", ");
   };
 
-  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
+  auto GetFunctionAddressAndName = [&](uint64_t FuncEntryPC) -> std::string {
+    std::string S;
+    raw_string_ostream Stream(S);
     if (this->Obj.getHeader().e_type == ELF::ET_REL) {
       auto Reloc = llvm::lower_bound(
           Relocations, FuncEntryPC,
           [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
-      OS << "\nFunction:: ";
       Expected<RelSymbol<ELFT>> RelSym =
           this->getRelocationTarget(*Reloc, RelocSymTab);
       if (!RelSym) {
         this->reportUniqueWarning(RelSym.takeError());
-        OS << format("0x%lx", FuncEntryPC);
+        Stream << format("0x%lx", FuncEntryPC);
       } else {
         SmallString<32> RelocName;
         this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
-        OS << RelSym->Name;
+        Stream << RelSym->Name;
         if (Reloc->Addend) {
           if (*Reloc->Addend >= 0)
-            OS << " + " << *Reloc->Addend;
+            Stream << " + " << *Reloc->Addend;
           else
-            OS << " - " << -(*Reloc->Addend);
+            Stream << " - " << -(*Reloc->Addend);
         }
-        OS << " (" << RelocName << ")";
+        Stream << " (" << RelocName << ")";
       }
-
     } else {
       std::string FuncSymNames = GetFunctionName(FuncEntryPC);
+      Stream << "0x" << to_string(format_hex(FuncEntryPC, 1));
       if (!FuncSymNames.empty())
-        OS << "\nFunction:: " << FuncSymNames;
-      OS << "\nFunction PC:: ";
-      OS << format("0x%lx", FuncEntryPC);
+        Stream << " <" << FuncSymNames << ">";
     }
+    return Stream.str();
   };
 
-  OS << "Per-function call graph information:: \n";
+  OS << "\nCall graph section '.llvm.callgraph' contains "
+     << this->FuncCGInfos.size() << " entries:\n";
+
+  int EntryIndex = 0;
   for (const auto &CGInfo : this->FuncCGInfos) {
-    PrintFunctionInfo(CGInfo.FunctionAddress);
+    OS << "\n  Entry " << EntryIndex++ << ":\n";
+    OS << "    Function:          "
+       << GetFunctionAddressAndName(CGInfo.FunctionAddress) << "\n";
+    OS << "    Indirect Target:   " << (CGInfo.IsIndirectTarget ? "Yes" : "No")
+       << "\n";
+    OS << "    Type ID:           " << format_hex(CGInfo.FunctionTypeId, 1)
+       << "\n";
 
-    OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber;
-    OS << "\nIsIndirectTarget:: "
-       << (CGInfo.IsIndirectTarget ? "true" : "false");
-    OS << "\nFunction Type ID:: 0x" << format("%lx", CGInfo.FunctionTypeId);
-    OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size();
-    if (CGInfo.DirectCallees.size() > 0) {
-      OS << "\n{";
-      for (auto CalleePC : CGInfo.DirectCallees) {
-        OS.PadToColumn(2);
-        PrintFunctionInfo(CalleePC);
-      }
-      OS << "\n}";
+    OS << "    Direct Callees (" << CGInfo.DirectCallees.size() << "):\n";
+    for (auto CalleePC : CGInfo.DirectCallees) {
+      OS << "      " << GetFunctionAddressAndName(CalleePC) << "\n";
     }
-    OS << "\nIndirect target type ID count:: " << CGInfo.IndirectTypeIDs.size();
-    if (CGInfo.IndirectTypeIDs.size() > 0) {
-      OS << "\n{";
-      for (auto TypeId : CGInfo.IndirectTypeIDs) {
-        OS << "\n";
-        OS.PadToColumn(2);
-        OS << "calleeTypeId: 0x" << format("%lx", TypeId);
-      }
-      OS << "\n}";
+
+    OS << "    Indirect Callees by Type ID (" << CGInfo.IndirectTypeIDs.size()
+       << "):\n";
+    for (auto TypeId : CGInfo.IndirectTypeIDs) {
+      OS << "      0x" << format_hex(TypeId, 1) << "\n";
     }
-    OS << "\n";
   }
 }
 

>From 33d06b4ded9cd9fe5ffe6447b091f1871a3fe02f Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 20 Oct 2025 17:46:44 -0700
Subject: [PATCH 30/44] Extract relocations handling to a common function.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 79 +++++++++++----------------
 1 file changed, 33 insertions(+), 46 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index c1e121dadaf04..b5d0fe4d80951 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -463,6 +463,9 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   // file.
   bool processCallGraphSection();
 
+  void getCallGraphRelocations(std::vector<Relocation<ELFT>> &Relocations,
+                               const Elf_Shdr *&RelocSymTab);
+
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
 };
@@ -5438,37 +5441,43 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   return true;
 }
 
-template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
-  if (!this->processCallGraphSection())
-    return;
-  if (this->FuncCGInfos.empty())
+template <class ELFT>
+void ELFDumper<ELFT>::getCallGraphRelocations(
+    std::vector<Relocation<ELFT>> &Relocations, const Elf_Shdr *&RelocSymTab) {
+  const Elf_Shdr *CGSection = findSectionByName(".llvm.callgraph");
+  if (!CGSection)
     return;
 
-  const Elf_Shdr *CGSection = this->findSectionByName(".llvm.callgraph");
   const Elf_Shdr *CGRelSection = nullptr;
-  std::vector<Relocation<ELFT>> Relocations;
-  const Elf_Shdr *RelocSymTab = nullptr;
-
-  if (CGSection) {
-    auto IsMatch = [&](const Elf_Shdr &Sec) { return &Sec == CGSection; };
-    Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> MapOrErr =
-        this->Obj.getSectionAndRelocations(IsMatch);
-    if (MapOrErr && !MapOrErr->empty()) {
-      CGRelSection = MapOrErr->front().second;
-    }
+  auto IsMatch = [&](const Elf_Shdr &Sec) { return &Sec == CGSection; };
+  Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> MapOrErr =
+      Obj.getSectionAndRelocations(IsMatch);
+  if (MapOrErr && !MapOrErr->empty()) {
+    CGRelSection = MapOrErr->front().second;
   }
 
   if (CGRelSection) {
-    this->forEachRelocationDo(*CGRelSection,
-                              [&](const Relocation<ELFT> &R, unsigned Ndx,
-                                  const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
-                                RelocSymTab = SymTab;
-                                Relocations.push_back(R);
-                              });
+    forEachRelocationDo(*CGRelSection,
+                        [&](const Relocation<ELFT> &R, unsigned Ndx,
+                            const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
+                          RelocSymTab = SymTab;
+                          Relocations.push_back(R);
+                        });
     llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) {
       return LHS.Offset < RHS.Offset;
     });
   }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
+  if (!this->processCallGraphSection())
+    return;
+  if (this->FuncCGInfos.empty())
+    return;
+
+  std::vector<Relocation<ELFT>> Relocations;
+  const Elf_Shdr *RelocSymTab = nullptr;
+  this->getCallGraphRelocations(Relocations, RelocSymTab);
 
   auto GetFunctionName = [&](uint64_t EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
@@ -5525,7 +5534,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
        << GetFunctionAddressAndName(CGInfo.FunctionAddress) << "\n";
     OS << "    Indirect Target:   " << (CGInfo.IsIndirectTarget ? "Yes" : "No")
        << "\n";
-    OS << "    Type ID:           " << format_hex(CGInfo.FunctionTypeId, 1)
+    OS << "    Type ID:           0x" << format_hex(CGInfo.FunctionTypeId, 1)
        << "\n";
 
     OS << "    Direct Callees (" << CGInfo.DirectCallees.size() << "):\n";
@@ -8375,34 +8384,12 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
 template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   if (!this->processCallGraphSection())
     return;
-  if (this->FuncCGInfos.size() == 0)
+  if (this->FuncCGInfos.empty())
     return;
 
-  const Elf_Shdr *CGSection = this->findSectionByName(".llvm.callgraph");
-  const Elf_Shdr *CGRelSection = nullptr;
   std::vector<Relocation<ELFT>> Relocations;
   const Elf_Shdr *RelocSymTab = nullptr;
-
-  if (CGSection) {
-    auto IsMatch = [&](const Elf_Shdr &Sec) { return &Sec == CGSection; };
-    Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> MapOrErr =
-        this->Obj.getSectionAndRelocations(IsMatch);
-    if (MapOrErr && !MapOrErr->empty()) {
-      CGRelSection = MapOrErr->front().second;
-    }
-  }
-
-  if (CGRelSection) {
-    this->forEachRelocationDo(*CGRelSection,
-                              [&](const Relocation<ELFT> &R, unsigned Ndx,
-                                  const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
-                                RelocSymTab = SymTab;
-                                Relocations.push_back(R);
-                              });
-    llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) {
-      return LHS.Offset < RHS.Offset;
-    });
-  }
+  this->getCallGraphRelocations(Relocations, RelocSymTab);
 
   auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =

>From 5f6cf07530c9a236e1734526eab84e855a356592 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Oct 2025 08:08:34 -0700
Subject: [PATCH 31/44] minor cleanups.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index b5d0fe4d80951..18f2ed8934cf7 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -191,7 +191,6 @@ struct GroupSection {
 // Per-function call graph information.
 struct FunctionCallgraphInfo {
   uint64_t FunctionAddress;
-  uint64_t FunctionAddressOffset;
   uint8_t FormatVersionNumber;
   bool IsIndirectTarget;
   uint64_t FunctionTypeId;
@@ -5470,9 +5469,7 @@ void ELFDumper<ELFT>::getCallGraphRelocations(
 }
 
 template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
-  if (!this->processCallGraphSection())
-    return;
-  if (this->FuncCGInfos.empty())
+  if (!this->processCallGraphSection() || this->FuncCGInfos.empty())
     return;
 
   std::vector<Relocation<ELFT>> Relocations;
@@ -8382,16 +8379,14 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
 }
 
 template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
-  if (!this->processCallGraphSection())
-    return;
-  if (this->FuncCGInfos.empty())
+  if (!this->processCallGraphSection() || this->FuncCGInfos.empty())
     return;
 
   std::vector<Relocation<ELFT>> Relocations;
   const Elf_Shdr *RelocSymTab = nullptr;
   this->getCallGraphRelocations(Relocations, RelocSymTab);
 
-  auto GetFunctionName = [&](typename ELFT::uint EntryPc) {
+  auto GetFunctionName = [&](uint64_t EntryPc) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
     if (FuncSymIndexes.empty())

>From d1b41b5fc3919c22ead660003129748b4cb3fc06 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Oct 2025 16:32:41 -0700
Subject: [PATCH 32/44] Fix readelf tests. callgraph section type.

---
 .../call-graph-info-callgraph-section.test    | 78 ++++++++-----------
 llvm/tools/llvm-readobj/ELFDumper.cpp         |  8 ++
 2 files changed, 42 insertions(+), 44 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 74bea550019ff..b049723fbc4c2 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -7,40 +7,34 @@
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
-# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
-# CHECK-NEXT: Per-function call graph information:: 
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: foo
-# CHECK-NEXT: Function PC:: 0x0
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: NOT_INDIRECT
-# CHECK-NEXT: Indirect callee count:: 0
-# CHECK-NEXT: Direct callee count:: 1
-# CHECK-NEXT: {
-# CHECK-NEXT:   CalleePC:: 0x5
-# CHECK-NEXT: }
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: bar
-# CHECK-NEXT: Function PC:: 0x6
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: UNKNOWN_TID
-# CHECK-NEXT: Indirect callee count:: 1
-# CHECK-NEXT: {
-# CHECK-NEXT:   callsite: 0x9
-# CHECK-NEXT:   calleeTypeId: 0x10
-# CHECK-NEXT: }
-# CHECK-NEXT: Direct callee count:: 0
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: baz
-# CHECK-NEXT: Function PC:: 0xa
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: KNOWN_TID
-# CHECK-NEXT: Function Type ID:: 0x20
-# CHECK-NEXT: Indirect callee count:: 0
-# CHECK-NEXT: Direct callee count:: 0
-
-
-# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
+
+# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 1 indirect targets.
+# CHECK-NEXT: Call graph section '.llvm.callgraph' contains 3 entries:
+# CHECK-EMPTY:
+# CHECK-NEXT:  Entry 0:
+# CHECK-NEXT:    Function:          0x2
+# CHECK-NEXT:    Indirect Target:   Yes
+# CHECK-NEXT:    Type ID:           0x0x0
+# CHECK-NEXT:    Direct Callees (1):
+# CHECK-NEXT:      0x13
+# CHECK-NEXT:    Indirect Callees by Type ID (0):
+# CHECK-EMPTY:
+# CHECK-NEXT:  Entry 1:
+# CHECK-NEXT:    Function:          0x1d
+# CHECK-NEXT:    Indirect Target:   Yes
+# CHECK-NEXT:    Type ID:           0x0x0
+# CHECK-NEXT:    Direct Callees (0):
+# CHECK-NEXT:    Indirect Callees by Type ID (1):
+# CHECK-NEXT:      0x0x10
+# CHECK-EMPTY:
+# CHECK-NEXT:  Entry 2:
+# CHECK-NEXT:    Function:          0x38
+# CHECK-NEXT:    Indirect Target:   Yes
+# CHECK-NEXT:    Type ID:           0x0x20
+# CHECK-NEXT:    Direct Callees (0):
+# CHECK-NEXT:    Indirect Callees by Type ID (0):
+
+# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 1 indirect targets.
 # LLVM: callgraph_info [
 # LLVM-NEXT:   Function {
 # LLVM-NEXT:     Name: foo
@@ -83,7 +77,7 @@
 # LLVM-NEXT:   }
 # LLVM-NEXT: ]
 
-# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
+# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 1 indirect targets.
 # JSON:     "callgraph_info": [
 # JSON-NEXT:       {
 # JSON-NEXT:         "Function": {
@@ -162,21 +156,19 @@ baz:                  #< baz is at 10 (a).
 qux:                  #< qux is at 11 (b).
  retq
 
-.section	.callgraph,"o", at progbits,.text
+.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
 .byte	0       #< Format version number.
-.byte	1       #< Flag IsIndirectTarget true
+.byte	3       #< Flag IsIndirectTarget true
 .quad	0       #< foo()'s entry address.
 .quad	0       #< TypeID: unknown.
-.long   1       #< Count of direct callees.
-.long	0       #< Count of indirect target type IDs.
+.byte   1       #< Count of direct callees.
 .quad   5       #< Direct callee foo's address>
 
 .byte	0       #< Format version number.
-.byte   1       #< Flag IsIndirectTarget true
+.byte   5       #< Flag IsIndirectTarget true
 .quad	6       #< bar()'s entry address.
 .quad	0       #< TypeID: unknown.
-.long   0       #< Count of direct callees
-.long	1       #< Count of indirect target type IDs
+.byte	1       #< Count of indirect target type IDs
 .quad   16      #< Indirect call type id.
 
 
@@ -184,8 +176,6 @@ qux:                  #< qux is at 11 (b).
 .byte   1       #< Flag IsIndirectTarget true
 .quad	10      #< baz()'s entry address.
 .quad   32      #< Indirect target type id.
-.long	0       #< Count of direct callees
-.long   0       #< Count of indirect call type ids
 
 # No call graph section entry for qux. 
 # Technically its "UNKNOWN" type id but will not be printed as such by llvm-readelf.  
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 18f2ed8934cf7..aaf4969e008a2 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5495,6 +5495,10 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
       auto Reloc = llvm::lower_bound(
           Relocations, FuncEntryPC,
           [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+      if (Reloc == Relocations.end()) {
+        Stream << format("0x%lx", FuncEntryPC);
+        return Stream.str();
+      }
       Expected<RelSymbol<ELFT>> RelSym =
           this->getRelocationTarget(*Reloc, RelocSymTab);
       if (!RelSym) {
@@ -8403,6 +8407,10 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
       auto Reloc = llvm::lower_bound(
           Relocations, FuncEntryPC,
           [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+      if (Reloc == Relocations.end()) {
+        W.printHex("Address", FuncEntryPC);      
+        return;
+      }          
       Expected<RelSymbol<ELFT>> RelSym =
           this->getRelocationTarget(*Reloc, RelocSymTab);
       if (!RelSym) {

>From 6368f56625d38056715e9dfd38121764a2358bde Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Oct 2025 17:35:50 -0700
Subject: [PATCH 33/44] Fix call graph tests.

---
 .../call-graph-info-callgraph-section.test    | 180 ++++----
 ...=> call-graph-info-err-invalid-flags.test} |  12 +-
 ...graph-info-err-invalid-format-version.test |   6 +-
 ...-info-err-malformed-callgraph-section.test |   4 +-
 .../call-graph-info-err-missing-directs.test  |  25 ++
 ...l-graph-info-err-no-callgraph-section.test |   4 +-
 .../llvm-readobj/ELF/call-graph-info.test     | 406 +++++++++---------
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 206 +++++----
 8 files changed, 443 insertions(+), 400 deletions(-)
 rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-func-kind.test => call-graph-info-err-invalid-flags.test} (67%)
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index b049723fbc4c2..639961686a22d 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -8,128 +8,132 @@
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
 
 
-# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 1 indirect targets.
+# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
+# CHECK-EMPTY:
 # CHECK-NEXT: Call graph section '.llvm.callgraph' contains 3 entries:
 # CHECK-EMPTY:
 # CHECK-NEXT:  Entry 0:
-# CHECK-NEXT:    Function:          0x2
-# CHECK-NEXT:    Indirect Target:   Yes
-# CHECK-NEXT:    Type ID:           0x0x0
+# CHECK-NEXT:    Offset:           0x2
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0x0
 # CHECK-NEXT:    Direct Callees (1):
-# CHECK-NEXT:      0x13
+# CHECK-NEXT:       Offset:        0x13
 # CHECK-NEXT:    Indirect Callees by Type ID (0):
 # CHECK-EMPTY:
 # CHECK-NEXT:  Entry 1:
-# CHECK-NEXT:    Function:          0x1d
-# CHECK-NEXT:    Indirect Target:   Yes
-# CHECK-NEXT:    Type ID:           0x0x0
+# CHECK-NEXT:    Offset:           0x1d
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0x0
 # CHECK-NEXT:    Direct Callees (0):
 # CHECK-NEXT:    Indirect Callees by Type ID (1):
-# CHECK-NEXT:      0x0x10
+# CHECK-NEXT:      0x10
 # CHECK-EMPTY:
 # CHECK-NEXT:  Entry 2:
-# CHECK-NEXT:    Function:          0x38
-# CHECK-NEXT:    Indirect Target:   Yes
-# CHECK-NEXT:    Type ID:           0x0x20
+# CHECK-NEXT:    Offset:           0x38
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0x20
 # CHECK-NEXT:    Direct Callees (0):
 # CHECK-NEXT:    Indirect Callees by Type ID (0):
 
-# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 1 indirect targets.
-# LLVM: callgraph_info [
+# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
+# LLVM-NEXT: callgraph_info [
 # LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: foo
-# LLVM-NEXT:     Address: 0x0
+# LLVM-NEXT:     Offset: 0x2
 # LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: NOT_INDIRECT
-# LLVM-NEXT:     Kind: 0
-# LLVM-NEXT:     NumIndirectCallSites: 0
-# LLVM-NEXT:     NumDirectCallSites: 1
+# LLVM-NEXT:     IsIndirectTarget: Yes
+# LLVM-NEXT:     TypeId: 0x0
+# LLVM-NEXT:     NumDirectCallees: 1
 # LLVM-NEXT:     DirectCallees [
 # LLVM-NEXT:       Entry {
-# LLVM-NEXT:         Address: 0x5
+# LLVM-NEXT:         Offset: 0x13
 # LLVM-NEXT:       }
 # LLVM-NEXT:     ]
+# LLVM-NEXT:     NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT:     IndirectTypeIDs [
+# LLVM-NEXT:     ]
 # LLVM-NEXT:   }
 # LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: bar
-# LLVM-NEXT:     Address: 0x6
+# LLVM-NEXT:     Offset: 0x1D
 # LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: UNKNOWN_TID
-# LLVM-NEXT:     Kind: 1
-# LLVM-NEXT:     NumIndirectCallSites: 1
-# LLVM-NEXT:     IndirectCallsites [
-# LLVM-NEXT:       IndirectCallsite {
-# LLVM-NEXT:         Address: 0x9
+# LLVM-NEXT:     IsIndirectTarget: Yes
+# LLVM-NEXT:     TypeId: 0x0
+# LLVM-NEXT:     NumDirectCallees: 0
+# LLVM-NEXT:     DirectCallees [
+# LLVM-NEXT:     ]
+# LLVM-NEXT:     NumIndirectTargetTypeIDs: 1
+# LLVM-NEXT:     IndirectTypeIDs [
+# LLVM-NEXT:       Entry {
 # LLVM-NEXT:         TypeId: 0x10
 # LLVM-NEXT:       }
 # LLVM-NEXT:     ]
-# LLVM-NEXT:     NumDirectCallSites: 0
 # LLVM-NEXT:   }
 # LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: baz
-# LLVM-NEXT:     Address: 0xA
+# LLVM-NEXT:     Offset: 0x38
 # LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: KNOWN_TID
-# LLVM-NEXT:     Kind: 2
+# LLVM-NEXT:     IsIndirectTarget: Yes
 # LLVM-NEXT:     TypeId: 0x20
-# LLVM-NEXT:     NumIndirectCallSites: 0
-# LLVM-NEXT:     NumDirectCallSites: 0
+# LLVM-NEXT:     NumDirectCallees: 0
+# LLVM-NEXT:     DirectCallees [
+# LLVM-NEXT:     ]
+# LLVM-NEXT:     NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT:     IndirectTypeIDs [
+# LLVM-NEXT:     ]
 # LLVM-NEXT:   }
 # LLVM-NEXT: ]
 
-# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 1 indirect targets.
+# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
 # JSON:     "callgraph_info": [
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "foo",
-# JSON-NEXT:           "Address": 0,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "NOT_INDIRECT",
-# JSON-NEXT:           "Kind": 0,
-# JSON-NEXT:           "NumIndirectCallSites": 0,
-# JSON-NEXT:           "NumDirectCallSites": 1,
-# JSON-NEXT:           "DirectCallees": [
-# JSON-NEXT:             {
-# JSON-NEXT:               "Entry": {
-# JSON-NEXT:                 "Address": 5
-# JSON-NEXT:               }
-# JSON-NEXT:             }
-# JSON-NEXT:           ]
-# JSON-NEXT:         }
-# JSON-NEXT:       },
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "bar",
-# JSON-NEXT:           "Address": 6,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "UNKNOWN_TID",
-# JSON-NEXT:           "Kind": 1,
-# JSON-NEXT:           "NumIndirectCallSites": 1,
-# JSON-NEXT:           "IndirectCallsites": [
-# JSON-NEXT:             {
-# JSON-NEXT:               "IndirectCallsite": {
-# JSON-NEXT:                 "Address": 9,
-# JSON-NEXT:                 "TypeId": 16
-# JSON-NEXT:               }
-# JSON-NEXT:             }
-# JSON-NEXT:           ],
-# JSON-NEXT:           "NumDirectCallSites": 0
-# JSON-NEXT:         }
-# JSON-NEXT:       },
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "baz",
-# JSON-NEXT:           "Address": 10,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "KNOWN_TID",
-# JSON-NEXT:           "Kind": 2,
-# JSON-NEXT:           "TypeId": 32,
-# JSON-NEXT:           "NumIndirectCallSites": 0,
-# JSON-NEXT:           "NumDirectCallSites": 0
-# JSON-NEXT:         }
-# JSON-NEXT:       }
-# JSON-NEXT:     ]
-
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Offset": 2,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 0,
+# JSON-NEXT:          "NumDirectCallees": 1,
+# JSON-NEXT:          "DirectCallees": [
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "Offset": 19
+# JSON-NEXT:              }
+# JSON-NEXT:            }
+# JSON-NEXT:          ],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 0,
+# JSON-NEXT:          "IndirectTypeIDs": []
+# JSON-NEXT:        }
+# JSON-NEXT:      },
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Offset": 29,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 0,
+# JSON-NEXT:          "NumDirectCallees": 0,
+# JSON-NEXT:          "DirectCallees": [],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 1,
+# JSON-NEXT:          "IndirectTypeIDs": [
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "TypeId": 16
+# JSON-NEXT:              }
+# JSON-NEXT:            }
+# JSON-NEXT:          ]
+# JSON-NEXT:        }
+# JSON-NEXT:      },
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Offset": 56,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 32,
+# JSON-NEXT:          "NumDirectCallees": 0,
+# JSON-NEXT:          "DirectCallees": [],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 0,
+# JSON-NEXT:          "IndirectTypeIDs": []
+# JSON-NEXT:        }
+# JSON-NEXT:      }
+# JSON-NEXT:    ]
+# JSON-NEXT:  }
+# JSON-NEXT:]
 
 .text
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
similarity index 67%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
index 985e00f2aac60..97766cd745036 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
@@ -1,4 +1,4 @@
-## Tests that --call-graph-info fails if .callgraph section has invalid
+## Tests that --call-graph-info fails if .llvm.callgraph section has invalid
 ## function kind value.
 
 # REQUIRES: x86-registered-target
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [FunctionKind] for function at [0x0]': Unknown value [4].
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's Flags': Unknown Flags. Expected one of [1, 3, 5, 7] but found [8]
 
 .text
 .globl _Z3foov
@@ -16,8 +16,8 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.callgraph,"o", at progbits,.text
-.quad	0   #< Format version number.
-.quad 0   #< Function entry address.
-.quad 4   #< Invalid function kind: must be one of 0, 1, 2, 3.
+.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
+.byte	0   #< Format version number.
+.byte	8   #< Only valid values are 1,3,5,7
+# Missing direct callees info
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
index 2956f5317d22b..024594cad6c9b 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
@@ -1,4 +1,4 @@
-## Tests that --call-graph-info fails if .callgraph section has unknown format
+## Tests that --call-graph-info fails if .llvm.callgraph section has unknown format
 ## version number.
 
 # REQUIRES: x86-registered-target
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'Unknown value': Unknown format version value [1] in .callgraph section.
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'Unknown value': Unknown format version value [1] in .llvm.callgraph section.
 
 .text
 .globl _Z3foov
@@ -16,6 +16,6 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.callgraph,"o", at progbits,.text
+.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
 .quad	1  #< Invalid format version number: the only supported version is 0.
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
index 0f2528da803e1..50cf9cb7c2ef1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
@@ -1,4 +1,4 @@
-## Tests that --call-graph-info fails if .callgraph section has invalid size.
+## Tests that --call-graph-info fails if .llvm.callgraph section has invalid size.
 
 # REQUIRES: x86-registered-target
 
@@ -15,7 +15,7 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.callgraph,"o", at progbits,.text
+.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
 # Each unit in .callgraph section must have 64bit size. Therefore, byte size
 # must be divisible by 64.
 .quad 0
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
new file mode 100644
index 0000000000000..e007b0cfa7953
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
@@ -0,0 +1,25 @@
+## Tests that --call-graph-info fails if .llvm.callgraph section has invalid
+## function kind value.
+
+# REQUIRES: x86-registered-target
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+
+# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [number of direct callsites] for function at [0x0]': unable to decode LEB128 at offset 0x00000012: malformed uleb128, extends past end
+
+.text
+.globl _Z3foov
+.type _Z3foov, at function
+_Z3foov:
+ callq _Z3foov at PLT
+
+.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
+.byte	0   #< Format version number.
+.byte	3   #< Flag IsIndirectTarget true and has direct calls
+.quad 0   #< Function entry address.
+.quad 0   #< TypeID: unknown. 
+# Missing direct callees info
+.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
index 28e23e8db6f6d..e5aa8b72536c6 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
@@ -1,4 +1,4 @@
-## Tests that --call-graph-info warns if there is no .callgraph section.
+## Tests that --call-graph-info warns if there is no .llvm.callgraph section.
 
 # REQUIRES: x86-registered-target
 
@@ -7,7 +7,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
 
-# CHECK: {{.*}}llvm-readelf{{.*}}: error: 'Missing section': No .callgraph section found.
+# CHECK: {{.*}}llvm-readelf{{.*}}: error: 'Missing section': No .llvm.callgraph section found.
 
 .text
 .globl _Z3foov
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 594a3b9650dfe..2ca4928763a5e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -41,219 +41,199 @@
 #     return 0;
 #   }
 
-# CHECK: Per-function call graph information:: 
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: foo
-# CHECK-NEXT: Function PC:: 0x1790
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: KNOWN_TID
-# CHECK-NEXT: Function Type ID:: 0x3ecbeef531f74424
-# CHECK-NEXT: Indirect callee count:: 0
-# CHECK-NEXT: Direct callee count:: 0
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: bar
-# CHECK-NEXT: Function PC:: 0x17a0
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: KNOWN_TID
-# CHECK-NEXT: Function Type ID:: 0x3ecbeef531f74424
-# CHECK-NEXT: Indirect callee count:: 0
-# CHECK-NEXT: Direct callee count:: 0
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: baz
-# CHECK-NEXT: Function PC:: 0x17b0
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: KNOWN_TID
-# CHECK-NEXT: Function Type ID:: 0x308e4b8159bc8654
-# CHECK-NEXT: Indirect callee count:: 0
-# CHECK-NEXT: Direct callee count:: 0
-# CHECK-EMPTY: 
-# CHECK-NEXT: Function:: main
-# CHECK-NEXT: Function PC:: 0x17c0
-# CHECK-NEXT: FormatVersionNumber:: 0
-# CHECK-NEXT: Function Kind:: KNOWN_TID
-# CHECK-NEXT: Function Type ID:: 0xfa6809609a76afca
-# CHECK-NEXT: Indirect callee count:: 3
-# CHECK-NEXT: {
-# CHECK-NEXT:   callsite: 0x17ef
-# CHECK-NEXT:   calleeTypeId: 0x3ecbeef531f74424
-# CHECK-NEXT:   callsite: 0x17df
-# CHECK-NEXT:   calleeTypeId: 0x3ecbeef531f74424
-# CHECK-NEXT:   callsite: 0x1804
-# CHECK-NEXT:   calleeTypeId: 0x308e4b8159bc8654
-# CHECK-NEXT: }
-# CHECK-NEXT: Direct callee count:: 3
-# CHECK-NEXT: {
-# CHECK-NEXT:   Callee:: foo
-# CHECK-NEXT:   CalleePC:: 0x1790
-# CHECK-NEXT:   Callee:: bar
-# CHECK-NEXT:   CalleePC:: 0x17a0
-# CHECK-NEXT:   Callee:: baz
-# CHECK-NEXT:   CalleePC:: 0x17b0
-# CHECK-NEXT: }
-
+# CHECK: Call graph section '.llvm.callgraph' contains 4 entries:
+# CHECK-EMPTY:
+# CHECK-NEXT: Entry 0:
+# CHECK-NEXT:    Address:           0x1790 <foo>
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0x3ecbeef531f74424
+# CHECK-NEXT:    Direct Callees (0):
+# CHECK-NEXT:    Indirect Callees by Type ID (0):
+# CHECK-EMPTY:
+# CHECK-NEXT: Entry 1:
+# CHECK-NEXT:    Address:           0x17a0 <bar>
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0x3ecbeef531f74424
+# CHECK-NEXT:    Direct Callees (0):
+# CHECK-NEXT:    Indirect Callees by Type ID (0):
+# CHECK-EMPTY:
+# CHECK-NEXT: Entry 2:
+# CHECK-NEXT:    Address:           0x17b0 <baz>
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0x308e4b8159bc8654
+# CHECK-NEXT:    Direct Callees (0):
+# CHECK-NEXT:    Indirect Callees by Type ID (0):
+# CHECK-EMPTY:
+# CHECK-NEXT: Entry 3:
+# CHECK-NEXT:    Address:           0x17c0 <main>
+# CHECK-NEXT:    Indirect Target:  Yes
+# CHECK-NEXT:    Type ID:          0xfa6809609a76afca
+# CHECK-NEXT:    Direct Callees (3):
+# CHECK-NEXT:       Address:           0x1790 <foo>
+# CHECK-NEXT:       Address:           0x17a0 <bar>
+# CHECK-NEXT:       Address:           0x17b0 <baz>
+# CHECK-NEXT:    Indirect Callees by Type ID (2):
+# CHECK-NEXT:      0x3ecbeef531f74424
+# CHECK-NEXT:      0x308e4b8159bc8654
 
 # LLVM: callgraph_info [
-# LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: foo
-# LLVM-NEXT:     Address: 0x1790
-# LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: KNOWN_TID
-# LLVM-NEXT:     Kind: 2
-# LLVM-NEXT:     TypeId: 0x3ECBEEF531F74424
-# LLVM-NEXT:     NumIndirectCallSites: 0
-# LLVM-NEXT:     NumDirectCallSites: 0
-# LLVM-NEXT:   }
-# LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: bar
-# LLVM-NEXT:     Address: 0x17A0
-# LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: KNOWN_TID
-# LLVM-NEXT:     Kind: 2
-# LLVM-NEXT:     TypeId: 0x3ECBEEF531F74424
-# LLVM-NEXT:     NumIndirectCallSites: 0
-# LLVM-NEXT:     NumDirectCallSites: 0
-# LLVM-NEXT:   }
-# LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: baz
-# LLVM-NEXT:     Address: 0x17B0
-# LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: KNOWN_TID
-# LLVM-NEXT:     Kind: 2
-# LLVM-NEXT:     TypeId: 0x308E4B8159BC8654
-# LLVM-NEXT:     NumIndirectCallSites: 0
-# LLVM-NEXT:     NumDirectCallSites: 0
-# LLVM-NEXT:   }
-# LLVM-NEXT:   Function {
-# LLVM-NEXT:     Name: main
-# LLVM-NEXT:     Address: 0x17C0
-# LLVM-NEXT:     Version: 0
-# LLVM-NEXT:     KindStr: KNOWN_TID
-# LLVM-NEXT:     Kind: 2
-# LLVM-NEXT:     TypeId: 0xFA6809609A76AFCA
-# LLVM-NEXT:     NumIndirectCallSites: 3
-# LLVM-NEXT:     IndirectCallsites [
-# LLVM-NEXT:       IndirectCallsite {
-# LLVM-NEXT:         Address: 0x17EF
-# LLVM-NEXT:         TypeId: 0x3ECBEEF531F74424
-# LLVM-NEXT:       }
-# LLVM-NEXT:       IndirectCallsite {
-# LLVM-NEXT:         Address: 0x17DF
-# LLVM-NEXT:         TypeId: 0x3ECBEEF531F74424
-# LLVM-NEXT:       }
-# LLVM-NEXT:       IndirectCallsite {
-# LLVM-NEXT:         Address: 0x1804
-# LLVM-NEXT:         TypeId: 0x308E4B8159BC8654
-# LLVM-NEXT:       }
-# LLVM-NEXT:     ]
-# LLVM-NEXT:     NumDirectCallSites: 3
-# LLVM-NEXT:     DirectCallees [
-# LLVM-NEXT:       Entry {
-# LLVM-NEXT:         Name: foo
-# LLVM-NEXT:         Address: 0x1790
-# LLVM-NEXT:       }
-# LLVM-NEXT:       Entry {
-# LLVM-NEXT:         Name: bar
-# LLVM-NEXT:         Address: 0x17A0
-# LLVM-NEXT:       }
-# LLVM-NEXT:       Entry {
-# LLVM-NEXT:         Name: baz
-# LLVM-NEXT:         Address: 0x17B0
-# LLVM-NEXT:       }
-# LLVM-NEXT:     ]
-# LLVM-NEXT:   }
-# LLVM-NEXT: ]
+# LLVM-NEXT:     Function {
+# LLVM-NEXT:       Name: foo
+# LLVM-NEXT:       Address: 0x1790
+# LLVM-NEXT:       Version: 0
+# LLVM-NEXT:       IsIndirectTarget: Yes
+# LLVM-NEXT:       TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:       NumDirectCallees: 0
+# LLVM-NEXT:       DirectCallees [
+# LLVM-NEXT:       ]
+# LLVM-NEXT:       NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT:       IndirectTypeIDs [
+# LLVM-NEXT:       ]
+# LLVM-NEXT:     }
+# LLVM-NEXT:     Function {
+# LLVM-NEXT:       Name: bar
+# LLVM-NEXT:       Address: 0x17A0
+# LLVM-NEXT:       Version: 0
+# LLVM-NEXT:       IsIndirectTarget: Yes
+# LLVM-NEXT:       TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:       NumDirectCallees: 0
+# LLVM-NEXT:       DirectCallees [
+# LLVM-NEXT:       ]
+# LLVM-NEXT:       NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT:       IndirectTypeIDs [
+# LLVM-NEXT:       ]
+# LLVM-NEXT:     }
+# LLVM-NEXT:     Function {
+# LLVM-NEXT:       Name: baz
+# LLVM-NEXT:       Address: 0x17B0
+# LLVM-NEXT:       Version: 0
+# LLVM-NEXT:       IsIndirectTarget: Yes
+# LLVM-NEXT:       TypeId: 0x308E4B8159BC8654
+# LLVM-NEXT:       NumDirectCallees: 0
+# LLVM-NEXT:       DirectCallees [
+# LLVM-NEXT:       ]
+# LLVM-NEXT:       NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT:       IndirectTypeIDs [
+# LLVM-NEXT:       ]
+# LLVM-NEXT:     }
+# LLVM-NEXT:     Function {
+# LLVM-NEXT:       Name: main
+# LLVM-NEXT:       Address: 0x17C0
+# LLVM-NEXT:       Version: 0
+# LLVM-NEXT:       IsIndirectTarget: Yes
+# LLVM-NEXT:       TypeId: 0xFA6809609A76AFCA
+# LLVM-NEXT:       NumDirectCallees: 3
+# LLVM-NEXT:       DirectCallees [
+# LLVM-NEXT:         Entry {
+# LLVM-NEXT:           Name: foo
+# LLVM-NEXT:           Address: 0x1790
+# LLVM-NEXT:         }
+# LLVM-NEXT:         Entry {
+# LLVM-NEXT:           Name: bar
+# LLVM-NEXT:           Address: 0x17A0
+# LLVM-NEXT:         }
+# LLVM-NEXT:         Entry {
+# LLVM-NEXT:           Name: baz
+# LLVM-NEXT:           Address: 0x17B0
+# LLVM-NEXT:         }
+# LLVM-NEXT:       ]
+# LLVM-NEXT:       NumIndirectTargetTypeIDs: 2
+# LLVM-NEXT:       IndirectTypeIDs [
+# LLVM-NEXT:         Entry {
+# LLVM-NEXT:           TypeId: 0x3ECBEEF531F74424
+# LLVM-NEXT:         }
+# LLVM-NEXT:         Entry {
+# LLVM-NEXT:           TypeId: 0x308E4B8159BC8654
+# LLVM-NEXT:         }
+# LLVM-NEXT:       ]
+# LLVM-NEXT:     }
+# LLVM-NEXT:   ]
 
 
 # JSON:     "callgraph_info": [
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "foo",
-# JSON-NEXT:           "Address": 6032,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "KNOWN_TID",
-# JSON-NEXT:           "Kind": 2,
-# JSON-NEXT:           "TypeId": 4524972987496481828,
-# JSON-NEXT:           "NumIndirectCallSites": 0,
-# JSON-NEXT:           "NumDirectCallSites": 0
-# JSON-NEXT:         }
-# JSON-NEXT:       },
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "bar",
-# JSON-NEXT:           "Address": 6048,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "KNOWN_TID",
-# JSON-NEXT:           "Kind": 2,
-# JSON-NEXT:           "TypeId": 4524972987496481828,
-# JSON-NEXT:           "NumIndirectCallSites": 0,
-# JSON-NEXT:           "NumDirectCallSites": 0
-# JSON-NEXT:         }
-# JSON-NEXT:       },
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "baz",
-# JSON-NEXT:           "Address": 6064,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "KNOWN_TID",
-# JSON-NEXT:           "Kind": 2,
-# JSON-NEXT:           "TypeId": 3498816979441845844,
-# JSON-NEXT:           "NumIndirectCallSites": 0,
-# JSON-NEXT:           "NumDirectCallSites": 0
-# JSON-NEXT:         }
-# JSON-NEXT:       },
-# JSON-NEXT:       {
-# JSON-NEXT:         "Function": {
-# JSON-NEXT:           "Name": "main",
-# JSON-NEXT:           "Address": 6080,
-# JSON-NEXT:           "Version": 0,
-# JSON-NEXT:           "KindStr": "KNOWN_TID",
-# JSON-NEXT:           "Kind": 2,
-# JSON-NEXT:           "TypeId": 18043682217572872138,
-# JSON-NEXT:           "NumIndirectCallSites": 3,
-# JSON-NEXT:           "IndirectCallsites": [
-# JSON-NEXT:             {
-# JSON-NEXT:               "IndirectCallsite": {
-# JSON-NEXT:                 "Address": 6127,
-# JSON-NEXT:                 "TypeId": 4524972987496481828
-# JSON-NEXT:               }
-# JSON-NEXT:             },
-# JSON-NEXT:             {
-# JSON-NEXT:               "IndirectCallsite": {
-# JSON-NEXT:                 "Address": 6111,
-# JSON-NEXT:                 "TypeId": 4524972987496481828
-# JSON-NEXT:               }
-# JSON-NEXT:             },
-# JSON-NEXT:             {
-# JSON-NEXT:               "IndirectCallsite": {
-# JSON-NEXT:                 "Address": 6148,
-# JSON-NEXT:                 "TypeId": 3498816979441845844
-# JSON-NEXT:               }
-# JSON-NEXT:             }
-# JSON-NEXT:           ],
-# JSON-NEXT:           "NumDirectCallSites": 3,
-# JSON-NEXT:           "DirectCallees": [
-# JSON-NEXT:             {
-# JSON-NEXT:               "Entry": {
-# JSON-NEXT:                 "Name": "foo",
-# JSON-NEXT:                 "Address": 6032
-# JSON-NEXT:               }
-# JSON-NEXT:             },
-# JSON-NEXT:             {
-# JSON-NEXT:               "Entry": {
-# JSON-NEXT:                 "Name": "bar",
-# JSON-NEXT:                 "Address": 6048
-# JSON-NEXT:               }
-# JSON-NEXT:             },
-# JSON-NEXT:             {
-# JSON-NEXT:               "Entry": {
-# JSON-NEXT:                 "Name": "baz",
-# JSON-NEXT:                 "Address": 6064
-# JSON-NEXT:               }
-# JSON-NEXT:             }
-# JSON-NEXT:           ]
-# JSON-NEXT:         }
-# JSON-NEXT:       }
-# JSON-NEXT:     ]
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Name": "foo",
+# JSON-NEXT:          "Address": 6032,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 4524972987496481828,
+# JSON-NEXT:          "NumDirectCallees": 0,
+# JSON-NEXT:          "DirectCallees": [],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 0,
+# JSON-NEXT:          "IndirectTypeIDs": []
+# JSON-NEXT:        }
+# JSON-NEXT:      },
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Name": "bar",
+# JSON-NEXT:          "Address": 6048,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 4524972987496481828,
+# JSON-NEXT:          "NumDirectCallees": 0,
+# JSON-NEXT:          "DirectCallees": [],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 0,
+# JSON-NEXT:          "IndirectTypeIDs": []
+# JSON-NEXT:        }
+# JSON-NEXT:      },
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Name": "baz",
+# JSON-NEXT:          "Address": 6064,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 3498816979441845844,
+# JSON-NEXT:          "NumDirectCallees": 0,
+# JSON-NEXT:          "DirectCallees": [],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 0,
+# JSON-NEXT:          "IndirectTypeIDs": []
+# JSON-NEXT:        }
+# JSON-NEXT:      },
+# JSON-NEXT:      {
+# JSON-NEXT:        "Function": {
+# JSON-NEXT:          "Name": "main",
+# JSON-NEXT:          "Address": 6080,
+# JSON-NEXT:          "Version": 0,
+# JSON-NEXT:          "IsIndirectTarget": true,
+# JSON-NEXT:          "TypeId": 18043682217572872138,
+# JSON-NEXT:          "NumDirectCallees": 3,
+# JSON-NEXT:          "DirectCallees": [
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "Name": "foo",
+# JSON-NEXT:                "Address": 6032
+# JSON-NEXT:              }
+# JSON-NEXT:            },
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "Name": "bar",
+# JSON-NEXT:                "Address": 6048
+# JSON-NEXT:              }
+# JSON-NEXT:            },
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "Name": "baz",
+# JSON-NEXT:                "Address": 6064
+# JSON-NEXT:              }
+# JSON-NEXT:            }
+# JSON-NEXT:          ],
+# JSON-NEXT:          "NumIndirectTargetTypeIDs": 2,
+# JSON-NEXT:          "IndirectTypeIDs": [
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "TypeId": 4524972987496481828
+# JSON-NEXT:              }
+# JSON-NEXT:            },
+# JSON-NEXT:            {
+# JSON-NEXT:              "Entry": {
+# JSON-NEXT:                "TypeId": 3498816979441845844
+# JSON-NEXT:              }
+# JSON-NEXT:            }
+# JSON-NEXT:          ]
+# JSON-NEXT:        }
+# JSON-NEXT:      }
+# JSON-NEXT:    ]
 
 --- !ELF
 FileHeader:
@@ -355,7 +335,7 @@ Sections:
     AddressAlign:    0x4
     Notes:
       - Name:            GNU
-        Desc:            EB389D8FA0BAAB684147C8107EEA8143CBF335AC
+        Desc:            8D01EC245D0E6A4AF9C1F32B730869B197731C34
         Type:            NT_PRPSINFO
   - Name:            .dynsym
     Type:            SHT_DYNSYM
@@ -602,13 +582,13 @@ Sections:
     Flags:           [ SHF_MERGE, SHF_STRINGS ]
     AddressAlign:    0x1
     EntSize:         0x1
-    Content:         004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974206530383561356338333634363166323964373366373339333433343437656439643533386265633129004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420653038356135633833363436316632396437336637333933343334343765643964353338626563312900
-  - Name:            .callgraph
-    Type:            SHT_PROGBITS
+    Content:         004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203934396232653930373439623765376438353739663838626231303366373535353832393465666229004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203934396232653930373439623765376438353739663838626231303366373535353832393465666229004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203861333966396231386139343632386138656463666433613137363432376364333161656330633829004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420653038356135633833363436316632396437336637333933343334343765643964353338626563312900
+  - Name:            .llvm.callgraph
+    Type:            0x6FFF4C0F
     Flags:           [ SHF_LINK_ORDER ]
     Link:            .text
     AddressAlign:    0x1
-    Content:         0000000000000000901700000000000002000000000000002444F731F5EECB3E000000000000000000000000000000000000000000000000A01700000000000002000000000000002444F731F5EECB3E000000000000000000000000000000000000000000000000B01700000000000002000000000000005486BC59814B8E30000000000000000000000000000000000000000000000000C0170000000000000200000000000000CAAF769A600968FA03000000000000002444F731F5EECB3EDF170000000000002444F731F5EECB3EEF170000000000005486BC59814B8E30041800000000000003000000000000009017000000000000A017000000000000B017000000000000
+    Content:         000190170000000000002444F731F5EECB3E0001A0170000000000002444F731F5EECB3E0001B0170000000000005486BC59814B8E300007C017000000000000CAAF769A600968FA039017000000000000A017000000000000B017000000000000022444F731F5EECB3E5486BC59814B8E30
 Symbols:
   - Name:            __abi_tag
     Type:            STT_OBJECT
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index aaf4969e008a2..9279dcd824c90 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -467,6 +467,19 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
 
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
+
+protected:
+  std::string getFunctionNames(uint64_t FuncAddr) {
+    SmallVector<uint32_t> FuncSymIndexes =
+        this->getSymbolIndexesForFunctionAddress(FuncAddr, std::nullopt);
+    if (FuncSymIndexes.empty())
+      return "";
+
+    SmallVector<std::string> FuncSymNames;
+    for (uint32_t Index : FuncSymIndexes)
+      FuncSymNames.push_back(this->getStaticSymbolName(Index));
+    return join(FuncSymNames, ", ");
+  }
 };
 
 template <class ELFT>
@@ -5364,7 +5377,18 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info's Flags");
-
+    switch (Flags) {
+    case 1:
+    case 3:
+    case 5:
+    case 7:
+      break;
+    default:
+      Error FlagsErr = createError(
+          "Unknown Flags. Expected one of [1, 3, 5, 7] but found [" +
+          std::to_string(Flags) + "]");
+      reportError(std::move(FlagsErr), "While reading call graph info's Flags");
+    }
     uint64_t FuncAddrOffset = Offset;
     typename ELFT::uint FuncAddr =
         Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr);
@@ -5476,53 +5500,60 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   const Elf_Shdr *RelocSymTab = nullptr;
   this->getCallGraphRelocations(Relocations, RelocSymTab);
 
-  auto GetFunctionName = [&](uint64_t EntryPc) {
-    SmallVector<uint32_t> FuncSymIndexes =
-        this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
-    if (FuncSymIndexes.empty())
-      return std::string("");
-
-    SmallVector<std::string> FuncSymNames;
-    for (uint32_t Index : FuncSymIndexes)
-      FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    return join(FuncSymNames, ", ");
+  auto PrintFunc = [](uint64_t FuncEntryPC, std::string FuncSymName,
+                      formatted_raw_ostream &OS) {
+    OS.PadToColumn(4);
+    OS << "Address:           ";
+    OS << to_string(format_hex(FuncEntryPC, 1));
+    if (!FuncSymName.empty())
+      OS << " <" << FuncSymName << ">";
+    OS << "\n";
   };
 
-  auto GetFunctionAddressAndName = [&](uint64_t FuncEntryPC) -> std::string {
-    std::string S;
-    raw_string_ostream Stream(S);
-    if (this->Obj.getHeader().e_type == ELF::ET_REL) {
-      auto Reloc = llvm::lower_bound(
-          Relocations, FuncEntryPC,
-          [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
-      if (Reloc == Relocations.end()) {
-        Stream << format("0x%lx", FuncEntryPC);
-        return Stream.str();
-      }
-      Expected<RelSymbol<ELFT>> RelSym =
-          this->getRelocationTarget(*Reloc, RelocSymTab);
-      if (!RelSym) {
-        this->reportUniqueWarning(RelSym.takeError());
-        Stream << format("0x%lx", FuncEntryPC);
-      } else {
+  auto PrintReloc =
+      [&](uint64_t FuncEntryPC,
+          typename std::vector<Relocation<ELFT>>::iterator &Reloc) {
+        OS.PadToColumn(4);
+        if (Reloc == Relocations.end()) {
+          OS << "Offset:";
+          OS.PadToColumn(22);
+          OS << to_string(format_hex(FuncEntryPC, 1));
+          OS << "\n";
+          return;
+        }
+        Expected<RelSymbol<ELFT>> RelSym =
+            this->getRelocationTarget(*Reloc, RelocSymTab);
+        if (!RelSym) {
+          this->reportUniqueWarning(RelSym.takeError());
+          OS << "Offset:";
+          OS.PadToColumn(22);
+          OS << to_string(format_hex(FuncEntryPC, 1));
+          OS << "\n";
+          return;
+        }
         SmallString<32> RelocName;
         this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
-        Stream << RelSym->Name;
+        OS << RelSym->Name;
         if (Reloc->Addend) {
           if (*Reloc->Addend >= 0)
-            Stream << " + " << *Reloc->Addend;
+            OS << " + " << *Reloc->Addend;
           else
-            Stream << " - " << -(*Reloc->Addend);
+            OS << " - " << -(*Reloc->Addend);
         }
-        Stream << " (" << RelocName << ")";
-      }
-    } else {
-      std::string FuncSymNames = GetFunctionName(FuncEntryPC);
-      Stream << "0x" << to_string(format_hex(FuncEntryPC, 1));
-      if (!FuncSymNames.empty())
-        Stream << " <" << FuncSymNames << ">";
+        OS << " (" << RelocName << ")";
+        OS << "\n";
+      };
+
+  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
+    if (this->Obj.getHeader().e_type != ELF::ET_REL) {
+      std::string FuncSymName = this->getFunctionNames(FuncEntryPC);
+      PrintFunc(FuncEntryPC, FuncSymName, OS);
+      return;
     }
-    return Stream.str();
+    auto Reloc = llvm::lower_bound(
+        Relocations, FuncEntryPC,
+        [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+    PrintReloc(FuncEntryPC, Reloc);
   };
 
   OS << "\nCall graph section '.llvm.callgraph' contains "
@@ -5530,23 +5561,27 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
 
   int EntryIndex = 0;
   for (const auto &CGInfo : this->FuncCGInfos) {
-    OS << "\n  Entry " << EntryIndex++ << ":\n";
-    OS << "    Function:          "
-       << GetFunctionAddressAndName(CGInfo.FunctionAddress) << "\n";
-    OS << "    Indirect Target:   " << (CGInfo.IsIndirectTarget ? "Yes" : "No")
-       << "\n";
-    OS << "    Type ID:           0x" << format_hex(CGInfo.FunctionTypeId, 1)
+    OS << "\n";
+    OS.PadToColumn(2);
+    OS << "Entry " << EntryIndex++ << ":\n";
+    PrintFunctionInfo(CGInfo.FunctionAddress);
+    OS.PadToColumn(4);
+    OS << "Indirect Target:  " << (CGInfo.IsIndirectTarget ? "Yes" : "No")
        << "\n";
-
-    OS << "    Direct Callees (" << CGInfo.DirectCallees.size() << "):\n";
+    OS.PadToColumn(4);
+    OS << "Type ID:          " << format_hex(CGInfo.FunctionTypeId, 1) << "\n";
+    OS.PadToColumn(4);
+    OS << "Direct Callees (" << CGInfo.DirectCallees.size() << "):\n";
     for (auto CalleePC : CGInfo.DirectCallees) {
-      OS << "      " << GetFunctionAddressAndName(CalleePC) << "\n";
+      OS.PadToColumn(6);
+      PrintFunctionInfo(CalleePC);
     }
-
-    OS << "    Indirect Callees by Type ID (" << CGInfo.IndirectTypeIDs.size()
+    OS.PadToColumn(4);
+    OS << "Indirect Callees by Type ID (" << CGInfo.IndirectTypeIDs.size()
        << "):\n";
     for (auto TypeId : CGInfo.IndirectTypeIDs) {
-      OS << "      0x" << format_hex(TypeId, 1) << "\n";
+      OS.PadToColumn(6);
+      OS << format_hex(TypeId, 1) << "\n";
     }
   }
 }
@@ -8390,33 +8425,27 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   const Elf_Shdr *RelocSymTab = nullptr;
   this->getCallGraphRelocations(Relocations, RelocSymTab);
 
-  auto GetFunctionName = [&](uint64_t EntryPc) {
-    SmallVector<uint32_t> FuncSymIndexes =
-        this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt);
-    if (FuncSymIndexes.empty())
-      return std::string("");
-
-    SmallVector<std::string> FuncSymNames;
-    for (uint32_t Index : FuncSymIndexes)
-      FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    return join(FuncSymNames, ", ");
+  auto PrintFunc = [](uint64_t FuncEntryPC, std::string FuncSymName,
+                      ScopedPrinter &W) {
+    if (!FuncSymName.empty())
+      W.printString("Name", FuncSymName);
+    W.printHex("Address", FuncEntryPC);
   };
 
-  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
-    if (this->Obj.getHeader().e_type == ELF::ET_REL) {
-      auto Reloc = llvm::lower_bound(
-          Relocations, FuncEntryPC,
-          [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
-      if (Reloc == Relocations.end()) {
-        W.printHex("Address", FuncEntryPC);      
-        return;
-      }          
-      Expected<RelSymbol<ELFT>> RelSym =
-          this->getRelocationTarget(*Reloc, RelocSymTab);
-      if (!RelSym) {
-        this->reportUniqueWarning(RelSym.takeError());
-        W.printHex("Address", FuncEntryPC);
-      } else {
+  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);
@@ -8424,17 +8453,21 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
         W.printString("Symbol", RelSym->Name);
         if (Reloc->Addend)
           W.printHex("Addend", (uintX_t)*Reloc->Addend);
-      }
-    } else {
-      std::string FuncSymName = GetFunctionName(FuncEntryPC);
-      if (!FuncSymName.empty())
-        W.printString("Name", FuncSymName);
-      W.printHex("Address", FuncEntryPC);
+      };
+
+  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);
@@ -8442,14 +8475,15 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget);
     W.printHex("TypeId", CGInfo.FunctionTypeId);
     W.printNumber("NumDirectCallees", CGInfo.DirectCallees.size());
-    W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size());
-    if (CGInfo.DirectCallees.size() > 0) {
+    {
       ListScope DCs(W, "DirectCallees");
       for (auto CalleePC : CGInfo.DirectCallees) {
+        DictScope D(W, "Entry");
         PrintFunctionInfo(CalleePC);
       }
     }
-    if (CGInfo.IndirectTypeIDs.size() > 0) {
+    W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size());
+    {
       ListScope ICSs(W, "IndirectTypeIDs");
       for (auto TypeId : CGInfo.IndirectTypeIDs) {
         DictScope ICS(W, "Entry");

>From ce75c888fda2db2ac23a7de10ed46860efb5fabf Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 22 Oct 2025 15:05:01 -0700
Subject: [PATCH 34/44] Fix tests. Address review comments.

---
 .../call-graph-info-callgraph-section.test    | 15 +++++------
 .../call-graph-info-err-invalid-flags.test    | 14 +++++-----
 ...graph-info-err-invalid-format-version.test | 12 ++++-----
 ...-info-err-malformed-callgraph-section.test | 25 -----------------
 ...info-err-malformed-callgraph-section3.test | 27 -------------------
 .../call-graph-info-err-missing-directs.test  | 14 +++++-----
 ...call-graph-info-err-missing-indirect.test} | 17 ++++++------
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 10 ++-----
 8 files changed, 38 insertions(+), 96 deletions(-)
 delete mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
 delete mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
 rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-malformed-callgraph-section2.test => call-graph-info-err-missing-indirect.test} (52%)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index 639961686a22d..bbb6246cab71a 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -3,12 +3,12 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines
-# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
-# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=LLVM
+# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=JSON
 
 
-# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
+# CHECK: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
 # CHECK-EMPTY:
 # CHECK-NEXT: Call graph section '.llvm.callgraph' contains 3 entries:
 # CHECK-EMPTY:
@@ -35,7 +35,7 @@
 # CHECK-NEXT:    Direct Callees (0):
 # CHECK-NEXT:    Indirect Callees by Type ID (0):
 
-# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
+# LLVM: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
 # LLVM-NEXT: callgraph_info [
 # LLVM-NEXT:   Function {
 # LLVM-NEXT:     Offset: 0x2
@@ -81,7 +81,7 @@
 # LLVM-NEXT:   }
 # LLVM-NEXT: ]
 
-# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
+# JSON: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
 # JSON:     "callgraph_info": [
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
@@ -160,7 +160,7 @@ baz:                  #< baz is at 10 (a).
 qux:                  #< qux is at 11 (b).
  retq
 
-.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
+.section	.llvm.callgraph,"o", at llvm_call_graph,.text
 .byte	0       #< Format version number.
 .byte	3       #< Flag IsIndirectTarget true
 .quad	0       #< foo()'s entry address.
@@ -182,6 +182,5 @@ qux:                  #< qux is at 11 (b).
 .quad   32      #< Indirect target type id.
 
 # No call graph section entry for qux. 
-# Technically its "UNKNOWN" type id but will not be printed as such by llvm-readelf.  
 
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
index 97766cd745036..7341912c6afd6 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
@@ -1,14 +1,14 @@
 ## Tests that --call-graph-info fails if .llvm.callgraph section has invalid
-## function kind value.
+## value for flags field.
 
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's Flags': Unknown Flags. Expected one of [1, 3, 5, 7] but found [8]
+# ERR: error: 'While reading call graph info's Flags': Unknown Flags. Expected a value in the range [0-7] but found [8]
 
 .text
 .globl _Z3foov
@@ -16,8 +16,8 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
+.section	.llvm.callgraph,"o", at llvm_call_graph,.text
 .byte	0   #< Format version number.
-.byte	8   #< Only valid values are 1,3,5,7
+.byte	8   #< Only valid values are 0 to 7
 # Missing direct callees info
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
index 024594cad6c9b..ddf5dd5589bf4 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
@@ -4,11 +4,11 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'Unknown value': Unknown format version value [1] in .llvm.callgraph section.
+# ERR: error: 'Unknown value': Unknown format version value [1] in .llvm.callgraph section.
 
 .text
 .globl _Z3foov
@@ -16,6 +16,6 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
-.quad	1  #< Invalid format version number: the only supported version is 0.
+.section	.llvm.callgraph,"o", at llvm_call_graph,.text
+.byte	1  #< Invalid format version number: the only supported version is 0.
 .text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
deleted file mode 100644
index 50cf9cb7c2ef1..0000000000000
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test
+++ /dev/null
@@ -1,25 +0,0 @@
-## Tests that --call-graph-info fails if .llvm.callgraph section has invalid size.
-
-# REQUIRES: x86-registered-target
-
-# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
- 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [number of indirect callsites] for function at [0x0]': unexpected end of data at offset 0x19 while reading [0x18, 0x20)
-
-.text
-.globl _Z3foov
-.type _Z3foov, at function
-_Z3foov:
- callq _Z3foov at PLT
-
-.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
-# Each unit in .callgraph section must have 64bit size. Therefore, byte size
-# must be divisible by 64.
-.quad 0
-.quad 0
-.quad 0
-.byte 0
-.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
deleted file mode 100644
index f2d682d23217a..0000000000000
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test
+++ /dev/null
@@ -1,27 +0,0 @@
-## Tests that --call-graph-info fails if .callgraph section does not have
-## an expected value, e.g., not as much call sites as the given count.
-
-# REQUIRES: x86-registered-target
-
-# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [direct callee PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)
-
-.text
-.globl _Z3foov
-.type _Z3foov, at function
-_Z3foov:
- callq _Z3foov
- retq
-
-.section	.callgraph,"o", at progbits,.text
-.quad 0  #< Format version number.
-.quad 0  #< Function entry address.
-.quad 0  #< Function kind.
-.quad 0  #< Indirect call site count.
-.quad 1  #< Direct call site count that follows.
-         #< Missing direct call entries here.
-.text
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
index e007b0cfa7953..2182dff50f6bc 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
@@ -1,14 +1,14 @@
-## Tests that --call-graph-info fails if .llvm.callgraph section has invalid
-## function kind value.
+## Tests that --call-graph-info fails if .llvm.callgraph section has flags
+## that indicate there are direct calls but the direct call info is missing.
 
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [number of direct callsites] for function at [0x0]': unable to decode LEB128 at offset 0x00000012: malformed uleb128, extends past end
+# ERR: error: 'While reading call graph info's [number of direct callsites] for function at [0x0]': unable to decode LEB128 at offset 0x00000012: malformed uleb128, extends past end
 
 .text
 .globl _Z3foov
@@ -16,7 +16,7 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.llvm.callgraph,"o", at llvm_call_graph_section,.text
+.section	.llvm.callgraph,"o", at llvm_call_graph,.text
 .byte	0   #< Format version number.
 .byte	3   #< Flag IsIndirectTarget true and has direct calls
 .quad 0   #< Function entry address.
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
similarity index 52%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
index 105831ec9dd65..d1b7fc8921453 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
@@ -4,11 +4,11 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
-# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [indirect target type id] for function at [0x0]': unexpected end of data at offset 0x20 while reading [0x20, 0x28)
+# ERR: error: 'While reading call graph info's [indirect type ID] for function at [0x0]': unexpected end of data at offset 0x13 while reading [0x13, 0x1b)
 
 .text
 .globl _Z3foov
@@ -16,10 +16,11 @@
 _Z3foov:
  callq _Z3foov at PLT
 
-.section	.callgraph,"o", at progbits,.text
-.quad 0  #< Format version number.
+.section	.llvm.callgraph,"o", at llvm_call_graph,.text
+.byte 0  #< Format version number.
+.byte 4  #< Flags
 .quad 0  #< Function entry address.
-.quad 0  #< Function kind.
-.quad 1  #< Indirect call site count that follows.
+.quad 0  #< Type ID
+.byte 1  #< Indirect call site count that follows.
          #< Missing indirect call entries here.
 .text
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 9279dcd824c90..f6e273f489922 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5377,15 +5377,9 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info's Flags");
-    switch (Flags) {
-    case 1:
-    case 3:
-    case 5:
-    case 7:
-      break;
-    default:
+    if (Flags > 7) {
       Error FlagsErr = createError(
-          "Unknown Flags. Expected one of [1, 3, 5, 7] but found [" +
+          "Unknown Flags. Expected a value in the range [0-7] but found [" +
           std::to_string(Flags) + "]");
       reportError(std::move(FlagsErr), "While reading call graph info's Flags");
     }

>From 54b2004bf6ca84593ad55b87e10e3a4c1155c012 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 22 Oct 2025 15:36:17 -0700
Subject: [PATCH 35/44] Address review comments.

---
 .../call-graph-info-err-missing-indirect.test    |  2 +-
 ...call-graph-info-err-no-callgraph-section.test |  8 ++++----
 .../tools/llvm-readobj/ELF/call-graph-info.test  | 16 ++++++++--------
 llvm/tools/llvm-readobj/ELFDumper.cpp            |  3 ++-
 4 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
index d1b7fc8921453..65c1c7f5dfc68 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
@@ -1,4 +1,4 @@
-## Tests that --call-graph-info fails if .callgraph section does not have
+## Tests that --call-graph-info fails if .llvm.callgraph section does not have
 ## an expected value, e.g., not as much call sites as the given count.
 
 # REQUIRES: x86-registered-target
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
index e5aa8b72536c6..ee1d6f8bdfde0 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
@@ -3,11 +3,11 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
-# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
-# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
 
-# CHECK: {{.*}}llvm-readelf{{.*}}: error: 'Missing section': No .llvm.callgraph section found.
+# CHECK: error: 'Missing section': No .llvm.callgraph section found.
 
 .text
 .globl _Z3foov
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 2ca4928763a5e..682c0ee78e99f 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -44,34 +44,34 @@
 # CHECK: Call graph section '.llvm.callgraph' contains 4 entries:
 # CHECK-EMPTY:
 # CHECK-NEXT: Entry 0:
-# CHECK-NEXT:    Address:           0x1790 <foo>
+# CHECK-NEXT:    Address:          0x1790 <foo>
 # CHECK-NEXT:    Indirect Target:  Yes
 # CHECK-NEXT:    Type ID:          0x3ecbeef531f74424
 # CHECK-NEXT:    Direct Callees (0):
 # CHECK-NEXT:    Indirect Callees by Type ID (0):
 # CHECK-EMPTY:
 # CHECK-NEXT: Entry 1:
-# CHECK-NEXT:    Address:           0x17a0 <bar>
+# CHECK-NEXT:    Address:          0x17a0 <bar>
 # CHECK-NEXT:    Indirect Target:  Yes
 # CHECK-NEXT:    Type ID:          0x3ecbeef531f74424
 # CHECK-NEXT:    Direct Callees (0):
 # CHECK-NEXT:    Indirect Callees by Type ID (0):
 # CHECK-EMPTY:
 # CHECK-NEXT: Entry 2:
-# CHECK-NEXT:    Address:           0x17b0 <baz>
+# CHECK-NEXT:    Address:          0x17b0 <baz>
 # CHECK-NEXT:    Indirect Target:  Yes
 # CHECK-NEXT:    Type ID:          0x308e4b8159bc8654
 # CHECK-NEXT:    Direct Callees (0):
 # CHECK-NEXT:    Indirect Callees by Type ID (0):
 # CHECK-EMPTY:
 # CHECK-NEXT: Entry 3:
-# CHECK-NEXT:    Address:           0x17c0 <main>
+# CHECK-NEXT:    Address:          0x17c0 <main>
 # CHECK-NEXT:    Indirect Target:  Yes
 # CHECK-NEXT:    Type ID:          0xfa6809609a76afca
 # CHECK-NEXT:    Direct Callees (3):
-# CHECK-NEXT:       Address:           0x1790 <foo>
-# CHECK-NEXT:       Address:           0x17a0 <bar>
-# CHECK-NEXT:       Address:           0x17b0 <baz>
+# CHECK-NEXT:       Address:          0x1790 <foo>
+# CHECK-NEXT:       Address:          0x17a0 <bar>
+# CHECK-NEXT:       Address:          0x17b0 <baz>
 # CHECK-NEXT:    Indirect Callees by Type ID (2):
 # CHECK-NEXT:      0x3ecbeef531f74424
 # CHECK-NEXT:      0x308e4b8159bc8654
@@ -584,7 +584,7 @@ Sections:
     EntSize:         0x1
     Content:         004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203934396232653930373439623765376438353739663838626231303366373535353832393465666229004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203934396232653930373439623765376438353739663838626231303366373535353832393465666229004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974203861333966396231386139343632386138656463666433613137363432376364333161656330633829004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420653038356135633833363436316632396437336637333933343334343765643964353338626563312900
   - Name:            .llvm.callgraph
-    Type:            0x6FFF4C0F
+    Type:            SHT_LLVM_CALL_GRAPH
     Flags:           [ SHF_LINK_ORDER ]
     Link:            .text
     AddressAlign:    0x1
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index f6e273f489922..62af6c07677db 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5497,7 +5497,8 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   auto PrintFunc = [](uint64_t FuncEntryPC, std::string FuncSymName,
                       formatted_raw_ostream &OS) {
     OS.PadToColumn(4);
-    OS << "Address:           ";
+    OS << "Address:";
+    OS.PadToColumn(21);
     OS << to_string(format_hex(FuncEntryPC, 1));
     if (!FuncSymName.empty())
       OS << " <" << FuncSymName << ">";

>From dfd37f2522b02dfe356d0a4ecc0bd494ee860d9e Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 22 Oct 2025 15:44:33 -0700
Subject: [PATCH 36/44] Cleanup ELFDumper.cpp

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 38 ++++++++++++---------------
 1 file changed, 17 insertions(+), 21 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 62af6c07677db..43df17bbc2f0d 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5331,27 +5331,22 @@ getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
 
 template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   const Elf_Shdr *CGSection = findSectionByName(".llvm.callgraph");
-  if (!CGSection) {
-    Error NoSectionErr = createError("No .llvm.callgraph section found.");
-    reportError(std::move(NoSectionErr), "Missing section");
-  }
+  if (!CGSection)
+    reportError(createError("No .llvm.callgraph section found."),
+                "Missing section");
 
   Expected<ArrayRef<uint8_t>> SectionBytesOrErr =
       Obj.getSectionContents(*CGSection);
   if (!SectionBytesOrErr) {
-    Error SectionReadErr = SectionBytesOrErr.takeError();
-    reportError(std::move(SectionReadErr),
+    reportError(SectionBytesOrErr.takeError(),
                 "Unable to read the .llvm.callgraph section");
   }
 
   auto PrintMalformedError = [&](Error &E, Twine FuncPC, StringRef Component) {
-    // auto Msg = llvm::Twine("Malformed callgraph section while reading [") +
-    // Component + llvm::Twine("] .\n");
-    std::string Msg =
-        (StringRef("While reading call graph info's [") + Component +
-         StringRef("] for function at [0x") + StringRef(FuncPC.str()) + "]")
-            .str();
-    reportError(std::move(E), StringRef(Msg));
+    reportError(std::move(E),
+                Twine("While reading call graph info's [" + Component +
+                      "] for function at [0x" + FuncPC + "]")
+                    .str());
   };
 
   DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(),
@@ -5367,10 +5362,10 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                   "While reading call graph info FormatVersionNumber");
 
     if (FormatVersionNumber != 0) {
-      Error FormatErr = createError("Unknown format version value [" +
-                                    std::to_string(FormatVersionNumber) +
-                                    "] in .llvm.callgraph section.");
-      reportError(std::move(FormatErr), "Unknown value");
+      reportError(createError("Unknown format version value [" +
+                              std::to_string(FormatVersionNumber) +
+                              "] in .llvm.callgraph section."),
+                  "Unknown value");
     }
 
     uint8_t Flags = Data.getU8(&Offset, &CGSectionErr);
@@ -5378,10 +5373,11 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
       reportError(std::move(CGSectionErr),
                   "While reading call graph info's Flags");
     if (Flags > 7) {
-      Error FlagsErr = createError(
-          "Unknown Flags. Expected a value in the range [0-7] but found [" +
-          std::to_string(Flags) + "]");
-      reportError(std::move(FlagsErr), "While reading call graph info's Flags");
+      reportError(
+          createError(
+              "Unknown Flags. Expected a value in the range [0-7] but found [" +
+              std::to_string(Flags) + "]"),
+          "While reading call graph info's Flags");
     }
     uint64_t FuncAddrOffset = Offset;
     typename ELFT::uint FuncAddr =

>From 8ebe0a21e387b885bb798919528c329608461511 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 22 Oct 2025 15:55:16 -0700
Subject: [PATCH 37/44] Use enum for handling Callgraph Flags

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 35 ++++++++++++++++-----------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 43df17bbc2f0d..0afa091045293 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5329,6 +5329,17 @@ getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
   return CallGraphSection;
 }
 
+namespace callgraph {
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+enum Flags : uint8_t {
+  None = 0,
+  IsIndirectTarget = 1u << 0,
+  HasDirectCallees = 1u << 1,
+  HasIndirectCallees = 1u << 2,
+  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ HasIndirectCallees)
+};
+} // namespace callgraph
+
 template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   const Elf_Shdr *CGSection = findSectionByName(".llvm.callgraph");
   if (!CGSection)
@@ -5357,10 +5368,10 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   while (Offset < CGSection->sh_size) {
     Error CGSectionErr = Error::success();
     uint8_t FormatVersionNumber = Data.getU8(&Offset, &CGSectionErr);
-    if (CGSectionErr)
+    if (CGSectionErr) {
       reportError(std::move(CGSectionErr),
                   "While reading call graph info FormatVersionNumber");
-
+    }
     if (FormatVersionNumber != 0) {
       reportError(createError("Unknown format version value [" +
                               std::to_string(FormatVersionNumber) +
@@ -5368,15 +5379,16 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                   "Unknown value");
     }
 
-    uint8_t Flags = Data.getU8(&Offset, &CGSectionErr);
+    uint8_t FlagsVal = Data.getU8(&Offset, &CGSectionErr);
     if (CGSectionErr)
       reportError(std::move(CGSectionErr),
                   "While reading call graph info's Flags");
-    if (Flags > 7) {
+    callgraph::Flags CGFlags = static_cast<callgraph::Flags>(FlagsVal);
+    if (FlagsVal > 7) {
       reportError(
           createError(
               "Unknown Flags. Expected a value in the range [0-7] but found [" +
-              std::to_string(Flags) + "]"),
+              std::to_string(FlagsVal) + "]"),
           "While reading call graph info's Flags");
     }
     uint64_t FuncAddrOffset = Offset;
@@ -5391,19 +5403,19 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     FunctionCallgraphInfo CGInfo;
     CGInfo.FunctionAddress = IsETREL ? FuncAddrOffset : FuncAddr;
     CGInfo.FormatVersionNumber = FormatVersionNumber;
-    bool IsIndirectTarget = Flags & 1; // LSB is set to 1 if indirect target.
+    bool IsIndirectTarget =
+        (CGFlags & callgraph::IsIndirectTarget) != callgraph::None;
     CGInfo.IsIndirectTarget = IsIndirectTarget;
     uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
     if (CGSectionErr)
       PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
                           "indirect type id");
     CGInfo.FunctionTypeId = TypeId;
-
     if (IsIndirectTarget && TypeId == 0)
       UnknownCount++;
 
     bool HasDirectCallees =
-        Flags & (1u << 1); // LSB 1 is set to 1 if direct callees present.
+        (CGFlags & callgraph::HasDirectCallees) != callgraph::None;
     if (HasDirectCallees) {
       // Read number of direct call sites for this function.
       uint64_t NumDirectCallees = Data.getULEB128(&Offset, &CGSectionErr);
@@ -5423,8 +5435,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
     }
 
     bool HasIndirectTypeIds =
-        Flags &
-        (1u << 2); // LSB 2 is set to 1 if indirect target type Ids present.
+        (CGFlags & callgraph::HasIndirectCallees) != callgraph::None;
     if (HasIndirectTypeIds) {
       uint64_t NumIndirectTargetTypeIDs =
           Data.getULEB128(&Offset, &CGSectionErr);
@@ -5447,10 +5458,6 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
   if (UnknownCount)
     reportUniqueWarning(".llvm.callgraph section has unknown type id for " +
                         std::to_string(UnknownCount) + " indirect targets.");
-
-  // // Sort function info by function PC.
-  // llvm::sort(FuncCGInfos,
-  //            [](const auto &A, const auto &B) { return A.first < B.first; });
   return true;
 }
 

>From c429dd820c90b8146d1074b9eb34808bf85e5dc2 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 23 Oct 2025 15:29:10 -0700
Subject: [PATCH 38/44] Update error message.

---
 .../ELF/call-graph-info-err-invalid-flags.test            | 2 +-
 llvm/tools/llvm-readobj/ELFDumper.cpp                     | 8 +++-----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
index 7341912c6afd6..de59a1beca951 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
@@ -8,7 +8,7 @@
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
-# ERR: error: 'While reading call graph info's Flags': Unknown Flags. Expected a value in the range [0-7] but found [8]
+# ERR: error: 'While reading call graph info's Flags': Unexpected value. Expected [0-7] but found [8]
 
 .text
 .globl _Z3foov
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 0afa091045293..c81425a70ef49 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5385,11 +5385,9 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
                   "While reading call graph info's Flags");
     callgraph::Flags CGFlags = static_cast<callgraph::Flags>(FlagsVal);
     if (FlagsVal > 7) {
-      reportError(
-          createError(
-              "Unknown Flags. Expected a value in the range [0-7] but found [" +
-              std::to_string(FlagsVal) + "]"),
-          "While reading call graph info's Flags");
+      reportError(createError("Unexpected value. Expected [0-7] but found [" +
+                              std::to_string(FlagsVal) + "]"),
+                  "While reading call graph info's Flags");
     }
     uint64_t FuncAddrOffset = Offset;
     typename ELFT::uint FuncAddr =

>From d04fbd0f2f9c11c86665fd8a3c80c9641e5b8384 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 31 Oct 2025 13:31:38 -0700
Subject: [PATCH 39/44] Address review comments.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index c81425a70ef49..53e2f05ebf697 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -451,13 +451,8 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   // information.
   SmallVector<FunctionCallgraphInfo, 16> FuncCGInfos;
 
-  // // Callgraph - 64 bit type id mapped to entry PC addresses of functions
-  // which
-  // // are of the given type id.
-  // MapVector<uint64_t, SmallVector<typename ELFT::uint>> TypeIdToIndirTargets;
-
-  // Callgraph - Read callgraph section and process its contents to populate
-  // Callgraph related data structures which will be used to dump callgraph
+  // Read the .llvm.callgraph section and process its contents to populate
+  // call graph related data structures which will be used to dump call graph
   // info. Returns false if there is no .llvm.callgraph section in the input
   // file.
   bool processCallGraphSection();
@@ -469,7 +464,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
 
 protected:
-  std::string getFunctionNames(uint64_t FuncAddr) {
+  std::string getFunctionNamesString(uint64_t FuncAddr) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(FuncAddr, std::nullopt);
     if (FuncSymIndexes.empty())
@@ -5542,7 +5537,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
 
   auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
     if (this->Obj.getHeader().e_type != ELF::ET_REL) {
-      std::string FuncSymName = this->getFunctionNames(FuncEntryPC);
+      std::string FuncSymName = this->getFunctionNamesString(FuncEntryPC);
       PrintFunc(FuncEntryPC, FuncSymName, OS);
       return;
     }
@@ -8453,7 +8448,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
 
   auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
     if (this->Obj.getHeader().e_type != ELF::ET_REL) {
-      std::string FuncSymName = this->getFunctionNames(FuncEntryPC);
+      std::string FuncSymName = this->getFunctionNamesString(FuncEntryPC);
       PrintFunc(FuncEntryPC, FuncSymName, W);
       return;
     }

>From 75bffeb04a936b05c8226061ac3d5b9fe6e2478a Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 31 Oct 2025 13:50:49 -0700
Subject: [PATCH 40/44] Function name printing to match stack size printing
 behavior.

---
 .../llvm-readobj/ELF/call-graph-info.test     | 42 ++++++++++++-------
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 25 +++++------
 2 files changed, 38 insertions(+), 29 deletions(-)

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 682c0ee78e99f..4e0395fc8f79d 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -78,7 +78,7 @@
 
 # LLVM: callgraph_info [
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Name: foo
+# LLVM-NEXT:       Functions: [foo]
 # LLVM-NEXT:       Address: 0x1790
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -91,7 +91,7 @@
 # LLVM-NEXT:       ]
 # LLVM-NEXT:     }
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Name: bar
+# LLVM-NEXT:       Functions: [bar]
 # LLVM-NEXT:       Address: 0x17A0
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -104,7 +104,7 @@
 # LLVM-NEXT:       ]
 # LLVM-NEXT:     }
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Name: baz
+# LLVM-NEXT:       Functions: [baz]
 # LLVM-NEXT:       Address: 0x17B0
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -117,7 +117,7 @@
 # LLVM-NEXT:       ]
 # LLVM-NEXT:     }
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Name: main
+# LLVM-NEXT:       Functions: [main]
 # LLVM-NEXT:       Address: 0x17C0
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -125,15 +125,15 @@
 # LLVM-NEXT:       NumDirectCallees: 3
 # LLVM-NEXT:       DirectCallees [
 # LLVM-NEXT:         Entry {
-# LLVM-NEXT:           Name: foo
+# LLVM-NEXT:           Functions: [foo]
 # LLVM-NEXT:           Address: 0x1790
 # LLVM-NEXT:         }
 # LLVM-NEXT:         Entry {
-# LLVM-NEXT:           Name: bar
+# LLVM-NEXT:           Functions: [bar]
 # LLVM-NEXT:           Address: 0x17A0
 # LLVM-NEXT:         }
 # LLVM-NEXT:         Entry {
-# LLVM-NEXT:           Name: baz
+# LLVM-NEXT:           Functions: [baz]
 # LLVM-NEXT:           Address: 0x17B0
 # LLVM-NEXT:         }
 # LLVM-NEXT:       ]
@@ -153,7 +153,9 @@
 # JSON:     "callgraph_info": [
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Name": "foo",
+# JSON-NEXT:          "Functions": [
+# JSON-NEXT:            "foo"
+# JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6032,
 # JSON-NEXT:          "Version": 0,
 # JSON-NEXT:          "IsIndirectTarget": true,
@@ -166,7 +168,9 @@
 # JSON-NEXT:      },
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Name": "bar",
+# JSON-NEXT:          "Functions": [
+# JSON-NEXT:            "bar"
+# JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6048,
 # JSON-NEXT:          "Version": 0,
 # JSON-NEXT:          "IsIndirectTarget": true,
@@ -179,7 +183,9 @@
 # JSON-NEXT:      },
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Name": "baz",
+# JSON-NEXT:          "Functions": [
+# JSON-NEXT:            "baz"
+# JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6064,
 # JSON-NEXT:          "Version": 0,
 # JSON-NEXT:          "IsIndirectTarget": true,
@@ -192,7 +198,9 @@
 # JSON-NEXT:      },
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Name": "main",
+# JSON-NEXT:          "Functions": [
+# JSON-NEXT:            "main"
+# JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6080,
 # JSON-NEXT:          "Version": 0,
 # JSON-NEXT:          "IsIndirectTarget": true,
@@ -201,19 +209,25 @@
 # JSON-NEXT:          "DirectCallees": [
 # JSON-NEXT:            {
 # JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Name": "foo",
+# JSON-NEXT:                "Functions": [
+# JSON-NEXT:                  "foo"
+# JSON-NEXT:                ],
 # JSON-NEXT:                "Address": 6032
 # JSON-NEXT:              }
 # JSON-NEXT:            },
 # JSON-NEXT:            {
 # JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Name": "bar",
+# JSON-NEXT:                "Functions": [
+# JSON-NEXT:                  "bar"
+# JSON-NEXT:                ],
 # JSON-NEXT:                "Address": 6048
 # JSON-NEXT:              }
 # JSON-NEXT:            },
 # JSON-NEXT:            {
 # JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Name": "baz",
+# JSON-NEXT:                "Functions": [
+# JSON-NEXT:                  "baz"
+# JSON-NEXT:                ],
 # JSON-NEXT:                "Address": 6064
 # JSON-NEXT:              }
 # JSON-NEXT:            }
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 53e2f05ebf697..505063d2325c8 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -464,16 +464,13 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
 
 protected:
-  std::string getFunctionNamesString(uint64_t FuncAddr) {
+  SmallVector<std::string> getFunctionNames(uint64_t FuncAddr) {
     SmallVector<uint32_t> FuncSymIndexes =
         this->getSymbolIndexesForFunctionAddress(FuncAddr, std::nullopt);
-    if (FuncSymIndexes.empty())
-      return "";
-
     SmallVector<std::string> FuncSymNames;
     for (uint32_t Index : FuncSymIndexes)
       FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    return join(FuncSymNames, ", ");
+    return FuncSymNames;
   }
 };
 
@@ -5490,14 +5487,14 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
   const Elf_Shdr *RelocSymTab = nullptr;
   this->getCallGraphRelocations(Relocations, RelocSymTab);
 
-  auto PrintFunc = [](uint64_t FuncEntryPC, std::string FuncSymName,
+  auto PrintFunc = [](uint64_t FuncEntryPC, ArrayRef<std::string> FuncNames,
                       formatted_raw_ostream &OS) {
     OS.PadToColumn(4);
     OS << "Address:";
     OS.PadToColumn(21);
     OS << to_string(format_hex(FuncEntryPC, 1));
-    if (!FuncSymName.empty())
-      OS << " <" << FuncSymName << ">";
+    if (!FuncNames.empty())
+      OS << " <" << join(FuncNames.begin(), FuncNames.end(), ", ") << ">";
     OS << "\n";
   };
 
@@ -5537,8 +5534,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
 
   auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
     if (this->Obj.getHeader().e_type != ELF::ET_REL) {
-      std::string FuncSymName = this->getFunctionNamesString(FuncEntryPC);
-      PrintFunc(FuncEntryPC, FuncSymName, OS);
+      PrintFunc(FuncEntryPC, this->getFunctionNames(FuncEntryPC), OS);
       return;
     }
     auto Reloc = llvm::lower_bound(
@@ -8416,10 +8412,10 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   const Elf_Shdr *RelocSymTab = nullptr;
   this->getCallGraphRelocations(Relocations, RelocSymTab);
 
-  auto PrintFunc = [](uint64_t FuncEntryPC, std::string FuncSymName,
+  auto PrintFunc = [](uint64_t FuncEntryPC, ArrayRef<std::string> FuncNames,
                       ScopedPrinter &W) {
-    if (!FuncSymName.empty())
-      W.printString("Name", FuncSymName);
+    if (!FuncNames.empty())
+      W.printList("Functions", FuncNames);
     W.printHex("Address", FuncEntryPC);
   };
 
@@ -8448,8 +8444,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
 
   auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
     if (this->Obj.getHeader().e_type != ELF::ET_REL) {
-      std::string FuncSymName = this->getFunctionNamesString(FuncEntryPC);
-      PrintFunc(FuncEntryPC, FuncSymName, W);
+      PrintFunc(FuncEntryPC, this->getFunctionNames(FuncEntryPC), W);
       return;
     }
     auto Reloc = llvm::lower_bound(

>From 77acb103d87f4072050c60eed108a7308bb4983d Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 31 Oct 2025 14:12:17 -0700
Subject: [PATCH 41/44] Refactor getCalGraphSection function as suggested in
 review.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 505063d2325c8..4887e8d031170 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5307,18 +5307,15 @@ static std::optional<object::SectionRef>
 getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
   // Get the .llvm.callgraph section.
   StringRef CallGraphSectionName(".llvm.callgraph");
-  std::optional<object::SectionRef> CallGraphSection;
   for (auto Sec : ObjF.sections()) {
-    StringRef Name;
-    if (Expected<StringRef> NameOrErr = Sec.getName())
-      Name = *NameOrErr;
-    else
+    if (Expected<StringRef> NameOrErr = Sec.getName()) {
+      StringRef Name = *NameOrErr;
+      if (Name == CallGraphSectionName)
+        return Sec;
+    } else
       consumeError(NameOrErr.takeError());
-
-    if (Name == CallGraphSectionName)
-      return Sec;
   }
-  return CallGraphSection;
+  return std::nullopt;
 }
 
 namespace callgraph {

>From 246275d7343bc258dd2b3c3836d3444a592012a4 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 11:36:17 -0800
Subject: [PATCH 42/44] Declutter ouput format. Remove GNU output.

---
 .../call-graph-info-callgraph-section.test    |  60 ++----
 .../call-graph-info-err-invalid-flags.test    |   1 -
 ...graph-info-err-invalid-format-version.test |   1 -
 .../call-graph-info-err-missing-directs.test  |   1 -
 .../call-graph-info-err-missing-indirect.test |   1 -
 ...l-graph-info-err-no-callgraph-section.test |   1 -
 .../llvm-readobj/ELF/call-graph-info.test     | 194 +++++++-----------
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 111 +---------
 8 files changed, 87 insertions(+), 283 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
index bbb6246cab71a..b0ba11dd2f0aa 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
@@ -3,37 +3,13 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --allow-empty -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=LLVM
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=JSON
 
 
-# CHECK: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
-# CHECK-EMPTY:
-# CHECK-NEXT: Call graph section '.llvm.callgraph' contains 3 entries:
-# CHECK-EMPTY:
-# CHECK-NEXT:  Entry 0:
-# CHECK-NEXT:    Offset:           0x2
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0x0
-# CHECK-NEXT:    Direct Callees (1):
-# CHECK-NEXT:       Offset:        0x13
-# CHECK-NEXT:    Indirect Callees by Type ID (0):
-# CHECK-EMPTY:
-# CHECK-NEXT:  Entry 1:
-# CHECK-NEXT:    Offset:           0x1d
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0x0
-# CHECK-NEXT:    Direct Callees (0):
-# CHECK-NEXT:    Indirect Callees by Type ID (1):
-# CHECK-NEXT:      0x10
-# CHECK-EMPTY:
-# CHECK-NEXT:  Entry 2:
-# CHECK-NEXT:    Offset:           0x38
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0x20
-# CHECK-NEXT:    Direct Callees (0):
-# CHECK-NEXT:    Indirect Callees by Type ID (0):
+## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
+# CHECK-NOT: .
 
 # LLVM: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
 # LLVM-NEXT: callgraph_info [
@@ -44,13 +20,12 @@
 # LLVM-NEXT:     TypeId: 0x0
 # LLVM-NEXT:     NumDirectCallees: 1
 # LLVM-NEXT:     DirectCallees [
-# LLVM-NEXT:       Entry {
+# LLVM-NEXT:       {
 # LLVM-NEXT:         Offset: 0x13
 # LLVM-NEXT:       }
 # LLVM-NEXT:     ]
 # LLVM-NEXT:     NumIndirectTargetTypeIDs: 0
-# LLVM-NEXT:     IndirectTypeIDs [
-# LLVM-NEXT:     ]
+# LLVM-NEXT:     IndirectTypeIDs: []
 # LLVM-NEXT:   }
 # LLVM-NEXT:   Function {
 # LLVM-NEXT:     Offset: 0x1D
@@ -61,11 +36,7 @@
 # LLVM-NEXT:     DirectCallees [
 # LLVM-NEXT:     ]
 # LLVM-NEXT:     NumIndirectTargetTypeIDs: 1
-# LLVM-NEXT:     IndirectTypeIDs [
-# LLVM-NEXT:       Entry {
-# LLVM-NEXT:         TypeId: 0x10
-# LLVM-NEXT:       }
-# LLVM-NEXT:     ]
+# LLVM-NEXT:     IndirectTypeIDs: [0x10]
 # LLVM-NEXT:   }
 # LLVM-NEXT:   Function {
 # LLVM-NEXT:     Offset: 0x38
@@ -76,8 +47,7 @@
 # LLVM-NEXT:     DirectCallees [
 # LLVM-NEXT:     ]
 # LLVM-NEXT:     NumIndirectTargetTypeIDs: 0
-# LLVM-NEXT:     IndirectTypeIDs [
-# LLVM-NEXT:     ]
+# LLVM-NEXT:     IndirectTypeIDs: []
 # LLVM-NEXT:   }
 # LLVM-NEXT: ]
 
@@ -92,9 +62,7 @@
 # JSON-NEXT:          "NumDirectCallees": 1,
 # JSON-NEXT:          "DirectCallees": [
 # JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Offset": 19
-# JSON-NEXT:              }
+# JSON-NEXT:              "Offset": 19
 # JSON-NEXT:            }
 # JSON-NEXT:          ],
 # JSON-NEXT:          "NumIndirectTargetTypeIDs": 0,
@@ -111,11 +79,7 @@
 # JSON-NEXT:          "DirectCallees": [],
 # JSON-NEXT:          "NumIndirectTargetTypeIDs": 1,
 # JSON-NEXT:          "IndirectTypeIDs": [
-# JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "TypeId": 16
-# JSON-NEXT:              }
-# JSON-NEXT:            }
+# JSON-NEXT:            16
 # JSON-NEXT:          ]
 # JSON-NEXT:        }
 # JSON-NEXT:      },
@@ -163,14 +127,14 @@ qux:                  #< qux is at 11 (b).
 .section	.llvm.callgraph,"o", at llvm_call_graph,.text
 .byte	0       #< Format version number.
 .byte	3       #< Flag IsIndirectTarget true
-.quad	0       #< foo()'s entry address.
+.quad	0       #< foo()'s address.
 .quad	0       #< TypeID: unknown.
 .byte   1       #< Count of direct callees.
 .quad   5       #< Direct callee foo's address>
 
 .byte	0       #< Format version number.
 .byte   5       #< Flag IsIndirectTarget true
-.quad	6       #< bar()'s entry address.
+.quad	6       #< bar()'s address.
 .quad	0       #< TypeID: unknown.
 .byte	1       #< Count of indirect target type IDs
 .quad   16      #< Indirect call type id.
@@ -178,7 +142,7 @@ qux:                  #< qux is at 11 (b).
 
 .byte	0       #< Format version number.
 .byte   1       #< Flag IsIndirectTarget true
-.quad	10      #< baz()'s entry address.
+.quad	10      #< baz()'s address.
 .quad   32      #< Indirect target type id.
 
 # No call graph section entry for qux. 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
index de59a1beca951..5bbbe3f13cf6c 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
@@ -4,7 +4,6 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
index ddf5dd5589bf4..fd7941bc06e92 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
@@ -4,7 +4,6 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
index 2182dff50f6bc..b58ede05ce26c 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
@@ -4,7 +4,6 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
index 65c1c7f5dfc68..fdee26c47c701 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
@@ -4,7 +4,6 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR
 
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
index ee1d6f8bdfde0..2d8a010d6b8c6 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
@@ -3,7 +3,6 @@
 # REQUIRES: x86-registered-target
 
 # RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
-# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
 # RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
 # RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
 
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 4e0395fc8f79d..6ffeb3ab6b884 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -1,84 +1,52 @@
 ## Tests how --call-graph-info prints the call graph information.
 # RUN: yaml2obj --docnum=1 %s -o %t
-# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
+# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --allow-empty -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=LLVM -DFILE=%t
 # RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=JSON -DFILE=%t
 
-# Yaml input is obtained by compiling the below source to object with:
-#   clang -fexperimental-call-graph-section test.c -o test.o
-# then to yaml with:
-#   obj2yaml test.o > test.yaml
-# Remove ProgramHeaders if obj2yaml fails.
+## Yaml input is obtained by compiling the below source to object with:
+##   clang -fexperimental-call-graph-section test.c -o test.o
+## then to yaml with:
+##   obj2yaml test.o > test.yaml
+## Remove ProgramHeaders if obj2yaml fails.
 
-# The content of the .callgraph section is fixed with this yaml in raw format.
+## The content of the .callgraph section is fixed with this yaml in raw format.
 
-# Source:
-#   void foo() {}
-#
-#   void bar() {}
-#
-#   int baz(char a) {
-#     return 0;
-#   }
-#
-#   int main() {
-#     // Indirect calls.
-#     void (*fp_foo)() = foo;
-#     fp_foo();
-#
-#     void (*fp_bar)() = bar;
-#     fp_bar();
-#
-#     char a;
-#     int (*fp_baz)(char) = baz;
-#     fp_baz(a);
-#
-#     // Direct calls.
-#     foo();
-#     bar();
-#     baz(a);
-#
-#     return 0;
-#   }
+## Source:
+##   void foo() {}
+##
+##   void bar() {}
+##
+##   int baz(char a) {
+##     return 0;
+##   }
+##
+##   int main() {
+##     // Indirect calls.
+##     void (*fp_foo)() = foo;
+##     fp_foo();
+##
+##     void (*fp_bar)() = bar;
+##     fp_bar();
+##
+##     char a;
+##     int (*fp_baz)(char) = baz;
+##     fp_baz(a);
+##
+##     // Direct calls.
+##     foo();
+##     bar();
+##     baz(a);
+##
+##     return 0;
+##   }
 
-# CHECK: Call graph section '.llvm.callgraph' contains 4 entries:
-# CHECK-EMPTY:
-# CHECK-NEXT: Entry 0:
-# CHECK-NEXT:    Address:          0x1790 <foo>
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0x3ecbeef531f74424
-# CHECK-NEXT:    Direct Callees (0):
-# CHECK-NEXT:    Indirect Callees by Type ID (0):
-# CHECK-EMPTY:
-# CHECK-NEXT: Entry 1:
-# CHECK-NEXT:    Address:          0x17a0 <bar>
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0x3ecbeef531f74424
-# CHECK-NEXT:    Direct Callees (0):
-# CHECK-NEXT:    Indirect Callees by Type ID (0):
-# CHECK-EMPTY:
-# CHECK-NEXT: Entry 2:
-# CHECK-NEXT:    Address:          0x17b0 <baz>
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0x308e4b8159bc8654
-# CHECK-NEXT:    Direct Callees (0):
-# CHECK-NEXT:    Indirect Callees by Type ID (0):
-# CHECK-EMPTY:
-# CHECK-NEXT: Entry 3:
-# CHECK-NEXT:    Address:          0x17c0 <main>
-# CHECK-NEXT:    Indirect Target:  Yes
-# CHECK-NEXT:    Type ID:          0xfa6809609a76afca
-# CHECK-NEXT:    Direct Callees (3):
-# CHECK-NEXT:       Address:          0x1790 <foo>
-# CHECK-NEXT:       Address:          0x17a0 <bar>
-# CHECK-NEXT:       Address:          0x17b0 <baz>
-# CHECK-NEXT:    Indirect Callees by Type ID (2):
-# CHECK-NEXT:      0x3ecbeef531f74424
-# CHECK-NEXT:      0x308e4b8159bc8654
+## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
+# CHECK-NOT: .
 
 # LLVM: callgraph_info [
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Functions: [foo]
+# LLVM-NEXT:       Names: [foo]
 # LLVM-NEXT:       Address: 0x1790
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -87,11 +55,10 @@
 # LLVM-NEXT:       DirectCallees [
 # LLVM-NEXT:       ]
 # LLVM-NEXT:       NumIndirectTargetTypeIDs: 0
-# LLVM-NEXT:       IndirectTypeIDs [
-# LLVM-NEXT:       ]
+# LLVM-NEXT:       IndirectTypeIDs: []
 # LLVM-NEXT:     }
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Functions: [bar]
+# LLVM-NEXT:       Names: [bar]
 # LLVM-NEXT:       Address: 0x17A0
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -100,11 +67,10 @@
 # LLVM-NEXT:       DirectCallees [
 # LLVM-NEXT:       ]
 # LLVM-NEXT:       NumIndirectTargetTypeIDs: 0
-# LLVM-NEXT:       IndirectTypeIDs [
-# LLVM-NEXT:       ]
+# LLVM-NEXT:       IndirectTypeIDs: []
 # LLVM-NEXT:     }
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Functions: [baz]
+# LLVM-NEXT:       Names: [baz]
 # LLVM-NEXT:       Address: 0x17B0
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
@@ -113,39 +79,31 @@
 # LLVM-NEXT:       DirectCallees [
 # LLVM-NEXT:       ]
 # LLVM-NEXT:       NumIndirectTargetTypeIDs: 0
-# LLVM-NEXT:       IndirectTypeIDs [
-# LLVM-NEXT:       ]
+# LLVM-NEXT:       IndirectTypeIDs: []
 # LLVM-NEXT:     }
 # LLVM-NEXT:     Function {
-# LLVM-NEXT:       Functions: [main]
+# LLVM-NEXT:       Names: [main]
 # LLVM-NEXT:       Address: 0x17C0
 # LLVM-NEXT:       Version: 0
 # LLVM-NEXT:       IsIndirectTarget: Yes
 # LLVM-NEXT:       TypeId: 0xFA6809609A76AFCA
 # LLVM-NEXT:       NumDirectCallees: 3
 # LLVM-NEXT:       DirectCallees [
-# LLVM-NEXT:         Entry {
-# LLVM-NEXT:           Functions: [foo]
+# LLVM-NEXT:         {
+# LLVM-NEXT:           Names: [foo]
 # LLVM-NEXT:           Address: 0x1790
 # LLVM-NEXT:         }
-# LLVM-NEXT:         Entry {
-# LLVM-NEXT:           Functions: [bar]
+# LLVM-NEXT:         {
+# LLVM-NEXT:           Names: [bar]
 # LLVM-NEXT:           Address: 0x17A0
 # LLVM-NEXT:         }
-# LLVM-NEXT:         Entry {
-# LLVM-NEXT:           Functions: [baz]
+# LLVM-NEXT:         {
+# LLVM-NEXT:           Names: [baz]
 # LLVM-NEXT:           Address: 0x17B0
 # LLVM-NEXT:         }
 # LLVM-NEXT:       ]
 # LLVM-NEXT:       NumIndirectTargetTypeIDs: 2
-# LLVM-NEXT:       IndirectTypeIDs [
-# LLVM-NEXT:         Entry {
-# LLVM-NEXT:           TypeId: 0x3ECBEEF531F74424
-# LLVM-NEXT:         }
-# LLVM-NEXT:         Entry {
-# LLVM-NEXT:           TypeId: 0x308E4B8159BC8654
-# LLVM-NEXT:         }
-# LLVM-NEXT:       ]
+# LLVM-NEXT:       IndirectTypeIDs: [0x3ECBEEF531F74424, 0x308E4B8159BC8654]
 # LLVM-NEXT:     }
 # LLVM-NEXT:   ]
 
@@ -153,7 +111,7 @@
 # JSON:     "callgraph_info": [
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Functions": [
+# JSON-NEXT:          "Names": [
 # JSON-NEXT:            "foo"
 # JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6032,
@@ -168,7 +126,7 @@
 # JSON-NEXT:      },
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Functions": [
+# JSON-NEXT:          "Names": [
 # JSON-NEXT:            "bar"
 # JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6048,
@@ -183,7 +141,7 @@
 # JSON-NEXT:      },
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Functions": [
+# JSON-NEXT:          "Names": [
 # JSON-NEXT:            "baz"
 # JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6064,
@@ -198,7 +156,7 @@
 # JSON-NEXT:      },
 # JSON-NEXT:      {
 # JSON-NEXT:        "Function": {
-# JSON-NEXT:          "Functions": [
+# JSON-NEXT:          "Names": [
 # JSON-NEXT:            "main"
 # JSON-NEXT:          ],
 # JSON-NEXT:          "Address": 6080,
@@ -208,42 +166,28 @@
 # JSON-NEXT:          "NumDirectCallees": 3,
 # JSON-NEXT:          "DirectCallees": [
 # JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Functions": [
-# JSON-NEXT:                  "foo"
-# JSON-NEXT:                ],
-# JSON-NEXT:                "Address": 6032
-# JSON-NEXT:              }
+# JSON-NEXT:              "Names": [
+# JSON-NEXT:                "foo"
+# JSON-NEXT:              ],
+# JSON-NEXT:              "Address": 6032
 # JSON-NEXT:            },
 # JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Functions": [
-# JSON-NEXT:                  "bar"
-# JSON-NEXT:                ],
-# JSON-NEXT:                "Address": 6048
-# JSON-NEXT:              }
+# JSON-NEXT:              "Names": [
+# JSON-NEXT:                "bar"
+# JSON-NEXT:              ],
+# JSON-NEXT:              "Address": 6048
 # JSON-NEXT:            },
 # JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "Functions": [
-# JSON-NEXT:                  "baz"
-# JSON-NEXT:                ],
-# JSON-NEXT:                "Address": 6064
-# JSON-NEXT:              }
+# JSON-NEXT:              "Names": [
+# JSON-NEXT:                "baz"
+# JSON-NEXT:              ],
+# JSON-NEXT:              "Address": 6064
 # JSON-NEXT:            }
 # JSON-NEXT:          ],
 # JSON-NEXT:          "NumIndirectTargetTypeIDs": 2,
 # JSON-NEXT:          "IndirectTypeIDs": [
-# JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "TypeId": 4524972987496481828
-# JSON-NEXT:              }
-# JSON-NEXT:            },
-# JSON-NEXT:            {
-# JSON-NEXT:              "Entry": {
-# JSON-NEXT:                "TypeId": 3498816979441845844
-# JSON-NEXT:              }
-# JSON-NEXT:            }
+# JSON-NEXT:            4524972987496481828,
+# JSON-NEXT:            3498816979441845844
 # JSON-NEXT:          ]
 # JSON-NEXT:        }
 # JSON-NEXT:      }
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 4887e8d031170..5f0d025fd76f8 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -656,7 +656,6 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
   void printVersionDefinitionSection(const Elf_Shdr *Sec) override;
   void printVersionDependencySection(const Elf_Shdr *Sec) override;
   void printCGProfile() override;
-  void printCallGraphInfo() override;
   void printBBAddrMaps(bool PrettyPGOAnalysis) override;
   void printAddrsig() override;
   void printNotes() override;
@@ -5476,100 +5475,6 @@ void ELFDumper<ELFT>::getCallGraphRelocations(
   }
 }
 
-template <class ELFT> void GNUELFDumper<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, ArrayRef<std::string> FuncNames,
-                      formatted_raw_ostream &OS) {
-    OS.PadToColumn(4);
-    OS << "Address:";
-    OS.PadToColumn(21);
-    OS << to_string(format_hex(FuncEntryPC, 1));
-    if (!FuncNames.empty())
-      OS << " <" << join(FuncNames.begin(), FuncNames.end(), ", ") << ">";
-    OS << "\n";
-  };
-
-  auto PrintReloc =
-      [&](uint64_t FuncEntryPC,
-          typename std::vector<Relocation<ELFT>>::iterator &Reloc) {
-        OS.PadToColumn(4);
-        if (Reloc == Relocations.end()) {
-          OS << "Offset:";
-          OS.PadToColumn(22);
-          OS << to_string(format_hex(FuncEntryPC, 1));
-          OS << "\n";
-          return;
-        }
-        Expected<RelSymbol<ELFT>> RelSym =
-            this->getRelocationTarget(*Reloc, RelocSymTab);
-        if (!RelSym) {
-          this->reportUniqueWarning(RelSym.takeError());
-          OS << "Offset:";
-          OS.PadToColumn(22);
-          OS << to_string(format_hex(FuncEntryPC, 1));
-          OS << "\n";
-          return;
-        }
-        SmallString<32> RelocName;
-        this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
-        OS << RelSym->Name;
-        if (Reloc->Addend) {
-          if (*Reloc->Addend >= 0)
-            OS << " + " << *Reloc->Addend;
-          else
-            OS << " - " << -(*Reloc->Addend);
-        }
-        OS << " (" << RelocName << ")";
-        OS << "\n";
-      };
-
-  auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
-    if (this->Obj.getHeader().e_type != ELF::ET_REL) {
-      PrintFunc(FuncEntryPC, this->getFunctionNames(FuncEntryPC), OS);
-      return;
-    }
-    auto Reloc = llvm::lower_bound(
-        Relocations, FuncEntryPC,
-        [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
-    PrintReloc(FuncEntryPC, Reloc);
-  };
-
-  OS << "\nCall graph section '.llvm.callgraph' contains "
-     << this->FuncCGInfos.size() << " entries:\n";
-
-  int EntryIndex = 0;
-  for (const auto &CGInfo : this->FuncCGInfos) {
-    OS << "\n";
-    OS.PadToColumn(2);
-    OS << "Entry " << EntryIndex++ << ":\n";
-    PrintFunctionInfo(CGInfo.FunctionAddress);
-    OS.PadToColumn(4);
-    OS << "Indirect Target:  " << (CGInfo.IsIndirectTarget ? "Yes" : "No")
-       << "\n";
-    OS.PadToColumn(4);
-    OS << "Type ID:          " << format_hex(CGInfo.FunctionTypeId, 1) << "\n";
-    OS.PadToColumn(4);
-    OS << "Direct Callees (" << CGInfo.DirectCallees.size() << "):\n";
-    for (auto CalleePC : CGInfo.DirectCallees) {
-      OS.PadToColumn(6);
-      PrintFunctionInfo(CalleePC);
-    }
-    OS.PadToColumn(4);
-    OS << "Indirect Callees by Type ID (" << CGInfo.IndirectTypeIDs.size()
-       << "):\n";
-    for (auto TypeId : CGInfo.IndirectTypeIDs) {
-      OS.PadToColumn(6);
-      OS << format_hex(TypeId, 1) << "\n";
-    }
-  }
-}
-
 template <class ELFT>
 void GNUELFDumper<ELFT>::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) {
   OS << "GNUStyle::printBBAddrMaps not implemented\n";
@@ -8412,7 +8317,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
   auto PrintFunc = [](uint64_t FuncEntryPC, ArrayRef<std::string> FuncNames,
                       ScopedPrinter &W) {
     if (!FuncNames.empty())
-      W.printList("Functions", FuncNames);
+      W.printList("Names", FuncNames);
     W.printHex("Address", FuncEntryPC);
   };
 
@@ -8430,7 +8335,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
           W.printHex("Offset", FuncEntryPC);
           return;
         }
-        DictScope DCs(W, "Entry");
+        DictScope DCs(W);
         SmallString<32> RelocName;
         this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
         W.printString("Relocation", RelocName);
@@ -8461,18 +8366,14 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
     {
       ListScope DCs(W, "DirectCallees");
       for (auto CalleePC : CGInfo.DirectCallees) {
-        DictScope D(W, "Entry");
+        DictScope D(W);
         PrintFunctionInfo(CalleePC);
       }
     }
     W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size());
-    {
-      ListScope ICSs(W, "IndirectTypeIDs");
-      for (auto TypeId : CGInfo.IndirectTypeIDs) {
-        DictScope ICS(W, "Entry");
-        W.printHex("TypeId", TypeId);
-      }
-    }
+    auto IndirectTypeIdsList = SmallVector<uint64_t, 4>(
+        CGInfo.IndirectTypeIDs.begin(), CGInfo.IndirectTypeIDs.end());
+    W.printHexList("IndirectTypeIDs", ArrayRef(IndirectTypeIdsList));
   }
 }
 

>From 402c90f0f2db9374850923bc0ea3cdaaa62dc5cb Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 11:43:38 -0800
Subject: [PATCH 43/44] Remove unnecessary header includes.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 5f0d025fd76f8..512eb17829cfb 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -48,10 +48,7 @@
 #include "llvm/Support/ARMBuildAttributes.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
-#include "llvm/Support/DataExtractor.h"
-#include "llvm/Support/Debug.h"
 #include "llvm/Support/Endian.h"
-#include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/FormatVariadic.h"

>From 87d3dd8322abfd5cd332138ec39247d8bbbe154f Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 11:45:42 -0800
Subject: [PATCH 44/44] More header include cleanups.

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 512eb17829cfb..ee61be571b1a6 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -75,7 +75,6 @@
 #include <optional>
 #include <string>
 #include <system_error>
-#include <utility>
 #include <vector>
 
 using namespace llvm;



More information about the llvm-commits mailing list