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

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


================
@@ -5263,6 +5302,274 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
   OS << "GNUStyle::printCGProfile not implemented\n";
 }
 
+template <class ELFT>
+static std::optional<object::SectionRef>
+getCallGraphSection(const object::ELFObjectFile<ELFT> &ObjF) {
+  // Get the .llvm.callgraph section.
+  StringRef CallGraphSectionName(".llvm.callgraph");
+  for (auto Sec : ObjF.sections()) {
+    if (Expected<StringRef> NameOrErr = Sec.getName()) {
+      StringRef Name = *NameOrErr;
+      if (Name == CallGraphSectionName)
+        return Sec;
+    } else
+      consumeError(NameOrErr.takeError());
+  }
+  return std::nullopt;
+}
+
+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)
+    reportError(createError("No .llvm.callgraph section found."),
+                "Missing section");
+
+  Expected<ArrayRef<uint8_t>> SectionBytesOrErr =
+      Obj.getSectionContents(*CGSection);
+  if (!SectionBytesOrErr) {
+    reportError(SectionBytesOrErr.takeError(),
+                "Unable to read the .llvm.callgraph section");
+  }
+
+  auto PrintMalformedError = [&](Error &E, Twine FuncPC, StringRef Component) {
+    reportError(std::move(E),
+                Twine("While reading call graph info's [" + Component +
+                      "] for function at [0x" + FuncPC + "]")
+                    .str());
+  };
+
+  DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(),
+                     ObjF.getBytesInAddress());
+
+  uint64_t UnknownCount = 0;
+  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),
+                  "While reading call graph info FormatVersionNumber");
+    }
+    if (FormatVersionNumber != 0) {
+      reportError(createError("Unknown format version value [" +
+                              std::to_string(FormatVersionNumber) +
+                              "] in .llvm.callgraph section."),
+                  "Unknown value");
+    }
+
+    uint8_t FlagsVal = Data.getU8(&Offset, &CGSectionErr);
+    if (CGSectionErr)
+      reportError(std::move(CGSectionErr),
+                  "While reading call graph info's Flags");
+    callgraph::Flags CGFlags = static_cast<callgraph::Flags>(FlagsVal);
+    if (FlagsVal > 7) {
+      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 =
+        Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr);
+    if (CGSectionErr)
+      reportError(std::move(CGSectionErr),
+                  "While reading call graph info function entry PC");
+
+    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(&Offset, &CGSectionErr);
+    if (CGSectionErr)
+      PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr),
+                          "indirect type id");
+    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(&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) {
+        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));
+      }
+    }
+
+    bool HasIndirectTypeIds =
+        (CGFlags & callgraph::HasIndirectCallees) != callgraph::None;
+    if (HasIndirectTypeIds) {
+      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 (UnknownCount)
+    reportUniqueWarning(".llvm.callgraph section has unknown type id for " +
+                        std::to_string(UnknownCount) + " indirect targets.");
+  return true;
+}
+
+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 *CGRelSection = nullptr;
+  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) {
+    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() {
----------------
Prabhuk wrote:

Dropped the GNU dumper implementation as suggested. 

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


More information about the llvm-commits mailing list