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

Paul Kirth via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 21 10:45:32 PST 2025


================
@@ -8094,6 +8305,76 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
   }
 }
 
+template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
+  if (!this->processCallGraphSection() || this->FuncCGInfos.empty()) {
+    return;
+  }
+
+  auto PrintNonRelocatableFuncSymbol = [&](uint64_t FuncEntryPC) {
+    SmallVector<std::string> FuncSymNames = this->getFunctionNames(FuncEntryPC);
+    if (!FuncSymNames.empty())
+      W.printList("Names", FuncSymNames);
+    W.printHex("Address", FuncEntryPC);
+  };
+
+  std::vector<Relocation<ELFT>> Relocations;
+  const Elf_Shdr *RelocSymTab = nullptr;
+  if (this->Obj.getHeader().e_type == ELF::ET_REL)
+    this->getCallGraphRelocations(Relocations, RelocSymTab);
+
+  auto PrintRelocatableFuncSymbol = [&](uint64_t RelocOffset) {
+    auto R = llvm::find_if(Relocations, [&](const Relocation<ELFT> &R) {
+      return R.Offset == RelocOffset;
+    });
+    if (R == Relocations.end()) {
+      this->reportUniqueWarning("unknown relocation at offset " +
+                                Twine(RelocOffset));
+      return;
+    }
+    Expected<RelSymbol<ELFT>> RelSymOrErr =
+        this->getRelocationTarget(*R, RelocSymTab);
+    if (!RelSymOrErr) {
+      this->reportUniqueWarning(RelSymOrErr.takeError());
+      return;
+    }
+    if (!RelSymOrErr->Name.empty()) {
+      W.printString("Name", RelSymOrErr->Name);
+    }
+  };
+
+  auto PrintFunc = [&](uint64_t FuncPC) {
+    uint64_t FuncEntryPC = FuncPC;
+    // Clear Thumb bit if it was set before symbol lookup.
+    if (this->Obj.getHeader().e_machine == ELF::EM_ARM)
+      FuncEntryPC = FuncPC & ~1;
+    if (this->Obj.getHeader().e_type == ELF::ET_REL)
+      PrintRelocatableFuncSymbol(FuncEntryPC);
+    else
+      PrintNonRelocatableFuncSymbol(FuncEntryPC);
+  };
+
+  ListScope CGI(W, "CallGraph");
+  for (const auto &CGInfo : this->FuncCGInfos) {
+    DictScope D(W, "Function");
+    PrintFunc(CGInfo.FunctionAddress);
+    W.printNumber("Version", CGInfo.FormatVersionNumber);
+    W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget);
+    W.printHex("TypeId", CGInfo.FunctionTypeId);
+    W.printNumber("NumDirectCallees", CGInfo.DirectCallees.size());
+    {
+      ListScope DCs(W, "DirectCallees");
+      for (auto CalleePC : CGInfo.DirectCallees) {
+        DictScope D(W);
+        PrintFunc(CalleePC);
+      }
+    }
+    W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size());
+    SmallVector<uint64_t, 4> IndirectTypeIdsList = SmallVector<uint64_t, 4>(
+        CGInfo.IndirectTypeIDs.begin(), CGInfo.IndirectTypeIDs.end());
----------------
ilovepi wrote:

```suggestion
    SmallVector<uint64_t, 4> IndirectTypeIdsList(CGInfo.IndirectTypeIDs.begin(), CGInfo.IndirectTypeIDs.end());
```
Can't recall if you need `.begin()/.end()` to convert w/ SmallSet, or if there is an overload in ADT.

Alternatively, you can avoid the conversion altogether. Looking at `printHexList()`, its going to put the items in a SmallVector on its own (https://github.com/llvm/llvm-project/blob/f8a803952e9e8daa2c2714d8346b696799e9c833/llvm/include/llvm/Support/ScopedPrinter.h#L364). Arguable that there should probably be a specialization for things automatically convertible to `ArraryRef` that just forwards to `printHexListImpl()`.

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


More information about the llvm-commits mailing list