[llvm] 392aa97 - [llvm-objcopy] Implement the PE-COFF specific --subsystem option

Martin Storsjö via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 10 04:44:44 PST 2022


Author: Martin Storsjö
Date: 2022-01-10T14:44:15+02:00
New Revision: 392aa97acc056d6d57f7a5b8f52cd9c3060869dd

URL: https://github.com/llvm/llvm-project/commit/392aa97acc056d6d57f7a5b8f52cd9c3060869dd
DIFF: https://github.com/llvm/llvm-project/commit/392aa97acc056d6d57f7a5b8f52cd9c3060869dd.diff

LOG: [llvm-objcopy] Implement the PE-COFF specific --subsystem option

This implements the parsing of the highly PE-COFF specific option
in ConfigManager.cpp, setting Optional<> values in COFFConfig, which
then are used in COFFObjcopy.

This should fix https://github.com/mstorsjo/llvm-mingw/issues/239.

Differential Revision: https://reviews.llvm.org/D116556

Added: 
    llvm/test/tools/llvm-objcopy/COFF/pe-fields.test
    llvm/test/tools/llvm-objcopy/COFF/subsystem.test

Modified: 
    llvm/docs/CommandGuide/llvm-objcopy.rst
    llvm/tools/llvm-objcopy/COFF/COFFConfig.h
    llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
    llvm/tools/llvm-objcopy/ConfigManager.cpp
    llvm/tools/llvm-objcopy/ObjcopyOpts.td

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 21f1a53e593cf..6193d637d0486 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -477,6 +477,13 @@ MACH-O-SPECIFIC OPTIONS
 
  Keep undefined symbols, even if they would otherwise be stripped.
 
+COFF-SPECIFIC OPTIONS
+---------------------
+
+.. option:: --subsystem <name>[:<version>]
+
+ Set the PE subsystem, and optionally subsystem version.
+
 SUPPORTED FORMATS
 -----------------
 

diff  --git a/llvm/test/tools/llvm-objcopy/COFF/pe-fields.test b/llvm/test/tools/llvm-objcopy/COFF/pe-fields.test
new file mode 100644
index 0000000000000..d2759590dc053
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/pe-fields.test
@@ -0,0 +1,20 @@
+## Test that options for altering PE header fields error out on object files.
+
+# RUN: yaml2obj %s -o %t.in.obj
+
+# RUN: not llvm-objcopy --subsystem windows %t.in.obj %t.out.obj 2>&1 | FileCheck %s -DFILE=%t.out.obj
+
+# CHECK: '[[FILE]]': unable to set subsystem on a relocatable object file
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            .text
+    Characteristics: [  ]
+    VirtualAddress:  4096
+    VirtualSize:     1
+    SectionData:     C3
+symbols:
+...

diff  --git a/llvm/test/tools/llvm-objcopy/COFF/subsystem.test b/llvm/test/tools/llvm-objcopy/COFF/subsystem.test
new file mode 100644
index 0000000000000..4d73ed83941c7
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/COFF/subsystem.test
@@ -0,0 +1,61 @@
+## Test the --subsystem option.
+
+# RUN: yaml2obj %s -o %t.in.exe
+
+# RUN: llvm-objcopy --subsystem=posix:7.4 --subsystem windows %t.in.exe %t.out.exe
+# RUN: llvm-readobj --file-headers %t.out.exe | FileCheck %s --check-prefix=WIN74
+
+# WIN74: MajorOperatingSystemVersion: 6
+# WIN74: MinorOperatingSystemVersion: 0
+# WIN74: MajorSubsystemVersion: 7
+# WIN74: MinorSubsystemVersion: 4
+# WIN74: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI
+
+# RUN: llvm-objcopy --subsystem=posix:7.4 --subsystem windows:9 %t.in.exe %t.out.exe
+# RUN: llvm-readobj --file-headers %t.out.exe | FileCheck %s --check-prefix=WIN90
+
+# WIN90: MajorOperatingSystemVersion: 6
+# WIN90: MinorOperatingSystemVersion: 0
+# WIN90: MajorSubsystemVersion: 9
+# WIN90: MinorSubsystemVersion: 0
+# WIN90: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI
+
+# RUN: not llvm-objcopy --subsystem=foobar %t.in.exe %t.err.exe 2>&1 | FileCheck %s --check-prefix=INVALID-SUBSYS
+
+# INVALID-SUBSYS: 'foobar' is not a valid subsystem{{$}}
+
+# RUN: not llvm-objcopy --subsystem=windows:foo %t.in.exe %t.err.exe 2>&1 | FileCheck %s --check-prefix=INVALID-MAJOR-NUMBER
+# RUN: not llvm-objcopy --subsystem=windows:8.bar %t.in.exe %t.err.exe 2>&1 | FileCheck %s --check-prefix=INVALID-MINOR-NUMBER
+
+# INVALID-MAJOR-NUMBER: 'foo' is not a valid subsystem major version
+# INVALID-MINOR-NUMBER: 'bar' is not a valid subsystem minor version
+
+--- !COFF
+OptionalHeader:
+  AddressOfEntryPoint: 4096
+  ImageBase:       1073741824
+  SectionAlignment: 4096
+  FileAlignment:   512
+  MajorOperatingSystemVersion: 6
+  MinorOperatingSystemVersion: 0
+  MajorImageVersion: 0
+  MinorImageVersion: 0
+  MajorSubsystemVersion: 6
+  MinorSubsystemVersion: 0
+  Subsystem:       IMAGE_SUBSYSTEM_WINDOWS_CUI
+  DLLCharacteristics: [  ]
+  SizeOfStackReserve: 1048576
+  SizeOfStackCommit: 4096
+  SizeOfHeapReserve: 1048576
+  SizeOfHeapCommit: 4096
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            .text
+    Characteristics: [  ]
+    VirtualAddress:  4096
+    VirtualSize:     1
+    SectionData:     C3
+symbols:
+...

diff  --git a/llvm/tools/llvm-objcopy/COFF/COFFConfig.h b/llvm/tools/llvm-objcopy/COFF/COFFConfig.h
index 3897ff47724bb..7bf673fa4af9b 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFConfig.h
+++ b/llvm/tools/llvm-objcopy/COFF/COFFConfig.h
@@ -9,11 +9,17 @@
 #ifndef LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
 #define LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
 
+#include "llvm/ADT/Optional.h"
+
 namespace llvm {
 namespace objcopy {
 
 // Coff specific configuration for copying/stripping a single file.
-struct COFFConfig {};
+struct COFFConfig {
+  Optional<unsigned> Subsystem;
+  Optional<unsigned> MajorSubsystemVersion;
+  Optional<unsigned> MinorSubsystemVersion;
+};
 
 } // namespace objcopy
 } // namespace llvm

diff  --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 38c9cd09433b6..e0039cd3a675f 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -130,7 +130,8 @@ static uint32_t flagsToCharacteristics(SectionFlag AllFlags, uint32_t OldChar) {
   return NewCharacteristics;
 }
 
-static Error handleArgs(const CommonConfig &Config, Object &Obj) {
+static Error handleArgs(const CommonConfig &Config,
+                        const COFFConfig &COFFConfig, Object &Obj) {
   // Perform the actual section removals.
   Obj.removeSections([&Config](const Section &Sec) {
     // Contrary to --only-keep-debug, --only-section fully removes sections that
@@ -256,18 +257,34 @@ static Error handleArgs(const CommonConfig &Config, Object &Obj) {
     if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink))
       return E;
 
+  if (COFFConfig.Subsystem || COFFConfig.MajorSubsystemVersion ||
+      COFFConfig.MinorSubsystemVersion) {
+    if (!Obj.IsPE)
+      return createStringError(
+          errc::invalid_argument,
+          "'" + Config.OutputFilename +
+              "': unable to set subsystem on a relocatable object file");
+    if (COFFConfig.Subsystem)
+      Obj.PeHeader.Subsystem = *COFFConfig.Subsystem;
+    if (COFFConfig.MajorSubsystemVersion)
+      Obj.PeHeader.MajorSubsystemVersion = *COFFConfig.MajorSubsystemVersion;
+    if (COFFConfig.MinorSubsystemVersion)
+      Obj.PeHeader.MinorSubsystemVersion = *COFFConfig.MinorSubsystemVersion;
+  }
+
   return Error::success();
 }
 
-Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &,
-                             COFFObjectFile &In, raw_ostream &Out) {
+Error executeObjcopyOnBinary(const CommonConfig &Config,
+                             const COFFConfig &COFFConfig, COFFObjectFile &In,
+                             raw_ostream &Out) {
   COFFReader Reader(In);
   Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
   if (!ObjOrErr)
     return createFileError(Config.InputFilename, ObjOrErr.takeError());
   Object *Obj = ObjOrErr->get();
   assert(Obj && "Unable to deserialize COFF object");
-  if (Error E = handleArgs(Config, *Obj))
+  if (Error E = handleArgs(Config, COFFConfig, *Obj))
     return createFileError(Config.InputFilename, std::move(E));
   COFFWriter Writer(*Obj, Out);
   if (Error E = Writer.write())

diff  --git a/llvm/tools/llvm-objcopy/ConfigManager.cpp b/llvm/tools/llvm-objcopy/ConfigManager.cpp
index 2e5cf9357a525..90730c421a46d 100644
--- a/llvm/tools/llvm-objcopy/ConfigManager.cpp
+++ b/llvm/tools/llvm-objcopy/ConfigManager.cpp
@@ -11,6 +11,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/BinaryFormat/COFF.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/CRC.h"
@@ -675,6 +676,7 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
 
   ConfigManager ConfigMgr;
   CommonConfig &Config = ConfigMgr.Common;
+  COFFConfig &COFFConfig = ConfigMgr.COFF;
   ELFConfig &ELFConfig = ConfigMgr.ELF;
   MachOConfig &MachOConfig = ConfigMgr.MachO;
   Config.InputFilename = Positional[0];
@@ -733,6 +735,46 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
                                VisibilityStr.str().c_str());
   }
 
+  for (const auto *Arg : InputArgs.filtered(OBJCOPY_subsystem)) {
+    StringRef Subsystem, Version;
+    std::tie(Subsystem, Version) = StringRef(Arg->getValue()).split(':');
+    COFFConfig.Subsystem =
+        StringSwitch<unsigned>(Subsystem.lower())
+            .Case("boot_application",
+                  COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
+            .Case("console", COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI)
+            .Case("efi_application", COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION)
+            .Case("efi_boot_service_driver",
+                  COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
+            .Case("efi_rom", COFF::IMAGE_SUBSYSTEM_EFI_ROM)
+            .Case("efi_runtime_driver",
+                  COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)
+            .Case("native", COFF::IMAGE_SUBSYSTEM_NATIVE)
+            .Case("posix", COFF::IMAGE_SUBSYSTEM_POSIX_CUI)
+            .Case("windows", COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI)
+            .Default(COFF::IMAGE_SUBSYSTEM_UNKNOWN);
+    if (*COFFConfig.Subsystem == COFF::IMAGE_SUBSYSTEM_UNKNOWN)
+      return createStringError(errc::invalid_argument,
+                               "'%s' is not a valid subsystem",
+                               Subsystem.str().c_str());
+    if (!Version.empty()) {
+      StringRef Major, Minor;
+      std::tie(Major, Minor) = Version.split('.');
+      unsigned Number;
+      if (Major.getAsInteger(10, Number))
+        return createStringError(errc::invalid_argument,
+                                 "'%s' is not a valid subsystem major version",
+                                 Major.str().c_str());
+      COFFConfig.MajorSubsystemVersion = Number;
+      Number = 0;
+      if (!Minor.empty() && Minor.getAsInteger(10, Number))
+        return createStringError(errc::invalid_argument,
+                                 "'%s' is not a valid subsystem minor version",
+                                 Minor.str().c_str());
+      COFFConfig.MinorSubsystemVersion = Number;
+    }
+  }
+
   Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
                             .Case("binary", FileFormat::Binary)
                             .Case("ihex", FileFormat::IHex)

diff  --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
index bc624442aa519..bfd66caf41ed0 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -105,6 +105,11 @@ defm strip_unneeded_symbols
          "if they are not needed by relocations">,
       MetaVarName<"filename">;
 
+defm subsystem
+    : Eq<"subsystem",
+         "Set PE subsystem and version">,
+      MetaVarName<"name[:version]">;
+
 def extract_dwo
     : Flag<["--"], "extract-dwo">,
       HelpText<


        


More information about the llvm-commits mailing list