[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