[llvm] [llvm-size] Fix --totals option for Mach-O files (PR #157904)

Ryan Mansfield via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 12 11:25:12 PDT 2025


https://github.com/rjmansfield updated https://github.com/llvm/llvm-project/pull/157904

>From 117ac71069d0651e8f4f0b1df9373f85b791816b Mon Sep 17 00:00:00 2001
From: Ryan Mansfield <ryan_mansfield at apple.com>
Date: Wed, 10 Sep 2025 12:55:14 -0400
Subject: [PATCH 1/2] [llvm-size] Fix --totals option for Mach-O files

The --totals option was not working for Mach-O files because the Darwin
segment size calculation skipped the totals accumulation.
---
 llvm/test/tools/llvm-size/totals.test | 148 ++++++++++++++++++++++++++
 llvm/tools/llvm-size/llvm-size.cpp    |  43 ++++++--
 2 files changed, 181 insertions(+), 10 deletions(-)

diff --git a/llvm/test/tools/llvm-size/totals.test b/llvm/test/tools/llvm-size/totals.test
index 6f97dc18fee78..b4ca1f6f2f77f 100644
--- a/llvm/test/tools/llvm-size/totals.test
+++ b/llvm/test/tools/llvm-size/totals.test
@@ -16,6 +16,16 @@
 # CHECK-NEXT: [[FILE2]]
 # CHECK-NEXT:      18      36      72     126      7e (TOTALS)
 
+# RUN: yaml2obj %s --docnum=3 -o %t-macho.o
+# RUN: yaml2obj %s --docnum=4 -o %t-macho2.o
+# RUN: llvm-size --totals %t-macho.o %t-macho2.o \
+# RUN:   | FileCheck %s --check-prefix=MACHO-CHECK --strict-whitespace --match-full-lines --implicit-check-not={{.}} -DFILE1=%t-macho.o -DFILE2=%t-macho2.o
+
+# MACHO-CHECK:__TEXT	__DATA	__OBJC	others	dec	hex
+# MACHO-CHECK-NEXT:20	100	0	32	152	98	[[FILE1]]
+# MACHO-CHECK-NEXT:20	200	0	32	252	fc	[[FILE2]]
+# MACHO-CHECK-NEXT:40	300	0	64	404	194	(TOTALS)
+
 --- !ELF
 FileHeader:
   Class:   ELFCLASS64
@@ -55,3 +65,141 @@ Sections:
     Type:    SHT_NOBITS
     Flags:   [SHF_ALLOC, SHF_WRITE]
     Size:    32
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x100000C
+  cpusubtype:      0x0
+  filetype:        0x1
+  ncmds:           2
+  sizeofcmds:      352
+  flags:           0x2000
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         312
+    segname:         ''
+    vmaddr:          0
+    vmsize:          152
+    fileoff:         384
+    filesize:        152
+    maxprot:         7
+    initprot:        7
+    nsects:          3
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x0
+        size:            20
+        offset:          0x180
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __data
+        segname:         __DATA
+        addr:            0x18
+        size:            100
+        offset:          0x198
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __compact_unwind
+        segname:         __LD
+        addr:            0x20
+        size:            32
+        offset:          0x200
+        align:           3
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x2000000
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        1
+    minos:           851968
+    sdk:             983040
+    ntools:          1
+    Tools:
+      - tool:            3
+        version:         68157696
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x100000C
+  cpusubtype:      0x0
+  filetype:        0x1
+  ncmds:           2
+  sizeofcmds:      352
+  flags:           0x2000
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         312
+    segname:         ''
+    vmaddr:          0
+    vmsize:          252
+    fileoff:         384
+    filesize:        252
+    maxprot:         7
+    initprot:        7
+    nsects:          3
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x0
+        size:            20
+        offset:          0x180
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __data
+        segname:         __DATA
+        addr:            0x18
+        size:            200
+        offset:          0x198
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __compact_unwind
+        segname:         __LD
+        addr:            0x20
+        size:            32
+        offset:          0x260
+        align:           3
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x2000000
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        1
+    minos:           851968
+    sdk:             983040
+    ntools:          1
+    Tools:
+      - tool:            3
+        version:         68157696
diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp
index afb6b2386218e..acc7843ffac8b 100644
--- a/llvm/tools/llvm-size/llvm-size.cpp
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -78,6 +78,7 @@ static OutputFormatTy OutputFormat;
 static bool DarwinLongFormat;
 static RadixTy Radix = RadixTy::decimal;
 static bool TotalSizes;
+static bool HasMachOFiles = false;
 
 static std::vector<std::string> InputFilenames;
 
@@ -92,6 +93,10 @@ static uint64_t TotalObjectData = 0;
 static uint64_t TotalObjectBss = 0;
 static uint64_t TotalObjectTotal = 0;
 
+// Darwin-specific totals
+static uint64_t TotalObjectObjc = 0;
+static uint64_t TotalObjectOthers = 0;
+
 static void error(const Twine &Message, StringRef File = "") {
   HadError = true;
   if (File.empty())
@@ -283,6 +288,7 @@ static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
   uint64_t total_data = 0;
   uint64_t total_objc = 0;
   uint64_t total_others = 0;
+  HasMachOFiles = true;
   for (const auto &Load : MachO->load_commands()) {
     if (Load.C.cmd == MachO::LC_SEGMENT_64) {
       MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
@@ -340,6 +346,14 @@ static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
   }
   uint64_t total = total_text + total_data + total_objc + total_others;
 
+  if (TotalSizes) {
+    TotalObjectText += total_text;
+    TotalObjectData += total_data;
+    TotalObjectObjc += total_objc;
+    TotalObjectOthers += total_others;
+    TotalObjectTotal += total;
+  }
+
   if (!BerkeleyHeaderPrinted) {
     outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
     BerkeleyHeaderPrinted = true;
@@ -852,16 +866,25 @@ static void printBerkeleyTotals() {
   std::string fmtbuf;
   raw_string_ostream fmt(fmtbuf);
   const char *radix_fmt = getRadixFmt();
-  fmt << "%#7" << radix_fmt << "\t"
-      << "%#7" << radix_fmt << "\t"
-      << "%#7" << radix_fmt << "\t";
-  outs() << format(fmtbuf.c_str(), TotalObjectText, TotalObjectData,
-                   TotalObjectBss);
-  fmtbuf.clear();
-  fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
-      << "%7" PRIx64 "\t";
-  outs() << format(fmtbuf.c_str(), TotalObjectTotal, TotalObjectTotal)
-         << "(TOTALS)\n";
+
+  if (HasMachOFiles) {
+    // Darwin format totals: __TEXT __DATA __OBJC others dec hex
+    outs() << TotalObjectText << "\t" << TotalObjectData << "\t"
+           << TotalObjectObjc << "\t" << TotalObjectOthers << "\t"
+           << TotalObjectTotal << "\t" << format("%" PRIx64, TotalObjectTotal)
+           << "\t(TOTALS)\n";
+  } else {
+    fmt << "%#7" << radix_fmt << "\t"
+        << "%#7" << radix_fmt << "\t"
+        << "%#7" << radix_fmt << "\t";
+    outs() << format(fmtbuf.c_str(), TotalObjectText, TotalObjectData,
+                     TotalObjectBss);
+    fmtbuf.clear();
+    fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+        << "%7" PRIx64 "\t";
+    outs() << format(fmtbuf.c_str(), TotalObjectTotal, TotalObjectTotal)
+           << "(TOTALS)\n";
+  }
 }
 
 int llvm_size_main(int argc, char **argv, const llvm::ToolContext &) {

>From 2762532942eed58adcd6916d68aa5e4c51989e4b Mon Sep 17 00:00:00 2001
From: Ryan Mansfield <ryan_mansfield at apple.com>
Date: Fri, 12 Sep 2025 13:24:46 -0400
Subject: [PATCH 2/2] [llvm-size] Add -z option for Mach-O to exclude
 __PAGEZERO size.

Fixes #86644
---
 llvm/test/tools/llvm-size/macho-pagezero.test | 56 +++++++++++++++++++
 llvm/tools/llvm-size/Opts.td                  |  2 +
 llvm/tools/llvm-size/llvm-size.cpp            | 10 +++-
 3 files changed, 66 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/tools/llvm-size/macho-pagezero.test

diff --git a/llvm/test/tools/llvm-size/macho-pagezero.test b/llvm/test/tools/llvm-size/macho-pagezero.test
new file mode 100644
index 0000000000000..eda36b972e2f1
--- /dev/null
+++ b/llvm/test/tools/llvm-size/macho-pagezero.test
@@ -0,0 +1,56 @@
+# Test the -z option to skip __PAGEZERO segment in Mach-O files
+
+# RUN: yaml2obj %s --docnum=1 -o %t-pagezero.o
+# RUN: llvm-size %t-pagezero.o | FileCheck %s --check-prefix=NORMAL --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+# RUN: llvm-size -z %t-pagezero.o | FileCheck %s --check-prefix=SKIP --match-full-lines  --strict-whitespace --implicit-check-not={{.}}
+
+# NORMAL:__TEXT	__DATA	__OBJC	others	dec	hex
+# NORMAL-NEXT:20	100	0	4096	4216	1078	
+
+# SKIP:__TEXT	__DATA	__OBJC	others	dec	hex
+# SKIP-NEXT:20	100	0	0	120	78	
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x100000C
+  cpusubtype:      0x0
+  filetype:        0x2
+  ncmds:           3
+  sizeofcmds:      216
+  flags:           0x2000
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __PAGEZERO
+    vmaddr:          0x0
+    vmsize:          4096
+    fileoff:         0
+    filesize:        0
+    maxprot:         0
+    initprot:        0
+    nsects:          0
+    flags:           0
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __TEXT
+    vmaddr:          0x100000000
+    vmsize:          20
+    fileoff:         248
+    filesize:        20
+    maxprot:         7
+    initprot:        5
+    nsects:          0
+    flags:           0
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __DATA
+    vmaddr:          0x100001000
+    vmsize:          100
+    fileoff:         268
+    filesize:        100
+    maxprot:         7
+    initprot:        3
+    nsects:          0
+    flags:           0
diff --git a/llvm/tools/llvm-size/Opts.td b/llvm/tools/llvm-size/Opts.td
index edae43f1abd24..65478730c2801 100644
--- a/llvm/tools/llvm-size/Opts.td
+++ b/llvm/tools/llvm-size/Opts.td
@@ -21,6 +21,8 @@ def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">;
 def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>;
 def : Separate<["--", "-"], "arch">, Alias<arch_EQ>;
 def l : F<"l", "When format is darwin, use long format to include addresses and offsets">, Group<grp_mach_o>;
+def z : F<"z", "Do not include __PAGEZERO segment in totals">,
+        Group<grp_mach_o>;
 
 def : F<"A", "Alias for --format">, Alias<format_EQ>, AliasArgs<["sysv"]>;
 def : F<"B", "Alias for --format">, Alias<format_EQ>, AliasArgs<["berkeley"]>;
diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp
index acc7843ffac8b..805f8ed1e6dcd 100644
--- a/llvm/tools/llvm-size/llvm-size.cpp
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -79,6 +79,7 @@ static bool DarwinLongFormat;
 static RadixTy Radix = RadixTy::decimal;
 static bool TotalSizes;
 static bool HasMachOFiles = false;
+static bool SkipPageZero = false;
 
 static std::vector<std::string> InputFilenames;
 
@@ -307,7 +308,9 @@ static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
         }
       } else {
         StringRef SegmentName = StringRef(Seg.segname);
-        if (SegmentName == "__TEXT")
+        if (SkipPageZero && SegmentName == "__PAGEZERO")
+          ; // Skip __PAGEZERO segment
+        else if (SegmentName == "__TEXT")
           total_text += Seg.vmsize;
         else if (SegmentName == "__DATA")
           total_data += Seg.vmsize;
@@ -333,7 +336,9 @@ static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
         }
       } else {
         StringRef SegmentName = StringRef(Seg.segname);
-        if (SegmentName == "__TEXT")
+        if (SkipPageZero && SegmentName == "__PAGEZERO")
+          ; // Skip __PAGEZERO segment
+        else if (SegmentName == "__TEXT")
           total_text += Seg.vmsize;
         else if (SegmentName == "__DATA")
           total_data += Seg.vmsize;
@@ -914,6 +919,7 @@ int llvm_size_main(int argc, char **argv, const llvm::ToolContext &) {
 
   ELFCommons = Args.hasArg(OPT_common);
   DarwinLongFormat = Args.hasArg(OPT_l);
+  SkipPageZero = Args.hasArg(OPT_z);
   TotalSizes = Args.hasArg(OPT_totals);
   StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley");
   if (V == "berkeley")



More information about the llvm-commits mailing list