This is an archive of the discontinued LLVM Phabricator instance.

[LLD][COFF] Add support for /FUNCTIONPADMIN command-line option
ClosedPublic

Authored by aganea on Jul 16 2018, 2:16 AM.

Details

Summary

This patch adds support for the /FUNCTIONPADMIN command-line option (allowing an image to be hotpatched) described here: https://msdn.microsoft.com/en-us/library/ms173524.aspx

Even though LLD understands /FUNCTIONPADMIN, all it does is ignore it.
I added parsing of this command-line option (note that it accepts an additional integer argument specifying the number of padding bytes, e.g. /FUNCTIONPADMIN:10) and added support for it in the section chunks and when writing PDB contributions.

Diff Detail

Repository
rL LLVM

Event Timeline

zturner requested changes to this revision.Jul 16 2018, 9:10 AM
zturner added reviewers: rnk, ruiu, pcc.
zturner added subscribers: ruiu, rnk.

You'll definitely need to add a test here, hopefully @ruiu or @rnk can suggest the best way to write one. But I think there should be at least 3. One that tests /FUNCTIONPADMIN with x86 produces 5, one that tests /FUNCTIONPADMIN with x64 produces 6, and one that tests that /FUNCTIONPADMIN:N produces N bytes of padding.

COFF/DriverUtils.cpp
271–273 ↗(On Diff #155632)

What about ARM64? The documentation doesn't say what the default value is here, but it should be easy to figure out.

COFF/PDB.cpp
118–122 ↗(On Diff #155632)

It looks like you may have run clang-format against the entire source files, rather than against only the lines that you changed. Would it be possible to revert these changes and only clang-format changed lines? There's a script called git-clang-format in the clang source tree. Usually what we do is add this location to PATH, and then run git clang-format That will only format touched lines.

790 ↗(On Diff #155632)

That is quite odd. Wouldn't this mean that the PDB has incorrect info? I wonder how this could possibly be correct. So either it's a bug in link.exe or there's something I don't understand about what PDB conumers use this information for.

This revision now requires changes to proceed.Jul 16 2018, 9:10 AM
COFF/DriverUtils.cpp
271–273 ↗(On Diff #155632)

Understood, I will try figuring this out.

COFF/PDB.cpp
118–122 ↗(On Diff #155632)

Yes, I ran clang-format against all touched files. I thought this was the intention to make everything use the same format, but can revert the changes of course.

790 ↗(On Diff #155632)

I don't think this means that the PDB has incorrect info, but rather that the padding bytes are not accounted for anywhere.
E.g. take a function SimpleFunction at RVA 0x3190:

The public symbol stored will hold its address:
PublicSymbol: [00003190][0001:00002190] ?SimpleFunction@@YAXXZ(void __cdecl SimpleFunction(void))

The function stored inside the compiland will hold the following info:
Function : static, [00003190][0001:00002190], len = 00000020, void __cdecl SimpleFunction(void)
FuncDebugStart : static, [00003194][0001:00002194]
FuncDebugEnd : static, [000031A7][0001:000021A7]

This stores the address, the length, and end of the prologue as well as start of the epilogue. None of these are concerned about padding bytes. The contribution also shows 0x20 bytes being contributed, which does *not* include padding bytes:
00003190 0001:00002190 00000020 C:\SomePath\SimpleFunction.obj

The padding bytes between functions are 'unaccounted' for, so to speak.
I think this is by design, otherwise you'd get different info from the compiland/function stream and the contribution stream.

Note that padding bytes are inserted in front of functions (=between section chunks), but function entry points/RVAs still point at the first code byte, not any padding.

zturner added inline comments.Jul 16 2018, 9:31 AM
COFF/PDB.cpp
118–122 ↗(On Diff #155632)

Usually we try to keep CLs limited to only changes that contribute to the functionality you're implementing in this patch. clang-formatting an entire file might be worthwile, but we should do it in a separate patch (before or after this patch doesn't matter) that way the only thing we see on the review is changes specifically related to implementing /FUNCTIONPADMIN

pcc added a comment.Jul 16 2018, 10:43 AM

Unless I'm missing something, doesn't this patch end up adding padding to the end of every code section rather than the beginning?

It seems like this ought to be implemented by changing the section layout code. I'm thinking that you would add code to http://llvm-cs.pcc.me.uk/tools/lld/COFF/Writer.cpp#742 that does this:

if (C requires padding) {
  RawSize += Padding;
  VirtualSize += Padding;
}

With that I don't think you need to add fields for storing the amount of padding required, you can just figure it out on the fly there.

COFF/DriverUtils.cpp
271–273 ↗(On Diff #155632)

And ARM32.

COFF/PDB.cpp
790 ↗(On Diff #155632)

If you move the implementation of /functionpadmin to section layout I don't think you would need to worry about that here.

In D49366#1163821, @pcc wrote:

Unless I'm missing something, doesn't this patch end up adding padding to the end of every code section rather than the beginning?

It seems like this ought to be implemented by changing the section layout code. I'm thinking that you would add code to http://llvm-cs.pcc.me.uk/tools/lld/COFF/Writer.cpp#742 that does this:

if (C requires padding) {
  RawSize += Padding;
  VirtualSize += Padding;
}

With that I don't think you need to add fields for storing the amount of padding required, you can just figure it out on the fly there.

Fully agree. I like your suggestion because it's also less invasive.

stefan_reinalter marked 3 inline comments as done.Jul 17 2018, 6:13 AM
stefan_reinalter added inline comments.
COFF/DriverUtils.cpp
271–273 ↗(On Diff #155632)

When building for ARM using MSVC/link.exe, using /FUNCTIONPADMIN yields:
LINK : fatal error LNK1146: no argument specified with option '/FUNCTIONPADMIN'

Seems like there is no default for these platforms.

stefan_reinalter marked an inline comment as done.

I updated the implementation according to the recommendation by Peter. I also added a comment regarding /FUNCTIONPADMIN default padding on ARM platforms.
Also ran git-clang-format against the source files so that the diff shows only my changes.

Tests are still missing though, I'm waiting for your advice on this.

pcc added a comment.Jul 17 2018, 12:16 PM

Probably the best way to write the test would be to have it create an input file with a .text section containing the entry point and a non-code section and relocations pointing from the .text section to itself as well as to the non-code section, and use llvm-objdump to test that (1) the .text section has an appropriate amount of padding behind it in the output file, (2) the non-code section does not receive padding and (3) the relocation points to the address of the section (i.e. not the address of the padding). You can see an example of this sort of test in lld/test/COFF/string-tail-merge.s. By using llvm-mc to assemble the test with different target triples it should be possible to write it in such a way that you don't need a separate test for each target.

COFF/Config.h
177 ↗(On Diff #155875)

Nit: we tend to name configuration fields after the flag that they're derived from. So this one should be called FunctionPadMin I think. With that you don't need the comment either because it becomes redundant with the name of the field, and you can group it with the other uint32_t fields below.

COFF/DriverUtils.cpp
271–273 ↗(On Diff #155632)

I wouldn't add a padding amount for IA64 here since the rest of the linker doesn't support it so it is basically dead code.

COFF/Writer.cpp
760 ↗(On Diff #155875)

It looks like you don't need to protect this code with an if statement because there's no harm in padding compiler-generated sections, and link.exe does it too (try running link.exe /functionpadmin:4096 /entry:ExitProcess /subsystem:console /out:exit.exe kernel32.lib and disassembling exit.exe).

761 ↗(On Diff #155875)

Taking a closer look at the code, it looks like RawSize is updated automatically at the end of the loop, so you don't need to update it here.

COFF/Writer.cpp
761 ↗(On Diff #155875)

Can we assume that C->hasData() is always true in this case?

pcc added inline comments.Jul 18 2018, 10:27 AM
COFF/Writer.cpp
761 ↗(On Diff #155875)

As it happens it should always be true for chunks in the text section (what it means is that the section is not uninitialized data, i.e. bss) but even in the case where it weren't true we wouldn't want to change RawSize because that variable contains the amount of initialized data in the section and it shouldn't be affected by any uninitialized data sections.

aganea commandeered this revision.EditedNov 30 2018, 6:01 AM
aganea added a reviewer: stefan_reinalter.

After discussing offline with Stefan, I will take ownership and will finish this patch.

aganea planned changes to this revision.Dec 4 2018, 9:25 AM

Just for the record, this is not as simple as I initially thought. We should only be padding functions that originate from hotpatch-enabled OBJs. Currently, LLVM currently does not generate /hotpatch OBJs (at least the flag isn't set). In theory, x64 and ARM OBJs should be fine to have this flag enabled. I'll do more research and get back.

aganea updated this revision to Diff 187590.EditedFeb 20 2019, 8:32 AM
aganea marked 5 inline comments as done.

Adressed all comments. Added tests as suggested by @pcc

I've also added proper handling for the [[ https://docs.microsoft.com/en-us/cpp/build/reference/hotpatch-create-hotpatchable-image?view=vs-2017 | /HOTPATCH ]] compile-time flag. If this flag is not present in the input, then /FUNCTIONPADMIN will not do anything for functions originating from that input. Linker-generated DLL thunks are always padded when providing /FUNCTIONPADMIN.

Incidentally, Clang currently doesn't generate the CompileSym3Flags::HotPatch flag in S_COMPILE3 records. So as it stands, /FUNCTIONPADMIN will currently not work with Clang OBJs. This shall be fixed in a subsequent patch.

Herald added a project: Restricted Project. · View Herald TranscriptFeb 20 2019, 8:32 AM
Herald added a subscriber: jdoerfert. · View Herald Transcript
aganea retitled this revision from Add support for /FUNCTIONPADMIN command-line option to [LLD][COFF] Add support for /FUNCTIONPADMIN command-line option.Feb 20 2019, 8:34 AM
ruiu added inline comments.Feb 20 2019, 10:00 AM
COFF/DriverUtils.cpp
260–261 ↗(On Diff #187590)

Return early.

COFF/InputFiles.h
163 ↗(On Diff #187590)

Why Optional?

aganea updated this revision to Diff 187618.Feb 20 2019, 10:42 AM
aganea marked 4 inline comments as done.
aganea added inline comments.
COFF/InputFiles.cpp
621 ↗(On Diff #187590)

There's a typo here - it shall read be continue; instead of return;.

COFF/InputFiles.h
163 ↗(On Diff #187590)

It's a reliquate. Fixed.
I'll also change it for PCHSignature above in a subsequent NFC patch.

aganea marked an inline comment as done.Feb 20 2019, 10:55 AM
aganea added inline comments.
COFF/InputFiles.cpp
621 ↗(On Diff #187590)

Sorry never mind, this was meant for the previous revision. Fixed now.

rnk added a comment.Feb 20 2019, 11:30 AM

Neat!

COFF/Driver.h
160 ↗(On Diff #187618)

Maybe parseFunctionPadMin to help readability? Initially I parsed it as "/function p admin", but I don't think that's right.

test/COFF/functionpadmin.test
7 ↗(On Diff #187618)

Why objdump -s instead of objdump -d to look at the disassembly? Maybe the idea is that part of the test is to show that .rdata sections and .pdata sections are not padded with 0xCC? I guess that's good.

aganea updated this revision to Diff 187642.Feb 20 2019, 11:45 AM
aganea marked 3 inline comments as done.
aganea added inline comments.
test/COFF/functionpadmin.test
7 ↗(On Diff #187618)

Yes that was part of Peter's suggestion for testing: ensure only code sections were padded.

aganea updated this revision to Diff 187645.Feb 20 2019, 11:55 AM

Sorry for all the updates. Fixed typo in COFF/InputFiles.h.

rnk accepted this revision.Feb 20 2019, 2:21 PM

lgtm

ruiu added inline comments.Feb 20 2019, 2:32 PM
COFF/Chunks.h
218 ↗(On Diff #187645)

LLVM coding standard recommends you omit virtual if override is given.

COFF/InputFiles.cpp
608 ↗(On Diff #187645)

It needs a function comment.

COFF/InputFiles.h
148 ↗(On Diff #187645)

Looks like you can directly use HotPatchable instead of this function, and you can remove this function.

aganea updated this revision to Diff 187683.Feb 20 2019, 2:51 PM
aganea marked 4 inline comments as done.
aganea added inline comments.
COFF/InputFiles.cpp
608 ↗(On Diff #187645)

Let me know if you wish to rephrase this in a different manner.

@ruiu Is this good to go? Any other changes you see?

ruiu added inline comments.Feb 22 2019, 11:12 AM
COFF/Chunks.cpp
609 ↗(On Diff #187683)

too short. -> too short:.

609–621 ↗(On Diff #187683)

Error messages should start with lowercase letter.

612 ↗(On Diff #187683)

. -> :

617–618 ↗(On Diff #187683)

Ditto

619–620 ↗(On Diff #187683)

else } if -> else if

COFF/Driver.cpp
1387–1390 ↗(On Diff #187683)

This place does not seem like a right place to add this code. We parse command line arguments much earlier.

COFF/InputFiles.cpp
602 ↗(On Diff #187683)

nit: omit {}

608 ↗(On Diff #187683)

Can you mention that this function initializes PCHSignature and HotPatchable by reading .debug$S seciton?

COFF/Options.td
35 ↗(On Diff #187683)

Newline before HelpText just like other definitions.

test/COFF/functionpadmin.test
1 ↗(On Diff #187683)

Blank line?

aganea updated this revision to Diff 187961.Feb 22 2019, 12:04 PM
aganea marked 10 inline comments as done.
aganea added inline comments.
COFF/Driver.cpp
1387–1390 ↗(On Diff #187683)

We need Config->Machine to be set before parsing /FUNCTIONPADMIN. In some cases this is extracted from inputs, run() a few lines above.

ruiu accepted this revision.Feb 22 2019, 12:11 PM

LGTM

COFF/Driver.cpp
1387–1390 ↗(On Diff #187683)

Ah, you are right. Thank you for clarifying.

COFF/Options.td
35 ↗(On Diff #187683)

You can use P just like above, can't you?

aganea marked 2 inline comments as done.Feb 22 2019, 12:16 PM
aganea added inline comments.
COFF/Options.td
35 ↗(On Diff #187683)

Yes, and that displays properly the help page, such as /functionpadmin:<value> instead of previous /functionpadmin<value>. Thank you for taking the time to review this!

This revision was not accepted when it landed; it landed in state Needs Review.Feb 22 2019, 5:46 PM
This revision was automatically updated to reflect the committed changes.
aganea marked an inline comment as done.