[llvm-branch-commits] [llvm] [llvm-readobj][ELF] Print the address of each --stack-sizes entry (PR #232331)

Prabhu Rajasekaran via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 19 04:47:38 PDT 2026


https://github.com/Prabhuk updated https://github.com/llvm/llvm-project/pull/232331

>From eefa27be50eaa21da33bd9939aff8f7069c01b5a Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 15 Oct 2026 06:55:36 -0700
Subject: [PATCH 1/4] [llvm-readobj][ELF] Print the address of each
 --stack-sizes entry

In a linked file, print the function address that each .stack_sizes
entry holds as Address in the LLVM and JSON output. The entries can then
be matched with --call-graph-info output by address, without looking up
function names.

Assisted-by: Gemini
---
 .../tools/llvm-readobj/ELF/stack-sizes.test   | 27 ++++++++++++++++--
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 28 +++++++++++++------
 2 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test b/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test
index 8ce1a6f1b6b27e..c9c5fd52142eae 100644
--- a/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test
+++ b/llvm/test/tools/llvm-readobj/ELF/stack-sizes.test
@@ -176,8 +176,8 @@ Symbols:
 # SYM-LLVM-NEXT: ]
 
 ## Check that we correctly report the stack sizes in an executable (non-relocatable)
-## object file. This also shows that the sh_link field is ignored in this situation
-## without warning.
+## object file, with the address of each function in the LLVM and JSON output. This
+## also shows that the sh_link field is ignored in this situation without warning.
 
 # RUN: yaml2obj --docnum=2 %s -o %t02
 # RUN: llvm-readelf --stack-sizes %t02 2>&1 \
@@ -185,6 +185,8 @@ Symbols:
 # RUN:                  --match-full-lines --implicit-check-not=warning:
 # RUN: llvm-readobj --stack-sizes %t02 2>&1 \
 # RUN:   | FileCheck %s --check-prefix=EXEC-LLVM --implicit-check-not=warning:
+# RUN: llvm-readobj --elf-output-style=JSON --pretty-print --stack-sizes %t02 2>&1 \
+# RUN:   | FileCheck %s --check-prefix=EXEC-JSON --implicit-check-not=warning:
 
 #      EXEC-GNU:         Size     Functions
 # EXEC-GNU-NEXT:           16     other
@@ -195,18 +197,32 @@ Symbols:
 # EXEC-LLVM:      StackSizes [
 # EXEC-LLVM-NEXT:   Entry {
 # EXEC-LLVM-NEXT:     Functions: [other]
+# EXEC-LLVM-NEXT:     Address: 0x0{{$}}
 # EXEC-LLVM-NEXT:     Size: 0x10
 # EXEC-LLVM-NEXT:   }
 # EXEC-LLVM-NEXT:   Entry {
 # EXEC-LLVM-NEXT:     Functions: [other_end]
+# EXEC-LLVM-NEXT:     Address: 0x10{{$}}
 # EXEC-LLVM-NEXT:     Size: 0x20
 # EXEC-LLVM-NEXT:   }
 # EXEC-LLVM-NEXT:   Entry {
 # EXEC-LLVM-NEXT:     Functions: [bar]
+# EXEC-LLVM-NEXT:     Address: 0x20{{$}}
 # EXEC-LLVM-NEXT:     Size: 0x30
 # EXEC-LLVM-NEXT:   }
 # EXEC-LLVM-NEXT: ]
 
+# EXEC-JSON:      "StackSizes": [
+# EXEC-JSON-NEXT:   {
+# EXEC-JSON-NEXT:     "Entry": {
+# EXEC-JSON-NEXT:       "Functions": [
+# EXEC-JSON-NEXT:         "other"
+# EXEC-JSON-NEXT:       ],
+# EXEC-JSON-NEXT:       "Address": 0,
+# EXEC-JSON-NEXT:       "Size": 16
+# EXEC-JSON-NEXT:     }
+# EXEC-JSON-NEXT:   },
+
 --- !ELF
 FileHeader:
   Class: ELFCLASS64
@@ -392,11 +408,13 @@ Symbols:
 # SUDDENEND-LLVM:      StackSizes [
 # SUDDENEND-LLVM-NEXT:   Entry {
 # SUDDENEND-LLVM-NEXT:     Functions: [foo]
+# SUDDENEND-LLVM-NEXT:     Address: 0x0{{$}}
 # SUDDENEND-LLVM-NEXT:     Size: 0x8
 # SUDDENEND-LLVM-NEXT:   }
 # SUDDENEND-LLVM-NEXT: warning: '[[FILE]]': SHT_PROGBITS section with index 2 ended while trying to extract a stack size entry
 # SUDDENEND-LLVM-NEXT:   Entry {
 # SUDDENEND-LLVM-NEXT:     Functions: [foo]
+# SUDDENEND-LLVM-NEXT:     Address: 0x0{{$}}
 # SUDDENEND-LLVM-NEXT:     Size: 0x8
 # SUDDENEND-LLVM-NEXT:   }
 # SUDDENEND-LLVM-NEXT: warning: '[[FILE]]': SHT_PROGBITS section with index 3 ended while trying to extract a stack size entry
@@ -638,14 +656,17 @@ Sections:
 # MULTIPLE-LLVM:      StackSizes [
 # MULTIPLE-LLVM-NEXT:   Entry {
 # MULTIPLE-LLVM-NEXT:     Functions: [other]
+# MULTIPLE-LLVM-NEXT:     Address: 0x0{{$}}
 # MULTIPLE-LLVM-NEXT:     Size: 0x10
 # MULTIPLE-LLVM-NEXT:   }
 # MULTIPLE-LLVM-NEXT:   Entry {
 # MULTIPLE-LLVM-NEXT:     Functions: [other_end]
+# MULTIPLE-LLVM-NEXT:     Address: 0x10{{$}}
 # MULTIPLE-LLVM-NEXT:     Size: 0x20
 # MULTIPLE-LLVM-NEXT:   }
 # MULTIPLE-LLVM-NEXT:   Entry {
 # MULTIPLE-LLVM-NEXT:     Functions: [bar]
+# MULTIPLE-LLVM-NEXT:     Address: 0x20{{$}}
 # MULTIPLE-LLVM-NEXT:     Size: 0x30
 # MULTIPLE-LLVM-NEXT:   }
 # MULTIPLE-LLVM-NEXT: ]
@@ -907,6 +928,7 @@ Sections:
 # MULTIPLE-SYMBOLS-LLVM:      StackSizes [
 # MULTIPLE-SYMBOLS-LLVM-NEXT:   Entry {
 # MULTIPLE-SYMBOLS-LLVM-NEXT:     Functions: [foo, bar]
+# MULTIPLE-SYMBOLS-LLVM-NEXT:     Address: 0x0{{$}}
 # MULTIPLE-SYMBOLS-LLVM-NEXT:     Size: 0x10
 # MULTIPLE-SYMBOLS-LLVM-NEXT:   }
 # MULTIPLE-SYMBOLS-LLVM-NEXT: ]
@@ -955,6 +977,7 @@ Symbols:
 # THUMB-LLVM:      StackSizes [
 # THUMB-LLVM-NEXT:   Entry {
 # THUMB-LLVM-NEXT:     Functions: [foo]
+# THUMB-LLVM-NEXT:     Address: 0x1000{{$}}
 # THUMB-LLVM-NEXT:     Size: 0x10
 # THUMB-LLVM-NEXT:   }
 # THUMB-LLVM-NEXT: ]
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 623ae0a98eb76c..77f2eaa28b3fa6 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -368,8 +368,12 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
                       unsigned Ndx, const Elf_Shdr *SymTab,
                       const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec,
                       const RelocationResolver &Resolver, DataExtractor Data);
+  // Prints a stack size entry. Address is the function's address: the value
+  // stored in the entry in a linked file, or the value that its relocation
+  // resolves to in a relocatable file.
   virtual void printStackSizeEntry(uint64_t Size,
-                                   ArrayRef<std::string> FuncNames) = 0;
+                                   ArrayRef<std::string> FuncNames,
+                                   uint64_t Address) = 0;
 
   void printRelocatableStackSizes(std::function<void()> PrintHeader);
   void printNonRelocatableStackSizes(std::function<void()> PrintHeader);
@@ -749,8 +753,8 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
   void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec,
                                     const Twine &Label, unsigned EntriesNum);
 
-  void printStackSizeEntry(uint64_t Size,
-                           ArrayRef<std::string> FuncNames) override;
+  void printStackSizeEntry(uint64_t Size, ArrayRef<std::string> FuncNames,
+                           uint64_t Address) override;
 
   void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
   void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -806,8 +810,8 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
                    bool /*ExtraSymInfo*/) const override;
   void printProgramHeaders() override;
   void printSectionMapping() override {}
-  void printStackSizeEntry(uint64_t Size,
-                           ArrayRef<std::string> FuncNames) override;
+  void printStackSizeEntry(uint64_t Size, ArrayRef<std::string> FuncNames,
+                           uint64_t Address) override;
   void printCallGraphRelocation(const CallGraphFunc<ELFT> &Func,
                                 const Elf_Shdr &RelSec, const Elf_Shdr &SymTab);
   // Returns the names of the functions at the address that the relocation for
@@ -7258,12 +7262,12 @@ bool ELFDumper<ELFT>::printFunctionStackSize(
   }
 
   if (FuncSymIndexes.empty()) {
-    printStackSizeEntry(StackSize, {"?"});
+    printStackSizeEntry(StackSize, {"?"}, SymValue);
   } else {
     SmallVector<std::string> FuncSymNames;
     for (uint32_t Index : FuncSymIndexes)
       FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    printStackSizeEntry(StackSize, FuncSymNames);
+    printStackSizeEntry(StackSize, FuncSymNames, SymValue);
   }
 
   return true;
@@ -7271,7 +7275,8 @@ bool ELFDumper<ELFT>::printFunctionStackSize(
 
 template <class ELFT>
 void GNUELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
-                                             ArrayRef<std::string> FuncNames) {
+                                             ArrayRef<std::string> FuncNames,
+                                             uint64_t /*Address*/) {
   OS.PadToColumn(2);
   OS << format_decimal(Size, 11);
   OS.PadToColumn(18);
@@ -9001,9 +9006,14 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printStackSizes() {
 
 template <class ELFT>
 void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
-                                              ArrayRef<std::string> FuncNames) {
+                                              ArrayRef<std::string> FuncNames,
+                                              uint64_t Address) {
   DictScope D(W, "Entry");
   W.printList("Functions", FuncNames);
+  // In a linked file, the function is identified by its address, as in
+  // --call-graph-info.
+  if (this->Obj.getHeader().e_type != ELF::ET_REL)
+    W.printHex("Address", Address);
   W.printHex("Size", Size);
 }
 

>From bfa7a558d45d3cf34aa78e288e584a3288c4d4e4 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Thu, 15 Oct 2026 14:32:07 -0700
Subject: [PATCH 2/4] Document printStackSizeEntry and say when Address is
 printed

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 889ea7b2cc5380..80e7190f19fdb4 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -368,9 +368,9 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
                       unsigned Ndx, const Elf_Shdr *SymTab,
                       const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec,
                       const RelocationResolver &Resolver, DataExtractor Data);
-  // Prints a stack size entry. Address is the function's address: the value
-  // stored in the entry in a linked file, or the value that its relocation
-  // resolves to in a relocatable file.
+  /// Prints a stack size entry for the functions in \p FuncNames. \p Address
+  /// is the function's address: the value stored in the entry in a linked
+  /// file, or the value that its relocation resolves to in a relocatable file.
   virtual void printStackSizeEntry(uint64_t Size,
                                    ArrayRef<std::string> FuncNames,
                                    uint64_t Address) = 0;
@@ -9009,8 +9009,8 @@ void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
                                               uint64_t Address) {
   DictScope D(W, "Entry");
   W.printList("Functions", FuncNames);
-  // In a linked file, the function is identified by its address, as in
-  // --call-graph-info.
+  // Print the address only for a linked file. In a relocatable file the field
+  // holds a placeholder for the relocation that applies to it.
   if (this->Obj.getHeader().e_type != ELF::ET_REL)
     W.printHex("Address", Address);
   W.printHex("Size", Size);

>From cbb430151cc43f37582b0d818a7bd0d85379c0ca Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Fri, 16 Oct 2026 15:36:45 -0700
Subject: [PATCH 3/4] Say why Address is not printed for a relocatable file

---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 2798524413c219..07967217a98185 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -9007,8 +9007,8 @@ void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
                                               uint64_t Address) {
   DictScope D(W, "Entry");
   W.printList("Functions", FuncNames);
-  // Print the address only for a linked file. In a relocatable file the field
-  // holds a placeholder for the relocation that applies to it.
+  // Print the address only for a linked file. In a relocatable file the value
+  // stored in the entry is just the implicit addend of its relocation.
   if (this->Obj.getHeader().e_type != ELF::ET_REL)
     W.printHex("Address", Address);
   W.printHex("Size", Size);

>From 15958e0a4758d3435b4742ba9c4c3b07751c420f Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 19 Oct 2026 04:43:03 -0700
Subject: [PATCH 4/4] Pass the address to printStackSizeEntry only for a linked
 file

The printer no longer decides based on the file type, and the comments
no longer describe the value in a relocatable file.
---
 llvm/tools/llvm-readobj/ELFDumper.cpp | 33 +++++++++++++++------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 07967217a98185..751c6af06c1506 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -377,11 +377,11 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
                       const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec,
                       const RelocationResolver &Resolver, DataExtractor Data);
   /// Prints a stack size entry for the functions in \p FuncNames. \p Address
-  /// is the function's address: the value stored in the entry in a linked
-  /// file, or the value that its relocation resolves to in a relocatable file.
+  /// is the function's address as stored in the entry, and is only set for a
+  /// linked file.
   virtual void printStackSizeEntry(uint64_t Size,
                                    ArrayRef<std::string> FuncNames,
-                                   uint64_t Address) = 0;
+                                   std::optional<uint64_t> Address) = 0;
 
   void printRelocatableStackSizes(std::function<void()> PrintHeader);
   void printNonRelocatableStackSizes(std::function<void()> PrintHeader);
@@ -762,7 +762,7 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
                                     const Twine &Label, unsigned EntriesNum);
 
   void printStackSizeEntry(uint64_t Size, ArrayRef<std::string> FuncNames,
-                           uint64_t Address) override;
+                           std::optional<uint64_t> Address) override;
 
   void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
   void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -819,7 +819,7 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
   void printProgramHeaders() override;
   void printSectionMapping() override {}
   void printStackSizeEntry(uint64_t Size, ArrayRef<std::string> FuncNames,
-                           uint64_t Address) override;
+                           std::optional<uint64_t> Address) override;
   void printCallGraphRelocation(const CallGraphFunc<ELFT> &Func,
                                 const Elf_Shdr &RelSec, const Elf_Shdr &SymTab);
   // Returns the names of the functions at the address that the relocation for
@@ -7266,22 +7266,27 @@ bool ELFDumper<ELFT>::printFunctionStackSize(
     return false;
   }
 
+  // The address is only printed for a linked file. In a relocatable file the
+  // entry holds a placeholder for its relocation rather than an address.
+  std::optional<uint64_t> Address;
+  if (Obj.getHeader().e_type != ELF::ET_REL)
+    Address = SymValue;
   if (FuncSymIndexes.empty()) {
-    printStackSizeEntry(StackSize, {"?"}, SymValue);
+    printStackSizeEntry(StackSize, {"?"}, Address);
   } else {
     SmallVector<std::string> FuncSymNames;
     for (uint32_t Index : FuncSymIndexes)
       FuncSymNames.push_back(this->getStaticSymbolName(Index));
-    printStackSizeEntry(StackSize, FuncSymNames, SymValue);
+    printStackSizeEntry(StackSize, FuncSymNames, Address);
   }
 
   return true;
 }
 
 template <class ELFT>
-void GNUELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
-                                             ArrayRef<std::string> FuncNames,
-                                             uint64_t /*Address*/) {
+void GNUELFDumper<ELFT>::printStackSizeEntry(
+    uint64_t Size, ArrayRef<std::string> FuncNames,
+    std::optional<uint64_t> /*Address*/) {
   OS.PadToColumn(2);
   OS << format_decimal(Size, 11);
   OS.PadToColumn(18);
@@ -9004,13 +9009,11 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printStackSizes() {
 template <class ELFT>
 void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
                                               ArrayRef<std::string> FuncNames,
-                                              uint64_t Address) {
+                                              std::optional<uint64_t> Address) {
   DictScope D(W, "Entry");
   W.printList("Functions", FuncNames);
-  // Print the address only for a linked file. In a relocatable file the value
-  // stored in the entry is just the implicit addend of its relocation.
-  if (this->Obj.getHeader().e_type != ELF::ET_REL)
-    W.printHex("Address", Address);
+  if (Address)
+    W.printHex("Address", *Address);
   W.printHex("Size", Size);
 }
 



More information about the llvm-branch-commits mailing list