[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