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

Prabhu Rajasekaran via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 12 17:04:53 PST 2025


================
@@ -5263,6 +5297,178 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
   OS << "GNUStyle::printCGProfile not implemented\n";
 }
 
+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() {
+  auto IsMatch = [](const Elf_Shdr &Sec) -> bool {
+    return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH;
+  };
+  Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> MapOrErr =
+      Obj.getSectionAndRelocations(IsMatch);
+  if (!MapOrErr || MapOrErr->empty()) {
+    reportWarning(createError("no SHT_LLVM_CALL_GRAPH section found"),
+                  FileName);
+    return false;
+  }
+  const Elf_Shdr *CGSection = MapOrErr->begin()->first;
+  Expected<ArrayRef<uint8_t>> SectionBytesOrErr =
+      Obj.getSectionContents(*CGSection);
+  if (!SectionBytesOrErr) {
+    reportWarning(createError("unable to read the .llvm.callgraph section"),
+                  FileName);
+    return false;
+  }
+
+  DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(),
+                     ObjF.getBytesInAddress());
+  DataExtractor::Cursor C(0);
+  uint64_t UnknownCount = 0;
+  while (C && C.tell() < CGSection->sh_size) {
+    uint8_t FormatVersionNumber = Data.getU8(C);
+    if (!C) {
+      reportWarning(createError("failed while reading FormatVersionNumber"),
+                    FileName);
+      return false;
+    }
+    if (FormatVersionNumber != 0) {
+      reportWarning(createError("unknown format version value [" +
+                                std::to_string(FormatVersionNumber) +
+                                "] in .llvm.callgraph section"),
+                    FileName);
+      return false;
+    }
+
+    uint8_t FlagsVal = Data.getU8(C);
+    if (!C) {
+      reportWarning(createError("failed while reading call graph info's Flags"),
+                    FileName);
+      return false;
+    }
+    callgraph::Flags CGFlags = static_cast<callgraph::Flags>(FlagsVal);
+    constexpr callgraph::Flags ValidFlags = callgraph::IsIndirectTarget |
+                                            callgraph::HasDirectCallees |
+                                            callgraph::HasIndirectCallees;
+    constexpr uint8_t ValidMask = static_cast<uint8_t>(ValidFlags);
+    if ((FlagsVal & ~ValidMask) != 0) {
+      reportWarning(createError("unexpected value. Expected [0-" +
+                                std::to_string(ValidMask) + "] but found [" +
+                                std::to_string(FlagsVal) + "]"),
+                    FileName);
+      return false;
+    }
+
+    uint64_t FuncAddrOffset = C.tell();
+    typename ELFT::uint FuncAddr =
+        Data.getUnsigned(C, sizeof(typename ELFT::uint));
+    if (!C) {
+      reportWarning(
+          createError("failed while reading call graph info function entry PC"),
+          FileName);
+      return false;
+    }
+
+    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 =
+        (CGFlags & callgraph::IsIndirectTarget) != callgraph::None;
+    CGInfo.IsIndirectTarget = IsIndirectTarget;
+    uint64_t TypeId = Data.getU64(C);
+    if (!C) {
+      reportWarning(C.takeError(), FileName);
+      return false;
+    }
+    CGInfo.FunctionTypeId = TypeId;
+    if (IsIndirectTarget && TypeId == 0)
+      UnknownCount++;
+
+    bool HasDirectCallees =
+        (CGFlags & callgraph::HasDirectCallees) != callgraph::None;
+    if (HasDirectCallees) {
+      // Read number of direct call sites for this function.
+      uint64_t NumDirectCallees = Data.getULEB128(C);
+      if (!C) {
+        reportWarning(C.takeError(), FileName);
+        return false;
+      }
+      // Read unique direct callees and populate FuncCGInfos.
+      for (uint64_t I = 0; I < NumDirectCallees; ++I) {
+        uint64_t CalleeOffset = C.tell();
+        typename ELFT::uint Callee =
+            Data.getUnsigned(C, sizeof(typename ELFT::uint));
+        if (!C) {
+          reportWarning(C.takeError(), FileName);
+          return false;
+        }
+        CGInfo.DirectCallees.insert((IsETREL ? CalleeOffset : Callee));
+      }
+    }
+
+    bool HasIndirectTypeIds =
+        (CGFlags & callgraph::HasIndirectCallees) != callgraph::None;
+    if (HasIndirectTypeIds) {
+      uint64_t NumIndirectTargetTypeIDs = Data.getULEB128(C);
+      if (!C) {
+        reportWarning(C.takeError(), FileName);
+        return false;
+      }
+      // Read unique indirect target type IDs and populate FuncCGInfos.
+      for (uint64_t I = 0; I < NumIndirectTargetTypeIDs; ++I) {
+        uint64_t TargetType = Data.getU64(C);
+        if (!C) {
+          reportWarning(C.takeError(), FileName);
+          return false;
+        }
+        CGInfo.IndirectTypeIDs.insert(TargetType);
+      }
+    }
+    FuncCGInfos.push_back(CGInfo);
+  }
+
+  if (UnknownCount)
+    reportUniqueWarning(".llvm.callgraph section has unknown type id for " +
----------------
Prabhuk wrote:

Using SHT_LLVM_CALLGRAPH across the board now. PTAL.

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


More information about the llvm-commits mailing list