[lld] [llvm] RFC: [llvm-readobj] Support FDO package metadata in readelf (PR #214375)

Dan McGregor via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 20:17:59 PDT 2026


https://github.com/dankm updated https://github.com/llvm/llvm-project/pull/214375

>From 88e2b08359df1e559a4ac15e1bf24b301ca4ea43 Mon Sep 17 00:00:00 2001
From: Dan McGregor <danmcgr at protonmail.com>
Date: Fri, 17 Jul 2026 13:40:40 -0600
Subject: [PATCH 1/2] ScopedPrinter: support pretty-printing JSON

Add a specific printObject implementation for JSON values.
For the default ScopedPrinter this supports printing with the
existing indentation level. For JSONScopedPrinter it prints a
raw attribute with no special handling.

I intend to use this for forthcoming support for FDO packakaging
metadata support in readobj.
---
 llvm/include/llvm/Support/JSON.h          |  7 ++++---
 llvm/include/llvm/Support/ScopedPrinter.h | 13 +++++++++++++
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index 3b145a69c74cb..a0118ca883ce6 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -984,8 +984,9 @@ class OStream {
  public:
   using Block = llvm::function_ref<void()>;
   // If IndentSize is nonzero, output is pretty-printed.
-  explicit OStream(llvm::raw_ostream &OS, unsigned IndentSize = 0)
-      : OS(OS), IndentSize(IndentSize) {
+  explicit OStream(llvm::raw_ostream &OS, unsigned IndentSize = 0,
+                   unsigned IndentLevel = 0)
+      : OS(OS), IndentSize(IndentSize), Indent(IndentSize * IndentLevel) {
     Stack.emplace_back();
   }
   ~OStream() {
@@ -1084,7 +1085,7 @@ class OStream {
   llvm::StringRef PendingComment;
   llvm::raw_ostream &OS;
   unsigned IndentSize;
-  unsigned Indent = 0;
+  unsigned Indent;
 };
 
 /// Serializes this Value to JSON, writing it to the provided stream.
diff --git a/llvm/include/llvm/Support/ScopedPrinter.h b/llvm/include/llvm/Support/ScopedPrinter.h
index f6fd2671e812f..3da97c40ff466 100644
--- a/llvm/include/llvm/Support/ScopedPrinter.h
+++ b/llvm/include/llvm/Support/ScopedPrinter.h
@@ -399,6 +399,15 @@ class LLVM_ABI ScopedPrinter {
     printString(Label, to_string(Value));
   }
 
+  virtual void printObject(StringRef Label, const llvm::json::Value &Value) {
+    std::string prettyValue;
+    raw_string_ostream stream(prettyValue);
+    llvm::json::OStream(stream, 2, getIndentLevel()).value(Value);
+    startLine() << Label << ": "
+                << prettyValue.substr(prettyValue.find_first_not_of(" "))
+                << "\n";
+  }
+
   virtual void objectBegin() { scopedBegin('{'); }
 
   virtual void objectBegin(StringRef Label) { scopedBegin(Label, '{'); }
@@ -672,6 +681,10 @@ class JSONScopedPrinter : public ScopedPrinter {
     JOS.attribute(Label, Value);
   }
 
+  void printObject(StringRef Label, const llvm::json::Value &Value) override {
+    JOS.attribute(Label, Value);
+  }
+
   void objectBegin() override {
     scopedBegin({Scope::Object, ScopeKind::NoAttribute});
   }

>From 30eb7f7cbe1fa54f9fcfa39c233410a6abfda231 Mon Sep 17 00:00:00 2001
From: Dan McGregor <danmcgr at protonmail.com>
Date: Mon, 13 Jul 2026 21:18:29 -0600
Subject: [PATCH 2/2] [llvm-readobj] support FDO metadata

Dump dlopen and package metadata notes in the same format as
GNU readelf in GNU mode. In LLVM and JSON modes, attempt to
pretty-print, falling back to raw strings if json parsing fails.

Fixes #194019
---
 lld/test/ELF/package-metadata.s       |  6 ++---
 llvm/include/llvm/BinaryFormat/ELF.h  |  1 +
 llvm/tools/llvm-readobj/ELFDumper.cpp | 39 +++++++++++++++++++++++++++
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/lld/test/ELF/package-metadata.s b/lld/test/ELF/package-metadata.s
index a70a8940d7c68..c4ca2e169d3ec 100644
--- a/lld/test/ELF/package-metadata.s
+++ b/lld/test/ELF/package-metadata.s
@@ -13,9 +13,9 @@
 
 # NOTE: .note.package
 # NOTE-NEXT: Owner
-# NOTE-NEXT: FDO 0x{{.*}} Unknown note type: (0xcafe1a7e)
-# FIRST-NEXT: description data: 7b 7d 00
-# SECOND-NEXT: description data: 7b 22 61 62 63 22 3a 31 32 33 7d 00
+# NOTE-NEXT: FDO 0x{{.*}} FDO_PACKAGING_METADATA
+# FIRST-NEXT: Packaging Metadata: {}
+# SECOND-NEXT: Packaging Metadata: {"abc":123}
 
 # RUN: not ld.lld a.o --package-metadata='%7b%' 2>&1 | FileCheck %s --check-prefix=ERR
 # RUN: not ld.lld a.o --package-metadata='%7b%7' 2>&1 | FileCheck %s --check-prefix=ERR
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 8c429a8e1428f..81d4f6a3001bd 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1818,6 +1818,7 @@ enum {
   NT_GNU_BUILD_ID = 3,
   NT_GNU_GOLD_VERSION = 4,
   NT_GNU_PROPERTY_TYPE_0 = 5,
+  NT_FDO_DLOPEN_METADATA = 0x407c0c0a,
   FDO_PACKAGING_METADATA = 0xcafe1a7e,
 };
 
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 6bd9efa0542a1..22bfa8652e0d3 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -5934,6 +5934,24 @@ static bool printLLVMOMPOFFLOADNote(raw_ostream &OS, uint32_t NoteType,
   return true;
 }
 
+struct FDONote {
+  std::string Type;
+  std::string Value;
+};
+
+template <typename ELFT>
+static std::optional<FDONote> getFDONote(typename ELFT::Word NoteType,
+                                         ArrayRef<uint8_t> Desc) {
+  switch (NoteType) {
+  case ELF::FDO_PACKAGING_METADATA:
+    return FDONote{"Packaging Metadata", toStringRef(Desc).rtrim('\0').str()};
+  case ELF::NT_FDO_DLOPEN_METADATA:
+    return FDONote{"Dlopen Metadata", toStringRef(Desc).rtrim('\0').str()};
+  default:
+    return std::nullopt;
+  }
+}
+
 constexpr EnumStringDef<unsigned> FreeBSDFeatureCtlFlagsDefs[] = {
     {{"ASLR_DISABLE"}, NT_FREEBSD_FCTL_ASLR_DISABLE},
     {{"PROTMAX_DISABLE"}, NT_FREEBSD_FCTL_PROTMAX_DISABLE},
@@ -6220,6 +6238,11 @@ const NoteType GNUNoteTypes[] = {
     {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"},
 };
 
+const NoteType FDONoteTypes[] = {
+    {ELF::FDO_PACKAGING_METADATA, "FDO_PACKAGING_METADATA"},
+    {ELF::NT_FDO_DLOPEN_METADATA, "FDO_DLOPEN_METADATA"},
+};
+
 const NoteType FreeBSDCoreNoteTypes[] = {
     {ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"},
     {ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"},
@@ -6377,6 +6400,8 @@ StringRef getNoteTypeName(const typename ELFT::Note &Note, unsigned ELFType) {
   StringRef Name = Note.getName();
   if (Name == "GNU")
     return FindNote(GNUNoteTypes);
+  if (Name == "FDO")
+    return FindNote(FDONoteTypes);
   if (Name == "FreeBSD") {
     if (ELFType == ELF::ET_CORE) {
       // FreeBSD also places the generic core notes in the FreeBSD namespace.
@@ -6530,6 +6555,11 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
     if (Name == "GNU") {
       if (printGNUNote<ELFT>(OS, Type, Descriptor, EMachine))
         return Error::success();
+    } else if (Name == "FDO") {
+      if (std::optional<FDONote> N = getFDONote<ELFT>(Type, Descriptor)) {
+        OS << "    " << N->Type << ": " << N->Value << '\n';
+        return Error::success();
+      }
     } else if (Name == "FreeBSD") {
       if (std::optional<FreeBSDNote> N =
               getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) {
@@ -8731,6 +8761,15 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() {
     if (Name == "GNU") {
       if (printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W, EMachine))
         return Error::success();
+    } else if (Name == "FDO") {
+      if (std::optional<FDONote> N = getFDONote<ELFT>(Type, Descriptor)) {
+        auto f = llvm::json::parse(N->Value);
+        if (f)
+          W.printObject(N->Type, f.get());
+        else
+          W.printString(N->Type, N->Value);
+        return Error::success();
+      }
     } else if (Name == "FreeBSD") {
       if (std::optional<FreeBSDNote> N =
               getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) {



More information about the llvm-commits mailing list