From 392aa97acc056d6d57f7a5b8f52cd9c3060869dd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martin=20Storsj=C3=B6?= Date: Mon, 3 Jan 2022 15:51:37 +0200 Subject: [PATCH] [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 --- llvm/docs/CommandGuide/llvm-objcopy.rst | 7 +++ llvm/test/tools/llvm-objcopy/COFF/pe-fields.test | 20 ++++++++ llvm/test/tools/llvm-objcopy/COFF/subsystem.test | 61 ++++++++++++++++++++++++ llvm/tools/llvm-objcopy/COFF/COFFConfig.h | 8 +++- llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 25 ++++++++-- llvm/tools/llvm-objcopy/ConfigManager.cpp | 42 ++++++++++++++++ llvm/tools/llvm-objcopy/ObjcopyOpts.td | 5 ++ 7 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 llvm/test/tools/llvm-objcopy/COFF/pe-fields.test create mode 100644 llvm/test/tools/llvm-objcopy/COFF/subsystem.test diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst index 21f1a53..6193d63 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 [:] + + 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 0000000..d275959 --- /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 0000000..4d73ed8 --- /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 3897ff4..7bf673f 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 Subsystem; + Optional MajorSubsystemVersion; + Optional MinorSubsystemVersion; +}; } // namespace objcopy } // namespace llvm diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index 38c9cd0..e0039cd 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> 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 2e5cf93..90730c4 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 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 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(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(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 bc62444..bfd66ca 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< -- 2.7.4