This is an archive of the discontinued LLVM Phabricator instance.

lld-link: Write an empty "repro" debug directory entry if /Brepro is passed
ClosedPublic

Authored by thakis on Sep 4 2018, 12:45 PM.

Details

Summary

If the coff timestamp is set to a hash, like lld-link does if /Brepro is passed, the coff spec suggests that a IMAGE_DEBUG_TYPE_REPRO entry is in the debug directory. This lets lld-link write such a section. See bug for details.

link.exe creates a zero-length repro debug directory entry with /debug /Brepro/ Without debug, it puts a longer hash in the repro section. This here lets lld-link always write a zero-length repro debug directory entry.

If the repro entry is the only debug directory entry, it's now no longer guaranteed that DebugDirectoryStart + DataEntry->Size is within a section, so also tweak initDebugDirectoryPtr() to look like initBaseRelocPtr(). Also fix llvm-readobj to not error out when dumping the contents of a zero-length debug directory entry. Both these llvm changes are needed to get llvm-readobj working for the new lld test in this patch.

Fixes PR38429.

Diff Detail

Repository
rL LLVM

Event Timeline

thakis created this revision.Sep 4 2018, 12:45 PM
zturner accepted this revision.Sep 4 2018, 1:02 PM
This revision is now accepted and ready to land.Sep 4 2018, 1:02 PM
rnk added inline comments.Sep 4 2018, 1:08 PM
lld/COFF/Writer.cpp
114–115 ↗(On Diff #163886)

format?

llvm/lib/Object/COFFObjectFile.cpp
640–641 ↗(On Diff #163886)

This seems like it loses the error check that the debug directory is in bounds. Is there a better idiom for ensuring that a range is inside the object?

pcc added a subscriber: pcc.Sep 4 2018, 1:17 PM

link.exe creates a zero-length repro debug directory entry with /debug /Brepro

Are you sure? That's not what I observed.

/mnt/c/src/tmp$ cl.exe /c empty.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.15.26726 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

empty.cpp
/mnt/c/src/tmp$ link.exe /Brepro empty.obj /noentry /dll
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/mnt/c/src/tmp$ dumpbin.exe /all empty.dll | grep repro
    30663452 repro         24 00001064      264    9C 15 A8 DA BA 36 1C DE 3B E0 CE 53 07 D4 02 0A E3 D6 16 41 E5 7E A7 34 E9 17 82 13 52 34 66 30
/mnt/c/src/tmp$ link.exe /Brepro empty.obj /noentry /dll /debug
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/mnt/c/src/tmp$ dumpbin.exe /all empty.dll | grep repro
    BFF6ACB7 repro         24 000011A8      3A8    A3 53 C2 0E CD F8 2F 36 62 4C 68 4C 7E CB CB E2 29 8D E0 58 3D 59 59 79 B3 F1 AD 30 B7 AC F6 BF
thakis added a comment.Sep 4 2018, 1:29 PM

pcc: what if you do cl /Zi /Brepro foo.c?

pcc added a comment.Sep 4 2018, 1:46 PM

Same result.

/mnt/c/src/tmp$ cl.exe /Zi /Brepro main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.15.26726 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.c
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
/debug
/Brepro
main.obj
/mnt/c/src/tmp$ dumpbin.exe /all main.exe | grep repro
    CD094136 repro         24 000774DC    75CDC    01 77 A3 B7 4A AF D1 37 EB FA 80 9D CA A0 A0 85 13 D5 7D F8 47 FD 31 11 81 74 B5 6B 36 41 09 CD
/mnt/c/src/tmp$ cat main.c
int main() {}

Also with separate compilation and linking:

/mnt/c/src/tmp$ cl.exe /Zi /Brepro empty.cpp /c
Microsoft (R) C/C++ Optimizing Compiler Version 19.15.26726 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

empty.cpp
/mnt/c/src/tmp$ link.exe /Brepro empty.obj /noentry /dll
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/mnt/c/src/tmp$ dumpbin.exe /all empty.dll | grep repro
    30663452 repro         24 00001064      264    9C 15 A8 DA BA 36 1C DE 3B E0 CE 53 07 D4 02 0A E3 D6 16 41 E5 7E A7 34 E9 17 82 13 52 34 66 30
/mnt/c/src/tmp$ link.exe /Brepro empty.obj /noentry /dll /debug
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/mnt/c/src/tmp$ dumpbin.exe /all empty.dll | grep repro
    C5E2911E repro         24 000011A8      3A8    AD 6F 0D 48 73 0A 9F 93 1D F7 42 04 88 11 EC 84 88 C7 06 59 BE D2 37 5A 55 B9 DE 09 1E 91 E2 C5
/mnt/c/src/tmp$ touch empty.c
/mnt/c/src/tmp$ cl.exe /Zi /Brepro empty.c /c
Microsoft (R) C/C++ Optimizing Compiler Version 19.15.26726 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

empty.c
/mnt/c/src/tmp$ link.exe /Brepro empty.obj /noentry /dll
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/mnt/c/src/tmp$ dumpbin.exe /all empty.dll | grep repro
    1B739ED0 repro         24 00001064      264    B9 0B 63 16 2D E7 5B D7 F6 93 F0 91 BA A6 A6 C6 C2 FF B2 D3 1A D1 A6 AB 2C 2C 47 67 D0 9E 73 1B
/mnt/c/src/tmp$ link.exe /Brepro empty.obj /noentry /dll /debug
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/mnt/c/src/tmp$ dumpbin.exe /all empty.dll | grep repro
    B3F770F9 repro         24 000011A8      3A8    F7 04 47 23 67 3D ED E0 F7 D5 A8 9C 24 8C FD 50 DE BD FD 4C 29 65 B3 5B 81 38 CB 67 F9 70 F7 B3
thakis added a comment.Sep 4 2018, 3:06 PM

MSVC on my laptop has the same behavior as on my desktop:

C:\src\chrome\src>type test.c
int main() {}

C:\src\chrome\src>cl /Brepro test.c /Zi
Microsoft (R) C/C++ Optimizing Compiler Version 19.14.26428.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.c
Microsoft (R) Incremental Linker Version 14.14.26428.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:test.exe
/Brepro
/debug
test.obj

C:\src\chrome\src>dumpbin /headers test.exe | findstr repro
    8441EC31 repro          0 00000000        0
pcc added a comment.Sep 4 2018, 3:20 PM

Interesting, maybe it was a bug in 14.14.26428.1 that was fixed in 14.15.26726.0?

I'll find out once I've installed the 14.14 toolset.

pcc added a comment.Sep 4 2018, 3:32 PM

I can reproduce your result with the 14.14 toolset:

/mnt/c/src/tmp$ cl.exe /Zi /Brepro main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.14.26433.3 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.c
Microsoft (R) Incremental Linker Version 14.14.26433.3
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
/debug
/Brepro
main.obj
/mnt/c/src/tmp$ dumpbin.exe /all main.exe | grep repro
    BADAC48C repro          0 00000000        0

So it looks like it was just a bug that was fixed.

thakis added a comment.EditedSep 4 2018, 3:40 PM

Quoting from the PE spec (see bug for more): "The raw data of this debug entry may be empty". So I don't think it was a bug. Maybe tools had problems with the zero-sized entries, but they didn't bother to update the spec. So I'd suggest to go with this and only add more code if needed (?). We could add a size and just add the 4-byte hash to TimeDateStamps to fill it in at the end, but while it's easy to do it feels a bit cargo-culty.

pcc added a comment.Sep 4 2018, 4:04 PM

I wasn't imagining that we'd write 32 bits of hash to the repro directory but rather the 64-bit xxhash that we currently truncate to 32 bits to get it to fit into the timestamp field. Without that, isn't there a high probability of collisions after 65536 files (which could be small depending on the project)?

pcc added a comment.Sep 4 2018, 4:25 PM

Admittedly though there isn't a regression and as you point out this output is compliant with the COFF spec so I'd be fine with this too.

thakis updated this revision to Diff 164018.Sep 5 2018, 5:28 AM
thakis marked an inline comment as done.
In D51652#1224017, @pcc wrote:

I wasn't imagining that we'd write 32 bits of hash to the repro directory but rather the 64-bit xxhash that we currently truncate to 32 bits to get it to fit into the timestamp field. Without that, isn't there a high probability of collisions after 65536 files (which could be small depending on the project)?

I don't know what tools use the timestamp for. The two main uses I know about are:

  1. I think it's used for dll binding
  1. It's part of the key under which executables get uploaded to symbol servers (the key is executable name, timestamp, size: https://cs.chromium.org/chromium/src/tools/clang/scripts/package.py?type=cs&q=img_fingerprint&sq=package:chromium&g=0&l=119).

Neither of these use the repro header as far as I can tell, and for compatibility reasons it's probably difficult to change them to make them use it. Having a bit of metadata that says "the timestamp isn't actually a time" makes sense, and that's what this patch does. I can see the argument for putting more bytes there, but I don't know of any practical advantages of doing it. Maybe I can put a FIXME about doing that?

(One thing that _is_ weird is that if you do cl /Zi /Brepro foo.cc (where link writes a 0-sized timestamp in the older VS version) link writes both the zero-sized repro entry but also sets the timestamp to the current time.)

llvm/lib/Object/COFFObjectFile.cpp
640–641 ↗(On Diff #163886)

This is a valid point, but the check that was here just checked that the first and the last object were both contained in some section. If the start was in one section and the end in another, the range would still be invalid and pass the check. What we really want here I think is to retrieve the section for the start and then check that it's large enough to also contain the end in the same section. (Same in initBaseRelocPtr.) That seems maybe out of scope for this patch? I added a FIXME.

rnk accepted this revision.Sep 5 2018, 10:32 AM

lgtm

I think @pcc's concerns are addressed, too.

llvm/lib/Object/COFFObjectFile.cpp
640–641 ↗(On Diff #163886)

Sounds good. Designing safe APIs is hard. =/

pcc added a comment.Sep 5 2018, 10:55 AM

It's part of the key under which executables get uploaded to symbol servers (the key is executable name, timestamp, size: https://cs.chromium.org/chromium/src/tools/clang/scripts/package.py?type=cs&q=img_fingerprint&sq=package:chromium&g=0&l=119).

Okay. For some reason I thought that symbol servers would use the repro debug directory, but maybe not.

This revision was automatically updated to reflect the committed changes.