[llvm] 7b0d00f - [llvm-objcopy] Add --verbose option to llvm-strip/llvm-objcopy (#196611)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 3 01:17:31 PDT 2026


Author: Aayush Shrivastava
Date: 2026-06-03T08:17:25Z
New Revision: 7b0d00f374097fc364ff7a40c846bb98f4950945

URL: https://github.com/llvm/llvm-project/commit/7b0d00f374097fc364ff7a40c846bb98f4950945
DIFF: https://github.com/llvm/llvm-project/commit/7b0d00f374097fc364ff7a40c846bb98f4950945.diff

LOG: [llvm-objcopy] Add --verbose option to llvm-strip/llvm-objcopy (#196611)

Added `--verbose / -v` option to `llvm-strip` and `llvm-objcopy` as part
of #123041, matching GNU strip's output format. When the flag is passed,
the tool prints one line per file processed.

copy from `'input.o' [elf64-x86-64] to 'output.o' [elf64-x86-64]`

Added: 
    llvm/test/tools/llvm-objcopy/verbose.test

Modified: 
    llvm/docs/CommandGuide/llvm-objcopy.rst
    llvm/docs/CommandGuide/llvm-strip.rst
    llvm/include/llvm/ObjCopy/CommonConfig.h
    llvm/include/llvm/ObjCopy/ObjCopy.h
    llvm/lib/ObjCopy/Archive.cpp
    llvm/lib/ObjCopy/ObjCopy.cpp
    llvm/tools/llvm-objcopy/CommonOpts.td
    llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
    llvm/tools/llvm-objcopy/llvm-objcopy.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 9219a5a5852a8..967253d984a35 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -294,6 +294,10 @@ multiple file formats.
  ``<file>``. If the section ``<name>`` is part of a segment, the new contents
  cannot be larger than the existing section.
 
+.. option:: --verbose, -v
+
+ Print the paths of files that are copied.
+
 .. option:: --version, -V
 
  Display the version of the :program:`llvm-objcopy` executable.

diff  --git a/llvm/docs/CommandGuide/llvm-strip.rst b/llvm/docs/CommandGuide/llvm-strip.rst
index a870e210dfea0..d6f4ab1d37461 100644
--- a/llvm/docs/CommandGuide/llvm-strip.rst
+++ b/llvm/docs/CommandGuide/llvm-strip.rst
@@ -110,6 +110,10 @@ multiple file formats.
  Remove from the output all local or undefined symbols that are not required by
  relocations. Also remove all debug sections.
 
+.. option:: --verbose, -v
+
+ Print the paths of files that are copied.
+
 .. option:: --version, -V
 
  Display the version of the :program:`llvm-strip` executable.

diff  --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index bad4bfdeaa46e..1f984f68c6287 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -276,6 +276,7 @@ struct CommonConfig {
   bool StripNonAlloc = false;
   bool StripSections = false;
   bool StripUnneeded = false;
+  bool Verbose = false;
   bool Weaken = false;
   bool DecompressDebugSections = false;
 

diff  --git a/llvm/include/llvm/ObjCopy/ObjCopy.h b/llvm/include/llvm/ObjCopy/ObjCopy.h
index 9554930e20970..d90c2e832400a 100644
--- a/llvm/include/llvm/ObjCopy/ObjCopy.h
+++ b/llvm/include/llvm/ObjCopy/ObjCopy.h
@@ -23,6 +23,14 @@ class Binary;
 namespace objcopy {
 class MultiFormatConfig;
 
+/// Returns the format name of \p B if it is an ObjectFile, or "" otherwise.
+LLVM_ABI StringRef getObjectFormatName(const object::Binary &B);
+
+/// Prints information about the input and output files involved in a copy
+/// operation to stdout.
+LLVM_ABI void printCopyMessage(StringRef InPath, StringRef InFormatName,
+                               StringRef OutPath, StringRef OutFormatName);
+
 /// Applies the transformations described by \p Config to
 /// each member in archive \p Ar.
 /// Writes a result in a file specified by \p Config.OutputFilename.

diff  --git a/llvm/lib/ObjCopy/Archive.cpp b/llvm/lib/ObjCopy/Archive.cpp
index a4e90ce387269..191174102c66f 100644
--- a/llvm/lib/ObjCopy/Archive.cpp
+++ b/llvm/lib/ObjCopy/Archive.cpp
@@ -28,11 +28,22 @@ objcopy::createNewArchiveMembers(const MultiFormatConfig &Config,
     if (!ChildNameOrErr)
       return createFileError(Ar.getFileName(), ChildNameOrErr.takeError());
 
+    auto MemberName = [&](StringRef Prefix) {
+      return (Twine(Prefix) + "(" + *ChildNameOrErr + ")").str();
+    };
+
     Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
     if (!ChildOrErr)
-      return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")",
+      return createFileError(MemberName(Ar.getFileName()),
                              ChildOrErr.takeError());
 
+    const CommonConfig &CC = Config.getCommonConfig();
+    if (CC.Verbose) {
+      StringRef FormatName = getObjectFormatName(*ChildOrErr->get());
+      printCopyMessage(MemberName(Ar.getFileName()), FormatName,
+                       MemberName(CC.OutputFilename), FormatName);
+    }
+
     SmallVector<char, 0> Buffer;
     raw_svector_ostream MemStream(Buffer);
 

diff  --git a/llvm/lib/ObjCopy/ObjCopy.cpp b/llvm/lib/ObjCopy/ObjCopy.cpp
index 152600f4f243f..288b2432e8e8b 100644
--- a/llvm/lib/ObjCopy/ObjCopy.cpp
+++ b/llvm/lib/ObjCopy/ObjCopy.cpp
@@ -32,6 +32,20 @@
 using namespace llvm;
 using namespace llvm::object;
 
+StringRef objcopy::getObjectFormatName(const object::Binary &B) {
+  if (const auto *OF = dyn_cast<ObjectFile>(&B))
+    return OF->getFileFormatName();
+  return {};
+}
+
+void objcopy::printCopyMessage(StringRef InPath, StringRef InFormatName,
+                               StringRef OutPath, StringRef OutFormatName) {
+  if (OutFormatName.empty())
+    OutFormatName = InFormatName;
+  outs() << "copy from '" << InPath << "' [" << InFormatName << "] to '"
+         << OutPath << "' [" << OutFormatName << "]\n";
+}
+
 /// The function executeObjcopyOnBinary does the dispatch based on the format
 /// of the input binary (ELF, MachO or COFF).
 Error objcopy::executeObjcopyOnBinary(const MultiFormatConfig &Config,

diff  --git a/llvm/test/tools/llvm-objcopy/verbose.test b/llvm/test/tools/llvm-objcopy/verbose.test
new file mode 100644
index 0000000000000..09544d40636e2
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/verbose.test
@@ -0,0 +1,91 @@
+## Test --verbose / -v for llvm-objcopy and llvm-strip.
+
+# RUN: rm -rf %t && mkdir %t
+# RUN: yaml2obj %s --docnum=1 -o %t/input.o
+# RUN: yaml2obj %s --docnum=2 -o %t/coff.o
+
+## --verbose and -v both print to stdout.
+# RUN: llvm-objcopy --verbose %t/input.o %t/output.o 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=OBJCOPY --implicit-check-not={{.}} \
+# RUN:     -DIN=%t/input.o -DOUT=%t/output.o
+# RUN: llvm-objcopy -v %t/input.o %t/output.o 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=OBJCOPY --implicit-check-not={{.}} \
+# RUN:     -DIN=%t/input.o -DOUT=%t/output.o
+# OBJCOPY: copy from '[[IN]]' [elf64-x86-64] to '[[OUT]]' [elf64-x86-64]
+
+## In-place: a single positional argument means input == output.
+# RUN: cp %t/input.o %t/inplace.o
+# RUN: llvm-objcopy --verbose %t/inplace.o 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=OBJCOPY --implicit-check-not={{.}} \
+# RUN:     -DIN=%t/inplace.o -DOUT=%t/inplace.o
+
+## The output format name reflects the requested output type, not the input.
+# RUN: llvm-objcopy --verbose -O binary %t/input.o %t/output.bin 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=BINARY --implicit-check-not={{.}} \
+# RUN:     -DIN=%t/input.o -DOUT=%t/output.bin
+# BINARY: copy from '[[IN]]' [elf64-x86-64] to '[[OUT]]' [binary]
+
+## Explicit input/output formats (-I ihex -O srec): both format names come
+## from the command-line flags rather than the file content.
+# RUN: printf ':0100000001FE\n:00000001FF\n' > %t/input.hex
+# RUN: llvm-objcopy --verbose -I ihex -O srec %t/input.hex %t/output.srec 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=IHEX --implicit-check-not={{.}} \
+# RUN:     -DIN=%t/input.hex -DOUT=%t/output.srec
+# IHEX: copy from '[[IN]]' [ihex] to '[[OUT]]' [srec]
+
+## Non-ELF input (COFF i386): the format name reflects the actual object type and architecture
+# RUN: llvm-objcopy --verbose %t/coff.o %t/coff.out 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=COFF --implicit-check-not={{.}} \
+# RUN:     -DIN=%t/coff.o -DOUT=%t/coff.out
+# COFF: copy from '[[IN]]' [COFF-i386] to '[[OUT]]' [COFF-i386]
+
+## llvm-strip --verbose and -v each print one line per input file (in-place).
+# RUN: cp %t/input.o %t/strip1.o
+# RUN: cp %t/input.o %t/strip2.o
+# RUN: llvm-strip --verbose %t/strip1.o %t/strip2.o 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=STRIP --implicit-check-not={{.}} \
+# RUN:     -DF1=%t/strip1.o -DF2=%t/strip2.o
+# RUN: cp %t/input.o %t/strip3.o
+# RUN: cp %t/input.o %t/strip4.o
+# RUN: llvm-strip -v %t/strip3.o %t/strip4.o 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=STRIP --implicit-check-not={{.}} \
+# RUN:     -DF1=%t/strip3.o -DF2=%t/strip4.o
+# STRIP:      copy from '[[F1]]' [elf64-x86-64] to '[[F1]]' [elf64-x86-64]
+# STRIP-NEXT: copy from '[[F2]]' [elf64-x86-64] to '[[F2]]' [elf64-x86-64]
+
+## Archive: one line per member using <archive>(<member>) syntax.
+# RUN: cp %t/input.o %t/member1.o
+# RUN: cp %t/input.o %t/member2.o
+# RUN: rm -f %t/archive.a
+# RUN: llvm-ar crs %t/archive.a %t/member1.o %t/member2.o
+# RUN: llvm-objcopy --verbose %t/archive.a %t/archive.out 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=ARCHIVE --implicit-check-not={{.}} \
+# RUN:     -DAR=%t/archive.a -DOUT=%t/archive.out \
+# RUN:     -DMEM1=member1.o -DMEM2=member2.o
+# ARCHIVE:      copy from '[[AR]]([[MEM1]])' [elf64-x86-64] to '[[OUT]]([[MEM1]])' [elf64-x86-64]
+# ARCHIVE-NEXT: copy from '[[AR]]([[MEM2]])' [elf64-x86-64] to '[[OUT]]([[MEM2]])' [elf64-x86-64]
+
+## llvm-strip on an archive modifies in-place (OUT == AR).
+# RUN: rm -f %t/strip.a
+# RUN: llvm-ar crs %t/strip.a %t/member1.o %t/member2.o
+# RUN: llvm-strip --verbose %t/strip.a 2>/dev/null \
+# RUN:   | FileCheck %s --check-prefix=ARCHIVE --implicit-check-not={{.}} \
+# RUN:     -DAR=%t/strip.a -DOUT=%t/strip.a \
+# RUN:     -DMEM1=member1.o -DMEM2=member2.o
+
+## No output without the flag.
+# RUN: llvm-objcopy %t/input.o %t/output.o 2>/dev/null | count 0
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_X86_64
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_I386
+  Characteristics: []
+sections: []
+symbols: []

diff  --git a/llvm/tools/llvm-objcopy/CommonOpts.td b/llvm/tools/llvm-objcopy/CommonOpts.td
index 76bc00285f962..cc4504f7df6d6 100644
--- a/llvm/tools/llvm-objcopy/CommonOpts.td
+++ b/llvm/tools/llvm-objcopy/CommonOpts.td
@@ -122,6 +122,11 @@ def regex
     : Flag<["--"], "regex">,
       HelpText<"Permit regular expressions in name comparison">;
 
+def verbose : Flag<["--"], "verbose">,
+              HelpText<"Print the paths of files that are copied">;
+def v : Flag<["-"], "v">, Alias<verbose>,
+        HelpText<"Alias for --verbose">;
+
 def version : Flag<["--"], "version">,
               HelpText<"Print the version and exit.">;
 def V : Flag<["-"], "V">,

diff  --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index c5a83208c92a1..8f555beb45c35 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -1148,6 +1148,7 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
   Config.ExtractMainPartition =
       InputArgs.hasArg(OBJCOPY_extract_main_partition);
   ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
+  Config.Verbose = InputArgs.hasArg(OBJCOPY_verbose);
   Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
   if (auto *Arg =
           InputArgs.getLastArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) {
@@ -1682,6 +1683,7 @@ objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr,
                         STRIP_disable_deterministic_archives, /*default=*/true);
 
   Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates);
+  Config.Verbose = InputArgs.hasArg(STRIP_verbose);
   Config.InputFormat = FileFormat::Unspecified;
   Config.OutputFormat = FileFormat::Unspecified;
 

diff  --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index c199149f1e014..482249f67d38c 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -132,6 +132,22 @@ static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,
   llvm_unreachable("unsupported output format");
 }
 
+/// Returns the format name string for explicit file formats (binary, ihex,
+/// srec). Returns "" for all other formats so callers can fall back to the
+/// input object's own format string (e.g. "elf64-x86-64").
+static StringRef toFileFormatName(FileFormat Fmt) {
+  switch (Fmt) {
+  case FileFormat::Binary:
+    return "binary";
+  case FileFormat::IHex:
+    return "ihex";
+  case FileFormat::SREC:
+    return "srec";
+  default:
+    return "";
+  }
+}
+
 /// The function executeObjcopy does the higher level dispatch based on the type
 /// of input (raw binary, archive or single object file) and takes care of the
 /// format-agnostic modifications, i.e. preserving dates.
@@ -156,6 +172,11 @@ static Error executeObjcopy(ConfigManager &ConfigMgr) {
       return createFileError(Config.InputFilename, BufOrErr.getError());
     MemoryBufferHolder = std::move(*BufOrErr);
 
+    if (Config.Verbose)
+      printCopyMessage(
+          Config.InputFilename, toFileFormatName(Config.InputFormat),
+          Config.OutputFilename, toFileFormatName(Config.OutputFormat));
+
     if (Config.InputFormat == FileFormat::Binary)
       ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
         // Handle FileFormat::Binary.
@@ -175,10 +196,14 @@ static Error executeObjcopy(ConfigManager &ConfigMgr) {
     BinaryHolder = std::move(*BinaryOrErr);
 
     if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) {
-      // Handle Archive.
       if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar))
         return E;
     } else {
+      if (Config.Verbose)
+        printCopyMessage(Config.InputFilename,
+                         getObjectFormatName(*BinaryHolder.getBinary()),
+                         Config.OutputFilename,
+                         toFileFormatName(Config.OutputFormat));
       // Handle llvm::object::Binary.
       ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
         return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(),


        


More information about the llvm-commits mailing list