Index: COFF/Config.h =================================================================== --- COFF/Config.h +++ COFF/Config.h @@ -173,6 +173,9 @@ // Used for /lldmap. std::string MapFile; + // Used for /functionpadmin. + uint32_t Padding = 0; + uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; Index: COFF/Driver.h =================================================================== --- COFF/Driver.h +++ COFF/Driver.h @@ -159,6 +159,9 @@ void parseSection(StringRef); void parseAligncomm(StringRef); +// Parses a string in the form of "[:]" +void parseFunctionpadmin(StringRef Arg, llvm::COFF::MachineTypes Machine); + // Parses a string in the form of "EMBED[,=]|NO". void parseManifest(StringRef Arg); Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -1278,6 +1278,10 @@ if (Config->Manifest == Configuration::Embed) addBuffer(createManifestRes(), false); + // Handle /functionpadmin + for (auto *Arg : Args.filtered(OPT_functionpadmin)) + parseFunctionpadmin(Arg->getValue(), Config->Machine); + // Read all input files given via the command line. run(); Index: COFF/DriverUtils.cpp =================================================================== --- COFF/DriverUtils.cpp +++ COFF/DriverUtils.cpp @@ -250,6 +250,32 @@ Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V); } +// Parses /functionpadmin option argument. +void parseFunctionpadmin(StringRef Arg, llvm::COFF::MachineTypes Machine) { + StringRef Name, Padding; + std::tie(Name, Padding) = Arg.split(':'); + if (!Padding.empty()) { + // Optional padding in bytes is given. + if (Padding.getAsInteger(0, Config->Padding)) { + error("/functionpadmin: invalid argument: " + Arg); + return; + } + } else { + // No optional argument given. + // Set default padding based on machine, similar to link.exe. + // There is no default padding for ARM platforms. + if (Machine == I386) { + Config->Padding = 5; + } else if (Machine == AMD64) { + Config->Padding = 6; + } else if (Machine == llvm::COFF::IMAGE_FILE_MACHINE_IA64) { + Config->Padding = 16; + } else { + error("/functionpadmin: invalid argument for this machine: " + Arg); + } + } +} + // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to Config. void parseManifest(StringRef Arg) { Index: COFF/Options.td =================================================================== --- COFF/Options.td +++ COFF/Options.td @@ -61,6 +61,7 @@ def timestamp : P<"timestamp", "Specify the PE header timestamp">; def version : P<"version", "Specify a version number in the PE header">; def wholearchive_file : P<"wholearchive", "Include all object files from this archive">; +def functionpadmin : Joined<["/", "-", "-?"], "functionpadmin">, HelpText<"Create hotpatchable image">; def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias; @@ -166,7 +167,6 @@ def _no : F; } -def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; Index: COFF/Writer.cpp =================================================================== --- COFF/Writer.cpp +++ COFF/Writer.cpp @@ -745,7 +745,23 @@ addBaserels(); uint64_t RawSize = 0, VirtualSize = 0; Sec->Header.VirtualAddress = RVA; + + // If /FUNCTIONPADMIN is used, functions are padded in order to create a + // hotpatchable image. + const bool IsCodeSection = + (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) && + (Sec->Header.Characteristics & IMAGE_SCN_MEM_READ) && + (Sec->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE); + uint32_t Padding = IsCodeSection ? Config->Padding : 0; + for (Chunk *C : Sec->getChunks()) { + + // Apply padding to SectionChunks only. + if ((Padding != 0) && dyn_cast(C)) { + RawSize += Padding; + VirtualSize += Padding; + } + VirtualSize = alignTo(VirtualSize, C->Alignment); C->setRVA(RVA + VirtualSize); C->OutputSectionOff = VirtualSize;