diff --git a/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h b/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h index d568aaf076ed..b8464966d148 100644 --- a/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h +++ b/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h @@ -31,15 +31,15 @@ public: : _baseAddress(0x400000), _stackReserve(1024 * 1024), _stackCommit(4096), _heapReserve(1024 * 1024), _heapCommit(4096), _subsystem(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN), - _machineType(llvm::COFF::IMAGE_FILE_MACHINE_I386), + _machineType(llvm::COFF::IMAGE_FILE_MACHINE_I386), _imageVersion(0, 0), _minOSVersion(6, 0), _nxCompat(true), _largeAddressAware(false), _baseRelocationEnabled(true), _terminalServerAware(true), _dynamicBaseEnabled(true), _imageType(ImageType::IMAGE_EXE) { setDeadStripping(true); } - struct OSVersion { - OSVersion(int v1, int v2) : majorVersion(v1), minorVersion(v2) {} + struct Version { + Version(int v1, int v2) : majorVersion(v1), minorVersion(v2) {} int majorVersion; int minorVersion; }; @@ -108,8 +108,11 @@ public: void setMachineType(MachineTypes type) { _machineType = type; } MachineTypes getMachineType() const { return _machineType; } - void setMinOSVersion(const OSVersion &version) { _minOSVersion = version; } - OSVersion getMinOSVersion() const { return _minOSVersion; } + void setImageVersion(const Version &version) { _imageVersion = version; } + Version getImageVersion() const { return _imageVersion; } + + void setMinOSVersion(const Version &version) { _minOSVersion = version; } + Version getMinOSVersion() const { return _minOSVersion; } void setNxCompat(bool nxCompat) { _nxCompat = nxCompat; } bool isNxCompat() const { return _nxCompat; } @@ -163,7 +166,8 @@ private: uint64_t _heapCommit; WindowsSubsystem _subsystem; MachineTypes _machineType; - OSVersion _minOSVersion; + Version _imageVersion; + Version _minOSVersion; bool _nxCompat; bool _largeAddressAware; bool _baseRelocationEnabled; diff --git a/lld/lib/Driver/WinLinkDriver.cpp b/lld/lib/Driver/WinLinkDriver.cpp index 747786a79ac2..8aca2a97440a 100644 --- a/lld/lib/Driver/WinLinkDriver.cpp +++ b/lld/lib/Driver/WinLinkDriver.cpp @@ -98,6 +98,20 @@ bool parseMemoryOption(StringRef arg, uint64_t &reserve, uint64_t &commit) { return false; } +// Parse an argument for /version or /subsystem. The expected string is +// "[.]". +bool parseVersion(StringRef arg, uint32_t &major, uint32_t &minor) { + StringRef majorVersion, minorVersion; + llvm::tie(majorVersion, minorVersion) = arg.split('.'); + if (minorVersion.empty()) + minorVersion = "0"; + if (majorVersion.getAsInteger(0, major)) + return true; + if (minorVersion.getAsInteger(0, minor)) + return true; + return false; +} + // Returns subsystem type for the given string. llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) { return llvm::StringSwitch(str.lower()) @@ -326,6 +340,13 @@ bool WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ct ctx.setMachineType(type); break; } + case OPT_version: { + uint32_t major, minor; + if (parseVersion(inputArg->getValue(), major, minor)) + return true; + ctx.setImageVersion(PECOFFLinkingContext::Version(major, minor)); + break; + } case OPT_subsystem: { // Parse /subsystem command line option. The form of /subsystem is // "subsystem_name[,majorOSVersion[.minorOSVersion]]". @@ -333,16 +354,10 @@ bool WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ct llvm::tie(subsystemStr, osVersion) = StringRef(inputArg->getValue()).split(','); if (!osVersion.empty()) { - StringRef majorVersion, minorVersion; - llvm::tie(majorVersion, minorVersion) = osVersion.split('.'); - if (minorVersion.empty()) - minorVersion = "0"; - int32_t major, minor; - if (majorVersion.getAsInteger(0, major)) + uint32_t major, minor; + if (parseVersion(osVersion, major, minor)) return true; - if (minorVersion.getAsInteger(0, minor)) - return true; - ctx.setMinOSVersion(PECOFFLinkingContext::OSVersion(major, minor)); + ctx.setMinOSVersion(PECOFFLinkingContext::Version(major, minor)); } // Parse subsystem name. llvm::COFF::WindowsSubsystem subsystem = diff --git a/lld/lib/Driver/WinLinkOptions.td b/lld/lib/Driver/WinLinkOptions.td index 53883fab3ee5..bf8eea24dccf 100644 --- a/lld/lib/Driver/WinLinkOptions.td +++ b/lld/lib/Driver/WinLinkOptions.td @@ -20,6 +20,7 @@ defm mllvm : P<"mllvm", "Options to pass to LLVM">; defm out : P<"out", "Path to file to write output">; defm stack : P<"stack", "Size of the stack">; defm machine : P<"machine", "Specify target platform">; +defm version : P<"version", "Specify a version number in the PE header">; defm subsystem : P<"subsystem", "Specify subsystem">; // We cannot use multiclass P because class name "incl" is different diff --git a/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp b/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp index eb28ff5b03ca..18e876804f68 100644 --- a/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp +++ b/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp @@ -179,10 +179,17 @@ public: // Sections in an executable file on disk should be sector-aligned (512 byte). _peHeader.FileAlignment = SECTOR_SIZE; + // The version number of the resultant executable/DLL. The number is purely + // informative, and neither the linker nor the loader won't use it. User can + // set the value using /version command line option. Default is 0.0. + PECOFFLinkingContext::Version imageVersion = context.getImageVersion(); + _peHeader.MajorImageVersion = imageVersion.majorVersion; + _peHeader.MinorImageVersion = imageVersion.minorVersion; + // The required Windows version number. This is the internal version and // shouldn't be confused with product name. Windows 7 is version 6.1 and // Windows 8 is 6.2, for example. - PECOFFLinkingContext::OSVersion minOSVersion = context.getMinOSVersion(); + PECOFFLinkingContext::Version minOSVersion = context.getMinOSVersion(); _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion; _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion; _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion; diff --git a/lld/test/pecoff/trivial.test b/lld/test/pecoff/trivial.test index 11c735de4c89..d3949c98ae9d 100644 --- a/lld/test/pecoff/trivial.test +++ b/lld/test/pecoff/trivial.test @@ -4,8 +4,8 @@ # # RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj # -# RUN: lld -flavor link /out:%t1 /subsystem:console,3.11 /entry:start \ -# RUN: /opt:noref -- %t.obj && llvm-readobj -file-headers %t1 \ +# RUN: lld -flavor link /out:%t1 /subsystem:console,3.11 /version:1.25 \ +# RUN: /entry:start /opt:noref -- %t.obj && llvm-readobj -file-headers %t1 \ # RUN: | FileCheck -check-prefix=FILE %s # # RUN: lld -flavor link /out:%t1 /entry:start /opt:noref -- %t.obj \ @@ -40,8 +40,8 @@ FILE: SectionAlignment: 4096 FILE: FileAlignment: 512 FILE: MajorOperatingSystemVersion: 3 FILE: MinorOperatingSystemVersion: 11 -FILE: MajorImageVersion: 0 -FILE: MinorImageVersion: 0 +FILE: MajorImageVersion: 1 +FILE: MinorImageVersion: 25 FILE: MajorSubsystemVersion: 3 FILE: MinorSubsystemVersion: 11 FILE: SizeOfImage: 8192 diff --git a/lld/unittests/DriverTests/WinLinkDriverTest.cpp b/lld/unittests/DriverTests/WinLinkDriverTest.cpp index 5cf9512a66d0..fec482ab715d 100644 --- a/lld/unittests/DriverTests/WinLinkDriverTest.cpp +++ b/lld/unittests/DriverTests/WinLinkDriverTest.cpp @@ -111,6 +111,18 @@ TEST_F(WinLinkParserTest, MachineX64) { EXPECT_TRUE(parse("link.exe", "/machine:x64", "a.obj", nullptr)); } +TEST_F(WinLinkParserTest, MajorImageVersion) { + EXPECT_FALSE(parse("link.exe", "/version:7", "foo.o", nullptr)); + EXPECT_EQ(7, _context.getImageVersion().majorVersion); + EXPECT_EQ(0, _context.getImageVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MajorMinorImageVersion) { + EXPECT_FALSE(parse("link.exe", "/version:72.35", "foo.o", nullptr)); + EXPECT_EQ(72, _context.getImageVersion().majorVersion); + EXPECT_EQ(35, _context.getImageVersion().minorVersion); +} + TEST_F(WinLinkParserTest, MinMajorOSVersion) { EXPECT_FALSE(parse("link.exe", "/subsystem:windows,3", "foo.o", nullptr)); EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI, _context.getSubsystem());