[llvm] [llvm-readobj] Dump callgraph section info for ELF (PR #157499)
Prabhu Rajasekaran via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 5 11:03:02 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/53] [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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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;
>From 20ea016d3e1166874881068bd3a3ecedc42a7c73 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 11:54:26 -0800
Subject: [PATCH 45/53] Rename test files to .S
---
...llgraph-section.test => call-graph-info-callgraph-section.S} | 0
...r-invalid-flags.test => call-graph-info-err-invalid-flags.S} | 0
...ersion.test => call-graph-info-err-invalid-format-version.S} | 0
...ssing-directs.test => call-graph-info-err-missing-directs.S} | 0
...ing-indirect.test => call-graph-info-err-missing-indirect.S} | 0
...-section.test => call-graph-info-err-no-callgraph-section.S} | 0
llvm/tools/llvm-readobj/ELFDumper.cpp | 2 +-
7 files changed, 1 insertion(+), 1 deletion(-)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-callgraph-section.test => call-graph-info-callgraph-section.S} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-flags.test => call-graph-info-err-invalid-flags.S} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-format-version.test => call-graph-info-err-invalid-format-version.S} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-missing-directs.test => call-graph-info-err-missing-directs.S} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-missing-indirect.test => call-graph-info-err-missing-indirect.S} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-no-callgraph-section.test => call-graph-info-err-no-callgraph-section.S} (100%)
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.S
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S
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.S
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.S
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.S
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.S
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.S
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.S
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.S
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.S
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.S
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.S
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index ee61be571b1a6..b99b86d38e408 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5425,7 +5425,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
"number of indirect target type IDs");
- // Read unique indirect target type IDs and populate FuncCGInfos.
+ // 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)
>From c5823aa7a30b329167a1945744e496fb5de60e70 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 16:21:49 -0800
Subject: [PATCH 46/53] Fix relocation printing handling.
---
llvm/tools/llvm-readobj/ELFDumper.cpp | 36 +++++++++++++--------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index b99b86d38e408..58258da781176 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5390,7 +5390,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
CGInfo.IsIndirectTarget = IsIndirectTarget;
uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+ PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
"indirect type id");
CGInfo.FunctionTypeId = TypeId;
if (IsIndirectTarget && TypeId == 0)
@@ -5402,7 +5402,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
// Read number of direct call sites for this function.
uint64_t NumDirectCallees = Data.getULEB128(&Offset, &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+ PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
"number of direct callsites");
// Read uniqeu direct callees and populate FuncCGInfos.
for (uint64_t I = 0; I < NumDirectCallees; ++I) {
@@ -5410,7 +5410,7 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
typename ELFT::uint Callee =
Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+ PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
"direct callee PC");
CGInfo.DirectCallees.insert((IsETREL ? CalleeOffset : Callee));
}
@@ -5422,14 +5422,14 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
uint64_t NumIndirectTargetTypeIDs =
Data.getULEB128(&Offset, &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+ PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
"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),
+ PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
"indirect type ID");
CGInfo.IndirectTypeIDs.insert(TargetType);
}
@@ -8324,20 +8324,20 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
W.printHex("Offset", FuncEntryPC);
return;
}
- Expected<RelSymbol<ELFT>> RelSym =
+ Expected<RelSymbol<ELFT>> RelSymOrErr =
this->getRelocationTarget(*Reloc, RelocSymTab);
- if (!RelSym) {
- this->reportUniqueWarning(RelSym.takeError());
- W.printHex("Offset", FuncEntryPC);
+ if (!RelSymOrErr) {
+ this->reportUniqueWarning(RelSymOrErr.takeError());
return;
}
- DictScope DCs(W);
- SmallString<32> RelocName;
- this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
- W.printString("Relocation", RelocName);
- W.printString("Symbol", RelSym->Name);
+ if(!RelSymOrErr->Name.empty())
+ W.printString("Name", RelSymOrErr->Name);
if (Reloc->Addend)
W.printHex("Addend", (uintX_t)*Reloc->Addend);
+ SmallString<32> RelocName;
+ this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
+ W.printString("Type", RelocName);
+ W.printHex("Offset", Reloc->Offset);
};
auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
@@ -8345,13 +8345,13 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
PrintFunc(FuncEntryPC, this->getFunctionNames(FuncEntryPC), W);
return;
}
- auto Reloc = llvm::lower_bound(
- Relocations, FuncEntryPC,
- [](const Relocation<ELFT> &R, uint64_t O) { return R.Offset < O; });
+ auto Reloc = llvm::find_if(
+ Relocations,
+ [&](const Relocation<ELFT> &R) { return R.Offset == FuncEntryPC; });
PrintReloc(FuncEntryPC, Reloc);
};
- ListScope CGI(W, "callgraph_info");
+ ListScope CGI(W, "CallGraph");
for (const auto &CGInfo : this->FuncCGInfos) {
DictScope D(W, "Function");
PrintFunctionInfo(CGInfo.FunctionAddress);
>From 8d3a27cf27fa1c22cf1e745b9862ba4b84665573 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 16:25:54 -0800
Subject: [PATCH 47/53] Remove addend, type and offset info from relocations.
---
llvm/tools/llvm-readobj/ELFDumper.cpp | 59 +++++++++++++--------------
1 file changed, 28 insertions(+), 31 deletions(-)
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 58258da781176..03d63dd8cd28f 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5390,7 +5390,8 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
CGInfo.IsIndirectTarget = IsIndirectTarget;
uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
+ PrintMalformedError(CGSectionErr,
+ Twine::utohexstr(CGInfo.FunctionAddress),
"indirect type id");
CGInfo.FunctionTypeId = TypeId;
if (IsIndirectTarget && TypeId == 0)
@@ -5402,7 +5403,8 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
// Read number of direct call sites for this function.
uint64_t NumDirectCallees = Data.getULEB128(&Offset, &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
+ PrintMalformedError(CGSectionErr,
+ Twine::utohexstr(CGInfo.FunctionAddress),
"number of direct callsites");
// Read uniqeu direct callees and populate FuncCGInfos.
for (uint64_t I = 0; I < NumDirectCallees; ++I) {
@@ -5410,7 +5412,8 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
typename ELFT::uint Callee =
Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
+ PrintMalformedError(CGSectionErr,
+ Twine::utohexstr(CGInfo.FunctionAddress),
"direct callee PC");
CGInfo.DirectCallees.insert((IsETREL ? CalleeOffset : Callee));
}
@@ -5422,14 +5425,16 @@ template <class ELFT> bool ELFDumper<ELFT>::processCallGraphSection() {
uint64_t NumIndirectTargetTypeIDs =
Data.getULEB128(&Offset, &CGSectionErr);
if (CGSectionErr)
- PrintMalformedError(CGSectionErr, Twine::utohexstr(CGInfo.FunctionAddress),
+ PrintMalformedError(CGSectionErr,
+ Twine::utohexstr(CGInfo.FunctionAddress),
"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(CGInfo.FunctionAddress),
+ PrintMalformedError(CGSectionErr,
+ Twine::utohexstr(CGInfo.FunctionAddress),
"indirect type ID");
CGInfo.IndirectTypeIDs.insert(TargetType);
}
@@ -8317,38 +8322,30 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
W.printHex("Address", FuncEntryPC);
};
- auto PrintReloc =
- [&](uint64_t FuncEntryPC,
- typename std::vector<Relocation<ELFT>>::iterator &Reloc) {
- if (Reloc == Relocations.end()) {
- W.printHex("Offset", FuncEntryPC);
- return;
- }
- Expected<RelSymbol<ELFT>> RelSymOrErr =
- this->getRelocationTarget(*Reloc, RelocSymTab);
- if (!RelSymOrErr) {
- this->reportUniqueWarning(RelSymOrErr.takeError());
- return;
- }
- if(!RelSymOrErr->Name.empty())
- W.printString("Name", RelSymOrErr->Name);
- if (Reloc->Addend)
- W.printHex("Addend", (uintX_t)*Reloc->Addend);
- SmallString<32> RelocName;
- this->Obj.getRelocationTypeName(Reloc->Type, RelocName);
- W.printString("Type", RelocName);
- W.printHex("Offset", Reloc->Offset);
- };
+ auto PrintReloc = [&](uint64_t RelocOffset) {
+ auto Reloc = llvm::find_if(Relocations, [&](const Relocation<ELFT> &R) {
+ return R.Offset == RelocOffset;
+ });
+ if (Reloc == Relocations.end()) {
+ W.printHex("Offset", RelocOffset);
+ return;
+ }
+ Expected<RelSymbol<ELFT>> RelSymOrErr =
+ this->getRelocationTarget(*Reloc, RelocSymTab);
+ if (!RelSymOrErr) {
+ this->reportUniqueWarning(RelSymOrErr.takeError());
+ return;
+ }
+ if (!RelSymOrErr->Name.empty())
+ W.printString("Name", RelSymOrErr->Name);
+ };
auto PrintFunctionInfo = [&](uint64_t FuncEntryPC) {
if (this->Obj.getHeader().e_type != ELF::ET_REL) {
PrintFunc(FuncEntryPC, this->getFunctionNames(FuncEntryPC), W);
return;
}
- auto Reloc = llvm::find_if(
- Relocations,
- [&](const Relocation<ELFT> &R) { return R.Offset == FuncEntryPC; });
- PrintReloc(FuncEntryPC, Reloc);
+ PrintReloc(FuncEntryPC);
};
ListScope CGI(W, "CallGraph");
>From aaa2cd4fc523537be583bc5a2b2f4d447575a6b4 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 16:30:45 -0800
Subject: [PATCH 48/53] Fix tests.
---
.../llvm-readobj/ELF/call-graph-info-callgraph-section.S | 4 ++--
llvm/test/tools/llvm-readobj/ELF/call-graph-info.test | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S
index b0ba11dd2f0aa..33b6094445580 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S
@@ -12,7 +12,7 @@
# CHECK-NOT: .
# LLVM: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
-# LLVM-NEXT: callgraph_info [
+# LLVM-NEXT: CallGraph [
# LLVM-NEXT: Function {
# LLVM-NEXT: Offset: 0x2
# LLVM-NEXT: Version: 0
@@ -52,7 +52,7 @@
# LLVM-NEXT: ]
# JSON: warning: '[[FILE]]': .llvm.callgraph section has unknown type id for 2 indirect targets.
-# JSON: "callgraph_info": [
+# JSON: "CallGraph": [
# JSON-NEXT: {
# JSON-NEXT: "Function": {
# JSON-NEXT: "Offset": 2,
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 6ffeb3ab6b884..1b289f41b1388 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -44,7 +44,7 @@
## 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: CallGraph [
# LLVM-NEXT: Function {
# LLVM-NEXT: Names: [foo]
# LLVM-NEXT: Address: 0x1790
@@ -108,7 +108,7 @@
# LLVM-NEXT: ]
-# JSON: "callgraph_info": [
+# JSON: "CallGraph": [
# JSON-NEXT: {
# JSON-NEXT: "Function": {
# JSON-NEXT: "Names": [
>From afc13ee0d9cfabe169c6e234803717131138a42f Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 17:00:06 -0800
Subject: [PATCH 49/53] Fix tests.
---
...=> call-graph-info-callgraph-section.test} | 0
...=> call-graph-info-err-invalid-flags.test} | 0
...raph-info-err-invalid-format-version.test} | 0
... call-graph-info-err-missing-directs.test} | 2 +-
...call-graph-info-err-missing-indirect.test} | 2 +-
...-graph-info-err-no-callgraph-section.test} | 0
.../ELF/call-graph-info-relocatable-file.s | 297 ++++++++++++++++++
7 files changed, 299 insertions(+), 2 deletions(-)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-callgraph-section.S => call-graph-info-callgraph-section.test} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-flags.S => call-graph-info-err-invalid-flags.test} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-format-version.S => call-graph-info-err-invalid-format-version.test} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-missing-directs.S => call-graph-info-err-missing-directs.test} (93%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-missing-indirect.S => call-graph-info-err-missing-indirect.test} (93%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-no-callgraph-section.S => call-graph-info-err-no-callgraph-section.test} (100%)
create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.S
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.S
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.S
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
similarity index 93%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.S
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
index b58ede05ce26c..39573b3213c34 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.S
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
@@ -7,7 +7,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 [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 [0x2]': unable to decode LEB128 at offset 0x00000012: malformed uleb128, extends past end
.text
.globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
similarity index 93%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.S
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
index fdee26c47c701..5bdfbe045cbda 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.S
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
@@ -7,7 +7,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 [indirect type ID] for function at [0x0]': unexpected end of data at offset 0x13 while reading [0x13, 0x1b)
+# ERR: error: 'While reading call graph info's [indirect type ID] for function at [0x2]': unexpected end of data at offset 0x13 while reading [0x13, 0x1b)
.text
.globl _Z3foov
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.S b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.S
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
new file mode 100644
index 0000000000000..e0256b95384dd
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
@@ -0,0 +1,297 @@
+## 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 --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
+
+## Assembly output was generated from the following source using this command
+## clang -fexperimental-call-graph-section -S test.cpp -o test.S
+##
+## void foo() {}
+##
+## void bar() {}
+##
+## int baz(char a) { return 0; }
+##
+## int caller() {
+## void (*fp_foo)() = foo;
+## fp_foo();
+##
+## void (*fp_bar)() = bar;
+## fp_bar();
+##
+## char a;
+## int (*fp_baz)(char) = baz;
+## fp_baz(a);
+##
+## foo();
+## bar();
+## baz(a);
+##
+## return 0;
+## }
+##
+
+## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
+# CHECK-NOT: .
+
+# LLVM: CallGraph [
+# LLVM-NEXT: Function {
+# LLVM-NEXT: Name: _Z3foov
+# LLVM-NEXT: Version: 0
+# LLVM-NEXT: IsIndirectTarget: Yes
+# LLVM-NEXT: TypeId: 0xF85C699BB8EF20A2
+# LLVM-NEXT: NumDirectCallees: 0
+# LLVM-NEXT: DirectCallees [
+# LLVM-NEXT: ]
+# LLVM-NEXT: NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT: IndirectTypeIDs: []
+# LLVM-NEXT: }
+# LLVM-NEXT: Function {
+# LLVM-NEXT: Name: _Z3barv
+# LLVM-NEXT: Version: 0
+# LLVM-NEXT: IsIndirectTarget: Yes
+# LLVM-NEXT: TypeId: 0xF85C699BB8EF20A2
+# LLVM-NEXT: NumDirectCallees: 0
+# LLVM-NEXT: DirectCallees [
+# LLVM-NEXT: ]
+# LLVM-NEXT: NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT: IndirectTypeIDs: []
+# LLVM-NEXT: }
+# LLVM-NEXT: Function {
+# LLVM-NEXT: Name: _Z3bazc
+# 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: Function {
+# LLVM-NEXT: Name: main
+# LLVM-NEXT: Version: 0
+# LLVM-NEXT: IsIndirectTarget: Yes
+# LLVM-NEXT: TypeId: 0xA9494DEF81A01DC
+# LLVM-NEXT: NumDirectCallees: 3
+# LLVM-NEXT: DirectCallees [
+# LLVM-NEXT: {
+# LLVM-NEXT: Name: _Z3foov
+# LLVM-NEXT: }
+# LLVM-NEXT: {
+# LLVM-NEXT: Name: _Z3barv
+# LLVM-NEXT: }
+# LLVM-NEXT: {
+# LLVM-NEXT: Name: _Z3bazc
+# LLVM-NEXT: }
+# LLVM-NEXT: ]
+# LLVM-NEXT: NumIndirectTargetTypeIDs: 2
+# LLVM-NEXT: IndirectTypeIDs: [0xF85C699BB8EF20A2, 0x308E4B8159BC8654]
+# LLVM-NEXT: }
+# LLVM-NEXT: ]
+
+# JSON: "CallGraph": [
+# JSON-NEXT: {
+# JSON-NEXT: "Function": {
+# JSON-NEXT: "Name": "_Z3foov",
+# JSON-NEXT: "Version": 0,
+# JSON-NEXT: "IsIndirectTarget": true,
+# JSON-NEXT: "TypeId": 17896295136807035042,
+# 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": "_Z3barv",
+# JSON-NEXT: "Version": 0,
+# JSON-NEXT: "IsIndirectTarget": true,
+# JSON-NEXT: "TypeId": 17896295136807035042,
+# 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": "_Z3bazc",
+# 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: "Version": 0,
+# JSON-NEXT: "IsIndirectTarget": true,
+# JSON-NEXT: "TypeId": 762397922298560988,
+# JSON-NEXT: "NumDirectCallees": 3,
+# JSON-NEXT: "DirectCallees": [
+# JSON-NEXT: {
+# JSON-NEXT: "Name": "_Z3foov"
+# JSON-NEXT: },
+# JSON-NEXT: {
+# JSON-NEXT: "Name": "_Z3barv"
+# JSON-NEXT: },
+# JSON-NEXT: {
+# JSON-NEXT: "Name": "_Z3bazc"
+# JSON-NEXT: }
+# JSON-NEXT: ],
+# JSON-NEXT: "NumIndirectTargetTypeIDs": 2,
+# JSON-NEXT: "IndirectTypeIDs": [
+# JSON-NEXT: 17896295136807035042,
+# JSON-NEXT: 3498816979441845844
+# JSON-NEXT: ]
+# JSON-NEXT: }
+# JSON-NEXT: }
+# JSON-NEXT: ]
+
+ .file "cg.cpp"
+ .text
+ .globl _Z3foov # -- Begin function _Z3foov
+ .p2align 4
+ .type _Z3foov, at function
+_Z3foov: # @_Z3foov
+.Lfunc_begin0:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z3foov, .Lfunc_end0-_Z3foov
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 1
+ .quad _Z3foov
+ .quad -550448936902516574
+ .text
+ # -- End function
+ .globl _Z3barv # -- Begin function _Z3barv
+ .p2align 4
+ .type _Z3barv, at function
+_Z3barv: # @_Z3barv
+.Lfunc_begin1:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z3barv, .Lfunc_end1-_Z3barv
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 1
+ .quad _Z3barv
+ .quad -550448936902516574
+ .text
+ # -- End function
+ .globl _Z3bazc # -- Begin function _Z3bazc
+ .p2align 4
+ .type _Z3bazc, at function
+_Z3bazc: # @_Z3bazc
+.Lfunc_begin2:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movb %dil, %al
+ movb %al, -1(%rbp)
+ xorl %eax, %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z3bazc, .Lfunc_end2-_Z3bazc
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 1
+ .quad _Z3bazc
+ .quad 3498816979441845844
+ .text
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4
+ .type main, at function
+main: # @main
+.Lfunc_begin3:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $48, %rsp
+ movl $0, -4(%rbp)
+ leaq _Z3foov(%rip), %rax
+ movq %rax, -16(%rbp)
+ callq *-16(%rbp)
+ leaq _Z3barv(%rip), %rax
+ movq %rax, -24(%rbp)
+ callq *-24(%rbp)
+ leaq _Z3bazc(%rip), %rax
+ movq %rax, -40(%rbp)
+ movq -40(%rbp), %rax
+ movsbl -25(%rbp), %edi
+ callq *%rax
+ callq _Z3foov
+ callq _Z3barv
+ movsbl -25(%rbp), %edi
+ callq _Z3bazc
+ xorl %eax, %eax
+ addq $48, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size main, .Lfunc_end3-main
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 7
+ .quad main
+ .quad 762397922298560988
+ .byte 3
+ .quad _Z3foov
+ .quad _Z3barv
+ .quad _Z3bazc
+ .byte 2
+ .quad -550448936902516574
+ .quad 3498816979441845844
+ .text
+ # -- End function
+ .ident "Fuchsia clang version 22.0.0git (git at github.com:Prabhuk/llvm-project.git aaa2cd4fc523537be583bc5a2b2f4d447575a6b4)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z3foov
+ .addrsig_sym _Z3barv
+ .addrsig_sym _Z3bazc
>From 6e22254930764e6a74289a2ac71af7c843085f3b Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 17:02:08 -0800
Subject: [PATCH 50/53] Rename files to .s
---
...callgraph-section.test => call-graph-info-callgraph-section.s} | 0
...err-invalid-flags.test => call-graph-info-err-invalid-flags.s} | 0
...-version.test => call-graph-info-err-invalid-format-version.s} | 0
...missing-directs.test => call-graph-info-err-missing-directs.s} | 0
...ssing-indirect.test => call-graph-info-err-missing-indirect.s} | 0
...ph-section.test => call-graph-info-err-no-callgraph-section.s} | 0
6 files changed, 0 insertions(+), 0 deletions(-)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-callgraph-section.test => call-graph-info-callgraph-section.s} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-flags.test => call-graph-info-err-invalid-flags.s} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-invalid-format-version.test => call-graph-info-err-invalid-format-version.s} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-missing-directs.test => call-graph-info-err-missing-directs.s} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-missing-indirect.test => call-graph-info-err-missing-indirect.s} (100%)
rename llvm/test/tools/llvm-readobj/ELF/{call-graph-info-err-no-callgraph-section.test => call-graph-info-err-no-callgraph-section.s} (100%)
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.s
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.s
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.s
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-flags.s
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.s
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.s
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.s
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-directs.s
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.s
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-missing-indirect.s
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.s
similarity index 100%
rename from llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test
rename to llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.s
>From 5b3bff0cec35606b5b1b6564730fea8f8081b1ff Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 17:15:10 -0800
Subject: [PATCH 51/53] Make relocatable test to be built from YAML.
---
.../ELF/call-graph-info-relocatable-file.s | 323 ++++++++++--------
1 file changed, 176 insertions(+), 147 deletions(-)
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
index e0256b95384dd..0596099e57a89 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
@@ -1,14 +1,16 @@
-## Tests --call-graph-info prints information from call graph section.
+## 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 --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
-# REQUIRES: x86-registered-target
+## 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.
-# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %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
-
-## Assembly output was generated from the following source using this command
-## clang -fexperimental-call-graph-section -S test.cpp -o test.S
+## YAML output was generated from the following source:
##
## void foo() {}
##
@@ -133,7 +135,7 @@
# JSON-NEXT: },
# JSON-NEXT: {
# JSON-NEXT: "Function": {
-# JSON-NEXT: "Name": "main",
+# JSON-NEXT: "Name": "caller",
# JSON-NEXT: "Version": 0,
# JSON-NEXT: "IsIndirectTarget": true,
# JSON-NEXT: "TypeId": 762397922298560988,
@@ -158,140 +160,167 @@
# JSON-NEXT: }
# JSON-NEXT: ]
- .file "cg.cpp"
- .text
- .globl _Z3foov # -- Begin function _Z3foov
- .p2align 4
- .type _Z3foov, at function
-_Z3foov: # @_Z3foov
-.Lfunc_begin0:
- .cfi_startproc
-# %bb.0: # %entry
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- popq %rbp
- .cfi_def_cfa %rsp, 8
- retq
-.Lfunc_end0:
- .size _Z3foov, .Lfunc_end0-_Z3foov
- .cfi_endproc
- .section .llvm.callgraph,"o", at llvm_call_graph,.text
- .byte 0
- .byte 1
- .quad _Z3foov
- .quad -550448936902516574
- .text
- # -- End function
- .globl _Z3barv # -- Begin function _Z3barv
- .p2align 4
- .type _Z3barv, at function
-_Z3barv: # @_Z3barv
-.Lfunc_begin1:
- .cfi_startproc
-# %bb.0: # %entry
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- popq %rbp
- .cfi_def_cfa %rsp, 8
- retq
-.Lfunc_end1:
- .size _Z3barv, .Lfunc_end1-_Z3barv
- .cfi_endproc
- .section .llvm.callgraph,"o", at llvm_call_graph,.text
- .byte 0
- .byte 1
- .quad _Z3barv
- .quad -550448936902516574
- .text
- # -- End function
- .globl _Z3bazc # -- Begin function _Z3bazc
- .p2align 4
- .type _Z3bazc, at function
-_Z3bazc: # @_Z3bazc
-.Lfunc_begin2:
- .cfi_startproc
-# %bb.0: # %entry
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- movb %dil, %al
- movb %al, -1(%rbp)
- xorl %eax, %eax
- popq %rbp
- .cfi_def_cfa %rsp, 8
- retq
-.Lfunc_end2:
- .size _Z3bazc, .Lfunc_end2-_Z3bazc
- .cfi_endproc
- .section .llvm.callgraph,"o", at llvm_call_graph,.text
- .byte 0
- .byte 1
- .quad _Z3bazc
- .quad 3498816979441845844
- .text
- # -- End function
- .globl main # -- Begin function main
- .p2align 4
- .type main, at function
-main: # @main
-.Lfunc_begin3:
- .cfi_startproc
-# %bb.0: # %entry
- pushq %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset %rbp, -16
- movq %rsp, %rbp
- .cfi_def_cfa_register %rbp
- subq $48, %rsp
- movl $0, -4(%rbp)
- leaq _Z3foov(%rip), %rax
- movq %rax, -16(%rbp)
- callq *-16(%rbp)
- leaq _Z3barv(%rip), %rax
- movq %rax, -24(%rbp)
- callq *-24(%rbp)
- leaq _Z3bazc(%rip), %rax
- movq %rax, -40(%rbp)
- movq -40(%rbp), %rax
- movsbl -25(%rbp), %edi
- callq *%rax
- callq _Z3foov
- callq _Z3barv
- movsbl -25(%rbp), %edi
- callq _Z3bazc
- xorl %eax, %eax
- addq $48, %rsp
- popq %rbp
- .cfi_def_cfa %rsp, 8
- retq
-.Lfunc_end3:
- .size main, .Lfunc_end3-main
- .cfi_endproc
- .section .llvm.callgraph,"o", at llvm_call_graph,.text
- .byte 0
- .byte 7
- .quad main
- .quad 762397922298560988
- .byte 3
- .quad _Z3foov
- .quad _Z3barv
- .quad _Z3bazc
- .byte 2
- .quad -550448936902516574
- .quad 3498816979441845844
- .text
- # -- End function
- .ident "Fuchsia clang version 22.0.0git (git at github.com:Prabhuk/llvm-project.git aaa2cd4fc523537be583bc5a2b2f4d447575a6b4)"
- .section ".note.GNU-stack","", at progbits
- .addrsig
- .addrsig_sym _Z3foov
- .addrsig_sym _Z3barv
- .addrsig_sym _Z3bazc
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+ SectionHeaderStringTable: .strtab
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x10
+ Content: 554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC20488D0500000000488945F8FF55F8488D0500000000488945F0FF55F0488D0500000000488945E0488B45E00FBE7DEFFFD0E800000000E8000000000FBE7DEFE80000000031C04883C4205DC3
+ - Name: .llvm.callgraph
+ Type: SHT_LLVM_CALL_GRAPH
+ Flags: [ SHF_LINK_ORDER ]
+ Link: .text
+ AddressAlign: 0x1
+ Content: 00010000000000000000A220EFB89B695CF800010000000000000000A220EFB89B695CF8000100000000000000005486BC59814B8E3000070000000000000000DC011AF8DE94940A0300000000000000000000000000000000000000000000000002A220EFB89B695CF85486BC59814B8E30
+ - Name: .comment
+ Type: SHT_PROGBITS
+ Flags: [ SHF_MERGE, SHF_STRINGS ]
+ AddressAlign: 0x1
+ EntSize: 0x1
+ Content: 004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420366532323235343933303736346536613734323839613261633731616637633834333038356633622900
+ - Name: .note.GNU-stack
+ Type: SHT_PROGBITS
+ AddressAlign: 0x1
+ - Name: .eh_frame
+ Type: SHT_X86_64_UNWIND
+ Flags: [ SHF_ALLOC ]
+ AddressAlign: 0x8
+ Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000000000000600000000410E108602430D06410C07080000001C0000003C000000000000000600000000410E108602430D06410C07080000001C0000005C000000000000000E00000000410E108602430D06490C07080000001C0000007C000000000000005400000000410E108602430D06024F0C07080000
+ - Name: .rela.text
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .text
+ Relocations:
+ - Offset: 0x3B
+ Symbol: _Z3foov
+ Type: R_X86_64_PC32
+ Addend: -4
+ - Offset: 0x49
+ Symbol: _Z3barv
+ Type: R_X86_64_PC32
+ Addend: -4
+ - Offset: 0x57
+ Symbol: _Z3bazc
+ Type: R_X86_64_PC32
+ Addend: -4
+ - Offset: 0x6A
+ Symbol: _Z3foov
+ Type: R_X86_64_PLT32
+ Addend: -4
+ - Offset: 0x6F
+ Symbol: _Z3barv
+ Type: R_X86_64_PLT32
+ Addend: -4
+ - Offset: 0x78
+ Symbol: _Z3bazc
+ Type: R_X86_64_PLT32
+ Addend: -4
+ - Name: .rela.llvm.callgraph
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .llvm.callgraph
+ Relocations:
+ - Offset: 0x2
+ Symbol: _Z3foov
+ Type: R_X86_64_64
+ - Offset: 0x14
+ Symbol: _Z3barv
+ Type: R_X86_64_64
+ - Offset: 0x26
+ Symbol: _Z3bazc
+ Type: R_X86_64_64
+ - Offset: 0x38
+ Symbol: _Z6callerv
+ Type: R_X86_64_64
+ - Offset: 0x49
+ Symbol: _Z3foov
+ Type: R_X86_64_64
+ - Offset: 0x51
+ Symbol: _Z3barv
+ Type: R_X86_64_64
+ - Offset: 0x59
+ Symbol: _Z3bazc
+ Type: R_X86_64_64
+ - Name: .rela.eh_frame
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .eh_frame
+ Relocations:
+ - Offset: 0x20
+ Symbol: .text
+ Type: R_X86_64_PC32
+ - Offset: 0x40
+ Symbol: .text
+ Type: R_X86_64_PC32
+ Addend: 16
+ - Offset: 0x60
+ Symbol: .text
+ Type: R_X86_64_PC32
+ Addend: 32
+ - Offset: 0x80
+ Symbol: .text
+ Type: R_X86_64_PC32
+ Addend: 48
+ - Name: .llvm_addrsig
+ Type: SHT_LLVM_ADDRSIG
+ Flags: [ SHF_EXCLUDE ]
+ Link: .symtab
+ AddressAlign: 0x1
+ Symbols: [ _Z3foov, _Z3barv, _Z3bazc ]
+ - Type: SectionHeaderTable
+ Sections:
+ - Name: .strtab
+ - Name: .text
+ - Name: .rela.text
+ - Name: .llvm.callgraph
+ - Name: .rela.llvm.callgraph
+ - Name: .comment
+ - Name: .note.GNU-stack
+ - Name: .eh_frame
+ - Name: .rela.eh_frame
+ - Name: .llvm_addrsig
+ - Name: .symtab
+Symbols:
+ - Name: cg.cpp
+ Type: STT_FILE
+ Index: SHN_ABS
+ - Name: .text
+ Type: STT_SECTION
+ Section: .text
+ - Name: _Z3foov
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Size: 0x6
+ - Name: _Z3barv
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Value: 0x10
+ Size: 0x6
+ - Name: _Z3bazc
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Value: 0x20
+ Size: 0xE
+ - Name: _Z6callerv
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Value: 0x30
+ Size: 0x54
+...
>From c4ea41da2c63cec808f40f7feaf997f6c1cd7ab0 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 17:22:03 -0800
Subject: [PATCH 52/53] Relocatable file tests fixed.
---
.../ELF/call-graph-info-relocatable-file.s | 324 ++++++++---------
.../ELF/call-graph-info-relocatable-obj.test | 326 ++++++++++++++++++
2 files changed, 473 insertions(+), 177 deletions(-)
create mode 100644 llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
index 0596099e57a89..6d6cd169c60e6 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-file.s
@@ -1,16 +1,14 @@
-## 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 --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
+## Tests --call-graph-info prints information from call graph section.
-## 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.
+# REQUIRES: x86-registered-target
-## YAML output was generated from the following source:
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %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
+
+## Assembly output was generated from the following source using this command
+## clang -fexperimental-call-graph-section -S test.cpp -o test.S
##
## void foo() {}
##
@@ -75,7 +73,7 @@
# LLVM-NEXT: IndirectTypeIDs: []
# LLVM-NEXT: }
# LLVM-NEXT: Function {
-# LLVM-NEXT: Name: main
+# LLVM-NEXT: Name: _Z6callerv
# LLVM-NEXT: Version: 0
# LLVM-NEXT: IsIndirectTarget: Yes
# LLVM-NEXT: TypeId: 0xA9494DEF81A01DC
@@ -135,7 +133,7 @@
# JSON-NEXT: },
# JSON-NEXT: {
# JSON-NEXT: "Function": {
-# JSON-NEXT: "Name": "caller",
+# JSON-NEXT: "Name": "_Z6callerv",
# JSON-NEXT: "Version": 0,
# JSON-NEXT: "IsIndirectTarget": true,
# JSON-NEXT: "TypeId": 762397922298560988,
@@ -160,167 +158,139 @@
# JSON-NEXT: }
# JSON-NEXT: ]
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_REL
- Machine: EM_X86_64
- SectionHeaderStringTable: .strtab
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
- AddressAlign: 0x10
- Content: 554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC20488D0500000000488945F8FF55F8488D0500000000488945F0FF55F0488D0500000000488945E0488B45E00FBE7DEFFFD0E800000000E8000000000FBE7DEFE80000000031C04883C4205DC3
- - Name: .llvm.callgraph
- Type: SHT_LLVM_CALL_GRAPH
- Flags: [ SHF_LINK_ORDER ]
- Link: .text
- AddressAlign: 0x1
- Content: 00010000000000000000A220EFB89B695CF800010000000000000000A220EFB89B695CF8000100000000000000005486BC59814B8E3000070000000000000000DC011AF8DE94940A0300000000000000000000000000000000000000000000000002A220EFB89B695CF85486BC59814B8E30
- - Name: .comment
- Type: SHT_PROGBITS
- Flags: [ SHF_MERGE, SHF_STRINGS ]
- AddressAlign: 0x1
- EntSize: 0x1
- Content: 004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420366532323235343933303736346536613734323839613261633731616637633834333038356633622900
- - Name: .note.GNU-stack
- Type: SHT_PROGBITS
- AddressAlign: 0x1
- - Name: .eh_frame
- Type: SHT_X86_64_UNWIND
- Flags: [ SHF_ALLOC ]
- AddressAlign: 0x8
- Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000000000000600000000410E108602430D06410C07080000001C0000003C000000000000000600000000410E108602430D06410C07080000001C0000005C000000000000000E00000000410E108602430D06490C07080000001C0000007C000000000000005400000000410E108602430D06024F0C07080000
- - Name: .rela.text
- Type: SHT_RELA
- Flags: [ SHF_INFO_LINK ]
- Link: .symtab
- AddressAlign: 0x8
- Info: .text
- Relocations:
- - Offset: 0x3B
- Symbol: _Z3foov
- Type: R_X86_64_PC32
- Addend: -4
- - Offset: 0x49
- Symbol: _Z3barv
- Type: R_X86_64_PC32
- Addend: -4
- - Offset: 0x57
- Symbol: _Z3bazc
- Type: R_X86_64_PC32
- Addend: -4
- - Offset: 0x6A
- Symbol: _Z3foov
- Type: R_X86_64_PLT32
- Addend: -4
- - Offset: 0x6F
- Symbol: _Z3barv
- Type: R_X86_64_PLT32
- Addend: -4
- - Offset: 0x78
- Symbol: _Z3bazc
- Type: R_X86_64_PLT32
- Addend: -4
- - Name: .rela.llvm.callgraph
- Type: SHT_RELA
- Flags: [ SHF_INFO_LINK ]
- Link: .symtab
- AddressAlign: 0x8
- Info: .llvm.callgraph
- Relocations:
- - Offset: 0x2
- Symbol: _Z3foov
- Type: R_X86_64_64
- - Offset: 0x14
- Symbol: _Z3barv
- Type: R_X86_64_64
- - Offset: 0x26
- Symbol: _Z3bazc
- Type: R_X86_64_64
- - Offset: 0x38
- Symbol: _Z6callerv
- Type: R_X86_64_64
- - Offset: 0x49
- Symbol: _Z3foov
- Type: R_X86_64_64
- - Offset: 0x51
- Symbol: _Z3barv
- Type: R_X86_64_64
- - Offset: 0x59
- Symbol: _Z3bazc
- Type: R_X86_64_64
- - Name: .rela.eh_frame
- Type: SHT_RELA
- Flags: [ SHF_INFO_LINK ]
- Link: .symtab
- AddressAlign: 0x8
- Info: .eh_frame
- Relocations:
- - Offset: 0x20
- Symbol: .text
- Type: R_X86_64_PC32
- - Offset: 0x40
- Symbol: .text
- Type: R_X86_64_PC32
- Addend: 16
- - Offset: 0x60
- Symbol: .text
- Type: R_X86_64_PC32
- Addend: 32
- - Offset: 0x80
- Symbol: .text
- Type: R_X86_64_PC32
- Addend: 48
- - Name: .llvm_addrsig
- Type: SHT_LLVM_ADDRSIG
- Flags: [ SHF_EXCLUDE ]
- Link: .symtab
- AddressAlign: 0x1
- Symbols: [ _Z3foov, _Z3barv, _Z3bazc ]
- - Type: SectionHeaderTable
- Sections:
- - Name: .strtab
- - Name: .text
- - Name: .rela.text
- - Name: .llvm.callgraph
- - Name: .rela.llvm.callgraph
- - Name: .comment
- - Name: .note.GNU-stack
- - Name: .eh_frame
- - Name: .rela.eh_frame
- - Name: .llvm_addrsig
- - Name: .symtab
-Symbols:
- - Name: cg.cpp
- Type: STT_FILE
- Index: SHN_ABS
- - Name: .text
- Type: STT_SECTION
- Section: .text
- - Name: _Z3foov
- Type: STT_FUNC
- Section: .text
- Binding: STB_GLOBAL
- Size: 0x6
- - Name: _Z3barv
- Type: STT_FUNC
- Section: .text
- Binding: STB_GLOBAL
- Value: 0x10
- Size: 0x6
- - Name: _Z3bazc
- Type: STT_FUNC
- Section: .text
- Binding: STB_GLOBAL
- Value: 0x20
- Size: 0xE
- - Name: _Z6callerv
- Type: STT_FUNC
- Section: .text
- Binding: STB_GLOBAL
- Value: 0x30
- Size: 0x54
-...
+ .file "cg.cpp"
+ .text
+ .globl _Z3foov # -- Begin function _Z3foov
+ .p2align 4
+ .type _Z3foov, at function
+_Z3foov: # @_Z3foov
+.Lfunc_begin0:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end0:
+ .size _Z3foov, .Lfunc_end0-_Z3foov
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 1
+ .quad _Z3foov
+ .quad -550448936902516574
+ .text
+ # -- End function
+ .globl _Z3barv # -- Begin function _Z3barv
+ .p2align 4
+ .type _Z3barv, at function
+_Z3barv: # @_Z3barv
+.Lfunc_begin1:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end1:
+ .size _Z3barv, .Lfunc_end1-_Z3barv
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 1
+ .quad _Z3barv
+ .quad -550448936902516574
+ .text
+ # -- End function
+ .globl _Z3bazc # -- Begin function _Z3bazc
+ .p2align 4
+ .type _Z3bazc, at function
+_Z3bazc: # @_Z3bazc
+.Lfunc_begin2:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movb %dil, %al
+ movb %al, -1(%rbp)
+ xorl %eax, %eax
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end2:
+ .size _Z3bazc, .Lfunc_end2-_Z3bazc
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 1
+ .quad _Z3bazc
+ .quad 3498816979441845844
+ .text
+ # -- End function
+ .globl _Z6callerv # -- Begin function _Z6callerv
+ .p2align 4
+ .type _Z6callerv, at function
+_Z6callerv: # @_Z6callerv
+.Lfunc_begin3:
+ .cfi_startproc
+# %bb.0: # %entry
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ leaq _Z3foov(%rip), %rax
+ movq %rax, -8(%rbp)
+ callq *-8(%rbp)
+ leaq _Z3barv(%rip), %rax
+ movq %rax, -16(%rbp)
+ callq *-16(%rbp)
+ leaq _Z3bazc(%rip), %rax
+ movq %rax, -32(%rbp)
+ movq -32(%rbp), %rax
+ movsbl -17(%rbp), %edi
+ callq *%rax
+ callq _Z3foov
+ callq _Z3barv
+ movsbl -17(%rbp), %edi
+ callq _Z3bazc
+ xorl %eax, %eax
+ addq $32, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Lfunc_end3:
+ .size _Z6callerv, .Lfunc_end3-_Z6callerv
+ .cfi_endproc
+ .section .llvm.callgraph,"o", at llvm_call_graph,.text
+ .byte 0
+ .byte 7
+ .quad _Z6callerv
+ .quad 762397922298560988
+ .byte 3
+ .quad _Z3foov
+ .quad _Z3barv
+ .quad _Z3bazc
+ .byte 2
+ .quad -550448936902516574
+ .quad 3498816979441845844
+ .text
+ # -- End function
+ .ident "Fuchsia clang version 22.0.0git (git at github.com:Prabhuk/llvm-project.git 5b3bff0cec35606b5b1b6564730fea8f8081b1ff)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z3foov
+ .addrsig_sym _Z3barv
+ .addrsig_sym _Z3bazc
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test
new file mode 100644
index 0000000000000..fa010a4d2967e
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test
@@ -0,0 +1,326 @@
+## 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 --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 output was generated from the following source:
+##
+## void foo() {}
+##
+## void bar() {}
+##
+## int baz(char a) { return 0; }
+##
+## int caller() {
+## void (*fp_foo)() = foo;
+## fp_foo();
+##
+## void (*fp_bar)() = bar;
+## fp_bar();
+##
+## char a;
+## int (*fp_baz)(char) = baz;
+## fp_baz(a);
+##
+## foo();
+## bar();
+## baz(a);
+##
+## return 0;
+## }
+##
+
+## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
+# CHECK-NOT: .
+
+# LLVM: CallGraph [
+# LLVM-NEXT: Function {
+# LLVM-NEXT: Name: _Z3foov
+# LLVM-NEXT: Version: 0
+# LLVM-NEXT: IsIndirectTarget: Yes
+# LLVM-NEXT: TypeId: 0xF85C699BB8EF20A2
+# LLVM-NEXT: NumDirectCallees: 0
+# LLVM-NEXT: DirectCallees [
+# LLVM-NEXT: ]
+# LLVM-NEXT: NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT: IndirectTypeIDs: []
+# LLVM-NEXT: }
+# LLVM-NEXT: Function {
+# LLVM-NEXT: Name: _Z3barv
+# LLVM-NEXT: Version: 0
+# LLVM-NEXT: IsIndirectTarget: Yes
+# LLVM-NEXT: TypeId: 0xF85C699BB8EF20A2
+# LLVM-NEXT: NumDirectCallees: 0
+# LLVM-NEXT: DirectCallees [
+# LLVM-NEXT: ]
+# LLVM-NEXT: NumIndirectTargetTypeIDs: 0
+# LLVM-NEXT: IndirectTypeIDs: []
+# LLVM-NEXT: }
+# LLVM-NEXT: Function {
+# LLVM-NEXT: Name: _Z3bazc
+# 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: Function {
+# LLVM-NEXT: Name: _Z6callerv
+# LLVM-NEXT: Version: 0
+# LLVM-NEXT: IsIndirectTarget: Yes
+# LLVM-NEXT: TypeId: 0xA9494DEF81A01DC
+# LLVM-NEXT: NumDirectCallees: 3
+# LLVM-NEXT: DirectCallees [
+# LLVM-NEXT: {
+# LLVM-NEXT: Name: _Z3foov
+# LLVM-NEXT: }
+# LLVM-NEXT: {
+# LLVM-NEXT: Name: _Z3barv
+# LLVM-NEXT: }
+# LLVM-NEXT: {
+# LLVM-NEXT: Name: _Z3bazc
+# LLVM-NEXT: }
+# LLVM-NEXT: ]
+# LLVM-NEXT: NumIndirectTargetTypeIDs: 2
+# LLVM-NEXT: IndirectTypeIDs: [0xF85C699BB8EF20A2, 0x308E4B8159BC8654]
+# LLVM-NEXT: }
+# LLVM-NEXT: ]
+
+# JSON: "CallGraph": [
+# JSON-NEXT: {
+# JSON-NEXT: "Function": {
+# JSON-NEXT: "Name": "_Z3foov",
+# JSON-NEXT: "Version": 0,
+# JSON-NEXT: "IsIndirectTarget": true,
+# JSON-NEXT: "TypeId": 17896295136807035042,
+# 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": "_Z3barv",
+# JSON-NEXT: "Version": 0,
+# JSON-NEXT: "IsIndirectTarget": true,
+# JSON-NEXT: "TypeId": 17896295136807035042,
+# 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": "_Z3bazc",
+# 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": "_Z6callerv",
+# JSON-NEXT: "Version": 0,
+# JSON-NEXT: "IsIndirectTarget": true,
+# JSON-NEXT: "TypeId": 762397922298560988,
+# JSON-NEXT: "NumDirectCallees": 3,
+# JSON-NEXT: "DirectCallees": [
+# JSON-NEXT: {
+# JSON-NEXT: "Name": "_Z3foov"
+# JSON-NEXT: },
+# JSON-NEXT: {
+# JSON-NEXT: "Name": "_Z3barv"
+# JSON-NEXT: },
+# JSON-NEXT: {
+# JSON-NEXT: "Name": "_Z3bazc"
+# JSON-NEXT: }
+# JSON-NEXT: ],
+# JSON-NEXT: "NumIndirectTargetTypeIDs": 2,
+# JSON-NEXT: "IndirectTypeIDs": [
+# JSON-NEXT: 17896295136807035042,
+# JSON-NEXT: 3498816979441845844
+# JSON-NEXT: ]
+# JSON-NEXT: }
+# JSON-NEXT: }
+# JSON-NEXT: ]
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+ SectionHeaderStringTable: .strtab
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x10
+ Content: 554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC20488D0500000000488945F8FF55F8488D0500000000488945F0FF55F0488D0500000000488945E0488B45E00FBE7DEFFFD0E800000000E8000000000FBE7DEFE80000000031C04883C4205DC3
+ - Name: .llvm.callgraph
+ Type: SHT_LLVM_CALL_GRAPH
+ Flags: [ SHF_LINK_ORDER ]
+ Link: .text
+ AddressAlign: 0x1
+ Content: 00010000000000000000A220EFB89B695CF800010000000000000000A220EFB89B695CF8000100000000000000005486BC59814B8E3000070000000000000000DC011AF8DE94940A0300000000000000000000000000000000000000000000000002A220EFB89B695CF85486BC59814B8E30
+ - Name: .comment
+ Type: SHT_PROGBITS
+ Flags: [ SHF_MERGE, SHF_STRINGS ]
+ AddressAlign: 0x1
+ EntSize: 0x1
+ Content: 004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420366532323235343933303736346536613734323839613261633731616637633834333038356633622900
+ - Name: .note.GNU-stack
+ Type: SHT_PROGBITS
+ AddressAlign: 0x1
+ - Name: .eh_frame
+ Type: SHT_X86_64_UNWIND
+ Flags: [ SHF_ALLOC ]
+ AddressAlign: 0x8
+ Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000000000000600000000410E108602430D06410C07080000001C0000003C000000000000000600000000410E108602430D06410C07080000001C0000005C000000000000000E00000000410E108602430D06490C07080000001C0000007C000000000000005400000000410E108602430D06024F0C07080000
+ - Name: .rela.text
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .text
+ Relocations:
+ - Offset: 0x3B
+ Symbol: _Z3foov
+ Type: R_X86_64_PC32
+ Addend: -4
+ - Offset: 0x49
+ Symbol: _Z3barv
+ Type: R_X86_64_PC32
+ Addend: -4
+ - Offset: 0x57
+ Symbol: _Z3bazc
+ Type: R_X86_64_PC32
+ Addend: -4
+ - Offset: 0x6A
+ Symbol: _Z3foov
+ Type: R_X86_64_PLT32
+ Addend: -4
+ - Offset: 0x6F
+ Symbol: _Z3barv
+ Type: R_X86_64_PLT32
+ Addend: -4
+ - Offset: 0x78
+ Symbol: _Z3bazc
+ Type: R_X86_64_PLT32
+ Addend: -4
+ - Name: .rela.llvm.callgraph
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .llvm.callgraph
+ Relocations:
+ - Offset: 0x2
+ Symbol: _Z3foov
+ Type: R_X86_64_64
+ - Offset: 0x14
+ Symbol: _Z3barv
+ Type: R_X86_64_64
+ - Offset: 0x26
+ Symbol: _Z3bazc
+ Type: R_X86_64_64
+ - Offset: 0x38
+ Symbol: _Z6callerv
+ Type: R_X86_64_64
+ - Offset: 0x49
+ Symbol: _Z3foov
+ Type: R_X86_64_64
+ - Offset: 0x51
+ Symbol: _Z3barv
+ Type: R_X86_64_64
+ - Offset: 0x59
+ Symbol: _Z3bazc
+ Type: R_X86_64_64
+ - Name: .rela.eh_frame
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ AddressAlign: 0x8
+ Info: .eh_frame
+ Relocations:
+ - Offset: 0x20
+ Symbol: .text
+ Type: R_X86_64_PC32
+ - Offset: 0x40
+ Symbol: .text
+ Type: R_X86_64_PC32
+ Addend: 16
+ - Offset: 0x60
+ Symbol: .text
+ Type: R_X86_64_PC32
+ Addend: 32
+ - Offset: 0x80
+ Symbol: .text
+ Type: R_X86_64_PC32
+ Addend: 48
+ - Name: .llvm_addrsig
+ Type: SHT_LLVM_ADDRSIG
+ Flags: [ SHF_EXCLUDE ]
+ Link: .symtab
+ AddressAlign: 0x1
+ Symbols: [ _Z3foov, _Z3barv, _Z3bazc ]
+ - Type: SectionHeaderTable
+ Sections:
+ - Name: .strtab
+ - Name: .text
+ - Name: .rela.text
+ - Name: .llvm.callgraph
+ - Name: .rela.llvm.callgraph
+ - Name: .comment
+ - Name: .note.GNU-stack
+ - Name: .eh_frame
+ - Name: .rela.eh_frame
+ - Name: .llvm_addrsig
+ - Name: .symtab
+Symbols:
+ - Name: cg.cpp
+ Type: STT_FILE
+ Index: SHN_ABS
+ - Name: .text
+ Type: STT_SECTION
+ Section: .text
+ - Name: _Z3foov
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Size: 0x6
+ - Name: _Z3barv
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Value: 0x10
+ Size: 0x6
+ - Name: _Z3bazc
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Value: 0x20
+ Size: 0xE
+ - Name: _Z6callerv
+ Type: STT_FUNC
+ Section: .text
+ Binding: STB_GLOBAL
+ Value: 0x30
+ Size: 0x54
+...
>From 53ffcaa903e5720066d9f4b36fc3a5d7f68ef972 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 4 Nov 2025 18:03:54 -0800
Subject: [PATCH 53/53] Nits. Alignment within test files.
---
.../tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test | 2 +-
llvm/test/tools/llvm-readobj/ELF/call-graph-info.test | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test
index fa010a4d2967e..f559d9881f509 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-relocatable-obj.test
@@ -40,7 +40,7 @@
## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
# CHECK-NOT: .
-# LLVM: CallGraph [
+# LLVM: CallGraph [
# LLVM-NEXT: Function {
# LLVM-NEXT: Name: _Z3foov
# LLVM-NEXT: Version: 0
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 1b289f41b1388..5a3bc67239696 100644
--- a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test
@@ -44,7 +44,7 @@
## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
# CHECK-NOT: .
-# LLVM: CallGraph [
+# LLVM: CallGraph [
# LLVM-NEXT: Function {
# LLVM-NEXT: Names: [foo]
# LLVM-NEXT: Address: 0x1790
More information about the llvm-commits
mailing list