diff --git a/llvm/test/tools/llvm-objcopy/COFF/subsystem.test b/llvm/test/tools/llvm-objcopy/COFF/subsystem.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/COFF/subsystem.test @@ -0,0 +1,42 @@ +## Test the --subsystem option. + +# RUN: yaml2obj %s -o %t.in.exe + +# RUN: llvm-objcopy --subsystem=posix:7.1 --subsystem windows %t.in.exe %t.out.exe +# RUN: llvm-readobj --file-headers %t.out.exe | FileCheck %s + +# CHECK: MajorOperatingSystemVersion: 6 +# CHECK: MinorOperatingSystemVersion: 0 +# CHECK: MajorSubsystemVersion: 7 +# CHECK: MinorSubsystemVersion: 1 +# CHECK: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI + +--- !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 --- 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 --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -130,7 +130,8 @@ 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,32 @@ if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink)) return E; + if (COFFConfig.Subsystem || COFFConfig.MajorSubsystemVersion || + COFFConfig.MinorSubsystemVersion) { + if (!Obj.IsPE) + return createStringError(llvm::errc::invalid_argument, + "Unable to set subsystem on 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 --- 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 @@ 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 @@ VisibilityStr.str().c_str()); } + for (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 --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -105,6 +105,11 @@ "if they are not needed by relocations">, MetaVarName<"filename">; +defm subsystem + : Eq<"subsystem", + "[COFF only] Set PE subsystem and version">, + MetaVarName<"name[:version]">; + def extract_dwo : Flag<["--"], "extract-dwo">, HelpText<